@pisell/private-materials 6.12.166 → 6.12.167

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 (144) hide show
  1. package/build/lowcode/assets-daily.json +11 -11
  2. package/build/lowcode/assets-dev.json +2 -2
  3. package/build/lowcode/assets-prod.json +11 -11
  4. package/build/lowcode/meta.js +1 -1
  5. package/build/lowcode/render/default/view.js +11 -11
  6. package/build/lowcode/view.js +11 -11
  7. package/es/components/HDLKiosk/app/ScreenRenderer.js +1 -1
  8. package/es/components/HDLKiosk/app/views.js +2 -1
  9. package/es/components/HDLKiosk/app/workflow/barcode.js +15 -32
  10. package/es/components/HDLKiosk/app/workflow/{guards.js → predicates.js} +9 -9
  11. package/es/components/HDLKiosk/app/workflow/quick.js +3 -6
  12. package/es/components/HDLKiosk/app/workflow/registry.js +1 -1
  13. package/es/components/HDLKiosk/app/workflow/scale.js +3 -12
  14. package/es/components/HDLKiosk/components/shell/HDLKioskBottomBarHost/index.js +12 -10
  15. package/es/components/HDLKiosk/components/shell/HDLKioskShell/index.js +1 -0
  16. package/es/components/HDLKiosk/docs/00-/346/226/207/344/273/266/345/257/274/350/210/252.md +7 -7
  17. package/es/components/HDLKiosk/docs/01-/346/240/270/345/277/203/350/277/220/350/241/214/346/234/272/345/210/266.md +46 -37
  18. package/es/components/HDLKiosk/docs/03-/344/270/232/345/212/241/346/265/201/347/250/213/344/270/216/344/272/244/344/272/222/350/247/204/345/210/231.md +2 -2
  19. package/es/components/HDLKiosk/docs/05-/344/272/244/346/230/223/345/256/211/345/205/250.md +1 -1
  20. package/es/components/HDLKiosk/docs/07-/345/220/216/347/273/255/345/267/245/344/275/234.md +1 -1
  21. package/es/components/HDLKiosk/docs/README.md +2 -2
  22. package/es/components/HDLKiosk/docs//345/256/236/346/226/275/350/277/233/345/272/246.md +3 -3
  23. package/es/components/HDLKiosk/docs//346/236/266/346/236/204/350/256/276/350/256/241.md +11 -11
  24. package/es/components/HDLKiosk/index.js +0 -1
  25. package/es/components/HDLKiosk/kernel/README.md +8 -9
  26. package/es/components/HDLKiosk/kernel/application/defineApplication.js +18 -19
  27. package/es/components/HDLKiosk/kernel/application/defineSlice.js +8 -13
  28. package/es/components/HDLKiosk/kernel/workflow/actor.js +88 -28
  29. package/es/components/HDLKiosk/kernel/workflow/compiler.js +28 -29
  30. package/es/components/HDLKiosk/kernel/workflow/selectors.js +9 -0
  31. package/es/components/HDLKiosk/runtime/HDLKioskWorkflowView.js +8 -7
  32. package/es/components/HDLKiosk/runtime/controllers/useHDLKioskCustomerController.js +1 -1
  33. package/es/components/HDLKiosk/runtime/createWorkflowSession.js +3 -3
  34. package/es/components/HDLKiosk/runtime/domainTransitionRunner.js +9 -9
  35. package/es/components/HDLKiosk/runtime/workflowEvent.js +9 -4
  36. package/es/components/HDLKiosk/runtime/workflowLifecycle.js +3 -3
  37. package/es/components/HDLKiosk/slices/catalog/PrimaryProductPromptDialog/index.js +1 -2
  38. package/es/components/HDLKiosk/slices/catalog/Screen/index.js +11 -38
  39. package/es/components/HDLKiosk/slices/catalog/Screen/model.js +1 -5
  40. package/es/components/HDLKiosk/slices/catalog/slice.js +4 -16
  41. package/es/components/HDLKiosk/slices/catalog/view.js +4 -6
  42. package/es/components/HDLKiosk/slices/customization/Screen/index.js +7 -9
  43. package/es/components/HDLKiosk/slices/customization/operation.js +23 -4
  44. package/es/components/HDLKiosk/slices/customization/slice.js +5 -9
  45. package/es/components/HDLKiosk/slices/customization/view.js +3 -2
  46. package/es/components/HDLKiosk/slices/fulfillment/Screen/index.js +6 -6
  47. package/es/components/HDLKiosk/slices/fulfillment/slice.js +5 -8
  48. package/es/components/HDLKiosk/slices/identity/Screen/index.js +18 -12
  49. package/es/components/HDLKiosk/slices/identity/slice.js +4 -11
  50. package/es/components/HDLKiosk/slices/index.js +3 -2
  51. package/es/components/HDLKiosk/slices/payment/Screen/index.js +5 -5
  52. package/es/components/HDLKiosk/slices/payment/slice.js +7 -8
  53. package/es/components/HDLKiosk/slices/primaryProductPrompt/slice.js +14 -0
  54. package/es/components/HDLKiosk/slices/primaryProductPrompt/view.js +23 -0
  55. package/es/components/HDLKiosk/utils.js +3 -2
  56. package/es/components/bigSale/components/BookingFloatingPanel/useBookingPanelController.js +1 -0
  57. package/es/components/bigSale/components/TimeBar/index.js +1 -0
  58. package/es/components/bigSale/dialog.js +1 -0
  59. package/es/components/bigSale/hooks/useBigSaleContext.js +1 -0
  60. package/es/components/bigSale/hooks/useSelectFloorMapResourceForBigSale.js +1 -0
  61. package/es/components/bigSale/index.js +1 -0
  62. package/es/components/bigSale/templates/HDLKiosk/auth/HDLKioskIdentityAuthPanel.js +17 -2
  63. package/es/components/bigSale/templates/Kiosk/index.js +1 -0
  64. package/es/components/sharedSecondaryBigSale/SharedSecondaryBigSaleReceiver.js +1 -0
  65. package/es/index.js +2 -0
  66. package/es/plus/pisellReservation/data/bookingToReservationTables.js +1 -1
  67. package/es/plus/pisellReservation/floorMap/floorPlanApi.js +1 -1
  68. package/es/plus/pisellReservation/floorMap/reservationMock.js +1 -1
  69. package/es/plus/pisellReservation/index.js +17 -0
  70. package/es/plus/saleDetail/Base.js +1 -0
  71. package/es/plus/saleDetail/components/Client/hooks/useClientSelectorProps.js +1 -0
  72. package/es/plus/saleDetail/components/DetailActions/index.js +1 -0
  73. package/es/plus/saleDetail/components/SummaryFinancialSections/index.js +1 -0
  74. package/es/plus/saleDetail/index.js +1 -0
  75. package/es/plus/saleDetail/tmpl/B/components/Dish/index.js +1 -0
  76. package/es/plus/saleDetail/tmpl/B/index.js +1 -0
  77. package/es/plus/salesSdk/index.js +52 -0
  78. package/es/plus/salesSdk/utils/addProductWithFlowDebounced.js +1 -1
  79. package/es/plus/salesSdk/utils/bookingDate.js +1 -1
  80. package/es/plus/salesSdk/utils/customerAuthGate.js +1 -1
  81. package/es/plus/salesSdk/utils/enrichCartLineHolderDisplay.js +1 -1
  82. package/es/pro/Login2.0/AuthModal.d.ts +1 -1
  83. package/es/pro/Login2.0/Login2.d.ts +2 -0
  84. package/es/pro/Login2.0/Login2.js +9 -1
  85. package/es/pro/Login2.0/Register2.d.ts +2 -0
  86. package/es/pro/Login2.0/Register2.js +9 -1
  87. package/es/pro/Login2.0/index.d.ts +2 -1
  88. package/es/pro/Login2.0/shared/types.d.ts +1 -1
  89. package/lib/components/HDLKiosk/app/ScreenRenderer.js +1 -1
  90. package/lib/components/HDLKiosk/app/views.js +2 -1
  91. package/lib/components/HDLKiosk/app/workflow/barcode.js +14 -31
  92. package/lib/components/HDLKiosk/app/workflow/{guards.js → predicates.js} +9 -9
  93. package/lib/components/HDLKiosk/app/workflow/quick.js +3 -6
  94. package/lib/components/HDLKiosk/app/workflow/registry.js +1 -1
  95. package/lib/components/HDLKiosk/app/workflow/scale.js +3 -12
  96. package/lib/components/HDLKiosk/components/shell/HDLKioskBottomBarHost/index.js +12 -10
  97. package/lib/components/HDLKiosk/components/shell/HDLKioskShell/index.js +1 -0
  98. package/lib/components/HDLKiosk/docs/00-/346/226/207/344/273/266/345/257/274/350/210/252.md +7 -7
  99. package/lib/components/HDLKiosk/docs/01-/346/240/270/345/277/203/350/277/220/350/241/214/346/234/272/345/210/266.md +46 -37
  100. package/lib/components/HDLKiosk/docs/03-/344/270/232/345/212/241/346/265/201/347/250/213/344/270/216/344/272/244/344/272/222/350/247/204/345/210/231.md +2 -2
  101. package/lib/components/HDLKiosk/docs/05-/344/272/244/346/230/223/345/256/211/345/205/250.md +1 -1
  102. package/lib/components/HDLKiosk/docs/07-/345/220/216/347/273/255/345/267/245/344/275/234.md +1 -1
  103. package/lib/components/HDLKiosk/docs/README.md +2 -2
  104. package/lib/components/HDLKiosk/docs//345/256/236/346/226/275/350/277/233/345/272/246.md +3 -3
  105. package/lib/components/HDLKiosk/docs//346/236/266/346/236/204/350/256/276/350/256/241.md +11 -11
  106. package/lib/components/HDLKiosk/index.js +0 -1
  107. package/lib/components/HDLKiosk/kernel/README.md +8 -9
  108. package/lib/components/HDLKiosk/kernel/application/defineApplication.js +18 -19
  109. package/lib/components/HDLKiosk/kernel/application/defineSlice.js +8 -14
  110. package/lib/components/HDLKiosk/kernel/workflow/actor.js +88 -28
  111. package/lib/components/HDLKiosk/kernel/workflow/compiler.js +28 -29
  112. package/lib/components/HDLKiosk/kernel/workflow/selectors.js +10 -0
  113. package/lib/components/HDLKiosk/runtime/HDLKioskWorkflowView.js +8 -7
  114. package/lib/components/HDLKiosk/runtime/controllers/useHDLKioskCustomerController.js +1 -1
  115. package/lib/components/HDLKiosk/runtime/createWorkflowSession.js +3 -3
  116. package/lib/components/HDLKiosk/runtime/domainTransitionRunner.js +9 -9
  117. package/lib/components/HDLKiosk/runtime/workflowEvent.js +9 -4
  118. package/lib/components/HDLKiosk/runtime/workflowLifecycle.js +3 -3
  119. package/lib/components/HDLKiosk/slices/catalog/PrimaryProductPromptDialog/index.js +1 -2
  120. package/lib/components/HDLKiosk/slices/catalog/Screen/index.js +13 -40
  121. package/lib/components/HDLKiosk/slices/catalog/Screen/model.js +0 -5
  122. package/lib/components/HDLKiosk/slices/catalog/slice.js +2 -15
  123. package/lib/components/HDLKiosk/slices/catalog/view.js +4 -6
  124. package/lib/components/HDLKiosk/slices/customization/Screen/index.js +7 -9
  125. package/lib/components/HDLKiosk/slices/customization/operation.js +23 -3
  126. package/lib/components/HDLKiosk/slices/customization/slice.js +3 -7
  127. package/lib/components/HDLKiosk/slices/customization/view.js +3 -2
  128. package/lib/components/HDLKiosk/slices/fulfillment/Screen/index.js +6 -6
  129. package/lib/components/HDLKiosk/slices/fulfillment/slice.js +4 -7
  130. package/lib/components/HDLKiosk/slices/identity/Screen/index.js +17 -11
  131. package/lib/components/HDLKiosk/slices/identity/slice.js +4 -11
  132. package/lib/components/HDLKiosk/slices/index.js +2 -1
  133. package/lib/components/HDLKiosk/slices/payment/Screen/index.js +5 -5
  134. package/lib/components/HDLKiosk/slices/payment/slice.js +6 -7
  135. package/lib/components/HDLKiosk/slices/primaryProductPrompt/slice.js +14 -0
  136. package/lib/components/HDLKiosk/slices/primaryProductPrompt/view.js +25 -0
  137. package/lib/components/HDLKiosk/utils.js +3 -2
  138. package/lib/components/bigSale/templates/HDLKiosk/auth/HDLKioskIdentityAuthPanel.js +16 -1
  139. package/lib/pro/Login2.0/Login2.d.ts +2 -0
  140. package/lib/pro/Login2.0/Login2.js +9 -1
  141. package/lib/pro/Login2.0/Register2.d.ts +2 -0
  142. package/lib/pro/Login2.0/Register2.js +9 -1
  143. package/package.json +3 -3
  144. package/es/private-materials/lowcode/authmodal/meta.d.ts +0 -1
@@ -27,7 +27,7 @@ function ScreenRenderer({ definition, snapshot, sales, send, commandState = { bu
27
27
  const modalBaseNodeId = ((_activeNode$presentat = activeNode.presentation) === null || _activeNode$presentat === void 0 ? void 0 : _activeNode$presentat.mode) === "modal" ? activeNode.presentation.base : void 0;
28
28
  const renderedNode = definition.nodes[modalBaseNodeId !== null && modalBaseNodeId !== void 0 ? modalBaseNodeId : snapshot.nodeId];
29
29
  const bindActivity = (activityId) => (action) => {
30
- send(action.kind === "outcome" ? _objectSpread2(_objectSpread2({}, action), {}, { activityId }) : action);
30
+ send(action.kind === "intent" || action.kind === "navigation" ? _objectSpread2(_objectSpread2({}, action), {}, { activityId }) : action);
31
31
  };
32
32
  const viewSnapshot = commandState.busy || commandState.error ? _objectSpread2(_objectSpread2({}, snapshot), {}, {
33
33
  status: commandState.busy ? "running" : snapshot.status,
@@ -3,13 +3,14 @@ import { renderCustomization } from "../slices/customization/view.js";
3
3
  import { renderFulfillment } from "../slices/fulfillment/view.js";
4
4
  import { renderIdentity } from "../slices/identity/view.js";
5
5
  import { renderPayment } from "../slices/payment/view.js";
6
+ import { renderPrimaryProductPrompt } from "../slices/primaryProductPrompt/view.js";
6
7
  //#region src/components/HDLKiosk/app/views.ts
7
8
  const hdlKioskViews = Object.freeze({
8
9
  identity: renderIdentity,
9
10
  fulfillment: renderFulfillment,
10
11
  customization: renderCustomization,
11
12
  catalog: renderCatalog,
12
- catalogWithPrimaryProductPrompt: renderCatalog,
13
+ primaryProductPrompt: renderPrimaryProductPrompt,
13
14
  paymentMethod: renderPayment,
14
15
  paymentCompleted: renderPayment
15
16
  });
@@ -1,54 +1,37 @@
1
1
  import { defineWorkflow } from "../../kernel/application/defineApplication.js";
2
- import { catalogWithPrimaryProductPromptSlice } from "../../slices/catalog/slice.js";
2
+ import { catalogSlice } from "../../slices/catalog/slice.js";
3
3
  import { customizationSlice } from "../../slices/customization/slice.js";
4
4
  import { fulfillmentSlice } from "../../slices/fulfillment/slice.js";
5
5
  import { paymentCompletedSlice, paymentMethodSlice } from "../../slices/payment/slice.js";
6
+ import { primaryProductPromptSlice } from "../../slices/primaryProductPrompt/slice.js";
6
7
  //#region src/components/HDLKiosk/app/workflow/barcode.ts
7
8
  /**
8
9
  * 文件角色:声明 Barcode 入口的有序业务步骤。
9
10
  * 运行机制:Workflow 从 Catalog 开始;首码在 Session readyForCommands 后由全局 Scanner
10
- * Controller 消费。Catalog 先询问是否需要麻辣烫,需要时进入 Customization;
11
- * 最终统一在 Payment 前确认 Fulfillment。
11
+ * Controller 消费。Catalog 完成补充购物后进入独立的主商品询问 Modal,需要时进入
12
+ * Customization;无论选择或拒绝,最终都直接进入 Fulfillment。
12
13
  * 边界:设备扫码属于 Session 输入,不是可前进/后退的 Activity;支付 prepare 由支付
13
14
  * Operation 在 dispatch 前统一保证,不绑定 Catalog transition。
14
15
  */
15
16
  const barcodeWorkflow = defineWorkflow({
16
17
  id: "hdl.barcode",
17
18
  steps: [
19
+ catalogSlice,
18
20
  {
19
- slice: catalogWithPrimaryProductPromptSlice,
20
- on: {
21
- completed: "fulfillment",
22
- primaryProductRequested: "customization"
23
- }
21
+ slice: primaryProductPromptSlice,
22
+ presentation: {
23
+ mode: "modal",
24
+ base: "catalog"
25
+ },
26
+ visitPolicy: "reuseResolution",
27
+ on: { skip: "fulfillment" }
24
28
  },
25
29
  {
26
30
  slice: customizationSlice,
27
- on: {
28
- completed: {
29
- target: "catalogWithPrimaryProductPrompt",
30
- repeat: {
31
- max: 1,
32
- exceeded: "blocked"
33
- }
34
- },
35
- skipped: {
36
- target: "catalogWithPrimaryProductPrompt",
37
- repeat: {
38
- max: 1,
39
- exceeded: "blocked"
40
- }
41
- }
42
- }
43
- },
44
- {
45
- slice: fulfillmentSlice,
46
- on: { back: "catalogWithPrimaryProductPrompt" }
47
- },
48
- {
49
- slice: paymentMethodSlice,
50
- on: { eftposSelected: "paymentCompleted" }
31
+ visitPolicy: "reuseResolution"
51
32
  },
33
+ fulfillmentSlice,
34
+ paymentMethodSlice,
52
35
  {
53
36
  slice: paymentCompletedSlice,
54
37
  end: "success"
@@ -1,6 +1,6 @@
1
1
  import { _objectSpread2 } from "../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
2
- //#region src/components/HDLKiosk/app/workflow/guards.ts
3
- const DEFAULT_GUARDS = Object.freeze({
2
+ //#region src/components/HDLKiosk/app/workflow/predicates.ts
3
+ const DEFAULT_PREDICATES = Object.freeze({
4
4
  "cart.notEmpty": ({ sales }) => sales.cart.lineCount > 0,
5
5
  "cart.noPendingMutation": ({ sales }) => !sales.cart.consistencyPending,
6
6
  "identity.satisfied": ({ sales }) => sales.customer !== void 0,
@@ -9,15 +9,15 @@ const DEFAULT_GUARDS = Object.freeze({
9
9
  "payment.notPending": ({ sales }) => sales.payment.status !== "pending" && sales.payment.status !== "unknown",
10
10
  "payment.succeeded": ({ sales }) => sales.payment.status === "succeeded"
11
11
  });
12
- function createGuardRegistry(overrides = {}) {
13
- const guards = Object.freeze(_objectSpread2(_objectSpread2({}, DEFAULT_GUARDS), overrides));
14
- const ids = Object.freeze(Object.keys(guards));
12
+ function createPredicateRegistry(overrides = {}) {
13
+ const predicates = Object.freeze(_objectSpread2(_objectSpread2({}, DEFAULT_PREDICATES), overrides));
14
+ const ids = Object.freeze(Object.keys(predicates));
15
15
  return Object.freeze({
16
16
  ids,
17
- has: (id) => Object.prototype.hasOwnProperty.call(guards, id),
18
- evaluate: (id, context) => guards[id](context)
17
+ has: (id) => Object.prototype.hasOwnProperty.call(predicates, id),
18
+ evaluate: (id, context) => predicates[id](context)
19
19
  });
20
20
  }
21
- const defaultGuardRegistry = createGuardRegistry();
21
+ const defaultPredicateRegistry = createPredicateRegistry();
22
22
  //#endregion
23
- export { defaultGuardRegistry };
23
+ export { defaultPredicateRegistry };
@@ -8,22 +8,19 @@ import { paymentCompletedSlice, paymentMethodSlice } from "../../slices/payment/
8
8
  /**
9
9
  * 文件角色:声明 Quick 入口的有序业务步骤。
10
10
  * 运行机制:数组顺序就是主路径;这里只保留偏离相邻前进/返回的特殊分支。
11
- * 边界:Guard、Command、Screen 与失败策略均由本地 Activity Contract 提供。
11
+ * 边界:Predicate、Command、Screen 与失败策略均由本地 Activity Contract 提供。
12
12
  */
13
13
  const quickOrderWorkflow = defineWorkflow({
14
14
  id: "hdl.quick",
15
15
  steps: [
16
16
  {
17
17
  slice: identitySlice,
18
- useGuards: ["beforeEnter"]
18
+ visitPolicy: "once"
19
19
  },
20
20
  customizationSlice,
21
21
  catalogSlice,
22
22
  fulfillmentSlice,
23
- {
24
- slice: paymentMethodSlice,
25
- on: { eftposSelected: "paymentCompleted" }
26
- },
23
+ paymentMethodSlice,
27
24
  {
28
25
  slice: paymentCompletedSlice,
29
26
  end: "success"
@@ -7,7 +7,7 @@ const defaultActivityRegistry = Object.freeze({
7
7
  has: (id) => manifestRegistry.has(id),
8
8
  get: (id) => manifestRegistry.has(id) ? manifestRegistry.require(id) : void 0,
9
9
  require: (id) => manifestRegistry.require(id),
10
- accepts: (id, outcome) => manifestRegistry.require(id).outcomes.includes(outcome)
10
+ accepts: (id, intent) => manifestRegistry.require(id).intents.some((availableIntent) => availableIntent === intent)
11
11
  });
12
12
  const BUILT_IN_WORKFLOW_DEFINITIONS = Object.freeze({
13
13
  quick: normalizeKioskWorkflow(hdlKioskApplication.workflows.quick),
@@ -14,19 +14,10 @@ import { paymentCompletedSlice, paymentMethodSlice } from "../../slices/payment/
14
14
  const scaleWorkflow = defineWorkflow({
15
15
  id: "hdl.scale",
16
16
  steps: [
17
- {
18
- slice: customizationSlice,
19
- on: { skipped: "$next" }
20
- },
21
- {
22
- slice: catalogSlice,
23
- on: { back: "customization" }
24
- },
17
+ customizationSlice,
18
+ catalogSlice,
25
19
  fulfillmentSlice,
26
- {
27
- slice: paymentMethodSlice,
28
- on: { eftposSelected: "paymentCompleted" }
29
- },
20
+ paymentMethodSlice,
30
21
  {
31
22
  slice: paymentCompletedSlice,
32
23
  end: "success"
@@ -1,5 +1,4 @@
1
1
  import { _objectSpread2 } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
2
- import { _objectWithoutProperties } from "../../../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectWithoutProperties.js";
3
2
  import { HDLKioskFloatingBar } from "../HDLKioskFloatingBar/index.js";
4
3
  import { HDLKioskCartSecondaryActions } from "../HDLKioskCartBarActions/index.js";
5
4
  import React, { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
@@ -10,11 +9,6 @@ import React, { createContext, useCallback, useContext, useEffect, useLayoutEffe
10
9
  * 并区分 Modal 下可见的 masked 状态与不可导航时的 hidden 状态。
11
10
  * 边界:不计算购物车/金额;声明数据必须来自 ViewModel/Projection。
12
11
  */
13
- const _excluded = [
14
- "overlayActive",
15
- "cartEnabled",
16
- "cartOpenBar"
17
- ];
18
12
  const HDLKioskBottomBarContext = createContext(null);
19
13
  const HDLKioskCartDrawerContext = createContext(null);
20
14
  const noop = () => void 0;
@@ -40,6 +34,17 @@ function useHDLKioskBottomBar(declaration) {
40
34
  }, [context, declaration]);
41
35
  useLayoutEffect(() => () => context === null || context === void 0 ? void 0 : context.unregister(owner.current), [context]);
42
36
  }
37
+ function toFloatingBarProps(declaration) {
38
+ const { ariaLabel, className, leading, summary, trailing, variant } = declaration;
39
+ return {
40
+ ariaLabel,
41
+ className,
42
+ leading,
43
+ summary,
44
+ trailing,
45
+ variant
46
+ };
47
+ }
43
48
  function resolveHDLKioskBottomBarState(mode, screenOverlayActive = false) {
44
49
  const effectiveMode = mode === "hidden" ? "hidden" : mode === "masked" || screenOverlayActive ? "masked" : "visible";
45
50
  return {
@@ -111,10 +116,7 @@ function HDLKioskBottomBarHost({ children, mode = "visible", fallback, cartOverl
111
116
  }),
112
117
  className: `hdl-floating-bar--cart-open${cartBarDeclaration.className ? ` ${cartBarDeclaration.className}` : ""}`
113
118
  }) : renderedDeclaration;
114
- const floatingBarDeclaration = activeBarDeclaration ? (() => {
115
- const { overlayActive: _overlayActive, cartEnabled: _cartEnabled, cartOpenBar: _cartOpenBar } = activeBarDeclaration;
116
- return _objectWithoutProperties(activeBarDeclaration, _excluded);
117
- })() : void 0;
119
+ const floatingBarDeclaration = activeBarDeclaration ? toFloatingBarProps(activeBarDeclaration) : void 0;
118
120
  useEffect(() => {
119
121
  if (presentation.drawerBlocked || !drawerAvailable) setCartOpen(false);
120
122
  }, [drawerAvailable, presentation.drawerBlocked]);
@@ -12,6 +12,7 @@ const INLINE_SCREEN_OPERATIONS = new Set([
12
12
  "cart.updateQuantity",
13
13
  "cart.removeLine",
14
14
  "product.syncPrimaryWeightedProduct",
15
+ "product.removePrimaryProduct",
15
16
  "checkout.requestStaffPaymentApproval",
16
17
  "checkout.payEftpos"
17
18
  ]);
@@ -46,7 +46,7 @@ HDLKiosk/
46
46
  | --- | --- |
47
47
  | `manifest.ts` | 唯一应用清单;收集 Slice 提供的 Activity/Operation,并绑定三条 Workflow |
48
48
  | `constants.ts` | `HDL_WORKFLOW_IDS` 等应用常量 |
49
- | `events.ts` | HDLKiosk Activity Outcome 的强类型事件联合 |
49
+ | `events.ts` | HDLKiosk Activity Intent 与 Back Navigation 的强类型事件联合 |
50
50
  | `operations.ts` | 共享 Operation 的唯一公共导入入口 |
51
51
  | `operations/` | `operations.ts` 背后的私有实现、Coordinator 策略和测试;外部代码不要逐文件跨层引用 |
52
52
  | `views.ts` | 应用层 Activity → React View 的完整同步注册表;不进入 Slice/Workflow/Kernel |
@@ -54,7 +54,7 @@ HDLKiosk/
54
54
  | `workflow/barcode.ts` | Barcode 的有序 Activity 列表与特殊转移 |
55
55
  | `workflow/scale.ts` | Scale 的有序 Activity 列表与特殊转移 |
56
56
  | `workflow/registry.ts` | 从 Manifest 派生 Activity Registry,并提供内置流程查询 |
57
- | `workflow/guards.ts` | HDLKiosk 本地 Guard Registry |
57
+ | `workflow/predicates.ts` | HDLKiosk 本地 Predicate Registry |
58
58
  | `workflow/policy.ts` | 支付门禁等商家应用策略校验;结构编译仍属于 Kernel |
59
59
  | `ScreenRenderer.tsx` | 按当前 Activity 从 `views.ts` 取 View;处理 modal base 叠加和缺失 View 的阻断呈现 |
60
60
  | `entry/model.ts` | 派生 Workflow 之外三个入口的可用性和展示模型 |
@@ -75,7 +75,7 @@ slices/<name>/
75
75
  └── model.ts # 仅本 Slice 使用的纯模型;按需存在
76
76
  ```
77
77
 
78
- 当前 Slice:`identity`、`fulfillment`、`customization`、`catalog`、`paymentMethod`、`paymentCompleted`。`paymentCompleted` 只是支付成功终态,与 `paymentMethod` 共用 Checkout Screen/View,不存在独立 Result Screen。
78
+ 当前 Slice:`identity`、`fulfillment`、`customization`、`catalog`、`primaryProductPrompt`、`paymentMethod`、`paymentCompleted`。`primaryProductPrompt` 是以 Catalog 为 base 的 Modal;`paymentCompleted` 是支付成功终态,与 `paymentMethod` 共用 Checkout Screen/View。
79
79
 
80
80
  - `slices/index.ts` 只汇总各目录导出的 `defineSlice` 结果,并统一约束 `screen.id` 必须是 HDLKiosk `ScreenId`;
81
81
  - 每个 `slice.ts` 只通过 `screen: { id }` 声明页面类别;实际 View 在 `app/views.ts` 中按 Activity 单独、完整地注册,使用同步本地组件引用;
@@ -84,7 +84,7 @@ slices/<name>/
84
84
 
85
85
  `Slice` 是作者侧唯一业务能力概念:`defineSlice` 定义能力,`application.slices` 收集能力,Workflow 的 `slice` 字段引用能力;运行期再编译为 Activity Contract。
86
86
 
87
- 命名关系必须保持单向:Slice `id` 是业务 Activity;`screen.id` 是页面类别;`app/views.ts` 按 Activity 注册实际 UI。`catalog` 和 `catalogWithPrimaryProductPrompt` 可以共享 Catalog Screen 与 View,但仍是不同的业务 Slice。非 EFTPOS 店员支付授权是 `paymentMethod` 页面内 Command,不创建 Kiosk 等待 Activity 或弹窗。
87
+ 命名关系必须保持单向:Slice `id` 是业务 Activity;`screen.id` 是页面类别;`app/views.ts` 按 Activity 注册实际 UI。`catalog` 负责商品浏览,`primaryProductPrompt` 负责主商品询问,两者通过 Modal base 组合。支付派发和店员支付授权是 `paymentMethod` 页面内 Command,不创建流程 Intent。
88
88
 
89
89
  ## 5. `components/`:共享展示能力
90
90
 
@@ -103,7 +103,7 @@ slices/<name>/
103
103
  | `createWorkflowSession.ts` | 读取内置 Definition,直接编译、校验并创建 Actor/Coordinator |
104
104
  | `HDLKioskWorkflowView.tsx` | 活跃会话的 React 组合层;连接 Shell、Renderer 和 Controllers |
105
105
  | `domainTransitionRunner.ts` | 将 Actor 的 exit effect 交给 Operation Coordinator |
106
- | `workflowContext.ts` | 只归并经过 Activity Outcome 确认的流程上下文 |
106
+ | `workflowContext.ts` | 只归并经过 Activity Intent 成功提交的流程上下文 |
107
107
  | `workflowLifecycle.ts` | Actor blocked/finished 的 Host 遥测与生命周期钩子 |
108
108
  | `useHostSnapshot.ts` | 订阅 Host Projection |
109
109
  | `useWorkflowSnapshot.ts` | 订阅 Actor Snapshot |
@@ -126,7 +126,7 @@ BigSale 专用 H5 登录实现位于 `bigSale/templates/HDLKiosk/auth/`。独立
126
126
  | 位置 | 角色 |
127
127
  | --- | --- |
128
128
  | `application/` | `defineSlice`、`defineKioskApplication`、Slice 收集和 authoring normalization |
129
- | `workflow/compiler.ts` | 编译有序 Step 数组,校验 ID、Outcome、可达性、modal base 和有界循环 |
129
+ | `workflow/compiler.ts` | 编译有序 Step 数组,校验 ID、Intent、可达性、modal base 和有界循环 |
130
130
  | `workflow/actor.ts` | 单会话串行状态机,拒绝迟到 Activity/Step 事件 |
131
131
  | `workflow/transition.ts` | advance/stay/blocked 等转移结果原语 |
132
132
  | `operations/coordinator.ts` | 通用排队、去重、timeout、dispose 和 epoch 协调 |
@@ -164,7 +164,7 @@ import {
164
164
  - `host.ts`:Host 总入口和 capabilities;
165
165
  - `sales.ts`:标准 ProductData、OS 加购流、购物车、客户、履约、支付和 Projection;
166
166
  - `devices.ts`:scanner/scale port;
167
- - `workflow*.ts`:Activity、Definition 和 Guard 协议;
167
+ - `workflow*.ts`:Activity、Definition、Predicate 和 Navigation Guard 协议;
168
168
  - `operations.ts`:允许请求的 Operation ID 与输入映射;
169
169
  - `results.ts` / `errors.ts` / `telemetry.ts`:结果、错误与观测。
170
170
 
@@ -13,11 +13,11 @@ HDLKiosk Core 不是 BigSale 的另一套页面,也不是本地订单 Store。
13
13
  ↓
14
14
  Runtime Controller 决定是否启动会话或消费设备事件
15
15
  ↓
16
- Screen 内部动作走 Command;离开步骤才产生 Activity Outcome
16
+ Screen 内部动作走 Command;离开步骤产生 Activity Intent,返回产生 Navigation
17
17
  ↓
18
- Workflow Actor 根据 Outcome 选择静态 Transition并管理串行状态
18
+ Workflow Actor 根据 Intent 选择静态 Transition,并按页面历史处理 Back Navigation
19
19
  ↓
20
- HDLKiosk Transition Runner 执行本地 Guard / Exit Command并解释交易结果
20
+ HDLKiosk Transition Runner 求值本地 Predicate、执行 Exit Command 并解释交易结果
21
21
  ↓
22
22
  Operation Coordinator 安全执行 Screen Command 或 Exit Command
23
23
  ↓
@@ -36,7 +36,7 @@ Screen Renderer 根据当前节点与最新 Projection 渲染
36
36
  | 流程状态 | entry、nodeId、fulfillment 本地选择、session epoch | Workflow Runtime | 只回答“顾客处于哪一步”,不能证明订单已写成功 |
37
37
  | 展示输入 | options 选中项、搜索词、Modal 内选择 | Screen | 可以随页面销毁,不得成为结账依据 |
38
38
 
39
- 核心规则是“流程、命令与事实分离”:Screen Command/Exit Command 发起业务写入;writer receipt 证明本次写入完成了什么;Projection 提供当前 UI 可观察事实;Workflow Definition 只描述 Activity Outcome 到目标节点的关系。
39
+ 核心规则是“流程、命令与事实分离”:Screen Command/Exit Command 发起业务写入;writer receipt 证明本次写入完成了什么;Projection 提供当前 UI 可观察事实;Workflow Definition 只描述 Activity Intent 到目标节点的关系,Back Navigation 不写入 Definition。
40
40
 
41
41
  ## 2. 目录与运行时角色
42
42
 
@@ -49,10 +49,10 @@ Screen Renderer 根据当前节点与最新 Projection 渲染
49
49
  | [`app/workflow/`](../app/workflow) | 三入口业务编排与本地策略 | Activity 数组与特殊分支 | Quick/Barcode/Scale Definition | 流程顺序集中在应用目录,不和 Slice 实现混杂 |
50
50
  | [`runtime/createWorkflowSession.ts`](../runtime/createWorkflowSession.ts) | 一次点餐会话的组合根 | entry、Host、locale、可选 Lifecycle 扩展 | Kernel 与 HDLKiosk 业务实现的稳定组合 | 避免页面各自创建状态机或副作用执行器 |
51
51
  | [`app/workflow/registry.ts`](../app/workflow/registry.ts) | Activity Registry 类型视图 | Application Manifest | 带 HDLKiosk 字面量类型的 Registry | Runtime 不再维护第二份 Activity 清单 |
52
- | [`kernel/workflow/compiler.ts`](../kernel/workflow/compiler.ts) | Definition 结构校验 | Workflow Definition + 注入的 Registry | 冻结后的可执行 Definition 或 issues | 防止未知 Outcome、非法跳转和无界循环进入 Actor |
52
+ | [`kernel/workflow/compiler.ts`](../kernel/workflow/compiler.ts) | Definition 结构校验 | Workflow Definition + 注入的 Registry | 冻结后的可执行 Definition 或 issues | 防止未知 Intent、非法跳转和无界循环进入 Actor |
53
53
  | [`app/workflow/policy.ts`](../app/workflow/policy.ts) | HDLKiosk 业务策略校验 | 编译后的 Definition | 支付门禁等业务 issues | 结构规则不与具体交易策略混在一起 |
54
- | [`kernel/workflow/actor.ts`](../kernel/workflow/actor.ts) | 单会话流程执行器 | Activity Outcome、Definition、Runner | WorkflowSnapshot、Transition | 解决双击、乱序导航和 Exit Command 完成前跳页 |
55
- | [`runtime/domainTransitionRunner.ts`](../runtime/domainTransitionRunner.ts) | Activity 离开策略执行器 | Exit Policy、Host Projection、Coordinator | advance/stay/blocked、支付回执 | Actor 不理解 Guard、购物车和支付 Operation |
54
+ | [`kernel/workflow/actor.ts`](../kernel/workflow/actor.ts) | 单会话流程执行器 | Activity Intent、Navigation、Definition、Runner | WorkflowSnapshot、Transition | 解决双击、乱序导航和 Exit Command 完成前跳页 |
55
+ | [`runtime/domainTransitionRunner.ts`](../runtime/domainTransitionRunner.ts) | Activity 离开策略执行器 | Exit Policy、Host Projection、Coordinator | advance/stay/blocked、支付回执 | Actor 不理解 Predicate、购物车和支付 Operation |
56
56
  | [`runtime/workflowLifecycle.ts`](../runtime/workflowLifecycle.ts) | 已提交状态的业务生命周期出口 | Actor 提交后的 Snapshot | telemetry | 诊断不进入状态机内核 |
57
57
  | [`kernel/operations/coordinator.ts`](../kernel/operations/coordinator.ts) | 通用副作用调度器 | Operation request + 应用 Policy | 规范化 OperationResult | 解决重复 intent、排队、超时和旧 Promise;不认识支付 |
58
58
  | [`app/operations.ts`](../app/operations.ts) | 共享 Operation 公共入口 | 跨 Slice Operation 与执行策略 | 稳定的统一导出面 | 避免消费者穿透私有实现目录 |
@@ -172,34 +172,41 @@ Workflow 用有序 Slice 数组描述静态编排。数组顺序是默认主路
172
172
  {
173
173
  id: 'hdl.barcode',
174
174
  steps: [
175
+ catalogSlice,
175
176
  {
176
- slice: catalogWithPrimaryProductPromptSlice,
177
- on: { completed: 'paymentMethod' },
177
+ slice: primaryProductPromptSlice,
178
+ presentation: { mode: 'modal', base: 'catalog' },
179
+ on: { complete: 'customization', skip: 'fulfillment' },
178
180
  },
179
- { slice: customizationSlice, on: { completed: 'catalogWithPrimaryProductPrompt' } },
180
- { slice: paymentMethodSlice, on: { back: 'catalogWithPrimaryProductPrompt' } },
181
- // ...
181
+ {
182
+ slice: customizationSlice,
183
+ on: { complete: 'fulfillment', skip: 'fulfillment' },
184
+ },
185
+ fulfillmentSlice,
186
+ paymentMethodSlice,
182
187
  { slice: paymentCompletedSlice, end: 'success' },
183
188
  ],
184
189
  }
185
190
  ```
186
191
 
187
- Workflow 作者不会填写 Screen、Guard、Operation 或 failureTarget;这些由 Slice 引用带入本地可信 Manifest。Compiler 使用的规范化结构只保留 Activity ID、presentation 与目标,不携带 React 组件、Host 方法或订单对象。Quick、Barcode、Scale 的差异只保留在 [`app/workflow/`](../app/workflow)。
192
+ Workflow 作者不会填写 Screen、Predicate、Operation 或 failureTarget;这些由 Slice 引用带入本地可信 Manifest。Compiler 使用的规范化结构只保留 Activity ID、presentation 与目标,不携带 React 组件、Host 方法或订单对象。Quick、Barcode、Scale 的差异只保留在 [`app/workflow/`](../app/workflow)。
188
193
 
189
194
  四个概念必须分开:
190
195
 
191
- - **Slice**:开发者定义的完整业务能力,包含 Screen、Outcome、Command 和退出 Effect;
196
+ - **Slice**:开发者定义的完整业务能力,包含 Screen、Intent、Command 和退出 Effect;
192
197
  - **Activity**:Slice 暴露给 Actor 的无 UI 运行契约,例如 `catalog`;
193
198
  - **Step**:Activity 在一条流程中的一次出现,是编排和运行时定位的稳定位置;
194
199
  - **Screen**:Activity 的本地 UI 实现,不属于 Workflow Definition。
195
200
 
196
201
  Slice 在一条流程只出现一次时,Step ID 默认就是 Slice ID;同一 Slice 多次出现时,每次必须显式使用 `slice:用途`,例如 `catalog:initial`、`catalog:final`。这样既保持常规流程易读,也不会把复用能力误认为同一个运行位置。
197
202
 
198
- Slice Outcome 使用 `next()` / `back()`,或在 `guard/effect` 上声明 `navigate`,决定相邻 Step 的默认移动。不会移动的 Outcome 只在 Workflow `on` 中指定特殊目标。没有下一项的 Step 不会被推测为成功,必须声明 `end: 'success' | 'blocked'`。
203
+ Slice Intent 使用 `next()`,或在 `intentPrecondition/effect` 上声明 `navigate: 'next'`,决定相邻 Step 的默认移动。不会自动前进的 Intent 只在 Workflow `on` 中指定目标。Back 是独立 Navigation,由 Actor 的实际页面历史解析。没有下一项的 Step 不会被推测为成功,必须声明 `end: 'success' | 'blocked'`。
204
+
205
+ Predicate 是本地可信的纯真假判断,例如 `cart.notEmpty`;Intent Precondition 表示某个 Intent 提交前必须满足的 Predicate。作者 API 使用 `intentPrecondition(predicateId)`,底层行为记录 `{ kind: 'precondition', predicate }`,编译后存入 `exitPolicy.precondition`。Predicate 只返回 boolean;它与能返回 `allow | bypass | cancel | redirect | defer | block` 的 Navigation Guard 是两套独立能力。
199
206
 
200
- 条件式步骤在 Slice `guards` 中声明本地纯函数,Workflow occurrence 使用 `{ slice: identitySlice, useGuards: ['beforeEnter'] }` 选择启用阶段。Application 为函数生成稳定 Guard ID;编译后 Node 只保留 `guardPhases` 数组,不保留函数或 Guard ID。Actor 的全局 `beforeEach` 管线在初始进入、前进、后退和显式目标跳转时都接收 `to` / `from` / `direction`。守卫返回结构化决策 `allow | skip | cancel | redirect | defer | block`;`skip` 才会按当前方向继续解析,不再用含义模糊的 boolean。Kernel 不读取 Identity、Fulfillment 等业务名称。
207
+ 业务 Workflow 当前不启用 Slice `beforeEnter` 守卫。Actor 仍保留全局 `beforeEach` 管线,用于 `sessionExit` 等会话安全策略;它不承载 Identity、Fulfillment 等步骤的业务进入条件。
201
208
 
202
- Actor 记录的是已成功向前离开的 **Step ID**,而不是 Slice ID。因此同一 Slice 可在 Flow 中出现多次,每个 occurrence 都可独立声明 `useGuards`;第一个被守卫跳过,不会影响第二个未启用守卫的 Step。导航在执行 Activity Exit Effect 之前完成解析;被 cancel/defer 的导航不会先写 Host。
209
+ Actor 分别记录已成功向前离开的 Step、每个 Step 已提交的 `complete | skip` Resolution,以及用户实际访问过的页面历史。`back` 按实际页面历史返回,Modal 和 Skip Step 不写入历史;历史为空时 Back 进入会话退出导航。Workflow occurrence 通过 `visitPolicy: 'always' | 'reuseResolution' | 'once'` 明确声明再次访问策略:`always` 重新展示,`reuseResolution` 再次向前时复用已提交 Resolution,`once` 提交后不再进入且不写入 Back 历史。用户通过 Back 退出一个已解决页面时,其旧 Resolution 失效。导航在执行 Activity Exit Effect 之前完成解析;被 cancel/defer 的导航不会先写 Host。
203
210
 
204
211
  退出流程也是导航目标 `{ kind: 'sessionExit' }`。全局 `beforeEach` 对它返回 `defer({ kind: 'confirmEndOrder' })`,Actor 保存带唯一 `navigationId` 的 `pendingNavigation`。Shell 只根据该 request 展示确认;旧 ID 不能恢复后来的导航。
205
212
 
@@ -210,30 +217,30 @@ Step ID 只标识“一次出现”。Runtime 判断加载哪类数据、是否
210
217
  [`slices/<name>/slice.ts`](../slices) 是本地可信 Slice 定义,每个 Slice 声明:
211
218
 
212
219
  - 业务 Activity 使用哪个 `screen.id`,以及该 Screen 的 `view`;
213
- - 可以产生哪些 Outcome;
220
+ - 可以接受哪些 Intent;
214
221
  - 支持 page 还是 modal;
215
- - 某个 Outcome 是否需要纯 Guard;
222
+ - 某个 Intent 是否需要纯 Predicate 作为 Precondition;
216
223
  - 离开 Slice 前是否需要执行 Effect;
217
224
  - Screen 内有哪些不会离开节点的 Command;
218
225
  - 使用该 Slice 的流程在支付前需要哪些业务事实。
219
226
 
220
- 例如 `identity.skipped` 的本地 Exit Policy 执行 `identity.sync`,`customization.completed` 检查 `customization.primaryReady`,`paymentMethod.cardSelected` 执行 `checkout.open`。`defineKioskApplication` 从 Slice 自动生成 Activity Contract 与 Operation 注册信息;未来后台只会看到 `Slice ID + Outcome → target`,不能替换这些安全规则。
227
+ 例如 `identity.complete` / `identity.skip` 可在携带输入时执行 `identity.sync`,`customization.complete` 执行主商品同步,`paymentMethod.complete` 检查 Host 已确认支付成功。EFTPOS 派发和店员支付授权是 Payment 页面 Command,不作为流程 Intent。`defineKioskApplication` 从 Slice 自动生成 Activity Contract 与 Operation 注册信息;远端定义只看到 `Slice ID + 标准 Intent → target`,不能替换这些安全规则。
221
228
 
222
229
  Slice 的业务定义与 React View 分离:Slice `id` 是 Workflow 和 Actor 使用的业务 Activity;`screen.id` 会被 Compiler 复制为 Workflow Node 的 `screenId`,供 Shell、Controller 和数据加载策略使用;`app/views.ts` 按 Activity 注册本地可信 View。各 Slice 的 `view.tsx` 仍负责 ViewModel 派生与组件装配,但不会进入 `defineSlice` 或 Workflow Definition。
223
230
 
224
231
  | Slice 定义 | 编译/运行期形态 | 唯一用途 |
225
232
  | --- | --- | --- |
226
- | `id` | Node `activity` | 业务状态、Outcome 和流程编排 |
233
+ | `id` | Node `activity` | 业务状态、Intent 和流程编排 |
227
234
  | `screen.id` | Node `screenId` | Shell 变体、数据加载和页面类别判断 |
228
235
  | `app/views.ts` 中的 Activity 映射 | 仅应用展示层使用 | React UI 渲染 |
229
236
 
230
- 因此 `id`、`screen` 和 `view` 不互相替代:`catalogWithPrimaryProductPrompt` 与 `catalog` 是两个 Slice ID,但共享 `screen.id = 'catalog'` 和同一个 View。店员支付授权不改变当前页面,因此是 `paymentMethod` 的 Screen Command,而不是独立 Activity。Application Manifest 是业务运行 Contract 的唯一索引,`app/views.ts` 是展示实现的唯一索引。View 使用同步本地组件引用,不产生运行期 UI chunk;纯 Compiler/Actor 依赖链也不会加载该注册表。Renderer 若发现 View 缺失,仍使用 `BlockingNotice` fail closed,且不会改变 Workflow。
237
+ 因此 `id`、`screen` 和 `view` 不互相替代:`catalog` 是商品浏览页面,`primaryProductPrompt` 是覆盖在 Catalog 上的独立 Modal Slice。店员支付授权不改变当前页面,因此是 `paymentMethod` 的 Screen Command,而不是独立 Activity。Application Manifest 是业务运行 Contract 的唯一索引,`app/views.ts` 是展示实现的唯一索引。View 使用同步本地组件引用,不产生运行期 UI chunk;纯 Compiler/Actor 依赖链也不会加载该注册表。Renderer 若发现 View 缺失,仍使用 `BlockingNotice` fail closed,且不会改变 Workflow。
231
238
 
232
239
  `slices/index.ts` 对整套 Slice 清单执行一次类型约束:`screen.id` 只能取 HDLKiosk 的非 Entry `ScreenId`。`app/views.ts` 使用完整的 `Record<ActivityId, HDLKioskSliceView>`,因此新增 Activity 若没有注册 View 会产生类型错误。Kernel 的 `defineSlice` 同时拒绝空 `screen.id`。
233
240
 
234
241
  Compiler 从实际使用的 Slice 汇总 Checkout Requirement ID,但不把判断函数或 Host 字段写入 Workflow。当前 HDLKiosk 默认注册表为空;未来增加其他业务要求时仍由 [`checkoutRequirements.ts`](../app/operations/checkoutRequirements.ts) 集中判断。
235
242
 
236
- `WorkflowEvent` 同时约束 Activity、Outcome 和必要输入:例如 `identity.skipped` 必须携带身份输入,`fulfillment.completed` 必须携带 fulfillment context patch。Barcode 入口的条码输入不是 Workflow Event,而是 Session Controller 在发布会话前交给 `barcode.consume` 的 Command 输入。Screen Command 由 `OperationInputMap` 约束 Command ID 与输入类型,不再通过 `unknown` 保持隐式默契。
243
+ `WorkflowEvent` 同时约束 Activity、Intent 和必要输入:例如 `identity.skip` 必须携带身份输入,`fulfillment.complete` 必须携带 fulfillment context patch。Back 使用不携带业务输入的 `NAVIGATE_BACK`。Barcode 入口的条码输入不是 Workflow Event,而是 Session Controller 在发布会话前交给 `barcode.consume` 的 Command 输入。Screen Command 由 `OperationInputMap` 约束 Command ID 与输入类型。
237
244
 
238
245
  Step 可选声明 Modal 展示关系:
239
246
 
@@ -249,7 +256,7 @@ presentation: { mode: 'modal', base: 'customization' }
249
256
 
250
257
  - workflow id、Step 数组及数量/transition 上限;
251
258
  - Activity 是否存在;重复 Activity 是否具有稳定且不冲突的 Step ID;
252
- - Outcome 是否由 Activity Contract 暴露;
259
+ - Intent 是否由 Activity Contract 暴露;
253
260
  - Definition 是否夹带 screen/guard/operation/failureTarget;
254
261
  - 默认相邻导航和显式 target 是否有效,所有 Step 是否从首项可达;
255
262
  - 无后继 Step 是否声明结束语义,首项是否能到达成功终点;
@@ -264,16 +271,16 @@ session 创建前完成,但结构错误与业务安全错误保持独立。
264
271
 
265
272
  数组天然只有一个首项;不可达 Step 会被拒绝。显式业务回路必须声明 `repeat.max`,Actor 按会话计数,超过限制进入阻塞态。普通 Back 属于用户导航,不计入业务回路限制。校验通过后,Compiler 把数组转成 Actor 查询高效的冻结节点 Map;运行中的 session 不替换 Definition。
266
273
 
267
- ### 6.4 Actor 如何处理 Activity Outcome
274
+ ### 6.4 Actor 如何处理 Activity Intent 与 Navigation
268
275
 
269
276
  Actor 使用单 mailbox:
270
277
 
271
278
  1. 忽略 success end、blocked 或 disposed 后的新事件;
272
279
  2. 同时校验事件的 `stepId` 和 `activityId`,拒绝旧 Screen 或同类 Activity 旧实例的迟到回调;
273
- 3. 校验 Outcome 是否由当前 Activity 暴露,并查找 Transition;
280
+ 3. 校验 Intent 是否由当前 Activity 暴露,并查找 Transition;Back 则读取页面历史;
274
281
  4. 通过注入的 Context Reducer 合并受限 contextPatch;
275
282
  5. 把 Exit Policy 交给 HDLKiosk Transition Runner;
276
- 6. Runner 用最新 Sales Projection 执行 Guard,并通过 Coordinator 执行 Exit Command;
283
+ 6. Runner 用最新 Sales Projection 求值 Precondition Predicate,并通过 Coordinator 执行 Exit Command;
277
284
  7. Runner 将结果归一为 `advance / stay / blocked`,支付成功回执也由 Runner 解释;
278
285
  8. Actor 只负责提交 target、当前 Step 或 blocked,并清理 active operation;
279
286
  9. 已提交 Snapshot 交给 Workflow Lifecycle 记录 telemetry;外层可组合额外观察器。
@@ -301,7 +308,7 @@ Coordinator 现在有两个明确调用来源:
301
308
  - Screen Command:Catalog 加购、Cart Overlay 数量修改、称重草稿同步等;由 `session.executeCommand` 执行,成功后 Actor 节点不变;
302
309
  - Activity Exit Command:Guest 同步、Checkout Prepare/Open 等;由 Transition Runner 从本地 Exit Policy 发起,只有 succeeded 才允许 Actor 转移。
303
310
 
304
- partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入口 fail closed;它不是伪造一个 Activity Outcome。
311
+ partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入口 fail closed;它不伪造 Activity Intent。
305
312
 
306
313
  当前 Operation 组:
307
314
 
@@ -313,6 +320,7 @@ partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入
313
320
  | `cart.addProduct` | cart | — | 调宿主商品 Flow,等待规格或其它业务选择后验证写入 |
314
321
  | `cart.update/remove` | cart | 15s | 数量修改/删除、summary 重算和目标行验证 |
315
322
  | `product.syncPrimaryWeightedProduct` | cart | — | 首次写入可能等待 OS 额外业务弹窗;更新或写入后验证重量/options 和写后证据 |
323
+ | `product.removePrimaryProduct` | cart | 15s | 将主商品置为不存在;本来不存在时直接成功,存在时验证删除结果 |
316
324
  | `checkout.prepare*` | checkout | 20s | fulfillment、cart、summary 稳定性与 token |
317
325
  | `checkout.open` | checkout | 120s | 消费 token、冻结危险写并打开 Host 支付 |
318
326
 
@@ -389,22 +397,22 @@ Host command
389
397
  - 尚无活跃或启动中的 session;
390
398
  - 支付状态未冻结。
391
399
 
392
- 通过 ScannerIngress/ScaleIngress 完成 eventId 去重、阈值检查和拒绝原因遥测。Device Controller 不向 Actor 发送 `detected` Outcome;Barcode 把条码作为 start request 的一部分,Scale 只发起 Scale session。
400
+ 通过 ScannerIngress/ScaleIngress 完成 eventId 去重、阈值检查和拒绝原因遥测。Device Controller 不向 Actor 发送流程 Intent;Barcode 把条码作为 start request 的一部分,Scale 只发起 Scale session。
393
401
 
394
402
  ### 8.3 Customer Controller
395
403
 
396
- Identity 节点内的登录是 Activity Outcome,不是 Screen 直接写订单:
404
+ Identity 节点内的登录是 Activity Intent,不是 Screen 直接写订单:
397
405
 
398
406
  ```text
399
407
  BigSale Login2/Register2
400
408
  → 暂存已认证 customer
401
- → identity.memberRecognized(customerId)
409
+ → identity.complete(customerId)
402
410
  → identity.sync Exit Effect
403
411
  → Adapter customer.select + consistency + repricing
404
412
  → 成功后进入下一节点
405
413
  ```
406
414
 
407
- 认证 UI 通过 HDLKiosk 应用展示层插槽注入,不进入 `defineSlice`、View 注册表或 Kernel。Slice 只声明 `memberRecognized` 在携带输入时执行 `identity.sync`;已登录客户的自动略过不携带输入,因此不会重复写客户。
415
+ 认证 UI 通过 HDLKiosk 应用展示层插槽注入,不进入 `defineSlice`、View 注册表或 Kernel。Slice 的 `complete` 在携带输入时执行 `identity.sync`;已登录客户的自动略过使用 `skip` 且不携带输入,因此不会重复写客户。
408
416
 
409
417
  Shell 登录不是 Workflow 跳转,而是 session-level 插入 Operation:
410
418
 
@@ -475,8 +483,9 @@ HDLKiosk root
475
483
  → components/shell + components/overlays + components/common
476
484
  → Activity Action callback
477
485
  → HDLKioskWorkflowView
478
- ├─ Outcome → Workflow Actor
479
- │ → HDLKiosk Transition Runner(Guard / Exit Command)
486
+ ├─ Intent → Workflow Actor
487
+ ├─ Back → Workflow Navigation
488
+ │ → HDLKiosk Transition Runner(Predicate / Exit Command)
480
489
  ├─ Command → session.executeCommand → Coordinator
481
490
  └─ Session Action → reset controller
482
491
  ```
@@ -499,7 +508,7 @@ Screen 只做三件事:
499
508
 
500
509
  1. 渲染 View DTO;
501
510
  2. 维护局部输入;
502
- 3. 内部业务写入发送 Screen Command,离开步骤发送 Activity Outcome。
511
+ 3. 内部业务写入发送 Screen Command,离开步骤发送 Activity Intent,返回发送 Navigation。
503
512
 
504
513
  Screen 不得:
505
514
 
@@ -515,13 +524,13 @@ CartDrawer 也遵循“Screen 声明、Shell 挂载”:Screen 只请求打开
515
524
 
516
525
  Customization 的规格 UI 通过 `components/common/HDLKioskSkuOptionsSelection` 接入共享 `skuOptionsSelection`。查询所得 ProductData 本身直接作为 `dataSource`,兼容层不再复制或重命名 option group/item,只在选择器 `SkuValue` 与业务命令所需 `HDLProductOptionSelection[]` 之间转换“用户选中结果”。临时独立运行缺少 Engine 上下文时,退化为直接读取同一 `option_group/option_item` 的单选控件。
517
526
 
518
- 称重主商品在 Customization 中先保持页面本地草稿。重量或 options 变化不写 Host;点击 Next 时才将当下完整草稿作为 `customization.completed` 的 Exit Effect 交给 `product.syncPrimaryWeightedProduct`。OS 完成其内部业务判断并返回加购成功,且 writer receipt/Projection 验证通过后,Actor 才进入下一节点。失败或取消留在当前页面,`partial/unknown` 阻断。底栏和 Drawer 仍只消费 Host `summary.due/cart.lines`,不在本地构造未确认的购物车。
527
+ 称重主商品在 Customization 中先保持页面本地草稿。重量或 options 变化不写 Host;点击 Next 时才将当下完整草稿作为 `customization.complete` 的 Exit Effect 交给 `product.syncPrimaryWeightedProduct`。OS 完成其内部业务判断并返回加购成功,且 writer receipt/Projection 验证通过后,Actor 才进入下一节点。失败或取消留在当前页面,`partial/unknown` 阻断。底栏和 Drawer 仍只消费 Host `summary.due/cart.lines`,不在本地构造未确认的购物车。
519
528
 
520
529
  Workflow View 负责控制器组合、自动推进和会话级 Dialog;Renderer 只负责选择要展示的组件。普通 `cart.add/update/remove` 和防抖的 `product.syncPrimaryWeightedProduct` 是 Screen Command,保持当前 Activity 挂载,只禁用冲突操作,不产生 transition;会员重报价和 Checkout 等 Activity Exit Command 才阻断整个 surface。
521
530
 
522
531
  OS 加购过程中的内部弹窗和业务判断不属于 HDLKiosk Activity、Host Contract 或 Checkout Requirement。HDLKiosk 不读写相关业务事实,只根据主商品加购操作的最终结果决定是否跳转。
523
532
 
524
- Barcode 的主商品询问采用相同原则,但它属于 `catalogWithPrimaryProductPrompt` Activity 的局部 Dialog:No 产生 `completed`,Yes 产生 `primaryProductRequested`。是否打开 Dialog 只由当前购物车是否包含主商品决定,不形成 `primaryProductPrompt` Workflow 节点。
533
+ Barcode 的主商品询问是独立 `primaryProductPrompt` Modal Slice:Yes 发出 `complete`,No 发出 `skip`。Actor 成功提交后直接记录同名 Resolution。Prompt 和 Customization occurrence 均声明 `visitPolicy: 'reuseResolution'`,因此拒绝或跳过后,从 Fulfillment 返回 Catalog 再次前进不会重复展示它们。正常完成 Customization 后可从 Fulfillment Back 返回编辑;若再退回 Catalog,旧 Customization Resolution 失效,之后前进会重新展示。跳过已有主商品前会先通过本地 Exit Effect 删除对应购物车行。
525
534
 
526
535
  ## 11. 四条端到端调用链
527
536
 
@@ -26,7 +26,7 @@ Entry → Identity → Customization → Catalog → Fulfillment
26
26
  → Payment Method / Payment Completed → Entry
27
27
  ```
28
28
 
29
- - Identity Slice 在 `guards.beforeEnter` 中声明进入条件,Quick 只在该 occurrence 配置 `useGuards: ['beforeEnter']`。本 Step 登录或 Skip 成功后守卫返回 `skip`,因此后退会跳过它。Fulfillment Back 因没有更早的可进入 Step,导航目标变为 `sessionExit`;全局守卫返回 `defer(confirmEndOrder)` 并打开结束订单确认。确认后返回 Entry,取消则留在当前页。
29
+ - Quick 的 Identity occurrence 显式声明 `visitPolicy: 'once'`。登录或 Skip 成功后,该 Step 在本会话不再进入,也不写入 Back 页面历史。当没有更早的已访问页面时,Back 的导航目标为 `sessionExit`;全局会话策略返回 `defer(confirmEndOrder)` 并打开结束订单确认。确认后返回 Entry,取消则留在当前页。
30
30
  - Fulfillment 统一位于 Payment 之前确认堂食/外带。
31
31
  - Customization 只在稳定正净重且必选 options 完整时启用 Next。
32
32
  - “I don't need Malatang” 直接跳过主商品并进入 Catalog;如果正在编辑已确认主商品,则先移除该行。
@@ -76,6 +76,6 @@ Identity 的 Skip 只表示当前以 Guest 继续。Shell 可在本地策略允
76
76
  - 尺寸校准以上线环境的实际渲染值反推:当 72px 在目标倍率下实测为 100px,而原型目标为 108px 时,当前自有视觉值统一乘以 `108 / 100 = 1.08`;外部生产组件不参与这一次修正。
77
77
  - Screen 不得直接 import BigSale/SalesSDK,也不本地计算最终金额。
78
78
  - Screen 内部写操作使用 Screen Command;离开节点的写操作使用 Activity Exit Effect。
79
- - `product.syncPrimaryWeightedProduct` 属于 Customization `completed` 的 Exit Effect,不是输入变化时的 Screen Command。
79
+ - `product.syncPrimaryWeightedProduct` 属于 Customization `complete` 的 Exit Effect,不是输入变化时的 Screen Command。
80
80
  - Shell 的购物车、金额和 Drawer 只消费 Host 已确认 Projection。
81
81
  - 调整节点顺序时同步更新 Workflow Definition、compiler/definition/actor/Screen 测试和实施进度。
@@ -31,7 +31,7 @@ Host 无法证明写后事实时,Operation 不能返回 succeeded。Cart 和 C
31
31
 
32
32
  ## 3. Checkout Prepared Freshness
33
33
 
34
- Prepare 不是某个页面之前或某个 Workflow outcome 的固定副作用。Checkout Coordinator 把它作为所有支付 dispatch 的前置条件,对当前事实版本执行 `ensurePrepared(currentVersion)`。
34
+ Prepare 不是某个页面之前或某个 Workflow Intent 的固定副作用。Checkout Coordinator 把它作为所有支付 dispatch 的前置条件,对当前事实版本执行 `ensurePrepared(currentVersion)`。
35
35
 
36
36
  事实版本至少包括 session epoch、cart fingerprint、customer/voucher/promotion revision、Summary 应付金额/币种、Requirement 集合和支付能力上下文。状态为:
37
37
 
@@ -35,7 +35,7 @@
35
35
  ### P1-1 商品和 Catalog
36
36
 
37
37
  - 将模板内临时固定商品 ID 改为租户/门店正式称重商品配置;商品结构已经切换为 OS `queryProducts`,不得恢复本地 fallback;
38
- - 确认 Quick 是否允许跳过称重主商品;若不允许,从 `customization` Activity Contract/Workflow 中删除 `skipped` Outcome;
38
+ - 确认 Quick 是否允许跳过称重主商品;若不允许,从 `customization` Activity Contract/Workflow 中移除 `skip` Intent;
39
39
  - 大目录分页/虚拟滚动与分类切换性能;分类、子分类和本地搜索首版已完成;
40
40
  - 缺货、下架、菜单不可用、价格变化;
41
41
  - Barcode 细分 added/requiresSelection/notFound/cancelled;