@jskit-ai/agent-docs 0.1.26 → 0.1.28

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 (46) hide show
  1. package/DISTR_AGENT.md +1 -0
  2. package/guide/agent/app-extras/assistant.md +9 -20
  3. package/guide/agent/app-extras/mobile-capacitor.md +381 -0
  4. package/guide/agent/app-extras/realtime.md +13 -12
  5. package/guide/agent/app-setup/a-more-interesting-shell.md +102 -87
  6. package/guide/agent/app-setup/authentication.md +50 -31
  7. package/guide/agent/app-setup/console.md +8 -8
  8. package/guide/agent/app-setup/database-layer.md +15 -15
  9. package/guide/agent/app-setup/initial-scaffolding.md +37 -5
  10. package/guide/agent/app-setup/multi-homing.md +50 -47
  11. package/guide/agent/app-setup/quickstart.md +11 -14
  12. package/guide/agent/app-setup/users.md +24 -24
  13. package/guide/agent/app-setup/working-with-the-jskit-cli.md +18 -10
  14. package/guide/agent/generators/advanced-cruds.md +111 -68
  15. package/guide/agent/generators/crud-generators.md +5 -5
  16. package/guide/agent/generators/ui-generators.md +44 -36
  17. package/guide/agent/index.md +3 -2
  18. package/package.json +1 -1
  19. package/patterns/INDEX.md +4 -1
  20. package/patterns/client-requests.md +23 -6
  21. package/patterns/crud-repository-mapping.md +1 -1
  22. package/patterns/crud-scaffolding.md +10 -1
  23. package/patterns/filters.md +28 -10
  24. package/patterns/generated-ui-contract-tracking.md +62 -0
  25. package/patterns/page-scaffolding.md +20 -2
  26. package/patterns/placements.md +67 -1
  27. package/patterns/surfaces.md +5 -1
  28. package/patterns/ui-testing.md +3 -0
  29. package/reference/autogen/KERNEL_MAP.md +46 -1
  30. package/reference/autogen/README.md +3 -0
  31. package/reference/autogen/packages/assistant.md +2 -1
  32. package/reference/autogen/packages/auth-web.md +20 -1
  33. package/reference/autogen/packages/crud-ui-generator.md +20 -8
  34. package/reference/autogen/packages/google-rewarded-core.md +222 -0
  35. package/reference/autogen/packages/google-rewarded-web.md +71 -0
  36. package/reference/autogen/packages/kernel.md +94 -10
  37. package/reference/autogen/packages/mobile-capacitor.md +76 -0
  38. package/reference/autogen/packages/realtime.md +1 -0
  39. package/reference/autogen/packages/shell-web.md +48 -3
  40. package/reference/autogen/packages/ui-generator.md +34 -2
  41. package/reference/autogen/packages/users-core.md +2 -0
  42. package/reference/autogen/packages/users-web.md +92 -4
  43. package/reference/autogen/packages/workspaces-web.md +3 -2
  44. package/reference/autogen/tooling/jskit-cli.md +114 -9
  45. package/templates/app/AGENTS.md +1 -0
  46. package/workflow/bootstrap.md +1 -0
@@ -249,6 +249,8 @@ packages/contacts/
249
249
 
250
250
  src/pages/w/[workspaceSlug]/admin/contacts/
251
251
  index.vue
252
+ listBulkActions.js
253
+ listFilters.js
252
254
  new.vue
253
255
  [contactId]/index.vue
254
256
  [contactId]/edit.vue
@@ -439,6 +441,8 @@ src/pages/w/[workspaceSlug]/admin/contacts/
439
441
  new.vue
440
442
  [contactId]/index.vue
441
443
  [contactId]/edit.vue
444
+ listBulkActions.js
445
+ listFilters.js
442
446
  _components/CrudAddEditForm.vue
443
447
  _components/CrudAddEditFormFields.js
444
448
  ```
@@ -449,13 +453,13 @@ This is the list-page container.
449
453
 
450
454
  Its job is usually to:
451
455
 
452
- - call `useCrudList()`
453
- - bind `records.searchQuery`
454
- - render list rows
456
+ - call `useCrudListScreen()`
457
+ - pass page-local `listFilters` and `listBulkActions`
458
+ - render the shared `CrudListScreen`
455
459
  - resolve list/view/edit/new URLs
456
460
  - pass route query state through when navigating deeper
457
461
 
458
- The actual list machinery lives uphill in `users-web` composables and the shared resource contract.
462
+ The actual list machinery lives in `users-web` shared screen composables and the shared resource contract.
459
463
 
460
464
  ### `[contactId]/index.vue`
461
465
 
@@ -463,8 +467,8 @@ This is the view-page container.
463
467
 
464
468
  Its job is usually to:
465
469
 
466
- - call `useCrudView()`
467
- - render the selected record
470
+ - call `useCrudViewScreen()`
471
+ - render the shared `CrudViewScreen`
468
472
  - resolve "back" and "edit" navigation
469
473
 
470
474
  Again, the runtime behavior is mostly uphill. The page is a route-level composition layer.
@@ -475,9 +479,9 @@ These are add/edit route wrappers.
475
479
 
476
480
  They usually:
477
481
 
478
- - call `useCrudAddEdit()`
482
+ - call `useCrudAddEditScreen()`
479
483
  - wire lookup runtime for lookup-backed fields
480
- - hand the form runtime into the shared form component
484
+ - hand the form runtime into the shared `CrudAddEditScreen`
481
485
 
482
486
  These files are mostly containers. That is deliberate.
483
487
 
@@ -735,15 +739,14 @@ The safe mental model is:
735
739
 
736
740
  ### `_components/CrudAddEditForm.vue`
737
741
 
738
- This is the shared rendering shell for the add/edit form.
742
+ This is the generated field bridge for the shared add/edit screen.
739
743
 
740
744
  It owns:
741
745
 
742
- - the card/surface layout
743
- - save/cancel buttons
744
746
  - which set of generated form fields is rendered in `new` vs `edit`
747
+ - lookup field prop forwarding into those fields
745
748
 
746
- It does **not** own persistence logic.
749
+ It does **not** own persistence logic or the shared screen chrome. `CrudAddEditScreen` from `users-web` owns the common title, load state, retry action, save/cancel action row, and form surface.
747
750
 
748
751
  ### `_components/CrudAddEditFormFields.js`
749
752
 
@@ -766,8 +769,8 @@ That is navigation wiring, not CRUD logic.
766
769
 
767
770
  A generated CRUD works because several layers cooperate:
768
771
 
769
- 1. the route page calls `useCrudList()`, `useCrudView()`, or `useCrudAddEdit()`
770
- 2. those composables use the shared resource contract and runtime helpers from `users-web`
772
+ 1. the route page calls `useCrudListScreen()`, `useCrudViewScreen()`, or `useCrudAddEditScreen()`
773
+ 2. those screen composables configure the lower-level list/view/add-edit runtimes from `users-web`
771
774
  3. the request hits the HTTP route from `registerRoutes.js`
772
775
  4. the route executes an action from `actions.js`
773
776
  5. the action delegates to the service in `service.js`
@@ -789,8 +792,9 @@ Use this rule of thumb when deciding where to edit:
789
792
  | Change default ordering, searchable fields, or ownership autofilter | `contactResource.js` | The shared resource is the single source of truth for both CRUD contract and internal JSON:API resource config |
790
793
  | Change SQL, joins, parent filters, or advanced search | `repository.js` | This is the data-access layer |
791
794
  | Add cross-record or domain rules on save/delete | `service.js` | This is business logic |
792
- | Change page layout and display behavior | the route pages and app-owned composables | This is presentation |
793
- | Change form field layout and inputs | `_components/CrudAddEditForm.vue` and `CrudAddEditFormFields.js` | This is the generated form layer |
795
+ | Change shared CRUD screen chrome, load states, or retry behavior | `users-web` shared screen components | Generated pages consume the shared screen contract |
796
+ | Change page-specific display behavior | the route pages, generated slots, and app-owned composables | This is presentation |
797
+ | Change form field layout and inputs | `_components/CrudAddEditForm.vue` and `CrudAddEditFormFields.js` | This is the generated form field layer |
794
798
 
795
799
  ## How mature CRUDs grow
796
800
 
@@ -824,10 +828,10 @@ This is the default generated list-page pattern for the `contacts` resource from
824
828
 
825
829
  #### Client side
826
830
 
827
- Enable query search in the list page:
831
+ The generated list page delegates search wiring to the shared list screen:
828
832
 
829
833
  ```js
830
- const records = useCrudList({
834
+ const screen = useCrudListScreen({
831
835
  resource: uiResource,
832
836
  apiSuffix: "/contacts",
833
837
  search: {
@@ -841,15 +845,14 @@ const records = useCrudList({
841
845
  });
842
846
  ```
843
847
 
844
- Then bind the input:
848
+ Then render the shared screen:
845
849
 
846
850
  ```vue
847
- <v-text-field
848
- v-model="records.searchQuery"
849
- :loading="records.isSearchDebouncing"
850
- />
851
+ <CrudListScreen :screen="screen" />
851
852
  ```
852
853
 
854
+ The shared screen binds the search input and passes the search query into the list request.
855
+
853
856
  The client runtime debounces the search input, writes the query string to `q`, and trims the list back to the first page when search changes.
854
857
 
855
858
  #### Server side
@@ -1006,27 +1009,33 @@ Important limits:
1006
1009
 
1007
1010
  For the agent-facing quick rule, see `patterns/crud-repository-mapping.md`.
1008
1011
 
1009
- ### Pattern 3: structured list filters from one shared definition
1012
+ ### Pattern 3: client-side structured list filters
1013
+
1014
+ Generated CRUD list pages include a client-side filter seam by default. The generated page imports `./listFilters.js` and passes `listFilters` into `useCrudListScreen(...)`. The shared list screen builds the filter runtime, passes the resulting query params into the list request, and renders `CrudListFilterSurface`.
1015
+
1016
+ If `listFilters.js` is empty, the filter surface renders nothing.
1010
1017
 
1011
- This is the default pattern once a CRUD list needs real filters.
1018
+ Use this first when adding filters. The server still needs explicit support for any query params you declare; JSKIT does not infer server filter semantics from the UI.
1012
1019
 
1013
1020
  Do not hand-build:
1014
1021
 
1015
1022
  - one filter shape in the page
1016
- - a second filter shape in `listQueryValidators.js`
1017
- - and a third filter shape in `repository.js`
1023
+ - custom chip/reset/query-param state
1024
+ - a second copy of the same client filter definitions
1018
1025
 
1019
1026
  Instead:
1020
1027
 
1021
- 1. define the filters once in a shared CRUD-package module
1022
- 2. build the server runtime from that definition with `createCrudListFilters(...)`
1023
- 3. choose the route/action invalid-value contract explicitly on each structured filter field with `createCrudListFilterQueryField(...)`
1024
- 4. build the client runtime from that same definition with `useCrudListFilters(...)`
1028
+ 1. edit the generated page-local `listFilters.js`
1029
+ 2. let `useCrudListScreen(...)` wire the filter query params into the list request
1030
+ 3. let `CrudListFilterSurface` render controls, chips, clear-one, and clear-all behavior
1031
+ 4. add explicit server support separately for the same query params
1025
1032
 
1026
- For example, a shared filter-definition module can look like this:
1033
+ For example, a generated page-local filter-definition module can look like this:
1027
1034
 
1028
1035
  ```js
1029
- export const CONTACTS_LIST_FILTER_DEFINITIONS = Object.freeze({
1036
+ import { defineCrudListFilters } from "@jskit-ai/users-web/client/filters";
1037
+
1038
+ const listFilters = defineCrudListFilters({
1030
1039
  onlyStaff: {
1031
1040
  type: "flag",
1032
1041
  label: "Staff"
@@ -1040,60 +1049,94 @@ export const CONTACTS_LIST_FILTER_DEFINITIONS = Object.freeze({
1040
1049
  label: "Archived"
1041
1050
  }
1042
1051
  });
1052
+
1053
+ export { listFilters };
1054
+ ```
1055
+
1056
+ ### Pattern 3B: client-side bulk list actions
1057
+
1058
+ Generated CRUD list pages also include a client-side bulk-action seam by default. The generated page imports `./listBulkActions.js` and passes `listBulkActions` into `useCrudListScreen(...)`. The shared list screen builds the bulk-action runtime and renders `CrudListBulkActionSurface`.
1059
+
1060
+ If `listBulkActions.js` is empty, selection controls and the bulk action bar stay hidden.
1061
+
1062
+ Use this first when adding selected-record actions. JSKIT does not invent server operations such as delete, archive, approve, or export; the action definition owns that behavior.
1063
+
1064
+ For example:
1065
+
1066
+ ```js
1067
+ import { defineCrudListBulkActions } from "@jskit-ai/users-web/client/bulkActions";
1068
+
1069
+ const listBulkActions = defineCrudListBulkActions([
1070
+ {
1071
+ key: "archive",
1072
+ label: "Archive",
1073
+ async run({ selectedIds, clearSelection, reload }) {
1074
+ await archiveContacts(selectedIds);
1075
+ clearSelection();
1076
+ await reload();
1077
+ }
1078
+ }
1079
+ ]);
1080
+
1081
+ export { listBulkActions };
1043
1082
  ```
1044
1083
 
1084
+ The generated runtime passes action handlers:
1085
+
1086
+ - `selectedIds`
1087
+ - `ids` as an alias for `selectedIds`
1088
+ - `selectedRecords`
1089
+ - `clearSelection()`
1090
+ - `records`
1091
+ - `reload`
1092
+
1093
+ Keep bulk action definitions page-local unless another page needs to share them.
1094
+
1045
1095
  #### Exact file checklist
1046
1096
 
1047
1097
  For a generated CRUD, treat this as the concrete file plan:
1048
1098
 
1099
+ - edit `src/pages/.../contacts/listFilters.js`
1100
+ - make sure the matching server route/action/repository code already accepts and applies the declared query params
1101
+
1102
+ If the filter contract should be shared with server code, promote it into a CRUD package module and import it from both sides:
1103
+
1049
1104
  - create `packages/contacts/src/shared/contactListFilters.js`
1050
1105
  - update `packages/contacts/src/server/registerRoutes.js` so the list route query validator lists structured filter params explicitly with `createCrudListFilterQueryField(...)`, unless you already extracted list-query composition into `packages/contacts/src/server/listQueryValidators.js`
1051
1106
  - update `packages/contacts/src/server/actions.js` so the list action input validator includes that same explicit contract choice
1052
1107
  - update `packages/contacts/src/server/repository.js` so the list query path builds the `createCrudListFilters(...)` runtime and calls `applyQuery(...)`
1053
- - update the app-owned list page or list-runtime composable, usually under `src/pages/.../contacts/` or `src/composables/...`, so it builds `useCrudListFilters(...)`, passes `queryParams` into `useCrudList(...)`, and renders chips / reset behavior from that runtime
1054
-
1055
- The only file in that list that is normally **new** is the shared module:
1056
-
1057
- - `packages/contacts/src/shared/contactListFilters.js`
1058
-
1059
- The others are normally edits to the generated CRUD package and the app-owned page layer that already exist.
1060
1108
 
1061
1109
  #### Client side
1062
1110
 
1063
- Build the filter runtime directly from the shared definitions:
1111
+ Generated CRUD list pages pass the page-local filter definitions into the shared list screen:
1064
1112
 
1065
1113
  ```js
1066
- const listFilters = useCrudListFilters(CONTACTS_LIST_FILTER_DEFINITIONS);
1114
+ import { listFilters } from "./listFilters.js";
1067
1115
 
1068
- const records = useCrudList({
1116
+ const screen = useCrudListScreen({
1069
1117
  resource: uiResource,
1070
1118
  apiSuffix: "/contacts?include=pets",
1071
- search: {
1072
- enabled: true,
1073
- mode: "query"
1074
- },
1075
- queryParams: listFilters.queryParams,
1076
- syncToRoute: {
1077
- enabled: true,
1078
- search: true,
1079
- queryParams: true,
1080
- queryParamBlacklist: ["include", "cursor", "limit"]
1081
- }
1119
+ listFilters,
1120
+ routeQueryBlacklist: Object.freeze(["include", "cursor", "limit"])
1082
1121
  });
1083
1122
  ```
1084
1123
 
1085
- That gives you, from one place:
1124
+ ```vue
1125
+ <CrudListScreen :screen="screen" />
1126
+ ```
1127
+
1128
+ Inside that shared screen runtime, `useCrudListFilters(...)` gives the list:
1086
1129
 
1087
- - `listFilters.values`
1088
- - `listFilters.queryParams`
1089
- - `listFilters.presets`
1090
- - `listFilters.activeChips`
1091
- - `listFilters.hasActiveFilters`
1092
- - `listFilters.clearChip(...)`
1093
- - `listFilters.clearFilters()`
1094
- - `listFilters.toggle(...)` for flag filters
1095
- - `listFilters.applyPreset(...)`
1096
- - `listFilters.matchesPreset(...)`
1130
+ - `filterRuntime.values`
1131
+ - `filterRuntime.queryParams`
1132
+ - `filterRuntime.presets`
1133
+ - `filterRuntime.activeChips`
1134
+ - `filterRuntime.hasActiveFilters`
1135
+ - `filterRuntime.clearChip(...)`
1136
+ - `filterRuntime.clearFilters()`
1137
+ - `filterRuntime.toggle(...)` for flag filters
1138
+ - `filterRuntime.applyPreset(...)`
1139
+ - `filterRuntime.matchesPreset(...)`
1097
1140
 
1098
1141
  So the same runtime owns:
1099
1142
 
@@ -1247,13 +1290,13 @@ async function list(query = {}, callOptions = {}) {
1247
1290
 
1248
1291
  #### Best practices
1249
1292
 
1250
- - Put the filter definitions in the CRUD package, not the page. Both server and client need them.
1293
+ - Keep client-only filters in the generated page-local `listFilters.js`. Move definitions into a CRUD package only when server code or another page needs to share them.
1251
1294
  - Keep the filter keys identical all the way through: definition key, query param key, and repository meaning.
1252
1295
  - Prefer `createCrudListFilterQueryField(...)` plus `createCrudListFilterQuerySchema(...)` at route/action boundaries so accepted structured-filter params stay visible in app code.
1253
1296
  - Use `type: "presence"` for null/not-null filters such as assigned vs unassigned storage. Do not model those as custom enums plus `applyQuery(...)` overrides unless the SQL semantics are genuinely different from `whereNotNull(...)` / `whereNull(...)`.
1254
1297
  - Use `createCrudListFilters(...)` unless the list semantics are truly unusual.
1255
1298
  - Use `q` for free-text and explicit query params for structured filters.
1256
- - Run `jskit doctor` after wiring filters. It flags inline/local filter-definition objects passed into `useCrudListFilters(...)` or `createCrudListFilters(...)`.
1299
+ - Run `jskit doctor` after wiring filters. It flags inline filter-definition objects passed into `useCrudListFilters(...)` or `createCrudListFilters(...)`.
1257
1300
 
1258
1301
  ### Pattern 4: lookup-backed structured filters
1259
1302
 
@@ -1265,7 +1308,7 @@ This is the next real-world step: filters like `supplierContactId`, `locationId`
1265
1308
 
1266
1309
  The right pattern is:
1267
1310
 
1268
- 1. keep the lookup filter in the shared definitions
1311
+ 1. keep the lookup filter in `listFilters.js` or in shared definitions when server code also imports it
1269
1312
  2. use `useCrudListFilters(...)` for state, chips, and query params
1270
1313
  3. use `useCrudListFilterLookups(...)` for option loading and label resolution
1271
1314
 
@@ -262,7 +262,7 @@ npx jskit generate crud-server-generator scaffold \
262
262
 
263
263
  This creates an app-local package under `packages/contacts/`.
264
264
 
265
- If the table should be CRUD-owned now but should **not** expose public HTTP CRUD routes yet, add:
265
+ If the table should already be CRUD-owned but should **not** expose public HTTP CRUD routes yet, add:
266
266
 
267
267
  ```bash
268
268
  --internal
@@ -322,7 +322,7 @@ npm run devlinks
322
322
 
323
323
  The same rule applies after later server scaffolds such as `addresses` and `comments`. The UI generator can still read the generated resource file directly, but the app runtime needs the local package install boundary to be completed before the CRUD can boot normally.
324
324
 
325
- For standard CRUDs, that file is now intentionally compact. It uses `defineCrudResource(...)` from `@jskit-ai/resource-crud-core`, authors the canonical `schema` / `searchSchema` / `defaultSort` / `autofilter` shape once, and lets JSKIT derive the standard CRUD operation contracts from it.
325
+ For standard CRUDs, that file is intentionally compact. It uses `defineCrudResource(...)` from `@jskit-ai/resource-crud-core`, authors the canonical `schema` / `searchSchema` / `defaultSort` / `autofilter` shape once, and lets JSKIT derive the standard CRUD operation contracts from it.
326
326
 
327
327
  ### Step 3: scaffold the UI
328
328
 
@@ -498,7 +498,7 @@ It will be the block added for the `w/[workspaceSlug]/admin/contacts/[contactId]
498
498
 
499
499
  Depending on what outlets already exist in the app, that block may try to place `Addresses` in the shell menu or in the contact subpages outlet. For this example, remove it either way and keep navigation manual from the contact view.
500
500
 
501
- This is an intentional manual cleanup for now:
501
+ This workflow has one intentional manual cleanup:
502
502
 
503
503
  - keep the routed pages
504
504
  - remove the auto menu link
@@ -657,7 +657,7 @@ If the contact view page should stay visible while child comment routes render u
657
657
  npx jskit generate ui-generator add-subpages \
658
658
  w/[workspaceSlug]/admin/contacts/[contactId]/index.vue \
659
659
  --title "Contact" \
660
- --subtitle "View and manage this contact."
660
+ --subtitle "Contact activity and notes."
661
661
  ```
662
662
 
663
663
  That is the point where the comments example deliberately overlaps with the `ui-generator` chapter. This pattern needs both:
@@ -772,4 +772,4 @@ They become one workflow:
772
772
  2. scaffold the server/resource package
773
773
  3. scaffold only the UI shape that the feature actually needs
774
774
 
775
- The next chapter is [Advanced CRUDs](/guide/generators/advanced-cruds). Read it as the structural follow-on to this one: this chapter teaches how to generate the CRUD, and the next one teaches how to reason about the files you now own.
775
+ The next chapter is [Advanced CRUDs](/guide/generators/advanced-cruds). Read it as the structural follow-on to this one: this chapter teaches how to generate the CRUD, and the next one teaches how to reason about the generated files the app owns.
@@ -80,7 +80,7 @@ The important default is this:
80
80
  - if JSKIT sees no nearer routed host, the new page gets a normal shell/menu placement
81
81
  - if JSKIT does see a nearer routed host, the new page gets linked into that host instead
82
82
 
83
- Open the app and you now have a real `/reports` page plus a real shell link for it.
83
+ Open the app to see a real `/reports` page plus a real shell link for it.
84
84
 
85
85
  ### Customizing the generated menu link
86
86
 
@@ -93,7 +93,8 @@ That is where things such as:
93
93
  - `label`
94
94
  - `order`
95
95
  - `icon`
96
- - a custom `componentToken`
96
+ - `owner`
97
+ - `surfaces`
97
98
 
98
99
  normally get adjusted.
99
100
 
@@ -145,21 +146,12 @@ Use this when the generated page link should go somewhere other than the generat
145
146
  Typical reasons:
146
147
 
147
148
  - you want the page in a different shell menu
148
- - you want the page link inside a specific existing outlet
149
+ - you want the page link inside a specific existing semantic placement
149
150
  - you do not want the link to land in the normal top-level menu
150
151
 
151
152
  For example, if a page should appear in a settings menu rather than the shell drawer, `--link-placement` is the override that says so.
152
153
 
153
- #### `--link-component-token`
154
-
155
- Use this when the page link should render with a different link component than the default inferred one.
156
-
157
- In practice, this often means:
158
-
159
- - using a tab-style link instead of a normal menu link
160
- - using a local custom link token for a special shell section
161
-
162
- This matters most once you start targeting non-default outlets. A settings or tab host may want a different link renderer than the main shell drawer.
154
+ The value should normally be semantic, such as `shell.primary-nav`, `page.section-nav`, or another `area.slot` placement from `jskit list-placements`. Concrete `host:position` outlets remain an escape hatch, not the default authoring path.
163
155
 
164
156
  #### `--link-to`
165
157
 
@@ -225,7 +217,7 @@ After the command, the page gains three important pieces:
225
217
  - a `ShellOutlet` for the child-page tabs
226
218
  - `RouterView`
227
219
 
228
- That means the page can now keep rendering shared content while child routes render underneath it.
220
+ That lets the page keep rendering shared content while child routes render underneath it.
229
221
 
230
222
  This is the first big distinction in this chapter:
231
223
 
@@ -255,7 +247,7 @@ The route page is still a normal page file. What changes is the inferred placeme
255
247
  - no parent host found -> shell/menu entry
256
248
  - nearest parent host found -> child link inside that host
257
249
 
258
- So JSKIT is not switching to a different generator. It is switching the inferred placement behavior because the route tree now has a routed host above the new page.
250
+ So JSKIT is not switching to a different generator. The route tree has a routed host above the new page, so the inferred placement behavior follows that host.
259
251
 
260
252
  ### Making a child page the default landing route
261
253
 
@@ -330,12 +322,13 @@ But the placement it wrote was **not** another top-level shell link.
330
322
  Instead, it targeted the host outlet:
331
323
 
332
324
  ```text
333
- reports:sub-pages
325
+ page.section-nav
334
326
  ```
335
327
 
336
- and used the tab-link renderer with:
328
+ with the host owner and relative route:
337
329
 
338
330
  ```js
331
+ owner: "reports",
339
332
  to: "./exports"
340
333
  ```
341
334
 
@@ -345,7 +338,7 @@ That is exactly the behavior you want:
345
338
  - `Exports` becomes a child tab under it
346
339
  - the child route renders under the parent instead of becoming another top-level menu entry
347
340
 
348
- This is also why `--link-placement`, `--link-component-token`, and `--link-to` are often unnecessary in the default nested case. Once the host exists, JSKIT already knows the likely child placement target, link renderer, and relative `to` value.
341
+ This is also why `--link-placement` and `--link-to` are often unnecessary in the default nested case. Once the host exists, JSKIT already knows the likely semantic placement, owner, and relative `to` value. The link renderer comes from `src/placementTopology.js`.
349
342
 
350
343
  ## Nested pages under a file-route host
351
344
 
@@ -365,7 +358,7 @@ Then upgrade that page into a host:
365
358
  npx jskit generate ui-generator add-subpages \
366
359
  home/contacts/[contactId].vue \
367
360
  --title "Contact" \
368
- --subtitle "View and manage this contact."
361
+ --subtitle "Contact activity and notes."
369
362
  ```
370
363
 
371
364
  This time the parent is a file route, not an `index.vue` route.
@@ -443,13 +436,13 @@ This is different from `page` in a very important way:
443
436
 
444
437
  So if the thing you are adding should live *inside* an existing shell region, not at its own route, `placed-element` is the right command.
445
438
 
446
- By default, the element goes into:
439
+ By default, the element targets the semantic status placement:
447
440
 
448
441
  ```text
449
- shell-layout:top-right
442
+ shell.status
450
443
  ```
451
444
 
452
- That is why this is such a good command for widgets, status panels, and compact shell extensions.
445
+ That is why this is such a good command for widgets, status panels, and compact shell extensions. The default shell topology maps `shell.status` to the concrete shell status outlet for each layout class.
453
446
 
454
447
  ### When `--surface` matters
455
448
 
@@ -476,19 +469,19 @@ A practical example is:
476
469
  npx jskit generate ui-generator placed-element \
477
470
  --name "Ops Panel" \
478
471
  --surface admin \
479
- --placement shell-layout:top-right
472
+ --placement shell.status
480
473
  ```
481
474
 
482
- `shell-layout:top-right` can exist across several surfaces. If your app has several enabled surfaces, `--surface admin` tells JSKIT which one this element is actually meant for.
475
+ `shell.status` can be global across several surfaces. If your app has several enabled surfaces, `--surface admin` tells JSKIT which one this element is actually meant for.
483
476
 
484
477
  So the rule of thumb is:
485
478
 
486
- - page-owned outlet target -> surface is often inferable
487
- - shared shell target in a multi-surface app -> pass `--surface`
479
+ - page-owned semantic placement target -> surface is often inferable
480
+ - shared shell semantic placement in a multi-surface app -> pass `--surface`
488
481
 
489
482
  ### `--placement`
490
483
 
491
- Use `--placement` when the default target `shell-layout:top-right` is not what you want.
484
+ Use `--placement` when the default target `shell.status` is not what you want.
492
485
 
493
486
  This is the option that answers:
494
487
 
@@ -496,6 +489,8 @@ This is the option that answers:
496
489
 
497
490
  In practice, it is the first override you will use for `placed-element`.
498
491
 
492
+ `--placement` expects a semantic placement id such as `shell.status`, `shell.global-actions`, or `settings.sections`. Concrete `host:position` outlets are exposed through topology, not used as normal placed-element authoring targets.
493
+
499
494
  ### `--path`
500
495
 
501
496
  Use `--path` when the component file should live somewhere other than `src/components`.
@@ -532,13 +527,15 @@ In the verified throwaway app, after generating `Alerts Widget`, I ran:
532
527
  ```bash
533
528
  npx jskit generate ui-generator outlet \
534
529
  src/components/AlertsWidgetElement.vue \
535
- --target alerts-widget:actions
530
+ --target alerts-widget:actions \
531
+ --placement page.actions
536
532
  ```
537
533
 
538
- That command touched only one file:
534
+ That command touched the Vue file and the topology file:
539
535
 
540
536
  ```text
541
537
  src/components/AlertsWidgetElement.vue
538
+ src/placementTopology.js
542
539
  ```
543
540
 
544
541
  and injected:
@@ -561,13 +558,16 @@ Use `outlet` when you want:
561
558
  - later content to be targetable there
562
559
  - no routed child-page behavior
563
560
 
564
- After adding the outlet, `npx jskit list-placements` showed the new target immediately:
561
+ After adding the outlet, `npx jskit list-placements` showed the new semantic placement immediately:
565
562
 
566
563
  ```text
567
- - alerts-widget:actions [src/components/AlertsWidgetElement.vue]
564
+ - page.actions: ...
565
+ - compact -> alerts-widget:actions
566
+ - medium -> alerts-widget:actions
567
+ - expanded -> alerts-widget:actions
568
568
  ```
569
569
 
570
- So `outlet` is the smallest possible way to make a Vue file part of the placement topology.
570
+ So `outlet` is the smallest possible way to add a concrete recipient and expose it through the public placement topology in the same change.
571
571
 
572
572
  ### When `outlet` is the smaller correct tool
573
573
 
@@ -588,7 +588,7 @@ That is why `outlet` is often the better choice for:
588
588
 
589
589
  If you do **not** need `RouterView` and you do **not** need child routes, `outlet` is usually the cleaner tool.
590
590
 
591
- ### Choosing a good custom `--target`
591
+ ### Choosing a good custom `--target` and `--placement`
592
592
 
593
593
  `--target` should be meaningful to humans, not just syntactically valid.
594
594
 
@@ -611,12 +611,20 @@ custom-area:slot1
611
611
 
612
612
  because the meaningful target name will later show up in:
613
613
 
614
- - `jskit list-placements`
615
- - placement entries
614
+ - `jskit list-placements --concrete`
615
+ - topology mappings
616
616
  - future generator commands
617
617
 
618
618
  So the target should describe the UI seam you are creating, not just satisfy the `host:position` format.
619
619
 
620
+ `--placement` is the public authoring target that other entries should use. It should be semantic, such as:
621
+
622
+ ```text
623
+ page.actions
624
+ ```
625
+
626
+ Adding an outlet without adding a semantic mapping would leave a low-level recipient that normal generators and humans will not discover by default.
627
+
620
628
  ## `add-subpages` versus `outlet`
621
629
 
622
630
  This is the distinction that most often causes hesitation.
@@ -676,4 +684,4 @@ That is why the commands fit together cleanly:
676
684
  - `page` can then create nested child routes under those hosts
677
685
  - `placed-element` and `outlet` handle the non-routed side of the same UI system
678
686
 
679
- Once the UI you want is no longer just page structure and starts needing real database-backed list/view/new/edit behavior, move to the CRUD generators chapter.
687
+ Once the UI you want needs real database-backed list/view/new/edit behavior instead of only page structure, move to the CRUD generators chapter.
@@ -4,7 +4,7 @@
4
4
 
5
5
  This guide is the main hands-on path through JSKIT.
6
6
 
7
- It now starts with a fast reproducible Quickstart, then steps back to the smaller scaffold-first chapters that explain how the app is assembled piece by piece. After that, it introduces the database-backed users layer, expands into console and workspace-aware app structure, and adds a small `App extras` section for optional runtime packages such as realtime and assistant. Finally, the guide breaks out generator-specific workflows into their own section.
7
+ It starts with a fast reproducible Quickstart, then steps back to the smaller scaffold-first chapters that explain how the app is assembled piece by piece. After that, it introduces the database-backed users layer, expands into console and workspace-aware app structure, and adds a small `App extras` section for optional runtime packages such as the Android Capacitor shell, realtime, and assistant. Finally, the guide breaks out generator-specific workflows into their own section.
8
8
 
9
9
  ## Table of Contents
10
10
 
@@ -22,6 +22,7 @@ It now starts with a fast reproducible Quickstart, then steps back to the smalle
22
22
 
23
23
  ### App Extras
24
24
 
25
+ - [Mobile Capacitor](/guide/app-extras/mobile-capacitor)
25
26
  - [Realtime](/guide/app-extras/realtime)
26
27
  - [Assistant](/guide/app-extras/assistant)
27
28
 
@@ -36,6 +37,6 @@ It now starts with a fast reproducible Quickstart, then steps back to the smalle
36
37
 
37
38
  - Start with `Quickstart` if you want the fastest route to a real workspace-enabled app and the first page-extension patterns.
38
39
  - Start with the rest of `App Setup` if you want to understand the base scaffold layer by layer.
39
- - Use `App Extras` once the base app structure is in place and you want optional runtime packages.
40
+ - Use `App Extras` once the base app structure is in place and you want optional runtime packages such as the Android shell, realtime, or assistant.
40
41
  - Jump into `Generators` if you already understand the runtime packages and want app-owned scaffolding workflows.
41
42
  - Inside `Generators`, read `CRUD Generators` before `Advanced CRUDs`: the first chapter teaches the workflow, and the second explains the generated anatomy and customization points.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Distributed JSKIT agent workflows, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
package/patterns/INDEX.md CHANGED
@@ -26,10 +26,12 @@ How to use it:
26
26
  - `page-redirects.md`
27
27
  - live actions, checkbox, toggle, patch button, inline action, `useCommand()`
28
28
  - `live-actions.md`
29
- - ajax, fetch, API call, request, endpoint, HTTP client, `useList()`, `useView()`, `useAddEdit()`, `useEndpointResource()`, `usersWebHttpClient`
29
+ - ajax, fetch, API call, request, endpoint, HTTP client, `useCrudListScreen()`, `useCrudViewScreen()`, `useCrudAddEditScreen()`, `useList()`, `useView()`, `useAddEdit()`, `useEndpointResource()`, `usersWebHttpClient`
30
30
  - `client-requests.md`
31
31
  - playwright, browser test, e2e, ui verification, authenticated ui test, test auth, dev login as, dev auth bypass
32
32
  - `ui-testing.md`
33
+ - generated UI contract, design contract, navigation roles, density, placeholder copy, card shells, shared CRUD screens
34
+ - `generated-ui-contract-tracking.md`
33
35
  - filter, filters, search facets, chips, date range, enum filter, lookup filter, `useCrudListFilters`, `createCrudListFilters`
34
36
  - `filters.md`
35
37
  - `searchSchema`, `search: true`, `applyFilter`, server search, query validators, backend filters, internal JSON REST filters
@@ -49,6 +51,7 @@ How to use it:
49
51
  - [live-actions.md](./live-actions.md)
50
52
  - [client-requests.md](./client-requests.md)
51
53
  - [ui-testing.md](./ui-testing.md)
54
+ - [generated-ui-contract-tracking.md](./generated-ui-contract-tracking.md)
52
55
  - [filters.md](./filters.md)
53
56
  - [server-search.md](./server-search.md)
54
57
  - [crud-repository-mapping.md](./crud-repository-mapping.md)