cabloy 5.1.134 → 5.1.136
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-backend-scaffold/SKILL.md +9 -0
- package/.claude/skills/cabloy-zova-source-reading/SKILL.md +24 -1
- package/.claude/skills/cabloy-zova-source-reading/references/analysis-modes.md +12 -0
- package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +28 -0
- package/.github/workflows/playwright-e2e.yml +2 -2
- package/.github/workflows/vona-test-pg.yml +1 -1
- package/.github/workflows/vona-test-sqlite3.yml +1 -1
- package/CHANGELOG.md +38 -0
- package/CLAUDE.md +1 -0
- package/cabloy-docs/.vitepress/config.mjs +7 -0
- package/cabloy-docs/backend/markdown-guide.md +189 -0
- package/cabloy-docs/frontend/component-guide.md +6 -0
- package/cabloy-docs/frontend/css-in-js-guide.md +6 -0
- package/cabloy-docs/frontend/markdown-guide.md +258 -0
- package/cabloy-docs/frontend/page-guide.md +48 -0
- package/cabloy-docs/frontend/page-params-guide.md +4 -2
- package/cabloy-docs/frontend/page-route-guide.md +2 -2
- package/cabloy-docs/frontend/zova-router-under-the-hood.md +2 -0
- package/cabloy-docs/frontend/zova-source-reading-map.md +53 -9
- package/cabloy-docs/fullstack/a-pay-payment-suite.md +333 -0
- package/cabloy-docs/fullstack/payment-sandbox-configuration.md +285 -0
- package/e2e/specs/a-commerce/commerce.spec.ts +503 -47
- package/e2e/specs/cabloy-basic/basic.spec.ts +30 -0
- package/package.json +1 -1
- package/vona/env/.env +2 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/init/error/boilerplate/config/errors.ts +6 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/entity/{{resourceName}}.tsx_ +2 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/entity/{{resourceName}}.tsx_ +2 -0
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-utils/localeutil/package.json +1 -1
- package/vona/packages-utils/localeutil/src/util.ts +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/patches/{zova-core@5.1.82.patch → zova-core@5.1.84.patch} +2 -2
- package/vona/pnpm-lock.yaml +826 -270
- package/vona/pnpm-workspace.yaml +1 -1
- package/vona/src/suite/a-commerce/modules/commerce-catalog/package.json +1 -1
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/.metadata/index.ts +99 -1
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/bean/meta.index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/bean/meta.version.ts +19 -0
- 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/controller/product.ts +3 -3
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productContentUpdate.tsx +14 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productContentView.tsx +19 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productCreate.tsx +48 -3
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productPublicDetail.tsx +15 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productUpdate.tsx +48 -3
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productView.tsx +52 -3
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/entity/productContent.tsx +36 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/model/product.ts +6 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/model/productContent.ts +22 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/service/product.ts +82 -6
- package/vona/src/suite/a-commerce/modules/commerce-catalog/test/productContent.test.ts +271 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/test/productPresentation.test.ts +81 -2
- package/vona/src/suite/a-commerce/modules/commerce-payment/package.json +4 -1
- package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/payScene.commerceOrder.ts +32 -15
- package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentAttempt.test.ts +8 -2
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/.metadata/index.ts +8 -2
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/.metadata/locales.ts +18 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/bean/ssrMenu.home.ts +3 -1
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/index.ts +1 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/checkout.ts +13 -2
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/lib/providerReference.ts +18 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +43 -8
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +74 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/providerReference.test.ts +33 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/refundLifecycle.test.ts +73 -0
- package/vona/src/suite/a-commerce/modules/commerce-trade/test/reservationExpiry.test.ts +2 -0
- package/vona/src/suite/a-home/modules/home-user/src/.metadata/index.ts +30 -0
- package/vona/src/suite/a-home/modules/home-user/src/bean/meta.version.ts +7 -2
- package/vona/src/suite/a-home/modules/home-user/src/config/locale/en-us.ts +4 -1
- package/vona/src/suite/a-home/modules/home-user/src/config/locale/zh-cn.ts +4 -1
- package/vona/src/suite/a-home/modules/home-user/src/config/roles.ts +4 -4
- package/vona/src/suite/a-home/modules/home-user/src/entity/role.ts +2 -2
- package/vona/src/suite/a-home/modules/home-user/src/entity/user.ts +8 -1
- package/vona/src/suite/a-home/modules/home-user/src/service/userAdapter.ts +6 -2
- package/vona/src/suite/a-home/modules/home-user/test/passportTest.test.ts +1 -0
- package/vona/src/suite/a-home/modules/home-user/test/role.test.ts +4 -4
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/package.json +64 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/.metadata/index.ts +54 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/bean/bean.markdown.ts +137 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/src/index.ts +1 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-markdown/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/bean/bean.ssr.ts +17 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-ssr/src/lib/beanSsrSiteBase.ts +11 -3
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -1
- package/vona/src/suite-vendor/a-cabloy/tsconfig.json +3 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.version.ts +1 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperation.tsx +8 -1
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/providerOperationFailure.ts +8 -0
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +187 -29
- package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +4 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +1 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +0 -6
- package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/index.ts +1 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +2 -1
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/bean/payProvider.stripe.ts +458 -22
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/index.ts +1 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/lib/stripeGateway.ts +85 -0
- package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/types/index.ts +2 -0
- package/vona/src/suite-vendor/a-pay/package.json +5 -5
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/bean.passport.ts +14 -0
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/bean/guard.passport.ts +4 -0
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/role.ts +3 -2
- package/vona/src/suite-vendor/a-vona/modules/a-user/src/types/user.ts +4 -0
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-utils/logger/package.json +2 -2
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +3 -3
- package/zova/packages-zova/zova-core/src/core/sys/resource.ts +1 -1
- package/zova/packages-zova/zova-core/src/core/sys/util.ts +8 -0
- package/zova/pnpm-lock.yaml +570 -11
- package/zova/src/front/config/config/config.cabloyCommerce.ts +0 -6
- package/zova/src/suite/a-commerce/modules/commerce-catalog/package.json +3 -1
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/api/openapi/schemas.ts +236 -36
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/api/openapi/types.ts +4962 -2402
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite/a-commerce/modules/commerce-catalog/src/page/product/controller.tsx +8 -3
- package/zova/src/suite/a-commerce/modules/commerce-member/src/.metadata/index.ts +6 -7
- package/zova/src/suite/a-commerce/modules/commerce-member/src/page/address/controller.tsx +1 -3
- package/zova/src/suite/a-commerce/modules/commerce-member/src/routes.ts +2 -3
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +13 -20
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/cart/controller.tsx +3 -9
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +9 -11
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/order/controller.tsx +0 -1
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/orders/controller.tsx +6 -4
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +78 -17
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/routes.ts +10 -13
- package/zova/src/suite/a-home/modules/home-api/mock/passport.fake.ts +3 -3
- package/zova/src/suite/a-home/modules/home-api/src/.metadata/index.ts +31 -1
- package/zova/src/suite/a-home/modules/home-api/src/api/homeBaseSiteCatalog.ts +24 -0
- package/zova/src/suite/a-home/modules/home-api/src/api/openapi/schemas.ts +329 -17
- package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +5866 -1829
- package/zova/src/suite/a-home/modules/home-api/src/apiSchema/homeBaseSiteCatalog.ts +13 -0
- package/zova/src/suite/a-home/modules/home-base/src/component/page/controller.tsx +1 -0
- package/zova/src/suite/a-home/modules/home-layoutweb/src/component/layoutWeb/render.header.tsx +0 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/detail.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/package.json +61 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/component/formFieldMarkdown.ts +36 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/component/markdownHtml.ts +36 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/index.ts +147 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/.metadata/this.ts +2 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/controller.tsx +111 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/render.tsx +40 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/formFieldMarkdown/style.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/markdownHtml/controller.tsx +17 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/markdownHtml/render.tsx +9 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/component/markdownHtml/style.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/src/lib/richTextContentStyle.ts +142 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/tsconfig.build.json +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-markdown/tsconfig.json +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +25 -3
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/types/page.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/types/pageEntry.ts +1 -0
- package/zova/src/suite/cabloy-basic/package.json +1 -0
- 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 +7 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-logger/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/bean/sys.router.ts +3 -2
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/test/lib/ssrProfile.test.ts +88 -0
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +5 -5
- package/.claude/scheduled_tasks.lock +0 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Markdown Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how Markdown works across the Cabloy Basic frontend and backend. The practical contract is deliberately split into two values:
|
|
4
|
+
|
|
5
|
+
- **Markdown** is the editable source value.
|
|
6
|
+
- **HTML** is a server-generated presentation projection.
|
|
7
|
+
|
|
8
|
+
The Zova frontend provides the editor and the trusted-HTML display component. The Vona backend owns Markdown parsing, HTML rendering, and sanitization.
|
|
9
|
+
|
|
10
|
+
Use this page together with:
|
|
11
|
+
|
|
12
|
+
- [Form Guide](/frontend/form-guide)
|
|
13
|
+
- [Component Guide](/frontend/component-guide)
|
|
14
|
+
- [SSR ClientOnly](/frontend/ssr-client-only)
|
|
15
|
+
- [Backend Markdown Guide](/backend/markdown-guide)
|
|
16
|
+
|
|
17
|
+
> [!TIP]
|
|
18
|
+
> **Choose the component by data contract**
|
|
19
|
+
>
|
|
20
|
+
> - Use `basic-markdown:formFieldMarkdown` or `ZFormFieldMarkdown` to edit a Markdown string in a schema-driven form.
|
|
21
|
+
> - Use `basic-markdown:markdownHtml` or `ZMarkdownHtml` to display HTML that was generated and sanitized by the server.
|
|
22
|
+
> - Do not pass arbitrary user HTML, browser-generated HTML, or HTML from an unknown pipeline to `ZMarkdownHtml`.
|
|
23
|
+
|
|
24
|
+
## The Markdown data contract
|
|
25
|
+
|
|
26
|
+
A typical business resource keeps the authoring value and the rendered value separate:
|
|
27
|
+
|
|
28
|
+
| Surface | Owner | Purpose | Public use |
|
|
29
|
+
| --------------------- | --------------------------------- | ------------------------------ | ------------------------------------------------ |
|
|
30
|
+
| `descriptionMarkdown` | Form and backend business service | Editable source text | Returned to an authorized editor |
|
|
31
|
+
| `descriptionHtml` | Backend business service | Server-derived HTML projection | Returned to a public read model when appropriate |
|
|
32
|
+
| `ZMarkdownHtml` | Zova presentation layer | Displays the HTML projection | Receives trusted HTML only |
|
|
33
|
+
|
|
34
|
+
The frontend editor never needs to submit HTML. On every relevant write path, the backend should derive HTML again from the submitted Markdown. This prevents a caller from choosing a different HTML representation than the server's rendering and sanitization policy.
|
|
35
|
+
|
|
36
|
+
The Commerce Catalog product content flow is the reference implementation:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
Admin Markdown field
|
|
40
|
+
→ descriptionMarkdown
|
|
41
|
+
→ Vona service calls bean.markdown.renderHtml(...)
|
|
42
|
+
→ descriptionMarkdown + descriptionHtml are persisted
|
|
43
|
+
→ public detail exposes descriptionHtml
|
|
44
|
+
→ ZMarkdownHtml displays the server projection
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The backend companion page documents the rendering and sanitizer contract in detail: [Backend Markdown Guide](/backend/markdown-guide).
|
|
48
|
+
|
|
49
|
+
## Configure the Markdown form field
|
|
50
|
+
|
|
51
|
+
The schema-driven renderer is selected with `ZovaRender.field`:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { Api, v } from 'vona-module-a-openapiutils';
|
|
55
|
+
import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
|
|
56
|
+
|
|
57
|
+
export class EntityArticleContent {
|
|
58
|
+
@Api.field(v.optional(), ZovaRender.field('basic-markdown:formFieldMarkdown'))
|
|
59
|
+
bodyMarkdown?: string;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The current resource options type does not add business-specific options. The renderer identifier is the important part of the contract:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
ZovaRender.field('basic-markdown:formFieldMarkdown');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The field value remains Markdown. It is not the editor's HTML output, and it should not be replaced with `innerHTML` or a browser DOM serialization.
|
|
70
|
+
|
|
71
|
+
A field can keep the derived HTML hidden from the editor-facing form while still exposing it in a separate public DTO. The Commerce Catalog entity follows this shape:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
@Api.field(
|
|
75
|
+
v.optional(),
|
|
76
|
+
ZovaRender.field('basic-markdown:formFieldMarkdown'),
|
|
77
|
+
)
|
|
78
|
+
descriptionMarkdown?: string;
|
|
79
|
+
|
|
80
|
+
@Api.field(v.optional(), ZovaRender.visible(false))
|
|
81
|
+
descriptionHtml?: string;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The exact persistence and DTO design belongs to the business module. Markdown does not require a paired HTML column, but a persisted projection can avoid repeating rendering work for public reads.
|
|
85
|
+
|
|
86
|
+
## What `ZFormFieldMarkdown` does
|
|
87
|
+
|
|
88
|
+
`ZFormFieldMarkdown` is a Zova component with the normal Controller/Render/Style split.
|
|
89
|
+
|
|
90
|
+
| Zova role | Responsibility in this component |
|
|
91
|
+
| ---------- | ------------------------------------------------------------------------------------------- |
|
|
92
|
+
| Controller | Owns the TipTap `Editor`, Markdown `value`, `readonly` state, form callbacks, and lifecycle |
|
|
93
|
+
| Render | Composes the standard `ZFormField`, field shell, `ClientOnly`, and `EditorContent` |
|
|
94
|
+
| Style | Creates the shared rich-text CSS class used by the editor and HTML display component |
|
|
95
|
+
|
|
96
|
+
This is a Markdown editor integrated with `ZFormField`; it is not a generic HTML editor API.
|
|
97
|
+
|
|
98
|
+
### Editor initialization
|
|
99
|
+
|
|
100
|
+
The Controller creates the browser-dependent TipTap editor in `$controllerMounted`. Its extension set includes:
|
|
101
|
+
|
|
102
|
+
- Markdown
|
|
103
|
+
- StarterKit
|
|
104
|
+
- nested `TaskList` and `TaskItem`
|
|
105
|
+
- Image
|
|
106
|
+
- TableKit
|
|
107
|
+
- Highlight
|
|
108
|
+
|
|
109
|
+
The initial value is interpreted as Markdown through:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
content: this.value,
|
|
113
|
+
contentType: 'markdown',
|
|
114
|
+
editable: !this.readonly,
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The editor DOM receives the current component style class and a minimum-height/padding class. The outer field shell uses Cabloy Basic's DaisyUI-oriented classes and adds an error border when the host form field is invalid.
|
|
118
|
+
|
|
119
|
+
### Editor-to-form flow
|
|
120
|
+
|
|
121
|
+
When TipTap emits an update, the controller calls:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
editor.getMarkdown();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
and forwards the Markdown string to the host form field's `setValue()` method. When the editor loses focus, the controller forwards the event to the host form field's `handleBlur()` method, so the normal form validation and touched-state path remains in control.
|
|
128
|
+
|
|
129
|
+
There are no component-specific Vue `emit()` events. The effective interaction boundary is the standard form-field contract plus the editor's update and blur callbacks.
|
|
130
|
+
|
|
131
|
+
### Form-to-editor flow
|
|
132
|
+
|
|
133
|
+
The render bean binds the current `propsBucket.value` and `propsBucket.readonly` values from `ZFormField`. If the form value changes externally, the controller updates the editor with:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
this.editor.commands.setContent(value, { contentType: 'markdown' });
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A private syncing flag prevents that programmatic update from immediately writing the same value back to the form. When `readonly` changes, the controller calls `editor.setEditable(!readonly)`.
|
|
140
|
+
|
|
141
|
+
When the component is disposed, the controller calls `editor.destroy()` so TipTap's DOM and event resources are released.
|
|
142
|
+
|
|
143
|
+
### SSR and `ClientOnly`
|
|
144
|
+
|
|
145
|
+
TipTap's `EditorContent` is browser-dependent. The editor is therefore inside Zova's `ClientOnly` component:
|
|
146
|
+
|
|
147
|
+
- SSR renders a minimum-height placeholder;
|
|
148
|
+
- the browser creates the editor after mounting;
|
|
149
|
+
- the editor should not be described as an SSR-rendered interactive control.
|
|
150
|
+
|
|
151
|
+
The read-only HTML display path is different: it does not need TipTap or `ClientOnly` because it receives already-generated HTML.
|
|
152
|
+
|
|
153
|
+
## Display server-generated HTML with `ZMarkdownHtml`
|
|
154
|
+
|
|
155
|
+
Use `ZMarkdownHtml` when a page has an HTML projection from a trusted backend rendering path:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { ZMarkdownHtml } from 'zova-module-basic-markdown';
|
|
159
|
+
|
|
160
|
+
export class ControllerArticlePage {
|
|
161
|
+
public render() {
|
|
162
|
+
const html = this.article?.bodyHtml;
|
|
163
|
+
if (!html) return null;
|
|
164
|
+
|
|
165
|
+
return <ZMarkdownHtml class="article-content" html={html} />;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`ZMarkdownHtml` accepts `class?: string` and `html?: string`. Its render bean produces one `div`, applies the shared rich-text class, and assigns the `html` prop through `innerHTML`.
|
|
171
|
+
|
|
172
|
+
> [!WARNING]
|
|
173
|
+
> `ZMarkdownHtml` does **not** sanitize its input. It does not parse Markdown. Its caller is responsible for providing HTML generated by `bean.markdown.renderHtml(...)` or by an equivalent, explicitly trusted sanitizer pipeline. Treating this component as a general-purpose safe HTML sink is incorrect.
|
|
174
|
+
|
|
175
|
+
The shared rich-text style covers headings, paragraphs, ordered and unordered lists, nested task-list structure, blockquotes, inline and block code, highlight classes, links, images, horizontal rules, and tables. The style is a presentation layer; it does not change the sanitizer or URL policy.
|
|
176
|
+
|
|
177
|
+
A public product page uses this boundary:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
publicProduct.descriptionHtml
|
|
181
|
+
→ product page controller
|
|
182
|
+
→ <ZMarkdownHtml html={descriptionHtml} />
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The public response should not expose the editable Markdown unless that is an intentional access-controlled contract.
|
|
186
|
+
|
|
187
|
+
## Supported Markdown features
|
|
188
|
+
|
|
189
|
+
The current frontend and backend extension sets are aligned around these features:
|
|
190
|
+
|
|
191
|
+
- StarterKit document basics: headings, paragraphs, emphasis, strong text, strike-through, blockquotes, horizontal rules, lists, and code;
|
|
192
|
+
- GitHub-flavored Markdown parsing on the backend;
|
|
193
|
+
- nested task lists;
|
|
194
|
+
- images;
|
|
195
|
+
- tables;
|
|
196
|
+
- highlight support in the TipTap document/rendering extensions.
|
|
197
|
+
|
|
198
|
+
The backend Markdown manager is configured with two-space indentation. Treat this as the current parser/serializer convention, not as a reason to reject every inbound document that uses another indentation style.
|
|
199
|
+
|
|
200
|
+
A representative input is:
|
|
201
|
+
|
|
202
|
+
````markdown
|
|
203
|
+
# Product details
|
|
204
|
+
|
|
205
|
+
This is **durable** content.
|
|
206
|
+
|
|
207
|
+
- [ ] Draft the description
|
|
208
|
+
- [x] Review the link
|
|
209
|
+
|
|
210
|
+
| Field | Value |
|
|
211
|
+
| ------ | ----- |
|
|
212
|
+
| Status | Ready |
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
const ready = true;
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
[Project home](https://example.com)
|
|
219
|
+
|
|
220
|
+

|
|
221
|
+
````
|
|
222
|
+
|
|
223
|
+
The exact HTML output is governed by the backend renderer and sanitizer. Do not infer support for arbitrary raw HTML, iframe embeds, inline styles, `data:` URLs, or custom classes from the presence of a TipTap extension.
|
|
224
|
+
|
|
225
|
+
## Common mistakes
|
|
226
|
+
|
|
227
|
+
1. **Storing editor HTML as the form value**
|
|
228
|
+
- Keep the form value as Markdown. The editor uses `getMarkdown()` and `setContent(..., { contentType: 'markdown' })` for that reason.
|
|
229
|
+
|
|
230
|
+
2. **Trusting a client-supplied HTML field**
|
|
231
|
+
- Ignore or overwrite submitted HTML. Rebuild it on the server from Markdown.
|
|
232
|
+
|
|
233
|
+
3. **Passing unknown HTML to `ZMarkdownHtml`**
|
|
234
|
+
- The component uses `innerHTML` and has no frontend sanitizer.
|
|
235
|
+
|
|
236
|
+
4. **Expecting task checkboxes to be interactive**
|
|
237
|
+
- The backend transforms rendered task inputs into disabled checkboxes for display.
|
|
238
|
+
|
|
239
|
+
5. **Expecting a toolbar or upload workflow from this module**
|
|
240
|
+
- The module supplies an editor surface, not a toolbar specification, image upload service, remote image importer, or configurable plugin registry.
|
|
241
|
+
|
|
242
|
+
6. **Treating browser validation or presentation CSS as security**
|
|
243
|
+
- The backend renderer/sanitizer is the relevant content boundary. External image availability, privacy, CSP, and application authorization remain separate concerns.
|
|
244
|
+
|
|
245
|
+
## Frontend checklist
|
|
246
|
+
|
|
247
|
+
Before integrating Markdown into a Zova feature, verify:
|
|
248
|
+
|
|
249
|
+
1. The editable field is a string containing Markdown.
|
|
250
|
+
2. The field metadata uses `basic-markdown:formFieldMarkdown`.
|
|
251
|
+
3. The form value is updated through the editor's Markdown API, not HTML.
|
|
252
|
+
4. Blur and readonly behavior still pass through the standard `ZFormField` contract.
|
|
253
|
+
5. The interactive editor is inside the expected `ClientOnly` boundary.
|
|
254
|
+
6. Public display uses `ZMarkdownHtml` only with server-derived sanitized HTML.
|
|
255
|
+
7. Public DTOs do not expose editable Markdown unless that is intentional.
|
|
256
|
+
8. The backend write path regenerates the HTML projection for every create, update, import, or backfill path that can change Markdown.
|
|
257
|
+
|
|
258
|
+
For the backend API and security details, continue with [Backend Markdown Guide](/backend/markdown-guide).
|
|
@@ -164,6 +164,54 @@ class StylePageCounter extends BeanStyleBase {
|
|
|
164
164
|
}
|
|
165
165
|
```
|
|
166
166
|
|
|
167
|
+
### Companion-member access in a split page
|
|
168
|
+
|
|
169
|
+
The three files have distinct primary ownership:
|
|
170
|
+
|
|
171
|
+
- **Controller** owns state, actions, and lifecycle work.
|
|
172
|
+
- **Render** owns TSX composition.
|
|
173
|
+
- **Style** owns scoped CSS-in-JS setup.
|
|
174
|
+
|
|
175
|
+
Within one split page or component, ordinary access to a companion member uses `this.member`; it does not require `@Use()` or a container lookup. Runtime lookup proceeds in this order:
|
|
176
|
+
|
|
177
|
+
| Current bean | Direct member lookup order |
|
|
178
|
+
| ------------ | ----------------------------------- |
|
|
179
|
+
| Controller | Controller only |
|
|
180
|
+
| Style | Style, then Controller |
|
|
181
|
+
| Render | Render, then Controller, then Style |
|
|
182
|
+
|
|
183
|
+
A member declared on the current bean shadows the next fallback surface. Keep names distinct when a Style class name and a Controller state/action could otherwise collide.
|
|
184
|
+
|
|
185
|
+
For example, the Render bean can read Controller state and actions directly, while it receives the generated Style class through the same direct access surface:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
@Render()
|
|
189
|
+
class RenderPageCounter extends BeanRenderBase {
|
|
190
|
+
public render() {
|
|
191
|
+
return <div class={this.cTextCenter}>count: {this.count}</div>;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The Style bean can likewise read Controller state while owning the generated class name:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
@Style()
|
|
200
|
+
class StylePageCounter extends BeanStyleBase {
|
|
201
|
+
cTextCenter: string;
|
|
202
|
+
|
|
203
|
+
protected async __init__() {
|
|
204
|
+
this.cTextCenter = this.$style({
|
|
205
|
+
opacity: this.count > 0 ? 1 : 0.6,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use explicit `@Use()` or bean/container lookup when the task needs a specific named instance, selector or scope boundary, identity passing, lifecycle control, or other interop. Those mechanisms are not the default for same-component companion access.
|
|
212
|
+
|
|
213
|
+
For the generated typing and runtime evidence behind this rule, see [Zova Source Reading Map](/frontend/zova-source-reading-map#3-split-controller-render-and-style-companion-access).
|
|
214
|
+
|
|
167
215
|
### More-file growth
|
|
168
216
|
|
|
169
217
|
When the page continues to grow, you can keep splitting responsibilities instead of forcing one large controller or render bean to absorb everything.
|
|
@@ -30,7 +30,9 @@ Because route params arrive as strings at the URL level, the schema is also wher
|
|
|
30
30
|
|
|
31
31
|
## Route record requirements
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
A page route that declares or consumes dynamic params **must** define `route.name`. Do not use an unnamed route with params.
|
|
34
|
+
|
|
35
|
+
The route name is required because Zova uses the named route identity to generate and resolve the page's typed `params` schema at runtime. This initializes the controller's `$params` surface during SSR and client navigation. A params schema declaration alone does not make `$params` available if the route is unnamed.
|
|
34
36
|
|
|
35
37
|
Representative route idea:
|
|
36
38
|
|
|
@@ -82,6 +84,6 @@ this.$router.push(url);
|
|
|
82
84
|
When adding or editing param-driven page behavior:
|
|
83
85
|
|
|
84
86
|
1. use the Zova refactor command when possible
|
|
85
|
-
2.
|
|
87
|
+
2. define `route.name` whenever the route has dynamic params
|
|
86
88
|
3. regenerate metadata when route typing depends on it
|
|
87
89
|
4. use `this.$params` and typed router helpers instead of manual parsing
|
|
@@ -42,7 +42,7 @@ This matters because path generation is modular by default.
|
|
|
42
42
|
|
|
43
43
|
## `name`
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
A route with dynamic params must define `name`. Typed param-aware routing uses the named route identity to generate and resolve the page's runtime params schema; an unnamed parameterized route does not provide the required `$params` contract.
|
|
46
46
|
|
|
47
47
|
## `component`
|
|
48
48
|
|
|
@@ -145,7 +145,7 @@ When editing page routing, do not only change the URL string.
|
|
|
145
145
|
|
|
146
146
|
It should also check whether the route change affects:
|
|
147
147
|
|
|
148
|
-
- params typing
|
|
148
|
+
- params typing and the required `route.name` for dynamic params
|
|
149
149
|
- auth behavior
|
|
150
150
|
- layout behavior
|
|
151
151
|
- whether `meta.locale` should participate in URL-locale behavior
|
|
@@ -417,6 +417,8 @@ When `_initControllerRoute(...)` updates a page controller, it resolves the rout
|
|
|
417
417
|
- `pageNameSchemas` for named routes
|
|
418
418
|
- `pagePathSchemas` for path-keyed routes
|
|
419
419
|
|
|
420
|
+
For page routes with dynamic params, define `route.name` and use the named-route metadata path. Do not rely on an unnamed parameterized route: the generated path-keyed record does not establish the typed controller `$params` contract.
|
|
421
|
+
|
|
420
422
|
Those schema records are generated into module metadata.
|
|
421
423
|
|
|
422
424
|
Representative generator source:
|
|
@@ -107,7 +107,51 @@ Use this path when you are asking questions like:
|
|
|
107
107
|
- `component.ts` shows how render is patched toward controller/render beans
|
|
108
108
|
- the concrete component controller shows the public authoring pattern for the case you care about
|
|
109
109
|
|
|
110
|
-
## 3.
|
|
110
|
+
## 3. Split Controller, Render, and Style companion access
|
|
111
|
+
|
|
112
|
+
Use this path when you are asking questions like:
|
|
113
|
+
|
|
114
|
+
- why can a Render Bean read Controller state with `this.member`?
|
|
115
|
+
- why can a Style Bean read Controller state, or a Render Bean read a generated Style class?
|
|
116
|
+
- does generated type augmentation create the runtime fallback behavior?
|
|
117
|
+
- when is `@Use()` or an explicit bean lookup actually needed?
|
|
118
|
+
|
|
119
|
+
### Read the docs first
|
|
120
|
+
|
|
121
|
+
- [Page Guide](/frontend/page-guide#companion-member-access-in-a-split-page)
|
|
122
|
+
- [Component Guide](/frontend/component-guide#controllerref-and-internal-companion-access)
|
|
123
|
+
- [CSS-in-JS Guide](/frontend/css-in-js-guide#style-beans-in-a-split-controllerrenderstyle-component)
|
|
124
|
+
|
|
125
|
+
### Then read source in this order
|
|
126
|
+
|
|
127
|
+
1. `zova/src/suite/a-home/modules/home-layoutadmin/src/.metadata/component/layoutAdmin.ts`
|
|
128
|
+
2. `zova/packages-zova/zova-core/src/composables/useController.ts`
|
|
129
|
+
3. `zova/packages-zova/zova-core/src/bean/beanStyleBase.ts`
|
|
130
|
+
4. `zova/packages-zova/zova-core/src/bean/beanControllerLike.ts`
|
|
131
|
+
5. `zova/packages-zova/zova-core/src/bean/beanRenderLike.ts`
|
|
132
|
+
6. `zova/packages-zova/zova-core/src/bean/beanContainer.ts`
|
|
133
|
+
7. `zova/packages-zova/zova-core/src/core/context/component.ts`
|
|
134
|
+
|
|
135
|
+
### What each file clarifies
|
|
136
|
+
|
|
137
|
+
- `layoutAdmin.ts` shows generated type augmentation: Style extends Controller and Render extends Style, so TypeScript exposes the companion members.
|
|
138
|
+
- `useController.ts` creates the Controller first, then Style, then Render in one component context.
|
|
139
|
+
- `beanStyleBase.ts` and `beanControllerLike.ts` show Style's runtime fallback from Style to Controller.
|
|
140
|
+
- `beanRenderLike.ts` extends that fallback so Render resolves Render, then Controller, then Style.
|
|
141
|
+
- `beanContainer.ts` shows that proxy handling keeps locally declared members local and asks `__get__`/`__set__` only for a missing member; this produces own-member shadowing.
|
|
142
|
+
- `component.ts` shows that the runtime uses the Controller's render when it has one and otherwise selects the Render Bean.
|
|
143
|
+
|
|
144
|
+
The source-confirmed direct lookup order is:
|
|
145
|
+
|
|
146
|
+
| Current bean | Direct member lookup order |
|
|
147
|
+
| ------------ | ----------------------------------- |
|
|
148
|
+
| Controller | Controller only |
|
|
149
|
+
| Style | Style, then Controller |
|
|
150
|
+
| Render | Render, then Controller, then Style |
|
|
151
|
+
|
|
152
|
+
Use `this.member` for normal same-component access. Use explicit `@Use()` or a bean/container lookup only when you need a named or specific instance, selector or scope boundary, identity passing, lifecycle control, or other interop. Generated type augmentation makes this authoring surface type-safe; the proxy fallback is the separate runtime mechanism that supplies missing companion members.
|
|
153
|
+
|
|
154
|
+
## 4. Bean lifecycle, instance scope, and helper APIs
|
|
111
155
|
|
|
112
156
|
Use this path when you are asking questions like:
|
|
113
157
|
|
|
@@ -134,7 +178,7 @@ Use this path when you are asking questions like:
|
|
|
134
178
|
- `beanContainer.ts` shows init, inject, and dispose flow
|
|
135
179
|
- `context/util.ts` shows how instance scope is applied around framework operations
|
|
136
180
|
|
|
137
|
-
##
|
|
181
|
+
## 5. Page routing, params, query, and layout-oriented route behavior
|
|
138
182
|
|
|
139
183
|
Use this path when you are asking questions like:
|
|
140
184
|
|
|
@@ -180,9 +224,9 @@ If your next question becomes any of these:
|
|
|
180
224
|
then continue immediately with:
|
|
181
225
|
|
|
182
226
|
- [Router View Hosts Guide](/frontend/router-view-hosts-guide)
|
|
183
|
-
- section
|
|
227
|
+
- section 6 on this page for the compact source-reading path
|
|
184
228
|
|
|
185
|
-
##
|
|
229
|
+
## 6. Router-view hosts, routertabs, and routerstack
|
|
186
230
|
|
|
187
231
|
Use this path when you are asking questions like:
|
|
188
232
|
|
|
@@ -225,7 +269,7 @@ If your next question becomes specifically about task-level title, dirty state,
|
|
|
225
269
|
- `model/stack.ts` shows the fullPath-based stack identity model and recency-based pruning
|
|
226
270
|
- active layout consumers show how the current shell turns host state into visible Admin or Web behavior
|
|
227
271
|
|
|
228
|
-
##
|
|
272
|
+
## 7. Model state, cache-oriented state, and broader data ownership
|
|
229
273
|
|
|
230
274
|
Use this path when you are asking questions like:
|
|
231
275
|
|
|
@@ -253,7 +297,7 @@ Use this path when you are asking questions like:
|
|
|
253
297
|
- built-in model beans show how the architecture is used in nontrivial cases
|
|
254
298
|
- consuming code shows whether the state really belongs in the model or only uses the model result
|
|
255
299
|
|
|
256
|
-
##
|
|
300
|
+
## 8. Command scene and command-bean invocation
|
|
257
301
|
|
|
258
302
|
Use this path when you are asking questions like:
|
|
259
303
|
|
|
@@ -318,7 +362,7 @@ Use this path when you are asking questions like:
|
|
|
318
362
|
- `controller.tsx` shows app-level meta setup, behavior-holder initialization, and behavior-wrapped `RouterView` render
|
|
319
363
|
- `config.ts` shows the root behavior injection point through `scope.config.behaviors`
|
|
320
364
|
|
|
321
|
-
##
|
|
365
|
+
## 9. Behavior scene and render-time interception
|
|
322
366
|
|
|
323
367
|
Use this path when you are asking questions like:
|
|
324
368
|
|
|
@@ -345,7 +389,7 @@ Use this path when you are asking questions like:
|
|
|
345
389
|
- composer/service files show how behavior chains are normalized and executed
|
|
346
390
|
- host dependencies show why host-scoped injection is part of the behavior design
|
|
347
391
|
|
|
348
|
-
##
|
|
392
|
+
## 10. SSR runtime and hydration handoff
|
|
349
393
|
|
|
350
394
|
Use this path when you are asking questions like:
|
|
351
395
|
|
|
@@ -374,7 +418,7 @@ Use this path when you are asking questions like:
|
|
|
374
418
|
- page/controller/model files show whether the bug is actually page-level logic
|
|
375
419
|
- the Vona side shows whether the problem happens before the Zova runtime is even entered
|
|
376
420
|
|
|
377
|
-
##
|
|
421
|
+
## 11. Modules, suites, and architectural placement
|
|
378
422
|
|
|
379
423
|
Use this path when you are asking questions like:
|
|
380
424
|
|