@jskit-ai/ui-generator 0.1.16 → 0.1.17

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.
@@ -38,8 +38,12 @@ async function writeAppFixture(appRoot, { configSource = "" } = {}) {
38
38
  path.join(appRoot, "src", "components", "ShellLayout.vue"),
39
39
  `<template>
40
40
  <div>
41
- <ShellOutlet host="shell-layout" position="primary-menu" default />
42
- <ShellOutlet host="shell-layout" position="top-right" />
41
+ <ShellOutlet
42
+ target="shell-layout:primary-menu"
43
+ default
44
+ default-link-component-token="local.main.ui.surface-aware-menu-link-item"
45
+ />
46
+ <ShellOutlet target="shell-layout:top-right" />
43
47
  </div>
44
48
  </template>
45
49
  `,
@@ -109,6 +113,36 @@ test("ui-generator page subcommand creates a file route and derives label from t
109
113
  });
110
114
  });
111
115
 
116
+ test("ui-generator page subcommand omits the auth guard for public surface links", async () => {
117
+ await withTempApp(async (appRoot) => {
118
+ await writeAppFixture(appRoot, {
119
+ configSource: `export const config = {
120
+ surfaceAccessPolicies: {
121
+ public: {}
122
+ },
123
+ surfaceDefinitions: {
124
+ home: { id: "home", pagesRoot: "home", enabled: true, accessPolicyId: "public" }
125
+ }
126
+ };
127
+ `
128
+ });
129
+
130
+ const targetFile = "home/reports/index.vue";
131
+ await runGeneratorSubcommand({
132
+ appRoot,
133
+ subcommand: "page",
134
+ args: [targetFile],
135
+ options: {
136
+ name: "Reports"
137
+ }
138
+ });
139
+
140
+ const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
141
+ assert.match(placementSource, /id: "ui-generator\.page\.home\.reports\.link"/);
142
+ assert.doesNotMatch(placementSource, /when: \(\{ auth \}\) => Boolean\(auth\?\.authenticated\)/);
143
+ });
144
+ });
145
+
112
146
  test("ui-generator page subcommand supports link placement options", async () => {
113
147
  await withTempApp(async (appRoot) => {
114
148
  await writeAppFixture(appRoot);
@@ -126,8 +160,7 @@ test("ui-generator page subcommand supports link placement options", async () =>
126
160
  });
127
161
 
128
162
  const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
129
- assert.match(placementSource, /host: "shell-layout"/);
130
- assert.match(placementSource, /position: "top-right"/);
163
+ assert.match(placementSource, /target: "shell-layout:top-right"/);
131
164
  assert.match(placementSource, /componentToken: "local\.main\.ui\.tab-link-item"/);
132
165
  assert.match(placementSource, /to: "\.\/notes"/);
133
166
  });
@@ -144,7 +177,7 @@ test("ui-generator page subcommand infers subpage link placement, tab token, and
144
177
  `<template>
145
178
  <SectionContainerShell>
146
179
  <template #tabs>
147
- <ShellOutlet host="contact-view" position="sub-pages" />
180
+ <ShellOutlet target="contact-view:sub-pages" />
148
181
  </template>
149
182
  <RouterView />
150
183
  </SectionContainerShell>
@@ -162,8 +195,7 @@ test("ui-generator page subcommand infers subpage link placement, tab token, and
162
195
  });
163
196
 
164
197
  const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
165
- assert.match(placementSource, /host: "contact-view"/);
166
- assert.match(placementSource, /position: "sub-pages"/);
198
+ assert.match(placementSource, /target: "contact-view:sub-pages"/);
167
199
  assert.match(placementSource, /componentToken: "local\.main\.ui\.tab-link-item"/);
168
200
  assert.match(placementSource, /to: "\.\/notes"/);
169
201
  });
@@ -181,7 +213,7 @@ test("ui-generator page subcommand prefers the nearest index-route parent host",
181
213
  `<template>
182
214
  <SectionContainerShell>
183
215
  <template #tabs>
184
- <ShellOutlet host="catalog" position="sub-pages" />
216
+ <ShellOutlet target="catalog:sub-pages" />
185
217
  </template>
186
218
  <RouterView />
187
219
  </SectionContainerShell>
@@ -194,7 +226,7 @@ test("ui-generator page subcommand prefers the nearest index-route parent host",
194
226
  `<template>
195
227
  <SectionContainerShell>
196
228
  <template #tabs>
197
- <ShellOutlet host="catalog-products" position="sub-pages" />
229
+ <ShellOutlet target="catalog-products:sub-pages" />
198
230
  </template>
199
231
  <RouterView />
200
232
  </SectionContainerShell>
@@ -213,8 +245,7 @@ test("ui-generator page subcommand prefers the nearest index-route parent host",
213
245
  });
214
246
 
215
247
  const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
216
- assert.match(placementSource, /host: "catalog-products"/);
217
- assert.match(placementSource, /position: "sub-pages"/);
248
+ assert.match(placementSource, /target: "catalog-products:sub-pages"/);
218
249
  assert.match(placementSource, /componentToken: "local\.main\.ui\.tab-link-item"/);
219
250
  assert.match(placementSource, /to: "\.\/variants"/);
220
251
  });
@@ -266,7 +297,28 @@ test("ui-generator page subcommand rejects unsupported options", async () => {
266
297
  });
267
298
  });
268
299
 
269
- test("ui-generator page subcommand rejects target files with a src/pages prefix", async () => {
300
+ test("ui-generator page subcommand accepts target files with a src/pages prefix", async () => {
301
+ await withTempApp(async (appRoot) => {
302
+ await writeAppFixture(appRoot);
303
+
304
+ const targetFile = "src/pages/w/[workspaceSlug]/admin/practice/index.vue";
305
+ const result = await runGeneratorSubcommand({
306
+ appRoot,
307
+ subcommand: "page",
308
+ args: [targetFile],
309
+ options: {}
310
+ });
311
+
312
+ assert.deepEqual(result.touchedFiles, [targetFile, "src/placement.js"]);
313
+ const pageSource = await readFile(path.join(appRoot, targetFile), "utf8");
314
+ assert.match(pageSource, /Practice/);
315
+ const placementSource = await readFile(path.join(appRoot, "src", "placement.js"), "utf8");
316
+ assert.match(placementSource, /id: "ui-generator\.page\.admin\.practice\.link"/);
317
+ assert.match(placementSource, /workspaceSuffix: "\/practice"/);
318
+ });
319
+ });
320
+
321
+ test("ui-generator page subcommand still rejects non-page src targets", async () => {
270
322
  await withTempApp(async (appRoot) => {
271
323
  await writeAppFixture(appRoot);
272
324
 
@@ -274,10 +326,10 @@ test("ui-generator page subcommand rejects target files with a src/pages prefix"
274
326
  runGeneratorSubcommand({
275
327
  appRoot,
276
328
  subcommand: "page",
277
- args: ["src/pages/w/[workspaceSlug]/admin/practice/index.vue"],
329
+ args: ["src/components/PracticePanel.vue"],
278
330
  options: {}
279
331
  }),
280
- /must be relative to src\/pages\/, without the src\/pages\/ prefix/
332
+ /must be relative to src\/pages\/ or start with src\/pages\/:/
281
333
  );
282
334
  });
283
335
  });
package/README.md DELETED
@@ -1,214 +0,0 @@
1
- # @jskit-ai/ui-generator
2
-
3
- Generate app-local UI pages, page links, placed elements, and routed subpage hosts for JSKIT apps.
4
-
5
- ## Quick Start
6
-
7
- List available placement targets in the current app:
8
-
9
- ```bash
10
- npx jskit list placements
11
- ```
12
-
13
- Create a normal page at an explicit file:
14
-
15
- ```bash
16
- npx jskit generate @jskit-ai/ui-generator page \
17
- admin/reports-dashboard/index.vue \
18
- --name "Reports Dashboard"
19
- ```
20
-
21
- Create a file-route page:
22
-
23
- ```bash
24
- npx jskit generate @jskit-ai/ui-generator page \
25
- admin/contacts/[contactId].vue \
26
- --name "Contact"
27
- ```
28
-
29
- Upgrade an existing page into a routed subpage host:
30
-
31
- ```bash
32
- npx jskit generate @jskit-ai/ui-generator add-subpages \
33
- admin/contacts/[contactId].vue \
34
- --title "Contact" \
35
- --subtitle "Manage contact modules."
36
- ```
37
-
38
- Create a reusable placed element:
39
-
40
- ```bash
41
- npx jskit generate @jskit-ai/ui-generator placed-element \
42
- --name "Alerts Widget" \
43
- --surface admin
44
- ```
45
-
46
- Inject a plain generic outlet into an existing Vue file:
47
-
48
- ```bash
49
- npx jskit generate @jskit-ai/ui-generator outlet \
50
- src/components/ContactSummaryCard.vue \
51
- --target contact-view:summary-actions
52
- ```
53
-
54
- ## Commands
55
-
56
- - `page <target-file>`: create a route page at that exact file relative to `src/pages/` and add a link placement entry for it.
57
- - `add-subpages <target-file>`: upgrade an existing page relative to `src/pages/` into the standard `SectionContainerShell + ShellOutlet + RouterView` host shape.
58
- - `placed-element`: create a reusable component and register a placement for it.
59
- - `outlet <target-file>`: inject a plain `ShellOutlet` into an existing Vue SFC.
60
-
61
- For `placed-element`, the default placement target is `shell-layout:top-right`.
62
- Use `--placement host:position` to override it.
63
-
64
- ## The Mental Model
65
-
66
- `page` and `add-subpages` operate on explicit page files relative to `src/pages/`. `outlet` still targets an explicit Vue file path relative to the app root.
67
-
68
- That means:
69
-
70
- - `catalog/index.vue` is obviously an index-route page
71
- - `catalog.vue` is obviously a file-route page
72
- - there is no extra route-shape flag to remember
73
- - the owning surface is derived from where the file lives
74
-
75
- This is the reference model for JSKIT page-producing generators.
76
-
77
- - `@jskit-ai/ui-generator page <target-file>` works from an explicit page file relative to `src/pages/`.
78
- - `@jskit-ai/crud-ui-generator crud <target-root>` works from an explicit route root relative to `src/pages/`.
79
- - `@jskit-ai/assistant page <target-file>` and `settings-page <target-file>` work from explicit page files relative to `src/pages/`.
80
-
81
- ## Page Links
82
-
83
- `page` creates a page file and appends a link placement block for it.
84
-
85
- These options control that generated page link:
86
-
87
- - `--link-placement`: where the page link renders
88
- - `--link-component-token`: how the page link is rendered
89
- - `--link-to`: explicit `props.to` override for the page link
90
-
91
- This is intentionally separate from `placed-element`, which still uses `--placement` because it places arbitrary UI, not a page link.
92
-
93
- ## Routed Subpages
94
-
95
- `add-subpages` is the only routed-subpages command.
96
-
97
- It upgrades an existing page so the page itself owns:
98
-
99
- - `SectionContainerShell`
100
- - `ShellOutlet host="..." position="sub-pages"`
101
- - `RouterView`
102
-
103
- `--target` controls that outlet target:
104
-
105
- - if omitted, the target is derived from the page path
106
- - `--target contact-view` means `contact-view:sub-pages`
107
- - `--target contact-view:secondary-tabs` uses an explicit custom position
108
-
109
- Derived target examples:
110
-
111
- - `src/pages/admin/catalog/index.vue` -> `catalog:sub-pages`
112
- - `src/pages/admin/catalog.vue` -> `catalog:sub-pages`
113
- - `src/pages/admin/contacts/[contactId].vue` -> `contacts-contact-id:sub-pages`
114
- - `src/pages/admin/catalog/products/index.vue` -> `catalog-products:sub-pages`
115
-
116
- If the page already contains a `RouterView`, `add-subpages` fails instead of trying to update an existing routed host.
117
-
118
- It also ensures the shared support scaffold exists:
119
-
120
- - `src/components/SectionContainerShell.vue`
121
- - `src/components/TabLinkItem.vue`
122
- - `packages/main/src/client/providers/MainClientProvider.js` registration for `local.main.ui.tab-link-item`
123
-
124
- ## Child Route Placement Rule
125
-
126
- Child routes attach differently depending on the parent page file shape.
127
-
128
- If the parent is a file route:
129
-
130
- - parent: `src/pages/admin/catalog.vue`
131
- - child pages go under: `src/pages/admin/catalog/...`
132
- - example child page: `src/pages/admin/catalog/products/index.vue`
133
-
134
- If the parent is an index route:
135
-
136
- - parent: `src/pages/admin/catalog/index.vue`
137
- - child pages go under: `src/pages/admin/catalog/index/...`
138
- - example child page: `src/pages/admin/catalog/index/products/index.vue`
139
-
140
- That `index/...` folder shape is the native file-based routing rule for nesting children under an `index.vue` page.
141
-
142
- ## Example: File Route Parent
143
-
144
- Create the parent page:
145
-
146
- ```bash
147
- npx jskit generate @jskit-ai/ui-generator page \
148
- admin/contacts/[contactId].vue \
149
- --name "Contact"
150
- ```
151
-
152
- Upgrade it to host subpages:
153
-
154
- ```bash
155
- npx jskit generate @jskit-ai/ui-generator add-subpages \
156
- admin/contacts/[contactId].vue \
157
- --title "Contact" \
158
- --subtitle "Manage contact modules."
159
- ```
160
-
161
- Generate a child page link inside that host:
162
-
163
- ```bash
164
- npx jskit generate @jskit-ai/ui-generator page \
165
- admin/contacts/[contactId]/notes/index.vue \
166
- --name "Notes"
167
- ```
168
-
169
- ## Example: Index Route Parent
170
-
171
- Create the parent page:
172
-
173
- ```bash
174
- npx jskit generate @jskit-ai/ui-generator page \
175
- admin/catalog/index.vue \
176
- --name "Catalog"
177
- ```
178
-
179
- Upgrade it to host subpages:
180
-
181
- ```bash
182
- npx jskit generate @jskit-ai/ui-generator add-subpages \
183
- admin/catalog/index.vue \
184
- --title "Catalog"
185
- ```
186
-
187
- Because the parent page is `index.vue`, nested child pages live under the matching `index/` folder.
188
-
189
- Generate a child page link inside that host:
190
-
191
- ```bash
192
- npx jskit generate @jskit-ai/ui-generator page \
193
- admin/catalog/index/products/index.vue \
194
- --name "Products"
195
- ```
196
-
197
- When `page` finds the nearest parent page upgraded with `add-subpages`, it reuses that parent’s real outlet target, defaults the link renderer to `local.main.ui.tab-link-item`, and derives `to` from the child route automatically.
198
-
199
- ## Generic Outlet Injection
200
-
201
- `outlet` is intentionally small.
202
-
203
- It only adds:
204
-
205
- - `import ShellOutlet from "@jskit-ai/shell-web/client/components/ShellOutlet";`
206
- - `<ShellOutlet host="..." position="..." />`
207
-
208
- It does not:
209
-
210
- - add `RouterView`
211
- - add `SectionContainerShell`
212
- - add routed subpage scaffolding
213
-
214
- Use `add-subpages` when the goal is routed child pages inside a page.