@pisell/private-materials 6.12.166 → 6.12.168

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 (115) 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 +7 -7
  8. package/es/components/HDLKiosk/app/views.js +2 -1
  9. package/es/components/HDLKiosk/app/workflow/barcode.js +12 -35
  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 +47 -40
  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 +3 -3
  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 +118 -28
  29. package/es/components/HDLKiosk/kernel/workflow/compiler.js +35 -41
  30. package/es/components/HDLKiosk/kernel/workflow/selectors.js +9 -0
  31. package/es/components/HDLKiosk/runtime/HDLKioskWorkflowView.js +10 -9
  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 +9 -11
  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/templates/HDLKiosk/auth/HDLKioskIdentityAuthPanel.js +17 -2
  57. package/es/pro/Login2.0/Login2.d.ts +2 -0
  58. package/es/pro/Login2.0/Login2.js +9 -1
  59. package/es/pro/Login2.0/Register2.d.ts +2 -0
  60. package/es/pro/Login2.0/Register2.js +9 -1
  61. package/lib/components/HDLKiosk/app/ScreenRenderer.js +7 -7
  62. package/lib/components/HDLKiosk/app/views.js +2 -1
  63. package/lib/components/HDLKiosk/app/workflow/barcode.js +11 -34
  64. package/lib/components/HDLKiosk/app/workflow/{guards.js → predicates.js} +9 -9
  65. package/lib/components/HDLKiosk/app/workflow/quick.js +3 -6
  66. package/lib/components/HDLKiosk/app/workflow/registry.js +1 -1
  67. package/lib/components/HDLKiosk/app/workflow/scale.js +3 -12
  68. package/lib/components/HDLKiosk/components/shell/HDLKioskBottomBarHost/index.js +12 -10
  69. package/lib/components/HDLKiosk/components/shell/HDLKioskShell/index.js +1 -0
  70. package/lib/components/HDLKiosk/docs/00-/346/226/207/344/273/266/345/257/274/350/210/252.md +7 -7
  71. 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 +47 -40
  72. 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 +3 -3
  73. package/lib/components/HDLKiosk/docs/05-/344/272/244/346/230/223/345/256/211/345/205/250.md +1 -1
  74. package/lib/components/HDLKiosk/docs/07-/345/220/216/347/273/255/345/267/245/344/275/234.md +1 -1
  75. package/lib/components/HDLKiosk/docs/README.md +2 -2
  76. package/lib/components/HDLKiosk/docs//345/256/236/346/226/275/350/277/233/345/272/246.md +3 -3
  77. package/lib/components/HDLKiosk/docs//346/236/266/346/236/204/350/256/276/350/256/241.md +11 -11
  78. package/lib/components/HDLKiosk/index.js +0 -1
  79. package/lib/components/HDLKiosk/kernel/README.md +8 -9
  80. package/lib/components/HDLKiosk/kernel/application/defineApplication.js +18 -19
  81. package/lib/components/HDLKiosk/kernel/application/defineSlice.js +8 -14
  82. package/lib/components/HDLKiosk/kernel/workflow/actor.js +118 -28
  83. package/lib/components/HDLKiosk/kernel/workflow/compiler.js +35 -41
  84. package/lib/components/HDLKiosk/kernel/workflow/selectors.js +10 -0
  85. package/lib/components/HDLKiosk/runtime/HDLKioskWorkflowView.js +10 -9
  86. package/lib/components/HDLKiosk/runtime/controllers/useHDLKioskCustomerController.js +1 -1
  87. package/lib/components/HDLKiosk/runtime/createWorkflowSession.js +3 -3
  88. package/lib/components/HDLKiosk/runtime/domainTransitionRunner.js +9 -9
  89. package/lib/components/HDLKiosk/runtime/workflowEvent.js +9 -4
  90. package/lib/components/HDLKiosk/runtime/workflowLifecycle.js +3 -3
  91. package/lib/components/HDLKiosk/slices/catalog/PrimaryProductPromptDialog/index.js +1 -2
  92. package/lib/components/HDLKiosk/slices/catalog/Screen/index.js +13 -40
  93. package/lib/components/HDLKiosk/slices/catalog/Screen/model.js +0 -5
  94. package/lib/components/HDLKiosk/slices/catalog/slice.js +2 -15
  95. package/lib/components/HDLKiosk/slices/catalog/view.js +4 -6
  96. package/lib/components/HDLKiosk/slices/customization/Screen/index.js +9 -11
  97. package/lib/components/HDLKiosk/slices/customization/operation.js +23 -3
  98. package/lib/components/HDLKiosk/slices/customization/slice.js +3 -7
  99. package/lib/components/HDLKiosk/slices/customization/view.js +3 -2
  100. package/lib/components/HDLKiosk/slices/fulfillment/Screen/index.js +6 -6
  101. package/lib/components/HDLKiosk/slices/fulfillment/slice.js +4 -7
  102. package/lib/components/HDLKiosk/slices/identity/Screen/index.js +17 -11
  103. package/lib/components/HDLKiosk/slices/identity/slice.js +4 -11
  104. package/lib/components/HDLKiosk/slices/index.js +2 -1
  105. package/lib/components/HDLKiosk/slices/payment/Screen/index.js +5 -5
  106. package/lib/components/HDLKiosk/slices/payment/slice.js +6 -7
  107. package/lib/components/HDLKiosk/slices/primaryProductPrompt/slice.js +14 -0
  108. package/lib/components/HDLKiosk/slices/primaryProductPrompt/view.js +25 -0
  109. package/lib/components/HDLKiosk/utils.js +3 -2
  110. package/lib/components/bigSale/templates/HDLKiosk/auth/HDLKioskIdentityAuthPanel.js +16 -1
  111. package/lib/pro/Login2.0/Login2.d.ts +2 -0
  112. package/lib/pro/Login2.0/Login2.js +9 -1
  113. package/lib/pro/Login2.0/Register2.d.ts +2 -0
  114. package/lib/pro/Login2.0/Register2.js +9 -1
  115. package/package.json +4 -4
@@ -6,8 +6,8 @@ import { locales } from "@pisell/utils";
6
6
  //#region src/components/HDLKiosk/app/ScreenRenderer.tsx
7
7
  /**
8
8
  * 文件角色:把当前 Workflow Node 交给其 Slice 自有 View 渲染。
9
- * 运行机制:从应用 View 注册表按 Activity 读取同步组件;Modal 节点先渲染 base
10
- * Slice,再叠加当前 Slice View。
9
+ * 运行机制:从应用 View 注册表按 Activity 读取同步组件;先渲染 Actor
10
+ * Surface 保留的 Page,再按顺序叠加 Modal Slice View。
11
11
  * 边界:只组装 View Context,不调用 Host、不创建 ViewModel、不决定下一节点。
12
12
  */
13
13
  function ScreenRenderer({ definition, snapshot, sales, send, commandState = { busy: false }, resetting, capabilities, businessMode = "prototype", scaleMinimumWeightKg = .01, renderIdentityAuth, memberAction = {
@@ -22,12 +22,12 @@ function ScreenRenderer({ definition, snapshot, sales, send, commandState = { bu
22
22
  methods: [],
23
23
  loading: false
24
24
  } }) {
25
- var _activeNode$presentat, _commandState$error;
25
+ var _definition$nodes$sna, _commandState$error;
26
26
  const activeNode = definition.nodes[snapshot.nodeId];
27
- const modalBaseNodeId = ((_activeNode$presentat = activeNode.presentation) === null || _activeNode$presentat === void 0 ? void 0 : _activeNode$presentat.mode) === "modal" ? activeNode.presentation.base : void 0;
28
- const renderedNode = definition.nodes[modalBaseNodeId !== null && modalBaseNodeId !== void 0 ? modalBaseNodeId : snapshot.nodeId];
27
+ const pageNode = (_definition$nodes$sna = definition.nodes[snapshot.surface.pageStepId]) !== null && _definition$nodes$sna !== void 0 ? _definition$nodes$sna : activeNode;
28
+ const overlayNodes = snapshot.surface.overlays.map((stepId) => definition.nodes[stepId]).filter((node) => Boolean(node));
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,
@@ -64,7 +64,7 @@ function ScreenRenderer({ definition, snapshot, sales, send, commandState = { bu
64
64
  };
65
65
  return /* @__PURE__ */ React.createElement(SliceView, context);
66
66
  };
67
- return /* @__PURE__ */ React.createElement(React.Fragment, null, renderNode(renderedNode), modalBaseNodeId ? renderNode(activeNode) : null);
67
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, renderNode(pageNode), overlayNodes.map((node) => /* @__PURE__ */ React.createElement(React.Fragment, { key: node.id }, renderNode(node))));
68
68
  }
69
69
  //#endregion
70
70
  export { ScreenRenderer };
@@ -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,31 @@
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
- }
24
- },
25
- {
26
- 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" }
21
+ slice: primaryProductPromptSlice,
22
+ presentation: { mode: "modal" },
23
+ visitPolicy: "once",
24
+ on: { omit: "fulfillment" }
51
25
  },
26
+ customizationSlice,
27
+ fulfillmentSlice,
28
+ paymentMethodSlice,
52
29
  {
53
30
  slice: paymentCompletedSlice,
54
31
  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` 负责主商品询问;Prompt 以 Modal 覆盖 Actor Surface 中当前实际 Page,不引用固定背景 Step。支付派发和店员支付授权是 `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,39 @@ 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' },
179
+ visitPolicy: 'once',
180
+ on: { omit: 'fulfillment' },
178
181
  },
179
- { slice: customizationSlice, on: { completed: 'catalogWithPrimaryProductPrompt' } },
180
- { slice: paymentMethodSlice, on: { back: 'catalogWithPrimaryProductPrompt' } },
181
- // ...
182
+ customizationSlice,
183
+ fulfillmentSlice,
184
+ paymentMethodSlice,
182
185
  { slice: paymentCompletedSlice, end: 'success' },
183
186
  ],
184
187
  }
185
188
  ```
186
189
 
187
- Workflow 作者不会填写 Screen、Guard、Operation 或 failureTarget;这些由 Slice 引用带入本地可信 Manifest。Compiler 使用的规范化结构只保留 Activity ID、presentation 与目标,不携带 React 组件、Host 方法或订单对象。Quick、Barcode、Scale 的差异只保留在 [`app/workflow/`](../app/workflow)。
190
+ Workflow 作者不会填写 Screen、Predicate、Operation 或 failureTarget;这些由 Slice 引用带入本地可信 Manifest。Compiler 使用的规范化结构只保留 Activity ID、presentation 与目标,不携带 React 组件、Host 方法或订单对象。Quick、Barcode、Scale 的差异只保留在 [`app/workflow/`](../app/workflow)。
188
191
 
189
192
  四个概念必须分开:
190
193
 
191
- - **Slice**:开发者定义的完整业务能力,包含 Screen、Outcome、Command 和退出 Effect;
194
+ - **Slice**:开发者定义的完整业务能力,包含 Screen、Intent、Command 和退出 Effect;
192
195
  - **Activity**:Slice 暴露给 Actor 的无 UI 运行契约,例如 `catalog`;
193
196
  - **Step**:Activity 在一条流程中的一次出现,是编排和运行时定位的稳定位置;
194
197
  - **Screen**:Activity 的本地 UI 实现,不属于 Workflow Definition。
195
198
 
196
199
  Slice 在一条流程只出现一次时,Step ID 默认就是 Slice ID;同一 Slice 多次出现时,每次必须显式使用 `slice:用途`,例如 `catalog:initial`、`catalog:final`。这样既保持常规流程易读,也不会把复用能力误认为同一个运行位置。
197
200
 
198
- Slice Outcome 使用 `next()` / `back()`,或在 `guard/effect` 上声明 `navigate`,决定相邻 Step 的默认移动。不会移动的 Outcome 只在 Workflow `on` 中指定特殊目标。没有下一项的 Step 不会被推测为成功,必须声明 `end: 'success' | 'blocked'`。
201
+ Slice Intent 使用 `next()`,或在 `intentPrecondition/effect` 上声明 `navigate: 'next'`,决定相邻 Step 的默认移动。不会自动前进的 Intent 只在 Workflow `on` 中指定目标。Back 是独立 Navigation,由 Actor 的实际页面历史解析。没有下一项的 Step 不会被推测为成功,必须声明 `end: 'success' | 'blocked'`。
199
202
 
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 等业务名称。
203
+ 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 是两套独立能力。
201
204
 
202
- Actor 记录的是已成功向前离开的 **Step ID**,而不是 Slice ID。因此同一 Slice 可在 Flow 中出现多次,每个 occurrence 都可独立声明 `useGuards`;第一个被守卫跳过,不会影响第二个未启用守卫的 Step。导航在执行 Activity Exit Effect 之前完成解析;被 cancel/defer 的导航不会先写 Host。
205
+ 业务 Workflow 当前不启用 Slice `beforeEnter` 守卫。Actor 仍保留全局 `beforeEach` 管线,用于 `sessionExit` 等会话安全策略;它不承载 Identity、Fulfillment 等步骤的业务进入条件。
206
+
207
+ Actor 分别记录已成功向前离开的 Step、每个 Step 已提交的 `complete | omit` Resolution,以及用户实际访问过的页面历史。`complete` 保留页面的 Back 路径;`omit` 表示将该 Step 从当前流程路径中排除,后续遇到时沿已提交的 `omit` transition 继续,也不写入 Back 历史。Modal 不写入页面历史;历史为空时 Back 进入会话退出导航。Workflow occurrence 只有 `visitPolicy: 'always' | 'once'`:`always` 保留已 `complete` Step 的正常访问,`once` 表示任意 Resolution 提交后不再进入且不写入 Back 历史。用户通过 Back 退出一个已解决页面时,其旧 Resolution 失效。导航在执行 Activity Exit Effect 之前完成解析;被 cancel/defer 的导航不会先写 Host。
203
208
 
204
209
  退出流程也是导航目标 `{ kind: 'sessionExit' }`。全局 `beforeEach` 对它返回 `defer({ kind: 'confirmEndOrder' })`,Actor 保存带唯一 `navigationId` 的 `pendingNavigation`。Shell 只根据该 request 展示确认;旧 ID 不能恢复后来的导航。
205
210
 
@@ -210,38 +215,38 @@ Step ID 只标识“一次出现”。Runtime 判断加载哪类数据、是否
210
215
  [`slices/<name>/slice.ts`](../slices) 是本地可信 Slice 定义,每个 Slice 声明:
211
216
 
212
217
  - 业务 Activity 使用哪个 `screen.id`,以及该 Screen 的 `view`;
213
- - 可以产生哪些 Outcome;
218
+ - 可以接受哪些 Intent;
214
219
  - 支持 page 还是 modal;
215
- - 某个 Outcome 是否需要纯 Guard;
220
+ - 某个 Intent 是否需要纯 Predicate 作为 Precondition;
216
221
  - 离开 Slice 前是否需要执行 Effect;
217
222
  - Screen 内有哪些不会离开节点的 Command;
218
223
  - 使用该 Slice 的流程在支付前需要哪些业务事实。
219
224
 
220
- 例如 `identity.skipped` 的本地 Exit Policy 执行 `identity.sync`,`customization.completed` 检查 `customization.primaryReady`,`paymentMethod.cardSelected` 执行 `checkout.open`。`defineKioskApplication` 从 Slice 自动生成 Activity Contract 与 Operation 注册信息;未来后台只会看到 `Slice ID + Outcome → target`,不能替换这些安全规则。
225
+ 例如 `identity.complete` / `identity.omit` 可在携带输入时执行 `identity.sync`,`customization.complete` 执行主商品同步,`paymentMethod.complete` 检查 Host 已确认支付成功。EFTPOS 派发和店员支付授权是 Payment 页面 Command,不作为流程 Intent。`defineKioskApplication` 从 Slice 自动生成 Activity Contract 与 Operation 注册信息;远端定义只看到 `Slice ID + 标准 Intent → target`,不能替换这些安全规则。
221
226
 
222
227
  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
228
 
224
229
  | Slice 定义 | 编译/运行期形态 | 唯一用途 |
225
230
  | --- | --- | --- |
226
- | `id` | Node `activity` | 业务状态、Outcome 和流程编排 |
231
+ | `id` | Node `activity` | 业务状态、Intent 和流程编排 |
227
232
  | `screen.id` | Node `screenId` | Shell 变体、数据加载和页面类别判断 |
228
233
  | `app/views.ts` 中的 Activity 映射 | 仅应用展示层使用 | React UI 渲染 |
229
234
 
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。
235
+ 因此 `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
236
 
232
237
  `slices/index.ts` 对整套 Slice 清单执行一次类型约束:`screen.id` 只能取 HDLKiosk 的非 Entry `ScreenId`。`app/views.ts` 使用完整的 `Record<ActivityId, HDLKioskSliceView>`,因此新增 Activity 若没有注册 View 会产生类型错误。Kernel 的 `defineSlice` 同时拒绝空 `screen.id`。
233
238
 
234
239
  Compiler 从实际使用的 Slice 汇总 Checkout Requirement ID,但不把判断函数或 Host 字段写入 Workflow。当前 HDLKiosk 默认注册表为空;未来增加其他业务要求时仍由 [`checkoutRequirements.ts`](../app/operations/checkoutRequirements.ts) 集中判断。
235
240
 
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` 保持隐式默契。
241
+ `WorkflowEvent` 同时约束 Activity、Intent 和必要输入:例如 `identity.omit` 必须携带身份输入,`fulfillment.complete` 必须携带 fulfillment context patch。Back 使用不携带业务输入的 `NAVIGATE_BACK`。Barcode 入口的条码输入不是 Workflow Event,而是 Session Controller 在发布会话前交给 `barcode.consume` 的 Command 输入。Screen Command 由 `OperationInputMap` 约束 Command ID 与输入类型。
237
242
 
238
243
  Step 可选声明 Modal 展示关系:
239
244
 
240
245
  ```ts
241
- presentation: { mode: 'modal', base: 'customization' }
246
+ presentation: { mode: 'modal' }
242
247
  ```
243
248
 
244
- 这只控制渲染层级。Compiler 会拒绝缺失、自引用或嵌套 Modal base。
249
+ 这只控制渲染层级。Actor Surface 保留进入 Modal 前实际展示的 Page,Workflow 不指定背景 Step。Compiler 会拒绝以 Modal 作为首节点或 Modal 直接进入另一个 Modal。
245
250
 
246
251
  ### 6.3 结构 Compiler 与业务 Policy 校验
247
252
 
@@ -249,7 +254,7 @@ presentation: { mode: 'modal', base: 'customization' }
249
254
 
250
255
  - workflow id、Step 数组及数量/transition 上限;
251
256
  - Activity 是否存在;重复 Activity 是否具有稳定且不冲突的 Step ID;
252
- - Outcome 是否由 Activity Contract 暴露;
257
+ - Intent 是否由 Activity Contract 暴露;
253
258
  - Definition 是否夹带 screen/guard/operation/failureTarget;
254
259
  - 默认相邻导航和显式 target 是否有效,所有 Step 是否从首项可达;
255
260
  - 无后继 Step 是否声明结束语义,首项是否能到达成功终点;
@@ -264,16 +269,16 @@ session 创建前完成,但结构错误与业务安全错误保持独立。
264
269
 
265
270
  数组天然只有一个首项;不可达 Step 会被拒绝。显式业务回路必须声明 `repeat.max`,Actor 按会话计数,超过限制进入阻塞态。普通 Back 属于用户导航,不计入业务回路限制。校验通过后,Compiler 把数组转成 Actor 查询高效的冻结节点 Map;运行中的 session 不替换 Definition。
266
271
 
267
- ### 6.4 Actor 如何处理 Activity Outcome
272
+ ### 6.4 Actor 如何处理 Activity Intent 与 Navigation
268
273
 
269
274
  Actor 使用单 mailbox:
270
275
 
271
276
  1. 忽略 success end、blocked 或 disposed 后的新事件;
272
277
  2. 同时校验事件的 `stepId` 和 `activityId`,拒绝旧 Screen 或同类 Activity 旧实例的迟到回调;
273
- 3. 校验 Outcome 是否由当前 Activity 暴露,并查找 Transition;
278
+ 3. 校验 Intent 是否由当前 Activity 暴露,并查找 Transition;Back 则读取页面历史;
274
279
  4. 通过注入的 Context Reducer 合并受限 contextPatch;
275
280
  5. 把 Exit Policy 交给 HDLKiosk Transition Runner;
276
- 6. Runner 用最新 Sales Projection 执行 Guard,并通过 Coordinator 执行 Exit Command;
281
+ 6. Runner 用最新 Sales Projection 求值 Precondition Predicate,并通过 Coordinator 执行 Exit Command;
277
282
  7. Runner 将结果归一为 `advance / stay / blocked`,支付成功回执也由 Runner 解释;
278
283
  8. Actor 只负责提交 target、当前 Step 或 blocked,并清理 active operation;
279
284
  9. 已提交 Snapshot 交给 Workflow Lifecycle 记录 telemetry;外层可组合额外观察器。
@@ -301,7 +306,7 @@ Coordinator 现在有两个明确调用来源:
301
306
  - Screen Command:Catalog 加购、Cart Overlay 数量修改、称重草稿同步等;由 `session.executeCommand` 执行,成功后 Actor 节点不变;
302
307
  - Activity Exit Command:Guest 同步、Checkout Prepare/Open 等;由 Transition Runner 从本地 Exit Policy 发起,只有 succeeded 才允许 Actor 转移。
303
308
 
304
- partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入口 fail closed;它不是伪造一个 Activity Outcome。
309
+ partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入口 fail closed;它不伪造 Activity Intent。
305
310
 
306
311
  当前 Operation 组:
307
312
 
@@ -313,6 +318,7 @@ partialFailure/unknown 的 Screen Command 会通过 Actor 的 system block 入
313
318
  | `cart.addProduct` | cart | — | 调宿主商品 Flow,等待规格或其它业务选择后验证写入 |
314
319
  | `cart.update/remove` | cart | 15s | 数量修改/删除、summary 重算和目标行验证 |
315
320
  | `product.syncPrimaryWeightedProduct` | cart | — | 首次写入可能等待 OS 额外业务弹窗;更新或写入后验证重量/options 和写后证据 |
321
+ | `product.removePrimaryProduct` | cart | 15s | 将主商品置为不存在;本来不存在时直接成功,存在时验证删除结果 |
316
322
  | `checkout.prepare*` | checkout | 20s | fulfillment、cart、summary 稳定性与 token |
317
323
  | `checkout.open` | checkout | 120s | 消费 token、冻结危险写并打开 Host 支付 |
318
324
 
@@ -389,22 +395,22 @@ Host command
389
395
  - 尚无活跃或启动中的 session;
390
396
  - 支付状态未冻结。
391
397
 
392
- 通过 ScannerIngress/ScaleIngress 完成 eventId 去重、阈值检查和拒绝原因遥测。Device Controller 不向 Actor 发送 `detected` Outcome;Barcode 把条码作为 start request 的一部分,Scale 只发起 Scale session。
398
+ 通过 ScannerIngress/ScaleIngress 完成 eventId 去重、阈值检查和拒绝原因遥测。Device Controller 不向 Actor 发送流程 Intent;Barcode 把条码作为 start request 的一部分,Scale 只发起 Scale session。
393
399
 
394
400
  ### 8.3 Customer Controller
395
401
 
396
- Identity 节点内的登录是 Activity Outcome,不是 Screen 直接写订单:
402
+ Identity 节点内的登录是 Activity Intent,不是 Screen 直接写订单:
397
403
 
398
404
  ```text
399
405
  BigSale Login2/Register2
400
406
  → 暂存已认证 customer
401
- → identity.memberRecognized(customerId)
407
+ → identity.complete(customerId)
402
408
  → identity.sync Exit Effect
403
409
  → Adapter customer.select + consistency + repricing
404
410
  → 成功后进入下一节点
405
411
  ```
406
412
 
407
- 认证 UI 通过 HDLKiosk 应用展示层插槽注入,不进入 `defineSlice`、View 注册表或 Kernel。Slice 只声明 `memberRecognized` 在携带输入时执行 `identity.sync`;已登录客户的自动略过不携带输入,因此不会重复写客户。
413
+ 认证 UI 通过 HDLKiosk 应用展示层插槽注入,不进入 `defineSlice`、View 注册表或 Kernel。Slice 的 `complete` 在携带输入时执行 `identity.sync`;已登录客户的自动略过使用 `omit` 且不携带输入,因此不会重复写客户。
408
414
 
409
415
  Shell 登录不是 Workflow 跳转,而是 session-level 插入 Operation:
410
416
 
@@ -421,7 +427,7 @@ Shell Login
421
427
 
422
428
  ### 8.4 Screen Data Controller
423
429
 
424
- 只在相关节点加载 Catalog、Weighted Product 或 Customization DTO;使用 AbortController 防止离开页面后的结果写回。Modal Step 通过 `presentation.base` 继续复用底层页面数据,避免称重读数和 options 被重置。
430
+ 只在相关节点加载 Catalog、Weighted Product 或 Customization DTO;使用 AbortController 防止离开页面后的结果写回。Modal 激活时,Screen Data Controller 继续使用 Actor Surface 中保留的 `pageStepId`,底层 Page 保持挂载和原数据,不按 Modal Activity 清空或重新加载。
425
431
 
426
432
  ### 8.5 返回 Entry 能力接口
427
433
 
@@ -475,8 +481,9 @@ HDLKiosk root
475
481
  → components/shell + components/overlays + components/common
476
482
  → Activity Action callback
477
483
  → HDLKioskWorkflowView
478
- ├─ Outcome → Workflow Actor
479
- │ → HDLKiosk Transition Runner(Guard / Exit Command)
484
+ ├─ Intent → Workflow Actor
485
+ ├─ Back → Workflow Navigation
486
+ │ → HDLKiosk Transition Runner(Predicate / Exit Command)
480
487
  ├─ Command → session.executeCommand → Coordinator
481
488
  └─ Session Action → reset controller
482
489
  ```
@@ -499,7 +506,7 @@ Screen 只做三件事:
499
506
 
500
507
  1. 渲染 View DTO;
501
508
  2. 维护局部输入;
502
- 3. 内部业务写入发送 Screen Command,离开步骤发送 Activity Outcome。
509
+ 3. 内部业务写入发送 Screen Command,离开步骤发送 Activity Intent,返回发送 Navigation。
503
510
 
504
511
  Screen 不得:
505
512
 
@@ -515,13 +522,13 @@ CartDrawer 也遵循“Screen 声明、Shell 挂载”:Screen 只请求打开
515
522
 
516
523
  Customization 的规格 UI 通过 `components/common/HDLKioskSkuOptionsSelection` 接入共享 `skuOptionsSelection`。查询所得 ProductData 本身直接作为 `dataSource`,兼容层不再复制或重命名 option group/item,只在选择器 `SkuValue` 与业务命令所需 `HDLProductOptionSelection[]` 之间转换“用户选中结果”。临时独立运行缺少 Engine 上下文时,退化为直接读取同一 `option_group/option_item` 的单选控件。
517
524
 
518
- 称重主商品在 Customization 中先保持页面本地草稿。重量或 options 变化不写 Host;点击 Next 时才将当下完整草稿作为 `customization.completed` 的 Exit Effect 交给 `product.syncPrimaryWeightedProduct`。OS 完成其内部业务判断并返回加购成功,且 writer receipt/Projection 验证通过后,Actor 才进入下一节点。失败或取消留在当前页面,`partial/unknown` 阻断。底栏和 Drawer 仍只消费 Host `summary.due/cart.lines`,不在本地构造未确认的购物车。
525
+ 称重主商品在 Customization 中先保持页面本地草稿。重量或 options 变化不写 Host;点击 Next 时才将当下完整草稿作为 `customization.complete` 的 Exit Effect 交给 `product.syncPrimaryWeightedProduct`。OS 完成其内部业务判断并返回加购成功,且 writer receipt/Projection 验证通过后,Actor 才进入下一节点。失败或取消留在当前页面,`partial/unknown` 阻断。底栏和 Drawer 仍只消费 Host `summary.due/cart.lines`,不在本地构造未确认的购物车。
519
526
 
520
527
  Workflow View 负责控制器组合、自动推进和会话级 Dialog;Renderer 只负责选择要展示的组件。普通 `cart.add/update/remove` 和防抖的 `product.syncPrimaryWeightedProduct` 是 Screen Command,保持当前 Activity 挂载,只禁用冲突操作,不产生 transition;会员重报价和 Checkout 等 Activity Exit Command 才阻断整个 surface。
521
528
 
522
529
  OS 加购过程中的内部弹窗和业务判断不属于 HDLKiosk Activity、Host Contract 或 Checkout Requirement。HDLKiosk 不读写相关业务事实,只根据主商品加购操作的最终结果决定是否跳转。
523
530
 
524
- Barcode 的主商品询问采用相同原则,但它属于 `catalogWithPrimaryProductPrompt` Activity 的局部 Dialog:No 产生 `completed`,Yes 产生 `primaryProductRequested`。是否打开 Dialog 只由当前购物车是否包含主商品决定,不形成 `primaryProductPrompt` Workflow 节点。
531
+ Barcode 的主商品询问是独立 `primaryProductPrompt` Modal Slice:Yes 发出 `complete`,No 发出 `omit`。Prompt 声明 `visitPolicy: 'once'`,因此任一选择成功提交后不再询问;关闭 Modal 只执行 Back,没有 Resolution,下次仍会展示。Customization 使用缺省 `always`:`complete` 后保留 Back 编辑能力;`omit` 会先通过本地 Exit Effect 删除已有主商品,再将该 Step 从后续路径和 Back 历史中排除。
525
532
 
526
533
  ## 11. 四条端到端调用链
527
534