cabloy 5.1.158 → 5.1.159
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.
- package/.cabloy-version +1 -1
- package/.claude/skills/cabloy-contract-loop/SKILL.md +24 -7
- package/.claude/skills/cabloy-contract-loop/evals/evals.json +12 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +28 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +19 -2
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +18 -0
- package/.claude/skills/cabloy-zova-source-reading/SKILL.md +14 -0
- package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +10 -0
- package/.github/workflows/docs-pages.yml +18 -2
- package/CHANGELOG.md +19 -0
- package/package.json +2 -1
- package/repo-docs/.vitepress/config.mjs +34 -4
- package/repo-docs/.vitepress/theme/components/GitHubRepositoriesNav.vue +255 -0
- package/repo-docs/.vitepress/theme/custom.css +21 -0
- package/repo-docs/.vitepress/theme/index.js +7 -1
- package/repo-docs/backend/resource-field-update.md +48 -1
- package/repo-docs/blogs/index.md +2 -0
- package/repo-docs/frontend/form-layout-guide.md +6 -6
- package/repo-docs/frontend/page-guide.md +12 -0
- package/repo-docs/frontend/page-meta-guide.md +27 -0
- package/repo-docs/frontend/ssr-architecture-overview.md +4 -0
- package/repo-docs/frontend/ssr-build-deploy-guide.md +1 -1
- package/repo-docs/frontend/zova-form-source-reading-map.md +2 -2
- package/repo-docs/frontend/zova-form-under-the-hood.md +1 -1
- package/repo-docs/frontend/zova-reactivity-under-the-hood.md +65 -9
- package/repo-docs/fullstack/contract-loop-playbook.md +2 -0
- package/repo-docs/fullstack/ssr-site-and-flavor-setup.md +198 -0
- package/repo-docs/fullstack/vona-zova-integration.md +2 -0
- package/repo-e2e/docs/playwright.config.ts +37 -0
- package/repo-e2e/docs/specs/blogs-index.spec.ts +180 -0
- package/repo-e2e/docs/specs/github-repositories-nav.spec.ts +69 -0
- package/repo-e2e/docs/test-results/.last-run.json +4 -0
- package/repo-e2e/specs/cabloy-basic.spec.ts +136 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +21 -80
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productCreate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productUpdate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productView.tsx +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectReq.tsx +3 -7
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +8 -0
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +51 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +10 -1
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +99 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/bean/pipe.filter.ts +85 -57
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/cli/templates/rest/render.ts +2 -0
- package/zova/packages-cli/cli-set-front/cli/templates/rest/rest.ts +13 -3
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +31 -11
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +3 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +1 -1
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +68 -8
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +4 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/formLayout.ts +0 -6
- package/zova/src/suite-vendor/a-zova/modules/a-form/test/lib/formLayout.test.ts +16 -9
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/model/sdk.ts +4 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +13 -2
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/schema.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +50 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx +60 -7
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/table.ts +6 -4
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.159
|
|
@@ -18,11 +18,11 @@ This includes the reverse fullstack direction where newly added frontend resourc
|
|
|
18
18
|
In that situation:
|
|
19
19
|
|
|
20
20
|
1. run the normal sync or regeneration flow first
|
|
21
|
-
2.
|
|
22
|
-
-
|
|
23
|
-
-
|
|
21
|
+
2. identify every affected flavor from the active repository’s root wrappers and lower-level Zova scripts, then run each matching paired SSR/REST build before `npm run deps:vona`
|
|
22
|
+
- `npm run build:zova:admin` and `npm run build:zova:web` are representative default wrappers only
|
|
23
|
+
- an independently named site must use its own source-confirmed paired root wrapper; do not relabel or assume a default wrapper refreshed it
|
|
24
24
|
3. do not treat `build:rest:*` alone as sufficient, because the SSR bundle and rest output must move together
|
|
25
|
-
4. run `npm run deps:vona`
|
|
25
|
+
4. run `npm run deps:vona` after the required flavor builds
|
|
26
26
|
5. if the generated `.zova-rest` artifacts already contain the expected changes but Vona still sees stale types, delete `vona/node_modules` and reinstall dependencies
|
|
27
27
|
|
|
28
28
|
Do not keep debugging source-level contract or renderer changes until the local file-package installation state is known to be healthy.
|
|
@@ -101,6 +101,22 @@ In this mode, first diagnose whether the visible stale behavior comes from skipp
|
|
|
101
101
|
|
|
102
102
|
Use this mode when generated artifacts already contain the expected keys, types, or resources, but installed local file dependencies still behave stale after the normal sync flow.
|
|
103
103
|
|
|
104
|
+
### Mode E: independent SSR flavor and site
|
|
105
|
+
|
|
106
|
+
Use this mode when the request adds a deployable Zova SSR surface that Vona must dispatch separately. This is more than a page or route inside an existing site.
|
|
107
|
+
|
|
108
|
+
Before proposing implementation, inspect the active repository rather than inferring names from Admin or Web:
|
|
109
|
+
|
|
110
|
+
1. edition marker and root `package.json` wrappers;
|
|
111
|
+
2. `zova/package.json` or its durable source for matching `dev:ssr:*`, `build:ssr:*`, and `build:rest:*` scripts;
|
|
112
|
+
3. flavor env/config and its `SITE_ID`, mount path, SSR profile, and artifact-copy targets;
|
|
113
|
+
4. a Vona `@SsrSite(...)` baseline and the owning site-module asset path;
|
|
114
|
+
5. the expected SSR release directory and flavor-specific `zova-rest-*` package.
|
|
115
|
+
|
|
116
|
+
The independent identity tuple—flavor, site ID, public path, bundle path, REST package, Vona site module, and root paired-build wrapper—must be selected together. The root wrapper must build both SSR and REST output before `npm run deps:vona`; `build:rest:*` alone is not enough. Default Basic or Start Admin/Web wrappers are validated specimens, not a command template for a differently named site.
|
|
117
|
+
|
|
118
|
+
Read [Independent SSR Site and Flavor Setup](../../../repo-docs/fullstack/ssr-site-and-flavor-setup.md) for the canonical setup and evidence procedure.
|
|
119
|
+
|
|
104
120
|
If the task is only backend scaffolding or only frontend scaffolding, the more specialized scaffold skills may be the better primary choice.
|
|
105
121
|
|
|
106
122
|
## Step 2: Start from the contract source of truth
|
|
@@ -225,7 +241,7 @@ Important Cabloy Basic reverse-sync rule:
|
|
|
225
241
|
- if the change was consumer-side, low-confidence, cross-edition, or happened outside the Claude hook path, run the reverse sync flow deliberately yourself
|
|
226
242
|
- prefer visible proof under `zova/src/**/.metadata/**` when it is available; if the effective handoff only appears in `.zova-rest`, treat the safeguard as conservative reminder/auto-sync assistance rather than strict proof
|
|
227
243
|
|
|
228
|
-
For Cabloy Start, verify the exact Start-specific flavor names, paths, SSR site baselines,
|
|
244
|
+
For Cabloy Start, verify the exact Start-specific flavor names, paths, SSR site baselines, project assets, and source-confirmed root wrappers in the active Start repository. If the work affects an independent SSR site, follow Mode E: build that site’s paired wrapper rather than assuming an Admin/Web command covers it.
|
|
229
245
|
|
|
230
246
|
### Schema-driven UI decision branch
|
|
231
247
|
|
|
@@ -273,10 +289,11 @@ Especially verify:
|
|
|
273
289
|
|
|
274
290
|
- active repo marker
|
|
275
291
|
- affected frontend flavor
|
|
276
|
-
- whether the change affects Admin,
|
|
292
|
+
- whether the change affects a default Admin/Web site, an independent site, or more than one site
|
|
293
|
+
- source-confirmed root wrapper for every affected flavor
|
|
277
294
|
- whether the generated output path is edition-specific
|
|
278
295
|
|
|
279
|
-
Do not silently reuse Basic-specific examples in Start workflows.
|
|
296
|
+
Do not silently reuse Basic-specific examples in Start workflows or treat default Admin/Web commands as an independent-site template.
|
|
280
297
|
|
|
281
298
|
## Step 7: End-to-end verification
|
|
282
299
|
|
|
@@ -30,6 +30,18 @@
|
|
|
30
30
|
"prompt": "One persisted Order domain needs read-only Admin select/view operations through presetResource and customer Web list/detail reads. Should Admin and Web share one API and ModelResource, or should I use mine/viewMine-style Web operations and a separate Web model? Include the Cabloy contract-loop steps.",
|
|
31
31
|
"expected_output": "Keeps one domain and persistence boundary while separating Admin select/view and explicit owner-scoped Web operations with audience-specific DTOs. Keeps ModelResource as the Admin Resource owner and accepts a dedicated Web model/pages only for the distinct self-service state. Requires backend-first OpenAPI inspection, constrained regeneration for all owned operations, and verification of Admin authorization, Web owner scope before pagination, SSR admission where relevant, and both consumer paths. Rejects forcing Web through presetResource and rejects a parallel Admin cache owner for an ordinary custom Admin action.",
|
|
32
32
|
"files": []
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": 6,
|
|
36
|
+
"prompt": "In Cabloy Start, add an independently mounted SSR site with a new Zova flavor. What must be discovered and built before Vona can serve it?",
|
|
37
|
+
"expected_output": "Detects the Start repository and discovers the site-specific flavor env/config, lower-level SSR and REST scripts, root paired wrapper, Vona @SsrSite baseline, artifact path, and generated zova-rest package. Requires a new aligned site identity tuple and paired SSR-plus-REST build followed by deps:vona. Rejects reusing or relabeling default Admin/Web wrappers, REST-only rebuilding, and Commerce-specific names or paths as a generic template.",
|
|
38
|
+
"files": []
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": 7,
|
|
42
|
+
"prompt": "A Vona consumer remains stale after an independent SSR flavor change. The new flavor artifacts appear correct. What is the safe Cabloy diagnosis order?",
|
|
43
|
+
"expected_output": "Requires proving that the selected site-specific wrapper generated both SSR and REST artifacts, confirming the matching Vona bundle/type identity, and running deps:vona before calling it local dependency drift. Does not suggest REST-only rebuilding or default Admin/Web commands for the independent site. Only after the normal handoff is proved does it recommend repairing Vona local installation state.",
|
|
44
|
+
"files": []
|
|
33
45
|
}
|
|
34
46
|
]
|
|
35
47
|
}
|
|
@@ -145,6 +145,34 @@ The Web model is valid only for the separate self-service state domain. It must
|
|
|
145
145
|
- backend tooling and type hints
|
|
146
146
|
- SSR or integration paths that depend on refreshed frontend output
|
|
147
147
|
|
|
148
|
+
## Independent SSR site/flavor handoff map
|
|
149
|
+
|
|
150
|
+
Use this map when Vona must serve a newly independent Zova SSR site, rather than when a route is added to an existing site.
|
|
151
|
+
|
|
152
|
+
1. authored site boundary
|
|
153
|
+
- flavor env and optional flavor config
|
|
154
|
+
- site-specific route, layout, admission, locale, and asset choices
|
|
155
|
+
- exact site ID and mounted public path
|
|
156
|
+
2. paired frontend artifacts
|
|
157
|
+
- SSR bundle and client assets copied to the owning Vona site module
|
|
158
|
+
- flavor-specific generated REST/type package copied to Vona’s local workspace
|
|
159
|
+
- one source-confirmed root wrapper that produces both outputs
|
|
160
|
+
3. Vona consumer registration
|
|
161
|
+
- independently packaged site module
|
|
162
|
+
- `@SsrSite(...)` identity, public path, bundle path, localized title, and diagnostics command
|
|
163
|
+
- typed site/public-path augmentation plus generated page/icon imports from the matching REST package
|
|
164
|
+
4. dependency handoff
|
|
165
|
+
- run the selected site wrapper first
|
|
166
|
+
- run `npm run deps:vona` only after both artifacts exist
|
|
167
|
+
- do not treat `build:rest:*` alone as a Vona SSR handoff
|
|
168
|
+
5. consumer proof
|
|
169
|
+
- Vona dispatch reaches the intended public path
|
|
170
|
+
- raw HTML is server-rendered and assets resolve
|
|
171
|
+
- browser hydration and route aliases preserve the mounted boundary
|
|
172
|
+
- admission, redirect, and cache behavior match the site contract
|
|
173
|
+
|
|
174
|
+
Default Admin/Web wrappers are specimens only. Discover the independently named site’s own scripts and generated paths from the active edition before recommending commands.
|
|
175
|
+
|
|
148
176
|
## Drift diagnosis matrix
|
|
149
177
|
|
|
150
178
|
### Source wrong
|
|
@@ -37,8 +37,9 @@ When one persisted domain has both Admin Resource and Web self-service consumers
|
|
|
37
37
|
|
|
38
38
|
- frontend-owned source is correct
|
|
39
39
|
- metadata generation completed when applicable
|
|
40
|
-
-
|
|
41
|
-
- `deps:vona` completed
|
|
40
|
+
- every affected flavor’s source-confirmed paired SSR/REST build completed successfully
|
|
41
|
+
- `deps:vona` completed after the paired builds
|
|
42
|
+
- `build:rest:*` was not treated as the only Vona SSR handoff
|
|
42
43
|
- backend consumers can resolve the refreshed frontend-generated handoff
|
|
43
44
|
- prefer visible proof under `zova/src/**/.metadata/**` when it is available
|
|
44
45
|
- this repo does not rely on a contract-loop pre-commit gate; the active safeguard is the Claude hook layer
|
|
@@ -48,6 +49,22 @@ When one persisted domain has both Admin Resource and Web self-service consumers
|
|
|
48
49
|
- `npm run tsc:zova`
|
|
49
50
|
- relevant flavor-specific or route-specific checks
|
|
50
51
|
|
|
52
|
+
### Independent SSR site/flavor verification
|
|
53
|
+
|
|
54
|
+
When Vona consumes a new independent SSR site, also verify:
|
|
55
|
+
|
|
56
|
+
- the active edition and exact flavor are confirmed from source
|
|
57
|
+
- `SITE_ID`, Vona `siteId`, Zova `APP_PUBLIC_PATH`, and Vona `publicPath` align; enabled site IDs and public paths remain unique
|
|
58
|
+
- Vona `bundlePath` exactly matches the copied SSR release directory
|
|
59
|
+
- the generated `zova-rest-*` package matches the Vona type import
|
|
60
|
+
- the source-confirmed root wrapper generated both the SSR bundle and REST/type output
|
|
61
|
+
- `npm run deps:vona` ran after that paired build, and Vona resolves the refreshed local package
|
|
62
|
+
- the site diagnostics command names that same wrapper rather than a default Admin/Web command
|
|
63
|
+
- raw Vona-served HTML proves server rendering at the intended mounted path
|
|
64
|
+
- client assets load, hydration completes without mismatch, and aliases do not duplicate the mount path
|
|
65
|
+
- public/private admission, redirect, and cache expectations match the selected SSR profile
|
|
66
|
+
- targeted browser or E2E evidence covers the site through Vona, not only a standalone frontend server
|
|
67
|
+
|
|
51
68
|
## Consumer drift verification
|
|
52
69
|
|
|
53
70
|
- source truth already looks correct
|
|
@@ -180,6 +180,24 @@ Important serialization reminder:
|
|
|
180
180
|
- for performance reasons, Vona response serialization is opt-in per API action
|
|
181
181
|
- verify that the target controller action explicitly uses `@Core.serializer()` before concluding that serializer metadata is broken
|
|
182
182
|
|
|
183
|
+
### Join-backed foreign-key filtering and sorting
|
|
184
|
+
|
|
185
|
+
Use this metadata/query-contract branch when a persisted relation field remains an identity, but a resource list must filter or order it by a human-readable column on the related table. This is a metadata-only refinement when the foreign-key column already exists; do not increment `fileVersion` solely for this behavior.
|
|
186
|
+
|
|
187
|
+
Keep the entity field name and identity schema unchanged. Put the relation mapping on the entity field so inferred projections retain it:
|
|
188
|
+
|
|
189
|
+
- `v.filter({ table, joinType, joinOn, originalName, op })` maps the public relation field to the related display column;
|
|
190
|
+
- `ZovaRender.column({ enableSorting: true })` exposes the table sort control when the list should support ordering;
|
|
191
|
+
- preserve existing relation picker/cell metadata and `v.tableIdentity()` (or the applicable identity schema).
|
|
192
|
+
|
|
193
|
+
For a required relation, `innerJoin` is usually appropriate. When the relation is optional or unmatched base rows must remain visible, choose the join semantics explicitly instead of copying an inner-join example.
|
|
194
|
+
|
|
195
|
+
Keep the public query and order key equal to the foreign-key field name. In the select request DTO, retain that inferred field and use `$makeSchema(...)` only to change its query-input schema and renderer, usually to `v.optional()`, `z.string()`, and Cabloy Basic's `basic-input:formFieldInput`. Do not replace the entity storage schema with a string or add a parallel display-name parameter unless the API intentionally exposes separate ID and display-name filters.
|
|
196
|
+
|
|
197
|
+
Verify direct DTO metadata, the emitted optional text query parameter, absence of an unintended alternate parameter, visible sortable-column metadata, and endpoint filtering plus ascending/descending ordering. Confirm that filtering and ordering resolve to the intended related display column and inject the expected join.
|
|
198
|
+
|
|
199
|
+
See [Existing Resource Field Update](../../../repo-docs/backend/resource-field-update.md#filter-and-sort-a-relation-by-its-display-field) for the public pattern and links to the ORM and frontend query-flow guides.
|
|
200
|
+
|
|
183
201
|
## Step 6: Apply the renderer branch deliberately
|
|
184
202
|
|
|
185
203
|
### Default rule: prefer shared renderer reuse
|
|
@@ -139,6 +139,20 @@ When a translated schema title, renderer, or validation surface remains stale af
|
|
|
139
139
|
|
|
140
140
|
The key distinction is between a genuinely stale generated contract and a valid schema facade captured from an earlier locale. `$computed` is only locale-safe when its dependency path reacquires the current schema source; wrapping an old facade or transformed snapshot does not make it current.
|
|
141
141
|
|
|
142
|
+
### Raw-`this` reactivity boundary
|
|
143
|
+
|
|
144
|
+
When an author reports that a controller field changed but a TSX render, `$computed`, or `$watch` did not react, inspect this lifecycle boundary before recommending framework-neutral rewrites:
|
|
145
|
+
|
|
146
|
+
1. inspect `zova-core/src/bean/beanContainer.ts` `BeanContainer._prepareBeanInstanceCommon(...)` to verify that `new BeanClass(...)` occurs before the framework exposes the reactive/proxied bean;
|
|
147
|
+
2. inspect `BeanContainer._initBeanInstance(...)` to verify that `__init__()` is invoked only after preparation;
|
|
148
|
+
3. check whether a class-field arrow callback or construction-time `bind(this)` captured raw `this` and later writes bean state;
|
|
149
|
+
4. distinguish this from a callback created in `__init__()`, whose lexical `this` is the exposed bean; and
|
|
150
|
+
5. verify whether the dependent render read through that same exposed identity.
|
|
151
|
+
|
|
152
|
+
State the consequence precisely: a proxy-based TSX render read can track normally, while a mutation through a raw construction-time callback changes the underlying field without notifying that dependency. An unrelated reactive update can later expose the changed value during another render.
|
|
153
|
+
|
|
154
|
+
Do **not** summarize this as “arrow functions are non-reactive,” and do not default to `ref.value`, `toRaw`, or generic Vue rewrites. Explain the Zova bean lifecycle first; recommend ordinary controller methods for normal actions, or closures created in `__init__()` only when a stable callback reference is required.
|
|
155
|
+
|
|
142
156
|
## Step 5: Explain Zova-native meaning first
|
|
143
157
|
|
|
144
158
|
When answering, explain the Zova role first, then add Vue analogies only if they help.
|
|
@@ -20,6 +20,16 @@ Use when the question is about:
|
|
|
20
20
|
- `$computed`
|
|
21
21
|
- `$params` / `$query`
|
|
22
22
|
- page render flow
|
|
23
|
+
- a class-field arrow callback, callback identity, or stale TSX after an apparent controller-state mutation
|
|
24
|
+
|
|
25
|
+
For a raw-`this` reactivity symptom, verify this expected sequence:
|
|
26
|
+
|
|
27
|
+
1. `BeanContainer` constructs the class instance before it exposes the reactive/proxied bean;
|
|
28
|
+
2. a class-field arrow callback captures that construction-time raw `this`;
|
|
29
|
+
3. `__init__()` runs after preparation, so a closure created there captures the exposed bean instead; and
|
|
30
|
+
4. a render dependency invalidates only when its mutation travels through the same exposed reactive identity used for the read.
|
|
31
|
+
|
|
32
|
+
Therefore, do not classify the issue as “arrow functions are non-reactive.” Distinguish a raw-captured write that changes the underlying field without a trigger from an ordinary controller method or `__init__()` callback that writes through the exposed bean.
|
|
23
33
|
|
|
24
34
|
## Component controller and wrapper path
|
|
25
35
|
|
|
@@ -5,6 +5,9 @@ on:
|
|
|
5
5
|
- main
|
|
6
6
|
paths:
|
|
7
7
|
- 'repo-docs/**'
|
|
8
|
+
- 'repo-e2e/docs/**'
|
|
9
|
+
- 'package.json'
|
|
10
|
+
- 'pnpm-lock.yaml'
|
|
8
11
|
- 'README.md'
|
|
9
12
|
- '.github/workflows/docs-pages.yml'
|
|
10
13
|
workflow_dispatch:
|
|
@@ -33,12 +36,25 @@ jobs:
|
|
|
33
36
|
node-version: 24
|
|
34
37
|
cache: pnpm
|
|
35
38
|
cache-dependency-path: repo-docs/pnpm-lock.yaml
|
|
39
|
+
- name: install root dependencies
|
|
40
|
+
run: pnpm install --frozen-lockfile
|
|
36
41
|
- name: install docs dependencies
|
|
37
42
|
run: pnpm --dir repo-docs install --frozen-lockfile
|
|
38
|
-
- name:
|
|
43
|
+
- name: install Chromium
|
|
44
|
+
run: pnpm exec playwright install --with-deps chromium
|
|
45
|
+
- name: build docs and verify layout
|
|
39
46
|
env:
|
|
40
47
|
GA_MEASUREMENT_ID: ${{ vars.GA_MEASUREMENT_ID }}
|
|
41
|
-
run:
|
|
48
|
+
run: npm run test:docs:e2e
|
|
49
|
+
- name: upload docs E2E artifacts
|
|
50
|
+
if: failure()
|
|
51
|
+
uses: actions/upload-artifact@v7
|
|
52
|
+
with:
|
|
53
|
+
name: docs-e2e-artifacts
|
|
54
|
+
path: |
|
|
55
|
+
repo-e2e/docs/playwright-report/
|
|
56
|
+
repo-e2e/docs/test-results/
|
|
57
|
+
if-no-files-found: ignore
|
|
42
58
|
- name: setup pages
|
|
43
59
|
uses: actions/configure-pages@v5
|
|
44
60
|
- name: upload pages artifact
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.159
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Add form layout support.
|
|
8
|
+
- Add render column support.
|
|
9
|
+
- Add OpenAPI title support for DTOs.
|
|
10
|
+
- Add form schema title support.
|
|
11
|
+
- Add configurable page title keys.
|
|
12
|
+
- Add blog styling enhancements.
|
|
13
|
+
|
|
14
|
+
### Improvements
|
|
15
|
+
|
|
16
|
+
- Update documentation for joined backend filtering and sorting.
|
|
17
|
+
- Document raw `this` reactivity boundaries.
|
|
18
|
+
- Document SSR site and flavor setup.
|
|
19
|
+
- Improve documentation links, layouts, page metadata guidance, and blog styling.
|
|
20
|
+
- Update site configuration and controller behavior.
|
|
21
|
+
|
|
3
22
|
## 5.1.158
|
|
4
23
|
|
|
5
24
|
### Improvements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.159",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "A Node.js fullstack framework",
|
|
6
6
|
"keywords": [
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"test": "cd vona && npm run test",
|
|
48
48
|
"test:e2e": "node repo-e2e/scripts/runE2e.ts --clean",
|
|
49
49
|
"test:e2e:fast": "node repo-e2e/scripts/runE2e.ts --fast",
|
|
50
|
+
"test:docs:e2e": "npm run docs:build && playwright test --config repo-e2e/docs/playwright.config.ts",
|
|
50
51
|
"db:reset": "cd vona && npm run db:reset",
|
|
51
52
|
"tsc": "npm run tsc:zova && pnpm --dir vona run tsc",
|
|
52
53
|
"tsc:zova": "pnpm --dir zova run tsc",
|
|
@@ -97,6 +97,7 @@ const fullstackGroups = [
|
|
|
97
97
|
},
|
|
98
98
|
{ text: 'Framework Performance', link: '/fullstack/framework-performance' },
|
|
99
99
|
{ text: 'Vona + Zova Integration', link: '/fullstack/vona-zova-integration' },
|
|
100
|
+
{ text: 'SSR Site and Flavor Setup', link: '/fullstack/ssr-site-and-flavor-setup' },
|
|
100
101
|
{ text: 'A-Pay Payment Suite', link: '/fullstack/a-pay-payment-suite' },
|
|
101
102
|
{
|
|
102
103
|
text: 'Payment Provider Sandbox Configuration',
|
|
@@ -191,19 +192,49 @@ export default defineConfig({
|
|
|
191
192
|
base: '/',
|
|
192
193
|
ignoreDeadLinks: [/^https?:\/\/localhost/],
|
|
193
194
|
head: gaHead,
|
|
195
|
+
transformPageData(pageData) {
|
|
196
|
+
if (!/^blogs\/[^/]+\/index\.md$/.test(pageData.relativePath)) return;
|
|
197
|
+
|
|
198
|
+
const pageClass = [pageData.frontmatter.pageClass, 'cabloy-blogs-article']
|
|
199
|
+
.filter(Boolean)
|
|
200
|
+
.join(' ');
|
|
201
|
+
return {
|
|
202
|
+
frontmatter: {
|
|
203
|
+
...pageData.frontmatter,
|
|
204
|
+
pageClass,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
},
|
|
194
208
|
markdown: {
|
|
195
209
|
lineNumbers: true,
|
|
196
210
|
},
|
|
197
211
|
themeConfig: {
|
|
198
212
|
nav: [
|
|
199
213
|
{ text: 'Home', link: '/' },
|
|
214
|
+
{
|
|
215
|
+
text: 'Docs',
|
|
216
|
+
items: [
|
|
217
|
+
{ text: 'Fullstack', link: '/fullstack/introduction', activeMatch: '^/fullstack/' },
|
|
218
|
+
{ text: 'Backend (Vona)', link: '/backend/introduction', activeMatch: '^/backend/' },
|
|
219
|
+
{ text: 'Frontend (Zova)', link: '/frontend/introduction', activeMatch: '^/frontend/' },
|
|
220
|
+
],
|
|
221
|
+
},
|
|
200
222
|
{ text: 'Blogs', link: '/blogs/', activeMatch: '^/blogs/' },
|
|
201
|
-
{ text: 'Fullstack', link: '/fullstack/introduction', activeMatch: '^/fullstack/' },
|
|
202
|
-
{ text: 'Backend (Vona)', link: '/backend/introduction', activeMatch: '^/backend/' },
|
|
203
|
-
{ text: 'Frontend (Zova)', link: '/frontend/introduction', activeMatch: '^/frontend/' },
|
|
204
223
|
{ text: 'Editions', link: '/editions/overview', activeMatch: '^/editions/' },
|
|
205
224
|
{ text: 'AI Development', link: '/ai/introduction', activeMatch: '^/ai/' },
|
|
206
225
|
{ text: 'Reference', link: '/reference/introduction', activeMatch: '^/reference/' },
|
|
226
|
+
{
|
|
227
|
+
component: 'GitHubRepositoriesNav',
|
|
228
|
+
props: {
|
|
229
|
+
repositories: [
|
|
230
|
+
{ text: 'github.com/cabloy/cabloy', link: 'https://github.com/cabloy/cabloy' },
|
|
231
|
+
{
|
|
232
|
+
text: 'github.com/cabloy/cabloy-start',
|
|
233
|
+
link: 'https://github.com/cabloy/cabloy-start',
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
207
238
|
],
|
|
208
239
|
sidebar: {
|
|
209
240
|
'/fullstack/': fullstackGroups,
|
|
@@ -634,7 +665,6 @@ export default defineConfig({
|
|
|
634
665
|
],
|
|
635
666
|
'/reference/': referenceGroups,
|
|
636
667
|
},
|
|
637
|
-
socialLinks: [{ icon: 'github', link: 'https://github.com/cabloy/cabloy' }],
|
|
638
668
|
search: {
|
|
639
669
|
provider: 'local',
|
|
640
670
|
},
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { nextTick, onMounted, onUnmounted, ref, useId } from 'vue';
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
repositories: {
|
|
6
|
+
type: Array,
|
|
7
|
+
required: true,
|
|
8
|
+
},
|
|
9
|
+
screenMenu: Boolean,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const isOpen = ref(false);
|
|
13
|
+
const root = ref();
|
|
14
|
+
const trigger = ref();
|
|
15
|
+
const menu = ref();
|
|
16
|
+
const menuId = useId();
|
|
17
|
+
|
|
18
|
+
function close({ returnFocus = false } = {}) {
|
|
19
|
+
isOpen.value = false;
|
|
20
|
+
if (returnFocus) {
|
|
21
|
+
trigger.value?.focus();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function open({ focusIndex } = {}) {
|
|
26
|
+
isOpen.value = true;
|
|
27
|
+
if (focusIndex === undefined) return;
|
|
28
|
+
|
|
29
|
+
await nextTick();
|
|
30
|
+
menu.value?.querySelectorAll('a')[focusIndex]?.focus();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function toggle() {
|
|
34
|
+
if (isOpen.value) {
|
|
35
|
+
close();
|
|
36
|
+
} else {
|
|
37
|
+
open();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function handleTriggerKeydown(event) {
|
|
42
|
+
if (event.key === 'ArrowDown') {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
open({ focusIndex: 0 });
|
|
45
|
+
} else if (event.key === 'ArrowUp') {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
open({ focusIndex: props.repositories.length - 1 });
|
|
48
|
+
} else if (event.key === 'Escape') {
|
|
49
|
+
close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function handleMenuKeydown(event) {
|
|
54
|
+
const links = [...menu.value.querySelectorAll('a')];
|
|
55
|
+
const index = links.indexOf(document.activeElement);
|
|
56
|
+
if (index === -1) return;
|
|
57
|
+
|
|
58
|
+
let nextIndex;
|
|
59
|
+
if (event.key === 'ArrowDown') {
|
|
60
|
+
nextIndex = (index + 1) % links.length;
|
|
61
|
+
} else if (event.key === 'ArrowUp') {
|
|
62
|
+
nextIndex = (index - 1 + links.length) % links.length;
|
|
63
|
+
} else if (event.key === 'Home') {
|
|
64
|
+
nextIndex = 0;
|
|
65
|
+
} else if (event.key === 'End') {
|
|
66
|
+
nextIndex = links.length - 1;
|
|
67
|
+
} else if (event.key === 'Escape') {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
close({ returnFocus: true });
|
|
70
|
+
return;
|
|
71
|
+
} else {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
links[nextIndex]?.focus();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function handlePointerDown(event) {
|
|
80
|
+
if (isOpen.value && !root.value?.contains(event.target)) {
|
|
81
|
+
close();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function handleFocusIn(event) {
|
|
86
|
+
if (isOpen.value && !root.value?.contains(event.target)) {
|
|
87
|
+
close();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onMounted(() => {
|
|
92
|
+
document.addEventListener('pointerdown', handlePointerDown);
|
|
93
|
+
document.addEventListener('focusin', handleFocusIn);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
onUnmounted(() => {
|
|
97
|
+
document.removeEventListener('pointerdown', handlePointerDown);
|
|
98
|
+
document.removeEventListener('focusin', handleFocusIn);
|
|
99
|
+
});
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<section v-if="screenMenu" class="cabloy-github-repositories cabloy-github-repositories--screen">
|
|
104
|
+
<p class="cabloy-github-repositories__title">GitHub repositories</p>
|
|
105
|
+
<a
|
|
106
|
+
v-for="repository in repositories"
|
|
107
|
+
:key="repository.link"
|
|
108
|
+
class="cabloy-github-repositories__screen-link"
|
|
109
|
+
:href="repository.link"
|
|
110
|
+
target="_blank"
|
|
111
|
+
rel="noopener noreferrer"
|
|
112
|
+
>
|
|
113
|
+
{{ repository.text }}
|
|
114
|
+
</a>
|
|
115
|
+
</section>
|
|
116
|
+
|
|
117
|
+
<div v-else ref="root" class="cabloy-github-repositories">
|
|
118
|
+
<button
|
|
119
|
+
ref="trigger"
|
|
120
|
+
class="cabloy-github-repositories__trigger"
|
|
121
|
+
type="button"
|
|
122
|
+
aria-label="GitHub repositories"
|
|
123
|
+
aria-haspopup="menu"
|
|
124
|
+
:aria-controls="menuId"
|
|
125
|
+
:aria-expanded="isOpen"
|
|
126
|
+
@click="toggle"
|
|
127
|
+
@keydown="handleTriggerKeydown"
|
|
128
|
+
>
|
|
129
|
+
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
|
|
130
|
+
<path
|
|
131
|
+
fill="currentColor"
|
|
132
|
+
d="M12 2C6.477 2 2 6.59 2 12.253c0 4.53 2.865 8.37 6.839 9.727.5.095.682-.223.682-.494 0-.244-.009-1.05-.014-1.906-2.782.621-3.369-1.215-3.369-1.215-.455-1.185-1.11-1.5-1.11-1.5-.908-.64.069-.627.069-.627 1.004.073 1.532 1.057 1.532 1.057.892 1.567 2.341 1.114 2.91.852.09-.663.349-1.115.635-1.371-2.22-.26-4.555-1.139-4.555-5.067 0-1.12.39-2.034 1.03-2.752-.104-.26-.447-1.306.098-2.724 0 0 .84-.278 2.75 1.051A9.3 9.3 0 0 1 12 6.89a9.3 9.3 0 0 1 2.504.347c1.909-1.33 2.748-1.051 2.748-1.051.546 1.418.203 2.464.1 2.724.64.718 1.028 1.632 1.028 2.752 0 3.938-2.339 4.804-4.566 5.058.359.32.678.947.678 1.91 0 1.38-.012 2.492-.012 2.832 0 .274.18.594.688.493C19.14 20.619 22 16.78 22 12.253 22 6.59 17.523 2 12 2Z"
|
|
133
|
+
/>
|
|
134
|
+
</svg>
|
|
135
|
+
</button>
|
|
136
|
+
|
|
137
|
+
<div
|
|
138
|
+
v-if="isOpen"
|
|
139
|
+
:id="menuId"
|
|
140
|
+
ref="menu"
|
|
141
|
+
class="cabloy-github-repositories__menu"
|
|
142
|
+
role="menu"
|
|
143
|
+
aria-label="GitHub repositories"
|
|
144
|
+
@keydown="handleMenuKeydown"
|
|
145
|
+
>
|
|
146
|
+
<a
|
|
147
|
+
v-for="repository in repositories"
|
|
148
|
+
:key="repository.link"
|
|
149
|
+
class="cabloy-github-repositories__menu-link"
|
|
150
|
+
:href="repository.link"
|
|
151
|
+
role="menuitem"
|
|
152
|
+
target="_blank"
|
|
153
|
+
rel="noopener noreferrer"
|
|
154
|
+
@click="close"
|
|
155
|
+
>
|
|
156
|
+
{{ repository.text }}
|
|
157
|
+
</a>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</template>
|
|
161
|
+
|
|
162
|
+
<style scoped>
|
|
163
|
+
.cabloy-github-repositories {
|
|
164
|
+
position: relative;
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.cabloy-github-repositories__trigger {
|
|
170
|
+
display: flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
width: 32px;
|
|
174
|
+
height: 32px;
|
|
175
|
+
border: 0;
|
|
176
|
+
border-radius: 6px;
|
|
177
|
+
background: transparent;
|
|
178
|
+
color: var(--vp-c-text-2);
|
|
179
|
+
cursor: pointer;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.cabloy-github-repositories__trigger:hover,
|
|
183
|
+
.cabloy-github-repositories__trigger[aria-expanded='true'] {
|
|
184
|
+
color: var(--vp-c-text-1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.cabloy-github-repositories__trigger:focus-visible,
|
|
188
|
+
.cabloy-github-repositories__menu-link:focus-visible,
|
|
189
|
+
.cabloy-github-repositories__screen-link:focus-visible {
|
|
190
|
+
outline: 2px solid var(--vp-c-brand-1);
|
|
191
|
+
outline-offset: 2px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.cabloy-github-repositories__trigger svg {
|
|
195
|
+
width: 20px;
|
|
196
|
+
height: 20px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.cabloy-github-repositories__menu {
|
|
200
|
+
position: absolute;
|
|
201
|
+
z-index: 1;
|
|
202
|
+
top: calc(100% + 4px);
|
|
203
|
+
right: 0;
|
|
204
|
+
min-width: 160px;
|
|
205
|
+
border: 1px solid var(--vp-c-divider);
|
|
206
|
+
border-radius: 8px;
|
|
207
|
+
padding: 4px;
|
|
208
|
+
background: var(--vp-c-bg-elv);
|
|
209
|
+
box-shadow: var(--vp-shadow-3);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.cabloy-github-repositories__menu-link,
|
|
213
|
+
.cabloy-github-repositories__screen-link {
|
|
214
|
+
display: block;
|
|
215
|
+
color: var(--vp-c-text-1);
|
|
216
|
+
text-decoration: none;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.cabloy-github-repositories__menu-link {
|
|
220
|
+
border-radius: 4px;
|
|
221
|
+
padding: 0.5rem 0.75rem;
|
|
222
|
+
font-size: 0.875rem;
|
|
223
|
+
line-height: 1.25rem;
|
|
224
|
+
white-space: nowrap;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.cabloy-github-repositories__menu-link:hover {
|
|
228
|
+
background: var(--vp-c-bg-soft);
|
|
229
|
+
color: var(--vp-c-brand-1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.cabloy-github-repositories--screen {
|
|
233
|
+
display: block;
|
|
234
|
+
padding: 12px 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.cabloy-github-repositories__title {
|
|
238
|
+
margin: 0 0 4px;
|
|
239
|
+
color: var(--vp-c-text-2);
|
|
240
|
+
font-size: 0.75rem;
|
|
241
|
+
font-weight: 600;
|
|
242
|
+
line-height: 1.25rem;
|
|
243
|
+
text-transform: uppercase;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.cabloy-github-repositories__screen-link {
|
|
247
|
+
padding: 0.5rem 0;
|
|
248
|
+
font-size: 1rem;
|
|
249
|
+
line-height: 1.5rem;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.cabloy-github-repositories__screen-link:hover {
|
|
253
|
+
color: var(--vp-c-brand-1);
|
|
254
|
+
}
|
|
255
|
+
</style>
|
|
@@ -63,6 +63,27 @@
|
|
|
63
63
|
color: #fdba74;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
@media (min-width: 960px) {
|
|
67
|
+
.cabloy-blogs-index .VPDoc:not(.has-sidebar) .container,
|
|
68
|
+
.cabloy-blogs-index .VPDoc:not(.has-sidebar) .content {
|
|
69
|
+
max-width: 1440px;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@media (min-width: 1280px) {
|
|
74
|
+
.cabloy-blogs-article .VPDoc.has-aside:not(.has-sidebar) .container {
|
|
75
|
+
max-width: 1216px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.cabloy-blogs-article .VPDoc.has-aside:not(.has-sidebar) .content {
|
|
79
|
+
max-width: 960px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.cabloy-blogs-article .VPDoc.has-aside:not(.has-sidebar) .content-container {
|
|
83
|
+
max-width: 896px;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
66
87
|
.cabloy-blog-grid {
|
|
67
88
|
display: grid;
|
|
68
89
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import DefaultTheme from 'vitepress/theme';
|
|
2
2
|
|
|
3
|
+
import GitHubRepositoriesNav from './components/GitHubRepositoriesNav.vue';
|
|
3
4
|
import './custom.css';
|
|
4
5
|
|
|
5
|
-
export default
|
|
6
|
+
export default {
|
|
7
|
+
extends: DefaultTheme,
|
|
8
|
+
enhanceApp({ app }) {
|
|
9
|
+
app.component('GitHubRepositoriesNav', GitHubRepositoriesNav);
|
|
10
|
+
},
|
|
11
|
+
};
|