@jskit-ai/assistant 0.1.73 → 0.1.75

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/assistant",
4
- version: "0.1.73",
4
+ version: "0.1.75",
5
5
  kind: "generator",
6
6
  description: "Install assistant runtime/config for one surface and scaffold assistant pages at explicit target files.",
7
7
  options: {
@@ -49,14 +49,7 @@ export default Object.freeze({
49
49
  inputType: "text",
50
50
  defaultValue: "",
51
51
  promptLabel: "Link placement",
52
- promptHint: "Optional target for the generated page link placement (format: host:position)."
53
- },
54
- "link-component-token": {
55
- required: false,
56
- inputType: "text",
57
- defaultValue: "",
58
- promptLabel: "Link component token",
59
- promptHint: "Optional component token override for the generated page link placement."
52
+ promptHint: "Optional semantic target for the generated page link placement (format: area.slot)."
60
53
  },
61
54
  "link-to": {
62
55
  required: false,
@@ -169,7 +162,7 @@ export default Object.freeze({
169
162
  descriptionKey: "page-target-file"
170
163
  }
171
164
  ],
172
- optionNames: ["name", "link-placement", "link-component-token", "link-to", "force"],
165
+ optionNames: ["name", "link-placement", "link-to", "force"],
173
166
  notes: [
174
167
  "The target file decides where the page lives.",
175
168
  "Page-link placement follows the same inference rules as ui-generator page.",
@@ -189,7 +182,7 @@ export default Object.freeze({
189
182
  "npx jskit generate assistant page \\",
190
183
  " admin/ops/copilot/index.vue \\",
191
184
  " --name \"Copilot\" \\",
192
- " --link-placement shell-layout:top-right"
185
+ " --link-placement shell.status"
193
186
  ]
194
187
  }
195
188
  ]
@@ -206,7 +199,7 @@ export default Object.freeze({
206
199
  descriptionKey: "page-target-file"
207
200
  }
208
201
  ],
209
- optionNames: ["surface", "name", "link-placement", "link-component-token", "link-to", "force"],
202
+ optionNames: ["surface", "name", "link-placement", "link-to", "force"],
210
203
  requiredOptionNames: ["surface"],
211
204
  notes: [
212
205
  "The target file decides where the settings page lives.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/assistant",
3
- "version": "0.1.73",
3
+ "version": "0.1.75",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -9,6 +9,6 @@
9
9
  "./server/buildTemplateContext": "./src/server/buildTemplateContext.js"
10
10
  },
11
11
  "dependencies": {
12
- "@jskit-ai/kernel": "0.1.64"
12
+ "@jskit-ai/kernel": "0.1.66"
13
13
  }
14
14
  }
@@ -16,6 +16,14 @@ function resolveLinkToPropLine(linkTo = "") {
16
16
  return ` to: ${JSON.stringify(linkTo)},\n`;
17
17
  }
18
18
 
19
+ function resolveOwnerLine(owner = "") {
20
+ const normalizedOwner = normalizeText(owner);
21
+ if (!normalizedOwner) {
22
+ return "";
23
+ }
24
+ return ` owner: ${JSON.stringify(normalizedOwner)},\n`;
25
+ }
26
+
19
27
  function resolveTemplateFilePath(relativePath = "") {
20
28
  return fileURLToPath(new URL(relativePath, import.meta.url));
21
29
  }
@@ -49,7 +57,6 @@ async function resolveAssistantPageGenerationContext({
49
57
  targetFile,
50
58
  context,
51
59
  placement: options?.["link-placement"],
52
- componentToken: options?.["link-component-token"],
53
60
  linkTo: options?.["link-to"]
54
61
  });
55
62
 
@@ -57,7 +64,7 @@ async function resolveAssistantPageGenerationContext({
57
64
  pageTarget,
58
65
  pageLabel: normalizeText(options?.name) || pageTarget.defaultName,
59
66
  linkPlacementTarget: String(linkTarget.placementTarget?.id || ""),
60
- linkComponentToken: String(linkTarget.componentToken || ""),
67
+ linkOwnerLine: resolveOwnerLine(linkTarget.placementTarget?.owner || ""),
61
68
  linkWorkspaceSuffix: pageTarget.routeUrlSuffix,
62
69
  linkNonWorkspaceSuffix: pageTarget.routeUrlSuffix,
63
70
  linkWhenLine: String(linkTarget.whenLine || ""),
@@ -76,9 +83,10 @@ function renderAssistantPageLinkPlacementBlock({
76
83
  " addPlacement({\n" +
77
84
  ` id: "${String(pageTarget?.placementId || "")}",\n` +
78
85
  ` target: "${String(generationContext?.linkPlacementTarget || "")}",\n` +
86
+ `${String(generationContext?.linkOwnerLine || "")}` +
87
+ " kind: \"link\",\n" +
79
88
  ` surfaces: ["${String(pageTarget?.surfaceId || "")}"],\n` +
80
89
  " order: 155,\n" +
81
- ` componentToken: "${String(generationContext?.linkComponentToken || "")}",\n` +
82
90
  " props: {\n" +
83
91
  ` label: "${String(generationContext?.pageLabel || "")}",\n` +
84
92
  ` surface: "${String(pageTarget?.surfaceId || "")}",\n` +
@@ -30,7 +30,7 @@ async function runGeneratorSubcommand({
30
30
  }
31
31
 
32
32
  const targetFile = requireSinglePositionalTargetFile(args, { context: "assistant page" });
33
- rejectUnexpectedOptions(options, ["name", "link-placement", "link-component-token", "link-to", "force"], {
33
+ rejectUnexpectedOptions(options, ["name", "link-placement", "link-to", "force"], {
34
34
  context: "assistant page"
35
35
  });
36
36
  const forceOverwrite = Object.prototype.hasOwnProperty.call(options, "force")
@@ -31,7 +31,7 @@ async function runGeneratorSubcommand({
31
31
  }
32
32
 
33
33
  const targetFile = requireSinglePositionalTargetFile(args, { context: "assistant settings-page" });
34
- rejectUnexpectedOptions(options, ["surface", "name", "link-placement", "link-component-token", "link-to", "force"], {
34
+ rejectUnexpectedOptions(options, ["surface", "name", "link-placement", "link-to", "force"], {
35
35
  context: "assistant settings-page"
36
36
  });
37
37
  const forceOverwrite = Object.prototype.hasOwnProperty.call(options, "force")
@@ -25,6 +25,61 @@ function toPagePath(targetFile = "") {
25
25
  return path.join("src/pages", targetFile);
26
26
  }
27
27
 
28
+ function renderTopologyVariant(outlet, { linkRenderer = "" } = {}) {
29
+ const rendererLines = linkRenderer
30
+ ? `,
31
+ renderers: {
32
+ link: "${linkRenderer}"
33
+ }`
34
+ : "";
35
+ return `{
36
+ outlet: "${outlet}"${rendererLines}
37
+ }`;
38
+ }
39
+
40
+ function renderTopologyEntry({
41
+ id = "",
42
+ owner = "",
43
+ surfaces = ["*"],
44
+ defaultPlacement = false,
45
+ outlet = "",
46
+ linkRenderer = ""
47
+ } = {}) {
48
+ const ownerLine = owner ? ` owner: "${owner}",\n` : "";
49
+ const defaultLine = defaultPlacement ? " default: true,\n" : "";
50
+ return ` {
51
+ id: "${id}",
52
+ ${ownerLine} surfaces: ${JSON.stringify(surfaces)},
53
+ ${defaultLine} variants: {
54
+ compact: ${renderTopologyVariant(outlet, { linkRenderer })},
55
+ medium: ${renderTopologyVariant(outlet, { linkRenderer })},
56
+ expanded: ${renderTopologyVariant(outlet, { linkRenderer })}
57
+ }
58
+ }`;
59
+ }
60
+
61
+ async function writePlacementTopology(appRoot, entries = []) {
62
+ const defaultEntries = [
63
+ renderTopologyEntry({
64
+ id: "shell.primary-nav",
65
+ surfaces: ["*"],
66
+ defaultPlacement: true,
67
+ outlet: "shell-layout:primary-menu",
68
+ linkRenderer: "local.main.ui.surface-aware-menu-link-item"
69
+ })
70
+ ];
71
+ await writeFileInApp(
72
+ appRoot,
73
+ "src/placementTopology.js",
74
+ `export default {
75
+ placements: [
76
+ ${[...defaultEntries, ...entries].join(",\n")}
77
+ ]
78
+ };
79
+ `
80
+ );
81
+ }
82
+
28
83
  async function writeAppFixture(appRoot, { configSource = "" } = {}) {
29
84
  await writeFileInApp(
30
85
  appRoot,
@@ -73,6 +128,7 @@ export default function getPlacements() {
73
128
  }
74
129
  `
75
130
  );
131
+ await writePlacementTopology(appRoot);
76
132
  }
77
133
 
78
134
  test("assistant page subcommand creates a runtime page at an explicit target file", async () => {
@@ -96,7 +152,9 @@ test("assistant page subcommand creates a runtime page at an explicit target fil
96
152
  const placementSource = await readFile(path.join(appRoot, "src/placement.js"), "utf8");
97
153
  assert.match(placementSource, /jskit:assistant\.page\.link:admin:\/ops\/copilot/);
98
154
  assert.match(placementSource, /id: "ui-generator\.page\.admin\.ops\.copilot\.link"/);
99
- assert.match(placementSource, /target: "shell-layout:primary-menu"/);
155
+ assert.match(placementSource, /target: "shell\.primary-nav"/);
156
+ assert.match(placementSource, /kind: "link"/);
157
+ assert.doesNotMatch(placementSource, /componentToken:/);
100
158
  assert.match(placementSource, /label: "Copilot"/);
101
159
  assert.match(placementSource, /scopedSuffix: "\/ops\/copilot"/);
102
160
  });
@@ -105,6 +163,15 @@ test("assistant page subcommand creates a runtime page at an explicit target fil
105
163
  test("assistant settings-page subcommand uses the target assistant surface and infers parent subpage placement", async () => {
106
164
  await withTempApp(async (appRoot) => {
107
165
  await writeAppFixture(appRoot);
166
+ await writePlacementTopology(appRoot, [
167
+ renderTopologyEntry({
168
+ id: "page.section-nav",
169
+ owner: "admin-settings",
170
+ surfaces: ["admin"],
171
+ outlet: "admin-settings:sub-pages",
172
+ linkRenderer: "local.main.ui.surface-aware-menu-link-item"
173
+ })
174
+ ]);
108
175
  await writeFileInApp(
109
176
  appRoot,
110
177
  "src/pages/w/[workspaceSlug]/admin/settings/index.vue",
@@ -137,8 +204,9 @@ test("assistant settings-page subcommand uses the target assistant surface and i
137
204
 
138
205
  const placementSource = await readFile(path.join(appRoot, "src/placement.js"), "utf8");
139
206
  assert.match(placementSource, /jskit:assistant\.settings-page\.link:admin:\/settings\/assistant:console/);
140
- assert.match(placementSource, /target: "admin-settings:sub-pages"/);
141
- assert.match(placementSource, /componentToken: "local\.main\.ui\.surface-aware-menu-link-item"/);
207
+ assert.match(placementSource, /target: "page\.section-nav"/);
208
+ assert.match(placementSource, /owner: "admin-settings"/);
209
+ assert.doesNotMatch(placementSource, /componentToken: "local\.main\.ui\.surface-aware-menu-link-item"/);
142
210
  assert.match(placementSource, /to: "\.\/assistant"/);
143
211
  assert.match(placementSource, /label: "Assistant"/);
144
212
  });
@@ -241,6 +309,15 @@ test("assistant page subcommand overwrites an existing page when --force is pass
241
309
  test("assistant settings-page subcommand overwrites an existing page when --force is passed", async () => {
242
310
  await withTempApp(async (appRoot) => {
243
311
  await writeAppFixture(appRoot);
312
+ await writePlacementTopology(appRoot, [
313
+ renderTopologyEntry({
314
+ id: "page.section-nav",
315
+ owner: "admin-settings",
316
+ surfaces: ["admin"],
317
+ outlet: "admin-settings:sub-pages",
318
+ linkRenderer: "local.main.ui.surface-aware-menu-link-item"
319
+ })
320
+ ]);
244
321
  await writeFileInApp(
245
322
  appRoot,
246
323
  "src/pages/w/[workspaceSlug]/admin/settings/index.vue",