@phis/ui 0.4.1 → 0.4.3

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 (246) hide show
  1. package/components/cms/phi-cms-layout-renderer.js +4 -3
  2. package/components/cms/phi-cms-page-render-scope.d.ts +4 -5
  3. package/components/cms/phi-cms-page-render-scope.js +43 -22
  4. package/components/cms/phi-cms-render-diagnostic-client.js +7 -3
  5. package/components/cms/phi-cms-render-error-boundary.js +2 -1
  6. package/components/controls/phi-bullet-list-control.js +31 -2
  7. package/components/controls/phi-ribbon-control.d.ts +29 -0
  8. package/components/controls/phi-ribbon-control.js +53 -0
  9. package/components/controls/phi-table-control.d.ts +3 -0
  10. package/components/controls/phi-table-control.js +13 -1
  11. package/components/controls/phi-tree-control.js +3 -3
  12. package/components/forms/shared-form-provider-registry.js +1 -4
  13. package/components/media/asset-collection-runtime.js +6 -16
  14. package/components/media/media-font-metrics.d.ts +3 -0
  15. package/components/media/media-font-metrics.js +43 -0
  16. package/components/media/phi-area-upload-widget.js +53 -97
  17. package/components/media/phi-image-preview-data.d.ts +13 -0
  18. package/components/media/phi-image-preview-data.js +184 -9
  19. package/components/media/phi-media-picker-binding.js +16 -13
  20. package/components/media/phi-media-upload.d.ts +15 -8
  21. package/components/media/phi-media-upload.js +18 -9
  22. package/components/media/phi-site-font-assets.js +7 -8
  23. package/components/overlays/phi-overlay-container-client.js +16 -1
  24. package/components/regions/presets/phi-settings-page-shell-tree.js +48 -1
  25. package/components/runtime/runtime-controller-materialization.js +12 -1
  26. package/components/state/scoped-state-store.js +20 -3
  27. package/components/tables/client/phi-table-binding.d.ts +2 -1
  28. package/components/tables/client/phi-table-binding.js +45 -21
  29. package/components/trees/client/phi-tree-binding.js +19 -11
  30. package/components/widgets/client/bullet-list-entries-editor.d.ts +26 -0
  31. package/components/widgets/client/bullet-list-entries-editor.js +103 -0
  32. package/components/widgets/config/choice-shared.js +12 -13
  33. package/components/widgets/config/command-buttons.d.ts +5 -1
  34. package/components/widgets/config/command-buttons.js +38 -81
  35. package/components/widgets/config/control-signal-config.d.ts +3 -1
  36. package/components/widgets/config/control-signal-config.js +19 -17
  37. package/components/widgets/config/image-source-parser.d.ts +1 -0
  38. package/components/widgets/config/image-source-parser.js +7 -8
  39. package/components/widgets/config/mask.d.ts +6 -1
  40. package/components/widgets/config/mask.js +24 -1
  41. package/components/widgets/config/parser-primitives.js +72 -0
  42. package/components/widgets/config/stack-tabs.d.ts +1 -1
  43. package/components/widgets/config/stack-tabs.js +2 -3
  44. package/components/widgets/config/strict-config-reader.d.ts +40 -0
  45. package/components/widgets/config/strict-config-reader.js +174 -0
  46. package/components/widgets/label-sets/effects.js +2 -0
  47. package/components/widgets/label-types/effects.d.ts +1 -0
  48. package/components/widgets/label-types/effects.js +1 -0
  49. package/components/widgets/navigation-descriptor-resolver.server.js +20 -8
  50. package/components/widgets/shared/bullet-list-vocabulary.d.ts +17 -1
  51. package/components/widgets/shared/bullet-list-vocabulary.js +19 -2
  52. package/components/widgets/shared/card-body-client.d.ts +16 -4
  53. package/components/widgets/shared/card-body-client.js +81 -24
  54. package/components/widgets/shared/card-vocabulary.d.ts +21 -4
  55. package/components/widgets/shared/card-vocabulary.js +28 -2
  56. package/components/widgets/shared/markdown-body-client.d.ts +2 -1
  57. package/components/widgets/shared/markdown-body-client.js +3 -1
  58. package/components/widgets/shared/markdown-render.js +8 -1
  59. package/components/widgets/signals/control-signal-capabilities.d.ts +0 -18
  60. package/components/widgets/signals/control-signal-capabilities.js +10 -22
  61. package/components/workspace/catalog-store.d.ts +4 -3
  62. package/components/workspace/catalog-store.js +6 -5
  63. package/gateway/dashboard-cards-route.js +1 -0
  64. package/gateway/internal-references.d.ts +6 -1
  65. package/gateway/internal-references.js +48 -23
  66. package/gateway/site-cms-write-validation.d.ts +9 -0
  67. package/gateway/site-cms-write-validation.js +78 -0
  68. package/gateway/site-proxy.js +19 -3
  69. package/helpers/renderable-block-serialization.js +1 -1
  70. package/next/config.d.mts +3 -0
  71. package/next/config.mjs +28 -1
  72. package/package.json +2 -2
  73. package/plugins/runtime/phi-slot-child-viewport-effects-observer.js +5 -1
  74. package/plugins/runtime-modules/accounting/presets.d.ts +9 -0
  75. package/plugins/runtime-modules/accounting/presets.js +37 -0
  76. package/plugins/runtime-modules/accounting/server.d.ts +1 -4
  77. package/plugins/runtime-modules/accounting/server.js +4 -7
  78. package/plugins/runtime-modules/app/presets.d.ts +18 -0
  79. package/plugins/runtime-modules/app/presets.js +67 -0
  80. package/plugins/runtime-modules/app/server.d.ts +1 -4
  81. package/plugins/runtime-modules/app/server.js +4 -7
  82. package/plugins/runtime-modules/asset/controller/runtime.js +2 -2
  83. package/plugins/runtime-modules/asset/services/collection.js +9 -1
  84. package/plugins/runtime-modules/asset/services/options.js +2 -2
  85. package/plugins/runtime-modules/builder/area-settings-answer.js +2 -1
  86. package/plugins/runtime-modules/builder/authoring-canvas.js +6 -6
  87. package/plugins/runtime-modules/builder/cards.js +3 -5
  88. package/plugins/runtime-modules/builder/clients/builder-region-overrides.js +3 -3
  89. package/plugins/runtime-modules/builder/clients/canvas-widget.js +15 -54
  90. package/plugins/runtime-modules/builder/clients/inspector-config-field.d.ts +4 -1
  91. package/plugins/runtime-modules/builder/clients/inspector-config-field.js +5 -34
  92. package/plugins/runtime-modules/builder/clients/inspector-link-target-field.js +1 -1
  93. package/plugins/runtime-modules/builder/clients/inspector-section-widget.js +8 -8
  94. package/plugins/runtime-modules/builder/clients/inspector-title.js +6 -6
  95. package/plugins/runtime-modules/builder/clients/layout-inspector.d.ts +1 -1
  96. package/plugins/runtime-modules/builder/clients/layout-inspector.js +11 -25
  97. package/plugins/runtime-modules/builder/clients/mode-switch.js +1 -1
  98. package/plugins/runtime-modules/builder/clients/pages-header.js +11 -11
  99. package/plugins/runtime-modules/builder/clients/pages-workspace.js +1 -1
  100. package/plugins/runtime-modules/builder/clients/region-visibility-fields.js +1 -1
  101. package/plugins/runtime-modules/builder/clients/runtime-module-authoring-boundary.js +1 -1
  102. package/plugins/runtime-modules/builder/clients/shells-workspace.js +15 -31
  103. package/plugins/runtime-modules/builder/clients/structure-canvas.js +1 -1
  104. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.d.ts +26 -0
  105. package/plugins/runtime-modules/builder/clients/use-region-draft-hydration.js +36 -0
  106. package/plugins/runtime-modules/builder/clients/widget-effects-editor.js +1 -1
  107. package/plugins/runtime-modules/builder/clients/widget-inspector.js +1 -0
  108. package/plugins/runtime-modules/builder/clients/workspace-scope-boundary.js +2 -2
  109. package/plugins/runtime-modules/builder/cms-instance-allocator.js +8 -28
  110. package/plugins/runtime-modules/builder/controller/client.d.ts +0 -15
  111. package/plugins/runtime-modules/builder/controller/definition.d.ts +0 -15
  112. package/plugins/runtime-modules/builder/controller/definition.js +0 -8
  113. package/plugins/runtime-modules/builder/controller/draft-command-controller.d.ts +1 -2
  114. package/plugins/runtime-modules/builder/controller/draft-command-controller.js +11 -30
  115. package/plugins/runtime-modules/builder/controller/draft-status-controller.js +15 -2
  116. package/plugins/runtime-modules/builder/controller/inspector-controller.d.ts +1 -2
  117. package/plugins/runtime-modules/builder/controller/inspector-controller.js +4 -7
  118. package/plugins/runtime-modules/builder/controller/page-controller.d.ts +1 -2
  119. package/plugins/runtime-modules/builder/controller/page-controller.js +16 -68
  120. package/plugins/runtime-modules/builder/controller/server-definition.d.ts +0 -15
  121. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.d.ts +2 -5
  122. package/plugins/runtime-modules/builder/controller/signal-wiring-controller.js +6 -50
  123. package/plugins/runtime-modules/builder/controller/workspace-controller.d.ts +2 -3
  124. package/plugins/runtime-modules/builder/controller/workspace-controller.js +89 -153
  125. package/plugins/runtime-modules/builder/data-providers.d.ts +1 -0
  126. package/plugins/runtime-modules/builder/data-providers.js +1 -0
  127. package/plugins/runtime-modules/builder/definition.d.ts +1 -0
  128. package/plugins/runtime-modules/builder/developer-workspace-store.d.ts +64 -30
  129. package/plugins/runtime-modules/builder/developer-workspace-store.js +255 -61
  130. package/plugins/runtime-modules/builder/edit-scaffold-drawer.js +1 -1
  131. package/plugins/runtime-modules/builder/history.d.ts +3 -1
  132. package/plugins/runtime-modules/builder/options-providers.js +3 -2
  133. package/plugins/runtime-modules/builder/page-catalog-client.js +19 -7
  134. package/plugins/runtime-modules/builder/page-meta-form.js +1 -0
  135. package/plugins/runtime-modules/builder/page-references-provider.js +1 -1
  136. package/plugins/runtime-modules/builder/persistence.js +33 -69
  137. package/plugins/runtime-modules/builder/public-route-collisions.d.ts +2 -2
  138. package/plugins/runtime-modules/builder/public-route-collisions.js +6 -19
  139. package/plugins/runtime-modules/builder/render-root-node-preview.server.js +20 -3
  140. package/plugins/runtime-modules/builder/render-root-node-scaffold.js +3 -3
  141. package/plugins/runtime-modules/builder/runtime-module-selection.d.ts +2 -12
  142. package/plugins/runtime-modules/builder/runtime-module-selection.js +16 -42
  143. package/plugins/runtime-modules/builder/services/navigation-table.js +9 -15
  144. package/plugins/runtime-modules/builder/services/page-source-tree.js +8 -8
  145. package/plugins/runtime-modules/builder/services/runtime-modules-table.js +10 -10
  146. package/plugins/runtime-modules/builder/services/signal-routes-table.js +3 -3
  147. package/plugins/runtime-modules/builder/signal-route-selection.d.ts +1 -2
  148. package/plugins/runtime-modules/builder/signal-route-selection.js +2 -2
  149. package/plugins/runtime-modules/builder/signal-wiring-options.js +3 -2
  150. package/plugins/runtime-modules/builder/structure-dnd.js +8 -61
  151. package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +13 -10
  152. package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +12 -18
  153. package/plugins/runtime-modules/builder/workspace-catalog-actions.d.ts +55 -0
  154. package/plugins/runtime-modules/builder/workspace-catalog-actions.js +142 -0
  155. package/plugins/runtime-modules/client-manifests/common.js +1 -1
  156. package/plugins/runtime-modules/{runtime-calendar-adapters.js → core/calendar-adapters.js} +2 -2
  157. package/plugins/runtime-modules/{client-calendar-adapters/core.js → core/client-calendar-adapters.js} +1 -1
  158. package/plugins/runtime-modules/core/content-entries/table.js +1 -1
  159. package/plugins/runtime-modules/core/data-providers.d.ts +3 -0
  160. package/plugins/runtime-modules/core/data-providers.js +2 -1
  161. package/plugins/runtime-modules/core/definition.d.ts +3 -0
  162. package/plugins/runtime-modules/core/definition.js +1 -1
  163. package/plugins/runtime-modules/core/messaging/data-providers.d.ts +1 -0
  164. package/plugins/runtime-modules/core/messaging/data-providers.js +1 -0
  165. package/plugins/runtime-modules/core/messaging/thread-inbox-table.js +1 -1
  166. package/plugins/runtime-modules/core/server.js +1 -1
  167. package/plugins/runtime-modules/core/themes.d.ts +44 -0
  168. package/plugins/runtime-modules/core/themes.js +12 -0
  169. package/plugins/runtime-modules/core/widgets/card/collection-view-binding.js +25 -9
  170. package/plugins/runtime-modules/core/widgets/card/config.d.ts +296 -254
  171. package/plugins/runtime-modules/core/widgets/card/config.js +52 -3
  172. package/plugins/runtime-modules/core/widgets/card/editor.js +30 -3
  173. package/plugins/runtime-modules/core/widgets/card/server.d.ts +3 -0
  174. package/plugins/runtime-modules/core/widgets/card/server.js +31 -3
  175. package/plugins/runtime-modules/core/widgets/collection-view/asset-binding.js +7 -6
  176. package/plugins/runtime-modules/core/widgets/collection-view/config.js +9 -2
  177. package/plugins/runtime-modules/core/widgets/description/config.d.ts +1 -25
  178. package/plugins/runtime-modules/core/widgets/description/editor.js +4 -61
  179. package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +1 -25
  180. package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +1 -25
  181. package/plugins/runtime-modules/core/widgets/image/config.js +2 -2
  182. package/plugins/runtime-modules/core/widgets/markdown/config.d.ts +3 -1
  183. package/plugins/runtime-modules/core/widgets/markdown/config.js +14 -0
  184. package/plugins/runtime-modules/core/widgets/markdown/editor.js +2 -3
  185. package/plugins/runtime-modules/core/widgets/result/config.d.ts +4 -25
  186. package/plugins/runtime-modules/core/widgets/result/config.js +2 -0
  187. package/plugins/runtime-modules/core/widgets/result/server.js +25 -19
  188. package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +1 -25
  189. package/plugins/runtime-modules/core/widgets/slot-upload/client.js +11 -2
  190. package/plugins/runtime-modules/core/widgets/table/client.js +8 -4
  191. package/plugins/runtime-modules/core/widgets/table/config.d.ts +3 -83
  192. package/plugins/runtime-modules/core/widgets/table/config.js +302 -452
  193. package/plugins/runtime-modules/core/widgets/tree/config.js +5 -2
  194. package/plugins/runtime-modules/dashboard/card-rows.d.ts +5 -0
  195. package/plugins/runtime-modules/dashboard/card-rows.js +59 -0
  196. package/plugins/runtime-modules/dashboard/clients/dashboard-card-view.js +4 -6
  197. package/plugins/runtime-modules/dashboard/services/collection.js +17 -7
  198. package/plugins/runtime-modules/groups/data-providers.d.ts +2 -0
  199. package/plugins/runtime-modules/groups/data-providers.js +3 -3
  200. package/plugins/runtime-modules/groups/definition.d.ts +2 -0
  201. package/plugins/runtime-modules/localization/data-providers.d.ts +3 -0
  202. package/plugins/runtime-modules/localization/data-providers.js +2 -0
  203. package/plugins/runtime-modules/localization/definition.d.ts +3 -0
  204. package/plugins/runtime-modules/localization/services/table.js +3 -7
  205. package/plugins/runtime-modules/observability/data-providers.d.ts +1 -0
  206. package/plugins/runtime-modules/observability/data-providers.js +1 -0
  207. package/plugins/runtime-modules/observability/definition.d.ts +1 -0
  208. package/plugins/runtime-modules/observability/module.d.ts +1 -0
  209. package/plugins/runtime-modules/observability/services/table.js +1 -1
  210. package/plugins/runtime-modules/phi-default-site-area-preset-tree.js +2 -105
  211. package/plugins/runtime-modules/public/presets.d.ts +115 -0
  212. package/plugins/runtime-modules/{area-base-presets.js → public/presets.js} +19 -119
  213. package/plugins/runtime-modules/public/server.d.ts +1 -4
  214. package/plugins/runtime-modules/public/server.js +4 -7
  215. package/plugins/runtime-modules/public/trees/phi-default-pub-error-page-tree.js +7 -8
  216. package/plugins/runtime-modules/public-base-page-references.d.ts +1 -1
  217. package/plugins/runtime-modules/public-base-page-references.js +1 -1
  218. package/plugins/runtime-modules/revisions/controller/client.js +11 -11
  219. package/plugins/runtime-modules/revisions/services/table.js +3 -3
  220. package/plugins/runtime-modules/theme/trees/phi-default-builder-theme-page-tree.js +22 -0
  221. package/plugins/runtime-modules/theme/widgets/brand-controls/client.d.ts +2 -1
  222. package/plugins/runtime-modules/theme/widgets/brand-controls/client.js +93 -58
  223. package/plugins/runtime-modules/theme/widgets/brand-controls/config.d.ts +2 -1
  224. package/plugins/runtime-modules/theme/widgets/brand-controls/config.js +23 -22
  225. package/plugins/runtime-modules/theme/widgets/brand-controls/plugin.js +1 -1
  226. package/plugins/runtime-modules/theme-descriptors.js +1 -1
  227. package/plugins/runtime-modules/user-management/data-providers.d.ts +2 -0
  228. package/plugins/runtime-modules/user-management/data-providers.js +1 -0
  229. package/plugins/runtime-modules/user-management/definition.d.ts +2 -0
  230. package/styles/control-shape.css +11 -11
  231. package/styles/controls.css +29 -3
  232. package/styles/layout.css +16 -7
  233. package/types/cms-config.js +142 -185
  234. package/types/cms-plugins.d.ts +4 -11
  235. package/types/core-widget-placements.d.ts +17 -3
  236. package/types/dashboard-cards.d.ts +9 -0
  237. package/types/media.d.ts +0 -3
  238. package/types/renderable-block.d.ts +6 -1
  239. package/types/table-widget.d.ts +9 -0
  240. package/types/table-widget.js +20 -0
  241. package/types.d.ts +1 -1
  242. package/widget-config.d.ts +2 -0
  243. package/widget-config.js +6 -0
  244. package/plugins/runtime-modules/area-base-presets.d.ts +0 -139
  245. /package/plugins/runtime-modules/{runtime-calendar-adapters.d.ts → core/calendar-adapters.d.ts} +0 -0
  246. /package/plugins/runtime-modules/{client-calendar-adapters/core.d.ts → core/client-calendar-adapters.d.ts} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cloneElement, isValidElement } from "react";
3
+ import { resolvePhiRenderFailureCode } from "../widgets/config/strict-config-reader";
3
4
  import { readPhiCmsRegionPageScope, resolvePhiCmsRegionPageScope } from "../../helpers/region-page-scope";
4
5
  import { isPhiCmsWidgetContentUnpublished } from "../../helpers/cms-content-binding";
5
6
  import { comparePhiCmsInstanceIds } from "../../types/cms-instance-id";
@@ -435,7 +436,7 @@ function renderContentWidget(widget, context) {
435
436
  }
436
437
  catch (error) {
437
438
  return renderCmsDiagnostic({
438
- code: "render-failed",
439
+ code: resolvePhiRenderFailureCode(error),
439
440
  kind: "widget",
440
441
  type: widget.widgetType,
441
442
  blockId: widget.id,
@@ -507,7 +508,7 @@ function withLayoutRenderKey(node, key) {
507
508
  /** What a Widget that threw while rendering on the server reports in its place. */
508
509
  export function buildPhiRenderFailureIssue(error, options) {
509
510
  return {
510
- code: "render-failed",
511
+ code: resolvePhiRenderFailureCode(error),
511
512
  kind: options.kind,
512
513
  type: options.typeKey ?? "unknown",
513
514
  blockId: options.blockId,
@@ -703,7 +704,7 @@ function renderLayoutNode(node, context) {
703
704
  }
704
705
  catch (error) {
705
706
  return renderCmsDiagnostic({
706
- code: "render-failed",
707
+ code: resolvePhiRenderFailureCode(error),
707
708
  kind: "layout",
708
709
  type: node.widgetType,
709
710
  blockId: node.id,
@@ -1,12 +1,11 @@
1
- import type { ReactNode } from "react";
1
+ import { type ReactNode } from "react";
2
2
  import type { PhiBlockRuntime } from "../../types";
3
3
  import type { PhiCmsSiteBridge } from "../../types/cms-plugins";
4
4
  import type { loadPhiCmsRootRequest } from "../../server-helpers/cms-root";
5
5
  type PhiCmsResolvedRootRequest = NonNullable<Awaited<ReturnType<typeof loadPhiCmsRootRequest>>["resolvedRequest"]>;
6
6
  /**
7
- * What a Page and each of its Region slots work out before they render: the Module scope of the Area,
8
- * the tree filtered to what this viewer may see, the registry of what the tree needs, and the Page's
9
- * Controllers for the Regions in question.
7
+ * What a Page and each of its Region slots work out before they render: the shared part above, and the
8
+ * Page's Controllers for the Regions in question.
10
9
  *
11
10
  * The Page and the slot used to carry this as two copies, four blocks apart, and the slot's trailed the
12
11
  * Page's. One resolution, parameterised by the Regions it is for.
@@ -36,7 +35,7 @@ export declare function resolvePhiCmsPageRenderScope({ cmsBridge, resolvedReques
36
35
  };
37
36
  runtimeRegistry: import("../../types/cms-plugins").PhiCmsRuntimeRenderRegistry;
38
37
  pageControllerSettings: import("../../types").PhiRuntimeControllerSetting[];
39
- controllerConfigOverlays: {
38
+ controllerConfigOverlays: readonly {
40
39
  address: `controller:${string}/${string}:${string}`;
41
40
  config: Record<string, unknown>;
42
41
  }[];
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cache } from "react";
2
3
  import { PhiRuntimeControllerServerHost } from "../runtime/runtime-controller-server-host";
3
4
  import { collectPhiRuntimeControllerConfigOverlays, materializePhiRuntimeControllerSettings, } from "../runtime/runtime-controller-materialization";
4
5
  import { PhiRuntimeControllerConfigOverlays } from "../runtime/runtime-controller-config-overlays";
@@ -9,15 +10,17 @@ import { buildPhiRuntimeModuleAccessRegistry, filterPhiCmsRenderableTreeForViewe
9
10
  import { resolvePhiCmsAreaRuntimeModuleScope, resolvePhiCmsTreeRuntimeRegistry, } from "./phi-cms-runtime-registry";
10
11
  import { resolvePhiPageTour } from "../../server-helpers/page-tour";
11
12
  import { PHI_PAGE_TOUR_CONFIG_KEY, PHI_PAGE_TOUR_CONTROLLER_FLAG, readPhiTourTargets, } from "../../types/tours";
12
- /**
13
- * What a Page and each of its Region slots work out before they render: the Module scope of the Area,
14
- * the tree filtered to what this viewer may see, the registry of what the tree needs, and the Page's
15
- * Controllers for the Regions in question.
13
+ /*
14
+ * What a Page and every one of its Region slots share: the Area's Module scope, the Page's Tour, the tree
15
+ * filtered for this viewer, the registry of what that tree needs, and the Controller config the tree
16
+ * carries. None of it depends on the Region.
16
17
  *
17
- * The Page and the slot used to carry this as two copies, four blocks apart, and the slot's trailed the
18
- * Page's. One resolution, parameterised by the Regions it is for.
18
+ * The Page and its six slots render in parallel and each worked all of it out again -- up to seven
19
+ * filters of the same tree and seven registries per request (audit R9). The root request is memoized per
20
+ * request (`loadPhiCmsRootRequest`), so every slot holds the same object, and React's `cache` keyed by it
21
+ * and the bridge keeps this for exactly that request.
19
22
  */
20
- export async function resolvePhiCmsPageRenderScope({ cmsBridge, resolvedRequest, regionTypes, includeOverlays = false, }) {
23
+ const resolvePhiCmsPageRenderBase = cache(async (cmsBridge, resolvedRequest) => {
21
24
  const runtimeModuleScope = await resolvePhiCmsAreaRuntimeModuleScope({
22
25
  cmsBridge,
23
26
  area: resolvedRequest.runtime.area,
@@ -47,20 +50,6 @@ export async function resolvePhiCmsPageRenderScope({ cmsBridge, resolvedRequest,
47
50
  });
48
51
  // What the Area runs for every Page: a Page setting for one of these configures it, it is not a second.
49
52
  const enclosingSettings = runtimeModuleScope.moduleSet.areaControllerSettings;
50
- const pageControllerSettings = materializePhiRuntimeControllerSettings({
51
- tree: filteredPageTree,
52
- ownerMountScope: "page",
53
- widgetPluginsByType: runtimeModuleScope.widgetDefinitionsByType,
54
- enclosingSettings,
55
- activeControllerTypes: [...runtimeModuleScope.moduleSet.controllerDescriptorsByType.keys()],
56
- regionTypes,
57
- ...(includeOverlays ? { includeOverlays: true } : {}),
58
- });
59
- const controllerDefinitionsByType = await resolvePhiRuntimeControllerDefinitions({
60
- catalog: cmsBridge.runtimeModuleCatalog,
61
- moduleSet: runtimeModuleScope.moduleSet,
62
- settings: pageControllerSettings,
63
- });
64
53
  const controllerConfigOverlays = collectPhiRuntimeControllerConfigOverlays({
65
54
  tree: filteredPageTree,
66
55
  ownerMountScope: "page",
@@ -83,8 +72,40 @@ export async function resolvePhiCmsPageRenderScope({ cmsBridge, resolvedRequest,
83
72
  runtimeModuleScope,
84
73
  filteredPageTree,
85
74
  runtimeRegistry,
75
+ enclosingSettings,
76
+ controllerConfigOverlays: controllerConfigOverlays,
77
+ };
78
+ });
79
+ /**
80
+ * What a Page and each of its Region slots work out before they render: the shared part above, and the
81
+ * Page's Controllers for the Regions in question.
82
+ *
83
+ * The Page and the slot used to carry this as two copies, four blocks apart, and the slot's trailed the
84
+ * Page's. One resolution, parameterised by the Regions it is for.
85
+ */
86
+ export async function resolvePhiCmsPageRenderScope({ cmsBridge, resolvedRequest, regionTypes, includeOverlays = false, }) {
87
+ const base = await resolvePhiCmsPageRenderBase(cmsBridge, resolvedRequest);
88
+ const { runtimeModuleScope, filteredPageTree } = base;
89
+ const pageControllerSettings = materializePhiRuntimeControllerSettings({
90
+ tree: filteredPageTree,
91
+ ownerMountScope: "page",
92
+ widgetPluginsByType: runtimeModuleScope.widgetDefinitionsByType,
93
+ enclosingSettings: base.enclosingSettings,
94
+ activeControllerTypes: [...runtimeModuleScope.moduleSet.controllerDescriptorsByType.keys()],
95
+ regionTypes,
96
+ ...(includeOverlays ? { includeOverlays: true } : {}),
97
+ });
98
+ const controllerDefinitionsByType = await resolvePhiRuntimeControllerDefinitions({
99
+ catalog: cmsBridge.runtimeModuleCatalog,
100
+ moduleSet: runtimeModuleScope.moduleSet,
101
+ settings: pageControllerSettings,
102
+ });
103
+ return {
104
+ runtimeModuleScope,
105
+ filteredPageTree,
106
+ runtimeRegistry: base.runtimeRegistry,
86
107
  pageControllerSettings,
87
- controllerConfigOverlays,
108
+ controllerConfigOverlays: base.controllerConfigOverlays,
88
109
  controllerDefinitionsByType,
89
110
  };
90
111
  }
@@ -13,6 +13,8 @@ function buildIssueTitle(issue) {
13
13
  return `${formatKind(issue.kind)} visibility unresolved`;
14
14
  if (issue.code === "missing-node")
15
15
  return `${formatKind(issue.kind)} root missing`;
16
+ if (issue.code === "invalid-config")
17
+ return `${formatKind(issue.kind)} settings invalid`;
16
18
  return `${formatKind(issue.kind)} not renderable`;
17
19
  }
18
20
  function buildIssueDescription(issue) {
@@ -24,9 +26,11 @@ function buildIssueDescription(issue) {
24
26
  ? "A Module its condition reads could not be asked this render."
25
27
  : issue.code === "missing-node"
26
28
  ? "The stored tree names a node it does not contain."
27
- : issue.moduleId
28
- ? `Module: ${issue.moduleId}.`
29
- : "The required renderer is unavailable.";
29
+ : issue.code === "invalid-config"
30
+ ? "Correct the field in the Inspector; on the live page this block is left out until then."
31
+ : issue.moduleId
32
+ ? `Module: ${issue.moduleId}.`
33
+ : "The required renderer is unavailable.";
30
34
  return `${issue.type}. ${moduleDescription}${issue.detail ? ` ${issue.detail}` : ""}`;
31
35
  }
32
36
  function buildIssueKey(issue) {
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Component } from "react";
4
4
  import { PhiCmsRenderDiagnostic } from "./phi-cms-render-diagnostic";
5
+ import { resolvePhiRenderFailureCode } from "../widgets/config/strict-config-reader";
5
6
  export class PhiCmsRenderErrorBoundary extends Component {
6
7
  state = {
7
8
  error: null,
@@ -20,7 +21,7 @@ export class PhiCmsRenderErrorBoundary extends Component {
20
21
  render() {
21
22
  if (this.state.error) {
22
23
  return _jsx(PhiCmsRenderDiagnostic, { issue: {
23
- code: "render-failed",
24
+ code: resolvePhiRenderFailureCode(this.state.error),
24
25
  kind: this.props.kind,
25
26
  type: this.props.typeKey ?? "unknown",
26
27
  blockId: this.props.blockId,
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { PhiIcon } from "../shell/phi-icon";
3
3
  import { phiSurfaceDrawsChrome, resolvePhiSurfaceStyle } from "../../helpers/surface-style";
4
4
  import { PHI_COLOR, PHI_RADIUS, PHI_SPACE } from "../../theme/antd-css-var-contract";
@@ -7,6 +7,21 @@ import { PhiBulletListFlag, resolvePhiBulletListLevel, resolvePhiBulletListStyle
7
7
  import { hasPhiFlag } from "../../helpers/flags";
8
8
  const PHI_BULLET_LIST_MARKER_SIZE = PHI_SPACE.xs;
9
9
  const PHI_BULLET_LIST_ICON_SIZE = 14;
10
+ /*
11
+ * An entry's state stands in place of the level's marker: a tick for what is included, a dash for what is
12
+ * not. Neither is the list's colour: included is the Theme's success, excluded the tertiary text, so a
13
+ * plan's list reads the same whatever marker its neutral entries take.
14
+ */
15
+ function renderStateMarker(state) {
16
+ switch (state) {
17
+ case "included":
18
+ return _jsx(PhiIcon, { name: "antd:check-outlined", size: PHI_BULLET_LIST_ICON_SIZE, style: { color: PHI_COLOR.success } });
19
+ case "excluded":
20
+ return _jsx(PhiIcon, { name: "antd:minus-outlined", size: PHI_BULLET_LIST_ICON_SIZE, style: { color: PHI_COLOR.textTertiary } });
21
+ default:
22
+ return null;
23
+ }
24
+ }
10
25
  function renderMarker(marker, icon, number) {
11
26
  switch (marker) {
12
27
  case "none":
@@ -69,5 +84,19 @@ export function PhiBulletListControl({ items, listStyle = resolvePhiBulletListSt
69
84
  ...surface.style,
70
85
  ...(boxed ? { padding: `${PHI_SPACE.sm} ${PHI_SPACE.md}` } : {}),
71
86
  ...(divided && index > 0 ? { borderTop: `1px solid ${PHI_COLOR.borderSecondary}`, paddingTop: gap } : {}),
72
- }, children: [marker === "none" ? null : (_jsx("span", { "aria-hidden": "true", style: { display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0, height: markerLineHeight }, children: renderMarker(marker, listStyle.icon, start + index) })), _jsxs("div", { style: { display: "grid", gap, flex: 1, minWidth: 0 }, children: [item.content, item.children?.length ? (_jsx(PhiBulletListControl, { items: item.children, listStyle: listStyle, depth: depth + 1, markerLineHeight: markerLineHeight })) : null] })] }, index))) }));
87
+ }, children: [(() => {
88
+ const stated = renderStateMarker(item.state);
89
+ /* An editor's marker is a button, so a list whose marker is `none` still offers one. */
90
+ const drawn = stated ?? renderMarker(marker === "none" && item.onMarkerClick ? "dot" : marker, listStyle.icon, start + index);
91
+ if (drawn == null)
92
+ return null;
93
+ const markerStyle = {
94
+ display: "inline-flex",
95
+ alignItems: "center",
96
+ justifyContent: "center",
97
+ flexShrink: 0,
98
+ height: markerLineHeight,
99
+ };
100
+ return item.onMarkerClick ? (_jsx("button", { type: "button", className: "phi-bullet-list__marker-button", "aria-label": `State: ${item.state ?? "neutral"}`, onClick: item.onMarkerClick, style: { ...markerStyle, padding: 0, border: 0, background: "none", cursor: "pointer", color: "inherit" }, children: drawn })) : (_jsx("span", { "aria-hidden": "true", style: markerStyle, children: drawn }));
101
+ })(), _jsxs("div", { style: { display: "grid", gap, flex: 1, minWidth: 0, ...(item.state === "excluded" ? { color: PHI_COLOR.textTertiary } : {}) }, children: [item.content, item.children?.length ? (_jsx(PhiBulletListControl, { items: item.children, listStyle: listStyle, depth: depth + 1, markerLineHeight: markerLineHeight })) : null] })] }, index))) }));
73
102
  }
@@ -0,0 +1,29 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ export type PhiRibbonControlProps = {
3
+ children: ReactNode;
4
+ /** The band's ground; absent, the Theme's primary. Its words are the light text on any ground. */
5
+ color?: string;
6
+ /**
7
+ * Where the band stands: `corner` flush with the box's top and end edge, its top end corner the box's
8
+ * own; `edge` a small padding down the end edge. Either way it floats: nothing in the box moves for it.
9
+ */
10
+ placement?: "corner" | "edge";
11
+ style?: CSSProperties;
12
+ };
13
+ /**
14
+ * A band of words at the end edge of a box: "Popular", "New", "-20 %".
15
+ *
16
+ * Ant Design draws this as `Badge.Ribbon`, with a fold that hangs past the box's edge. Not that one: it
17
+ * wraps the box in an element of its own, and its fold needs the box not to clip -- a card clips, so its
18
+ * picture keeps its corners. This band stands inside the box and needs nothing from it but a position
19
+ * to stand against. It is drawn on the server as well, without hooks.
20
+ *
21
+ * It floats and takes no room, so boxes side by side in a row start their content on one line whether
22
+ * they carry a band or not. A band on a line of its own pushed its box's words down; squeezed into the
23
+ * box's top inset it was too thin to read. In the corner it stands over the inset and a little into the
24
+ * first line's end, which a long eyebrow in a narrow box can reach.
25
+ *
26
+ * It sits above what the box draws, the highlight's ring included, and takes no clicks, so a card that
27
+ * is a link stays one under its band.
28
+ */
29
+ export declare function PhiRibbonControl({ children, color, placement, style }: PhiRibbonControlProps): import("react").JSX.Element;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PHI_COLOR, PHI_FONT_SIZE, PHI_SPACE } from "../../theme/antd-css-var-contract";
3
+ /**
4
+ * A band of words at the end edge of a box: "Popular", "New", "-20 %".
5
+ *
6
+ * Ant Design draws this as `Badge.Ribbon`, with a fold that hangs past the box's edge. Not that one: it
7
+ * wraps the box in an element of its own, and its fold needs the box not to clip -- a card clips, so its
8
+ * picture keeps its corners. This band stands inside the box and needs nothing from it but a position
9
+ * to stand against. It is drawn on the server as well, without hooks.
10
+ *
11
+ * It floats and takes no room, so boxes side by side in a row start their content on one line whether
12
+ * they carry a band or not. A band on a line of its own pushed its box's words down; squeezed into the
13
+ * box's top inset it was too thin to read. In the corner it stands over the inset and a little into the
14
+ * first line's end, which a long eyebrow in a narrow box can reach.
15
+ *
16
+ * It sits above what the box draws, the highlight's ring included, and takes no clicks, so a card that
17
+ * is a link stays one under its band.
18
+ */
19
+ export function PhiRibbonControl({ children, color, placement = "edge", style }) {
20
+ const radius = "var(--phi-control-radius-sm)";
21
+ const position = placement === "corner"
22
+ ? {
23
+ top: 0,
24
+ paddingBlock: PHI_SPACE.xxs,
25
+ // The top end corner is the box's own, which clips it; only the corner inside the box is rounded.
26
+ borderEndStartRadius: radius,
27
+ }
28
+ : {
29
+ top: PHI_SPACE.sm,
30
+ paddingBlock: PHI_SPACE.xxs,
31
+ borderStartStartRadius: radius,
32
+ borderEndStartRadius: radius,
33
+ };
34
+ return (_jsx("span", { className: "phi-ribbon-control", style: {
35
+ position: "absolute",
36
+ insetInlineEnd: 0,
37
+ zIndex: 4,
38
+ maxWidth: "70%",
39
+ boxSizing: "border-box",
40
+ paddingInline: PHI_SPACE.sm,
41
+ background: color ?? PHI_COLOR.primary,
42
+ color: PHI_COLOR.textLightSolid,
43
+ fontSize: PHI_FONT_SIZE.sm,
44
+ fontWeight: 600,
45
+ lineHeight: 2,
46
+ whiteSpace: "nowrap",
47
+ overflow: "hidden",
48
+ textOverflow: "ellipsis",
49
+ pointerEvents: "none",
50
+ ...position,
51
+ ...style,
52
+ }, children: children }));
53
+ }
@@ -44,7 +44,10 @@ export type PhiTableControlColumn<TRow extends Record<string, unknown>> = {
44
44
  export type PhiTableControlPagination = {
45
45
  page: number;
46
46
  pageSize: number;
47
+ /** The rows the whole query has. Absent, the Table pages without a count: back, this page, next. */
47
48
  total?: number;
49
+ /** Without a count: whether a page follows this one. */
50
+ hasMore?: boolean;
48
51
  pageSizeOptions?: readonly number[];
49
52
  showSizeChanger?: boolean;
50
53
  onChange: (page: number, pageSize: number) => void;
@@ -1022,7 +1022,19 @@ export function PhiTableControl({ rows, rowIdentityPath, columns, columnOrder, s
1022
1022
  ...{ [PHI_VISUAL_ANCHOR_PART_ATTRIBUTE]: "pagination" },
1023
1023
  current: pagination.page,
1024
1024
  pageSize: pagination.pageSize,
1025
- total: pagination.total,
1025
+ ...(pagination.total === undefined
1026
+ ? {
1027
+ /*
1028
+ * No count: a total that reaches exactly one row past this page when another follows, and
1029
+ * only the current page's number drawn -- page numbers beyond it would be a guess.
1030
+ */
1031
+ total: pagination.page * pagination.pageSize + (pagination.hasMore ? 1 : 0),
1032
+ showLessItems: true,
1033
+ itemRender: (page, type, element) => type === "page" ? (page === pagination.page ? element : null)
1034
+ : type === "jump-prev" || type === "jump-next" ? null
1035
+ : element,
1036
+ }
1037
+ : { total: pagination.total }),
1026
1038
  pageSizeOptions: pagination.pageSizeOptions?.map(String),
1027
1039
  showSizeChanger: pagination.showSizeChanger,
1028
1040
  onChange: pagination.onChange,
@@ -154,8 +154,8 @@ export function PhiTreeControl({ nodes, nodeIdentityPath, parentNodeIdentityPath
154
154
  padding: token.paddingSM,
155
155
  background: token.colorBgContainer,
156
156
  border: `1px solid ${token.colorBorderSecondary}`,
157
- // The frame is the surface; its corner is the Site's step, with the old token as the fallback
158
- // for a Tree rendered outside the Provider.
159
- borderRadius: "var(--phi-surface-radius, var(--ant-border-radius-lg))",
157
+ // The frame is the surface; its corner is the Site's step, declared by the Provider every Tree
158
+ // renders under (THEME.md, "Control shape").
159
+ borderRadius: "var(--phi-surface-radius)",
160
160
  }, children: tree })) : tree;
161
161
  }
@@ -31,11 +31,8 @@ placeholderRows = 1) {
31
31
  * Control line, which on the one-row placeholder is exactly the capsule and on a taller one is the
32
32
  * capsule held -- the same answer the field itself will give when it arrives, so the shape does not
33
33
  * change under the reader at the moment the chunk lands.
34
- *
35
- * Here the fallback is the plain surface radius and not the large one: this is an ordinary `div`,
36
- * not an Ant Design Control, so nothing has redefined `--ant-border-radius` on it.
37
34
  */
38
- borderRadius: "var(--phi-control-radius-grown-md, var(--ant-border-radius))",
35
+ borderRadius: "var(--phi-control-radius-grown-md)",
39
36
  background: "var(--ant-color-fill-quaternary)",
40
37
  } }));
41
38
  return function PhiLazyFormFieldControl(props) {
@@ -3,9 +3,9 @@ import { useEffect, useMemo } from "react";
3
3
  import { PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS } from "../../constants/media-library-provider-keys";
4
4
  import { usePhiCollectionProvider } from "../widgets/client/shared/phi-collection-provider";
5
5
  import { usePhiMediaSpaceSelectionAllowed } from "./media-space-selection";
6
+ import { readPhiMediaAssetTiles, readPhiMediaFolders, readPhiMediaPagination, readPhiMediaSpace, readPhiMediaSpaces, } from "./phi-image-preview-data";
6
7
  import { PHI_ASSET_CONTROLLER_STORE_KEY, } from "./asset-controller-signals";
7
8
  import { setPhiImagePreviewError, setPhiImagePreviewLoading, setPhiImagePreviewResults, usePhiImagePreviewStore, } from "./phi-image-preview-store";
8
- import { isPhiRecord } from "../../helpers/is-record";
9
9
  export const PHI_ASSET_COLLECTION_DATA_SOURCE = {
10
10
  providerKey: PHI_MEDIA_LIBRARY_DATA_PROVIDER_KEYS.collection,
11
11
  resourceKey: "assets",
@@ -29,22 +29,12 @@ export function buildPhiAssetCollectionQuery(state) {
29
29
  };
30
30
  }
31
31
  export function applyPhiAssetCollectionData(data, resolvedCollectionRequestKey) {
32
- const folders = Array.isArray(data.meta?.folders)
33
- ? data.meta.folders.filter(isPhiRecord)
34
- : [];
35
- const pagination = isPhiRecord(data.meta?.pagination)
36
- ? data.meta.pagination
37
- : null;
38
32
  setPhiImagePreviewResults(PHI_ASSET_CONTROLLER_STORE_KEY, {
39
- activeSpace: isPhiRecord(data.meta?.activeSpace)
40
- ? data.meta.activeSpace
41
- : null,
42
- spaces: Array.isArray(data.meta?.spaces)
43
- ? data.meta.spaces.filter(isPhiRecord)
44
- : [],
45
- assets: data.items.filter(isPhiRecord),
46
- folders,
47
- pagination,
33
+ activeSpace: readPhiMediaSpace(data.meta?.activeSpace),
34
+ spaces: readPhiMediaSpaces(data.meta?.spaces),
35
+ assets: readPhiMediaAssetTiles(data.items),
36
+ folders: readPhiMediaFolders(data.meta?.folders),
37
+ pagination: readPhiMediaPagination(data.meta?.pagination),
48
38
  resolvedCollectionRequestKey,
49
39
  });
50
40
  }
@@ -0,0 +1,3 @@
1
+ import { type PhiFontMetrics } from "@phis/contracts/media";
2
+ import { type PhiConfigReader } from "../widgets/config/strict-config-reader";
3
+ export declare function readPhiFontMetricsFields(reader: PhiConfigReader): PhiFontMetrics;
@@ -0,0 +1,43 @@
1
+ import { isPhiFontSubsetKey } from "@phis/contracts/media";
2
+ import { requirePhiField, } from "../widgets/config/strict-config-reader";
3
+ /*
4
+ * A font's metrics, read as the contract states them (`@phis/contracts/media`).
5
+ *
6
+ * The server measures them once, at upload, and writes every field. Two readers took them on trust: the
7
+ * public reference projection checked nothing below `font`, and the font list of the Theme took a stored
8
+ * `meta.font` for metrics as soon as it had a unit grid -- a record missing its ascent would have set a
9
+ * fallback face with `NaN` overrides. Both read through this now, so a wrong record is an error naming
10
+ * the field. `coverage` is the one field allowed to be absent: a font uploaded before cuts existed has
11
+ * none and is delivered whole.
12
+ */
13
+ const nullableNumber = (reader, key) => reader.number(key) ?? null;
14
+ const requiredNumber = (reader, key) => requirePhiField(reader, key, "a number", reader.number);
15
+ function readPhiFontCoverage(reader) {
16
+ const subsets = requirePhiField(reader, "subsets", "a list of subset keys", (key) => reader.value(key, "a list of subset keys", (value) => Array.isArray(value) && value.every(isPhiFontSubsetKey) ? value : undefined));
17
+ const restRanges = reader.value("restRanges", "a list of [start, end] code point ranges", (value) => Array.isArray(value) && value.every((entry) => Array.isArray(entry) && entry.length === 2 &&
18
+ Number.isInteger(entry[0]) && Number.isInteger(entry[1]) &&
19
+ entry[0] >= 0 && entry[0] <= entry[1] && entry[1] <= 0x10ffff)
20
+ ? value
21
+ : undefined) ?? [];
22
+ return { subsets, restRanges };
23
+ }
24
+ export function readPhiFontMetricsFields(reader) {
25
+ const unitsPerEm = requiredNumber(reader, "unitsPerEm");
26
+ if (unitsPerEm <= 0)
27
+ reader.fail("unitsPerEm", "a positive number");
28
+ const coverage = reader.object("coverage", readPhiFontCoverage);
29
+ return {
30
+ format: requirePhiField(reader, "format", "a font container", (key) => reader.oneOf(key, ["sfnt", "woff", "woff2"])),
31
+ familyName: reader.string("familyName")?.trim() || null,
32
+ unitsPerEm,
33
+ ascent: requiredNumber(reader, "ascent"),
34
+ descent: requiredNumber(reader, "descent"),
35
+ lineGap: requiredNumber(reader, "lineGap"),
36
+ capHeight: nullableNumber(reader, "capHeight"),
37
+ xHeight: nullableNumber(reader, "xHeight"),
38
+ xWidthAvg: nullableNumber(reader, "xWidthAvg"),
39
+ xWidthAvgSource: reader.oneOf("xWidthAvgSource", ["glyphs", "os2"]) ?? null,
40
+ category: reader.oneOf("category", ["serif", "sans-serif", "monospace"]) ?? null,
41
+ ...(coverage ? { coverage } : {}),
42
+ };
43
+ }