@phis/ui 0.4.3 → 0.4.4

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 (132) hide show
  1. package/components/controls/phi-anchor-nav-control.js +4 -1
  2. package/components/controls/phi-background-control.js +21 -6
  3. package/components/controls/phi-bullet-list-control.d.ts +7 -2
  4. package/components/controls/phi-bullet-list-control.js +4 -3
  5. package/components/controls/phi-color-control.js +4 -2
  6. package/components/controls/phi-drawer-control-adapter.js +3 -2
  7. package/components/controls/phi-inline-text-editor.js +7 -1
  8. package/components/controls/phi-length-control.js +1 -1
  9. package/components/controls/phi-ribbon-control.d.ts +13 -4
  10. package/components/controls/phi-ribbon-control.js +10 -6
  11. package/components/controls/phi-ribbon-wrap-control.d.ts +26 -0
  12. package/components/controls/phi-ribbon-wrap-control.js +25 -0
  13. package/components/controls/phi-select-control.js +5 -2
  14. package/components/controls/phi-series-chart-control.d.ts +48 -0
  15. package/components/controls/phi-series-chart-control.js +82 -0
  16. package/components/controls/phi-text-control.js +57 -3
  17. package/components/controls/phi-tree-control.js +1 -1
  18. package/components/controls/phi-truncated-hover-text.d.ts +15 -0
  19. package/components/controls/phi-truncated-hover-text.js +25 -0
  20. package/components/forms/link-target-form-control.d.ts +1 -1
  21. package/components/forms/link-target-form-control.js +2 -45
  22. package/components/forms/phi-form-widget-frame.js +4 -2
  23. package/components/layouts/clients/phi-three-column-layout-client.js +10 -1
  24. package/components/navigation/phi-link.js +6 -1
  25. package/components/regions/clients/cms-region-container-client.js +6 -1
  26. package/components/regions/phi-cms-region-container.js +3 -0
  27. package/components/regions/phi-cms-region-shell.d.ts +12 -1
  28. package/components/regions/phi-cms-region-shell.js +81 -19
  29. package/components/root/phi-config-provider.d.ts +13 -0
  30. package/components/root/phi-config-provider.js +12 -1
  31. package/components/root/phi-root-background.d.ts +7 -0
  32. package/components/root/phi-root-background.js +20 -1
  33. package/components/root/phi-root-theme-resolver.js +3 -0
  34. package/components/root/phi-shell-chrome-overlay.d.ts +12 -1
  35. package/components/root/phi-shell-chrome-overlay.js +19 -3
  36. package/components/shell/phi-antd-icon.d.ts +1 -0
  37. package/components/shell/phi-antd-icon.js +3 -0
  38. package/components/shell/use-phi-shell-sticky-top.js +61 -16
  39. package/components/surface/phi-surface-tone.d.ts +13 -0
  40. package/components/surface/phi-surface-tone.js +21 -0
  41. package/components/widgets/client/bullet-list-entries-editor.js +7 -1
  42. package/components/widgets/client/html-editor.js +1 -1
  43. package/components/widgets/client/shared/use-page-reference-options.d.ts +17 -0
  44. package/components/widgets/client/shared/use-page-reference-options.js +57 -0
  45. package/components/widgets/config/background.d.ts +32 -1
  46. package/components/widgets/config/background.js +41 -2
  47. package/components/widgets/label-sets/background.js +2 -0
  48. package/components/widgets/label-types/background.d.ts +1 -0
  49. package/components/widgets/label-types/background.js +1 -0
  50. package/components/widgets/shared/bullet-list-vocabulary.d.ts +10 -0
  51. package/components/widgets/shared/bullet-list-vocabulary.js +5 -2
  52. package/components/widgets/shared/card-body-client.d.ts +5 -3
  53. package/components/widgets/shared/card-body-client.js +49 -6
  54. package/components/widgets/shared/card-vocabulary.d.ts +12 -2
  55. package/components/widgets/shared/card-vocabulary.js +7 -0
  56. package/components/widgets/shared/markdown-body-client.js +39 -10
  57. package/helpers/cms-container-chrome.js +6 -1
  58. package/helpers/locale.d.ts +8 -0
  59. package/helpers/locale.js +18 -0
  60. package/helpers/shell-region-style.js +2 -1
  61. package/helpers/surface-style.d.ts +12 -1
  62. package/helpers/surface-style.js +28 -2
  63. package/module-dashboard-cards.d.ts +1 -1
  64. package/module-dashboard-cards.js +1 -1
  65. package/package.json +1 -1
  66. package/plugins/runtime-modules/area-definitions.d.ts +0 -42
  67. package/plugins/runtime-modules/area-definitions.js +3 -8
  68. package/plugins/runtime-modules/builder/clients/inspector-config-field.js +20 -1
  69. package/plugins/runtime-modules/builder/clients/layout-scaffold-overlays.js +10 -5
  70. package/plugins/runtime-modules/builder/edit-scaffold-drawer.js +3 -2
  71. package/plugins/runtime-modules/builder/render-root-node-scaffold.js +25 -15
  72. package/plugins/runtime-modules/builder/trees/phi-default-builder-area-preset-tree.js +4 -0
  73. package/plugins/runtime-modules/builder/widgets/structure-region/built-in.js +15 -4
  74. package/plugins/runtime-modules/client-authoring-module.d.ts +8 -1
  75. package/plugins/runtime-modules/client-authoring-module.js +13 -3
  76. package/plugins/runtime-modules/core/widgets/brand/client.d.ts +12 -3
  77. package/plugins/runtime-modules/core/widgets/brand/client.js +3 -11
  78. package/plugins/runtime-modules/core/widgets/brand/home.d.ts +15 -0
  79. package/plugins/runtime-modules/core/widgets/brand/home.js +18 -0
  80. package/plugins/runtime-modules/core/widgets/brand/plugin.js +3 -2
  81. package/plugins/runtime-modules/core/widgets/brand/server.d.ts +2 -2
  82. package/plugins/runtime-modules/core/widgets/brand/server.js +2 -2
  83. package/plugins/runtime-modules/core/widgets/card/config.d.ts +6 -1
  84. package/plugins/runtime-modules/core/widgets/card/config.js +19 -3
  85. package/plugins/runtime-modules/core/widgets/card/editor.js +13 -3
  86. package/plugins/runtime-modules/core/widgets/card/server.js +1 -0
  87. package/plugins/runtime-modules/core/widgets/description/client.js +3 -3
  88. package/plugins/runtime-modules/core/widgets/description/config.d.ts +1 -0
  89. package/plugins/runtime-modules/core/widgets/description/editor.js +3 -2
  90. package/plugins/runtime-modules/core/widgets/description/server.js +3 -2
  91. package/plugins/runtime-modules/core/widgets/entry-list/config.d.ts +1 -0
  92. package/plugins/runtime-modules/core/widgets/gallery/config.d.ts +1 -0
  93. package/plugins/runtime-modules/core/widgets/header-navigation/client.js +2 -2
  94. package/plugins/runtime-modules/core/widgets/html/client.js +1 -1
  95. package/plugins/runtime-modules/core/widgets/markdown/config.d.ts +320 -2
  96. package/plugins/runtime-modules/core/widgets/markdown/config.js +18 -4
  97. package/plugins/runtime-modules/core/widgets/markdown/editor.js +9 -1
  98. package/plugins/runtime-modules/core/widgets/markdown-toc/client.js +6 -1
  99. package/plugins/runtime-modules/core/widgets/markdown-toc/config.d.ts +14 -0
  100. package/plugins/runtime-modules/core/widgets/markdown-toc/config.js +14 -2
  101. package/plugins/runtime-modules/core/widgets/quick-links/authoring.js +7 -2
  102. package/plugins/runtime-modules/core/widgets/quick-links/client.js +2 -2
  103. package/plugins/runtime-modules/core/widgets/quick-links/items.d.ts +0 -2
  104. package/plugins/runtime-modules/core/widgets/quick-links/items.js +0 -15
  105. package/plugins/runtime-modules/core/widgets/result/config.d.ts +1 -0
  106. package/plugins/runtime-modules/core/widgets/sidebar-navigation/authoring.js +1 -1
  107. package/plugins/runtime-modules/core/widgets/sidebar-navigation/client.js +1 -1
  108. package/plugins/runtime-modules/core/widgets/simple-text/client.js +8 -3
  109. package/plugins/runtime-modules/core/widgets/simple-text/config.d.ts +1 -0
  110. package/plugins/runtime-modules/core/widgets/simple-text/editor.js +2 -2
  111. package/plugins/runtime-modules/core/widgets/table/config.d.ts +3 -0
  112. package/plugins/runtime-modules/dashboard/card-rows.d.ts +3 -1
  113. package/plugins/runtime-modules/dashboard/card-rows.js +30 -2
  114. package/plugins/runtime-modules/dashboard/clients/dashboard-card-view.js +29 -7
  115. package/plugins/runtime-modules/dashboard/trees/phi-default-builder-dashboard-page-tree.js +5 -40
  116. package/plugins/runtime-modules/theme/widgets/brand-controls/client.js +19 -15
  117. package/plugins/runtime-modules/theme/widgets/brand-controls/config.js +5 -9
  118. package/server-helpers/cms-link-targets.js +6 -0
  119. package/styles/controls.css +64 -2
  120. package/styles/layout.css +45 -2
  121. package/styles/shell.css +73 -0
  122. package/theme/phi-antd-token-resolver.js +20 -1
  123. package/theme/phi-theme-tokens.d.ts +1 -1
  124. package/theme/phi-theme-tokens.js +1 -0
  125. package/theme/phi-theme.d.ts +5 -0
  126. package/theme/phi-theme.js +21 -0
  127. package/theme/phi-tokens.d.ts +16 -15
  128. package/theme/phi-tokens.js +16 -15
  129. package/types/cms-plugins.d.ts +2 -0
  130. package/types/dashboard-cards.d.ts +39 -4
  131. package/types/dashboard-cards.js +6 -0
  132. package/types/site-theme.d.ts +10 -2
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { cloneElement, isValidElement, Suspense, useCallback, useEffect, useRef, useState } from "react";
4
- import { usePhiConfig } from "../../../components/root/phi-config-provider";
4
+ import { usePhiPageModeToken } from "../../../components/root/phi-config-provider";
5
+ import { usePhiPageModeTone } from "../../../components/surface/phi-surface-tone";
5
6
  import { PhiEditScaffoldDrawer } from "./edit-scaffold-drawer";
6
7
  import { comparePhiCmsInstanceIds } from "../../../types/cms-instance-id";
7
8
  import { PhiSlotChildFrame } from "../../../plugins/runtime/phi-slot-child-frame";
@@ -10,7 +11,7 @@ import { PhiPlusButtonWidget, } from "./clients/layout-scaffold-buttons";
10
11
  import { usePhiStructureDroppable, } from "./structure-dnd";
11
12
  import { PhiInlineTextEditor } from "../../../components/controls/phi-inline-text-editor";
12
13
  import { PhiButtonControl } from "../../../components/controls/phi-button-control";
13
- import { usePhiAuthoringLayoutDefinition, usePhiRuntimeModuleAuthoringRegistration, } from "../../../plugins/runtime-modules/client-authoring-module";
14
+ import { usePhiAuthoringLayoutDefinitionResolver, usePhiRuntimeModuleAuthoringRegistration, } from "../../../plugins/runtime-modules/client-authoring-module";
14
15
  import { usePhiRuntimeModuleState } from "../../../components/runtime/runtime-module-context";
15
16
  import { PhiCmsRenderDiagnostic } from "../../../components/cms/phi-cms-render-diagnostic";
16
17
  import { PhiCmsRenderErrorBoundary } from "../../../components/cms/phi-cms-render-error-boundary";
@@ -116,7 +117,8 @@ function PhiAuthoringWidgetLoader({ widget, config, slotSizePolicy, demandContro
116
117
  function PhiAuthoringLayoutLoader({ type, kind, blockId, children, }) {
117
118
  const activeModules = usePhiRuntimeModuleState();
118
119
  const layoutType = resolvePhiAuthoringLayoutType(type);
119
- const definition = usePhiAuthoringLayoutDefinition(layoutType);
120
+ const resolveLayoutDefinition = usePhiAuthoringLayoutDefinitionResolver();
121
+ const definition = resolveLayoutDefinition(layoutType);
120
122
  if (!definition) {
121
123
  return _jsx(PhiCmsRenderDiagnostic, { issue: {
122
124
  code: "missing-renderer",
@@ -138,7 +140,7 @@ function PhiAuthoringLayoutLoader({ type, kind, blockId, children, }) {
138
140
  } });
139
141
  }
140
142
  const LazyAuthoringLayout = definition.component;
141
- return (_jsx(PhiCmsRenderErrorBoundary, { kind: kind, blockId: blockId, typeKey: type, moduleId: definition.ownerModuleId, children: _jsx(Suspense, { fallback: null, children: _jsx(LazyAuthoringLayout, { children: (plugin) => children(plugin, definition, activeModules.widgetDefinitionsByType) }) }) }));
143
+ return (_jsx(PhiCmsRenderErrorBoundary, { kind: kind, blockId: blockId, typeKey: type, moduleId: definition.ownerModuleId, children: _jsx(Suspense, { fallback: null, children: _jsx(LazyAuthoringLayout, { children: (plugin) => children(plugin, definition, activeModules.widgetDefinitionsByType, resolveLayoutDefinition) }) }) }));
142
144
  }
143
145
  function resolveRootNodeEditSlotAnchor(rootNode) {
144
146
  return rootNode.editSlotAnchor ?? undefined;
@@ -206,10 +208,12 @@ function resolveEffectsPreviewDurationMs(config) {
206
208
  }
207
209
  function PhiEffectsPreviewButton({ config, blockKind, onRun, }) {
208
210
  const previewLabel = formatEffectsPreviewLabel(config, blockKind);
211
+ // In the editor's mode, not a tone's: the button is the Builder's, whatever mode the Widget is drawn in.
212
+ const pageModeTone = usePhiPageModeTone();
209
213
  return (_jsx("span", { style: { display: "contents" }, onMouseDown: (event) => {
210
214
  event.preventDefault();
211
215
  event.stopPropagation();
212
- }, onClick: (event) => event.stopPropagation(), children: _jsx(PhiButtonControl, { className: "phi-layout-affordance phi-layout-affordance--effects-preview", ariaLabel: previewLabel, tooltip: previewLabel, icon: _jsx(PhiIcon, { name: "node-index", size: "inherit" }), type: "text", size: "small", onClick: onRun, style: {
216
+ }, onClick: (event) => event.stopPropagation(), children: pageModeTone.wrap(_jsx(PhiButtonControl, { className: "phi-layout-affordance phi-layout-affordance--effects-preview", ariaLabel: previewLabel, tooltip: previewLabel, icon: _jsx(PhiIcon, { name: "node-index", size: "inherit" }), type: "text", size: "small", onClick: onRun, style: {
213
217
  position: "absolute",
214
218
  top: "50%",
215
219
  left: "50%",
@@ -219,7 +223,7 @@ function PhiEffectsPreviewButton({ config, blockKind, onRun, }) {
219
223
  // whose children `styles/layout.css` stretches, and a stylesheet answer did not hold there.
220
224
  width: "var(--ant-control-height)",
221
225
  "--phi-layout-affordance-size": "var(--ant-control-height)",
222
- } }) }));
226
+ } })) }));
223
227
  }
224
228
  function PhiSignalWiringToolButton({ onOpen }) {
225
229
  return (_jsx("span", { style: { display: "contents" }, onPointerDown: (event) => {
@@ -264,7 +268,8 @@ function findPhiWidgetAuthoringTextTarget(container, clientX, clientY) {
264
268
  }) ?? null;
265
269
  }
266
270
  function PhiWidgetEffectsPreviewFrame({ kind, slotSizePolicy, surfacePolicy, blockId, regionKey, config: committedConfig, title, scaffoldLabel, editorInteraction = "inert", onSelect, dropTarget, tools, style, children, }) {
267
- const { token } = usePhiConfig();
271
+ // The editor's colours, not a tone's: a Widget inside a toned Surface is marked in the page's mode.
272
+ const token = usePhiPageModeToken();
268
273
  const selected = usePhiDeveloperBuilderStateValue((state) => blockId != null &&
269
274
  regionKey != null &&
270
275
  state.selectedRootRegionKey === regionKey &&
@@ -525,10 +530,6 @@ function PhiLayoutEffectsPreviewFrame({ kind, slotSizePolicy, blockId, config: c
525
530
  });
526
531
  } })) : null] }, hasPreviewEffects ? `layout-effects-preview-${blockId ?? "root"}-${previewRunId}` : undefined));
527
532
  }
528
- function PhiAuthoringLayoutEffectsPreviewFrame({ type, ...props }) {
529
- const definition = usePhiAuthoringLayoutDefinition(resolvePhiAuthoringLayoutType(type));
530
- return (_jsx(PhiLayoutEffectsPreviewFrame, { ...props, slotSizePolicy: definition?.slotSizePolicy }));
531
- }
532
533
  /**
533
534
  * The band under a scaffold: what this node is called, and then what it is.
534
535
  *
@@ -821,11 +822,20 @@ export function renderPhiRootNodeScaffold(rootNode, onOpenInsert, onOpenInspecto
821
822
  nodeKind: normalizedRootNode.kind,
822
823
  })
823
824
  : undefined, chrome: renderRootScaffoldToolbar(), children: wrapRootScaffoldChild(child) }));
824
- const renderChildLayoutNode = (node) => {
825
+ /*
826
+ * A nested Layout's frame states its policy on the element the parent Layout sizes its slot by.
827
+ *
828
+ * It was looked up inside that element, which is too late: the parent reads the element's props
829
+ * before it renders, found a Layout with no policy, took the Layout default -- fill both ways -- and
830
+ * let the slot grow and shrink in its column. A Grid of cards in a Flex was squeezed below its
831
+ * content on the canvas and the cards ran out of their scaffold, while the page laid it out at its
832
+ * content height, as its `fill-inline` says.
833
+ */
834
+ const renderChildLayoutNodeWith = (resolveLayoutDefinition) => function renderChildLayoutNode(node) {
825
835
  const nodeKind = "layout";
826
836
  const rootProps = resolveLayoutNodeRootProps(node);
827
837
  const rootPropsGeometry = resolvePhiRenderableBlockGeometry(normalizePhiBuilderRootNodeDraft(rootProps).rootNodeGeometry);
828
- return (_jsx(PhiAuthoringLayoutEffectsPreviewFrame, { type: node.widgetType, kind: nodeKind, blockId: node.id, config: node.config, explicitInlineSize: rootPropsGeometry.explicitInline, explicitBlockSize: rootPropsGeometry.explicitBlock, children: renderPhiRootNodeScaffold({ ...rootProps, regionKey }, (options) => onOpenInsert?.({
838
+ return (_jsx(PhiLayoutEffectsPreviewFrame, { kind: nodeKind, slotSizePolicy: resolveLayoutDefinition(resolvePhiAuthoringLayoutType(node.widgetType))?.slotSizePolicy, blockId: node.id, config: node.config, explicitInlineSize: rootPropsGeometry.explicitInline, explicitBlockSize: rootPropsGeometry.explicitBlock, children: renderPhiRootNodeScaffold({ ...rootProps, regionKey }, (options) => onOpenInsert?.({
829
839
  ...options,
830
840
  slotIndex: options?.slotIndex ?? 0,
831
841
  targetNodeId: node.id,
@@ -861,8 +871,8 @@ export function renderPhiRootNodeScaffold(rootNode, onOpenInsert, onOpenInspecto
861
871
  renderInsertPicker: options?.renderInsertPicker,
862
872
  }) }));
863
873
  };
864
- const scaffold = (_jsx(PhiAuthoringLayoutLoader, { type: normalizedRootNode.typeKey, kind: "layout", blockId: normalizedRootNode.id ?? -1, children: (plugin, definition, widgetDefinitionsByType) => {
865
- const rendered = resolvePhiRootNodeRenderedBody(normalizedRootNode, "editor", plugin, definition, widgetDefinitionsByType, regionKey, renderChildLayoutNode, onDeleteRootNode
874
+ const scaffold = (_jsx(PhiAuthoringLayoutLoader, { type: normalizedRootNode.typeKey, kind: "layout", blockId: normalizedRootNode.id ?? -1, children: (plugin, definition, widgetDefinitionsByType, resolveLayoutDefinition) => {
875
+ const rendered = resolvePhiRootNodeRenderedBody(normalizedRootNode, "editor", plugin, definition, widgetDefinitionsByType, regionKey, renderChildLayoutNodeWith(resolveLayoutDefinition), onDeleteRootNode
866
876
  ? (widget) => onDeleteRootNode({
867
877
  nodeId: widget.id,
868
878
  nodeKey: widget.widgetType,
@@ -1490,14 +1490,18 @@ async function buildPhiDefaultBuilderPagePresetTemplateTree({ page, runtime, pre
1490
1490
  key: "createPage",
1491
1491
  emits: [{ capabilityId: "command", value: "createPage" }],
1492
1492
  label: labels.pages.newPage,
1493
+ tooltip: labels.pages.newPage,
1493
1494
  icon: "plus",
1495
+ display: "icon",
1494
1496
  variant: "primary",
1495
1497
  },
1496
1498
  {
1497
1499
  key: "editPageMeta",
1498
1500
  emits: [{ capabilityId: "command", value: "editPageMeta" }],
1499
1501
  label: labels.pages.pageMeta,
1502
+ tooltip: labels.pages.pageMeta,
1500
1503
  icon: "edit",
1504
+ display: "icon",
1501
1505
  },
1502
1506
  {
1503
1507
  key: "deletePage",
@@ -40,6 +40,7 @@ import { PhiTypographyControl } from "../../../../../components/controls/phi-typ
40
40
  import { PhiIcon } from "../../../../../components/shell/phi-icon";
41
41
  import { PhiSurfaceTone } from "../../../../../components/surface/phi-surface-tone";
42
42
  import { resolvePhiSurfaceToneClassName } from "../../../../../types/surface";
43
+ import { PHI_SURFACE_TONE_GROUND, phiSurfaceTakesToneGround } from "../../../../../helpers/surface-style";
43
44
  /** The Region's root Layout placed into another tree, at the slot it is moved to. */
44
45
  function buildStructureRootLayoutNode(draft, parentLayoutNodeId, slotIndex) {
45
46
  if (!draft.rootNode) {
@@ -353,16 +354,26 @@ export function PhiStructureRegionScaffold({ config, serverPreview, structureDra
353
354
  const shouldStretchAvailableHeight = shouldFillAvailableHeight || shouldStretchContentRegion;
354
355
  const hasExplicitSidebarWidth = isFullHeightRegion && draftGeometry.explicitInline;
355
356
  const regionToneClassName = resolvePhiSurfaceToneClassName(effectiveDraft?.surface?.tone);
356
- const slotBackgroundStyle = effectiveDraft?.surface?.background
357
- ? resolvePhiBackgroundWidgetStyle(effectiveDraft.surface.background)
358
- : {};
359
357
  /*
360
358
  * Whether the author gave this Region a ground. The Builder writes a Background config onto every
361
359
  * Region draft it persists, so only a config that paints something counts. An unauthored Region is
362
360
  * transparent in the live Shell, and the Canvas keeps that legible: its slot paints a half-transparent
363
361
  * container tint, so the Theme Root Background stays visible through it.
364
362
  */
365
- const hasAuthoredGround = phiBackgroundWidgetConfigPaintsGround(effectiveDraft?.surface?.background ?? null);
363
+ const paintsAuthoredGround = phiBackgroundWidgetConfigPaintsGround(effectiveDraft?.surface?.background ?? null);
364
+ /*
365
+ * A toned Region takes its tone's ground where its Background paints none, by the rule the live Region
366
+ * shell follows (`phiSurfaceTakesToneGround`). The slot carries the tone's class, so the ground resolves
367
+ * in the tone's mode; it is a ground of the Region's own, so the Canvas tint does not cover it.
368
+ */
369
+ const toneGround = phiSurfaceTakesToneGround(effectiveDraft?.surface?.tone, paintsAuthoredGround);
370
+ const hasAuthoredGround = paintsAuthoredGround || toneGround;
371
+ const slotBackgroundStyle = {
372
+ ...(effectiveDraft?.surface?.background
373
+ ? resolvePhiBackgroundWidgetStyle(effectiveDraft.surface.background)
374
+ : {}),
375
+ ...(toneGround ? { backgroundColor: PHI_SURFACE_TONE_GROUND } : {}),
376
+ };
366
377
  const slotBorderStyle = effectiveDraft?.surface?.border ? resolvePhiBorderWidgetStyle(effectiveDraft.surface.border) : {};
367
378
  const regionConfig = (effectiveDraft?.regionConfig ?? null);
368
379
  const regionPaddingStyle = resolvePhiPaddingStyle(normalizePhiPaddingWidgetConfig(effectiveDraft?.regionConfig) ?? {});
@@ -28,4 +28,11 @@ export declare function createPhiRuntimeModuleAuthoringClient({ moduleId, Widget
28
28
  layouts?: readonly PhiAuthoringLayoutModuleLoader[];
29
29
  }): ComponentType<PhiRuntimeModuleAuthoringClientProps>;
30
30
  export declare function usePhiRuntimeModuleAuthoringRegistration(moduleId: PhiRuntimeModuleId | null | undefined): PhiRuntimeModuleAuthoringRegistration | null;
31
- export declare function usePhiAuthoringLayoutDefinition(type: string): PhiAuthoringLayoutDefinition | null;
31
+ /**
32
+ * The authoring Layout definitions, as a lookup by type rather than one type's answer.
33
+ *
34
+ * A Layout names its nested Layouts only while it renders its children, and a parent Layout sizes each
35
+ * slot by the props of the element standing in it, before that element renders: a nested Layout's
36
+ * element has to carry its policy already, and a hook inside it would be too late.
37
+ */
38
+ export declare function usePhiAuthoringLayoutDefinitionResolver(): (type: string) => PhiAuthoringLayoutDefinition | null;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, lazy, useContext, useMemo, } from "react";
3
+ import { createContext, lazy, useCallback, useContext, useMemo, } from "react";
4
4
  const PhiRuntimeModuleAuthoringContext = createContext(new Map());
5
5
  function createPhiLazyAuthoringLayout(load) {
6
6
  return lazy(async () => {
@@ -54,8 +54,7 @@ export function usePhiRuntimeModuleAuthoringRegistration(moduleId) {
54
54
  const modules = useContext(PhiRuntimeModuleAuthoringContext);
55
55
  return moduleId ? modules.get(moduleId) ?? null : null;
56
56
  }
57
- export function usePhiAuthoringLayoutDefinition(type) {
58
- const modules = useContext(PhiRuntimeModuleAuthoringContext);
57
+ function findPhiAuthoringLayoutDefinition(modules, type) {
59
58
  for (const registration of modules.values()) {
60
59
  const definition = registration.layoutsByType.get(type);
61
60
  if (definition) {
@@ -64,3 +63,14 @@ export function usePhiAuthoringLayoutDefinition(type) {
64
63
  }
65
64
  return null;
66
65
  }
66
+ /**
67
+ * The authoring Layout definitions, as a lookup by type rather than one type's answer.
68
+ *
69
+ * A Layout names its nested Layouts only while it renders its children, and a parent Layout sizes each
70
+ * slot by the props of the element standing in it, before that element renders: a nested Layout's
71
+ * element has to carry its policy already, and a hook inside it would be too late.
72
+ */
73
+ export function usePhiAuthoringLayoutDefinitionResolver() {
74
+ const modules = useContext(PhiRuntimeModuleAuthoringContext);
75
+ return useCallback((type) => findPhiAuthoringLayoutDefinition(modules, type), [modules]);
76
+ }
@@ -11,10 +11,19 @@ export type PhiBrandWidgetConfig = {
11
11
  export declare const PHI_BRAND_LINE_FALLBACK_ICONS: Record<PhiBrandWidgetLineMode, string>;
12
12
  export type PhiBrandWidgetClientProps = PhiClientBlockBaseProps<PhiNoLabels, PhiBrandWidgetConfig> & {
13
13
  fallbackTitle?: ReactNode;
14
- /** The language being read, which the way home keeps. */
15
- locale?: string;
14
+ /**
15
+ * Where the Brand leads, resolved on the Server: the Theme's `homeLinkTarget`, or the Site root in the
16
+ * language being read. A Page reference means nothing in a browser, so the address travels instead.
17
+ */
18
+ home?: PhiBrandWidgetHome;
16
19
  interactive?: boolean;
17
20
  };
21
+ /** A ready address, and whether it leaves the Site -- the shape `resolvePhiLinkHref` answers. */
22
+ export type PhiBrandWidgetHome = {
23
+ href: string;
24
+ newTab: boolean;
25
+ external: boolean;
26
+ };
18
27
  /**
19
28
  * The Brand in the Site frame: what `PhiBrandControl` shows, and where clicking it leads.
20
29
  *
@@ -22,4 +31,4 @@ export type PhiBrandWidgetClientProps = PhiClientBlockBaseProps<PhiNoLabels, Phi
22
31
  * because the Theme workspace preview shows the same Brand without being in a frame and without
23
32
  * leading anywhere.
24
33
  */
25
- export declare function PhiBrandWidgetClient({ config, locale, fallbackTitle, interactive, }: PhiBrandWidgetClientProps): import("react").JSX.Element | null;
34
+ export declare function PhiBrandWidgetClient({ config, home, fallbackTitle, interactive, }: PhiBrandWidgetClientProps): import("react").JSX.Element | null;
@@ -1,13 +1,12 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import Link from "next/link";
4
3
  import { PhiBrandControl, PhiBrandLineControl, phiBrandControlIsEmpty, } from "../../../../../components/controls/phi-brand-control";
5
4
  import { isPhiBrandWidgetLineMode, } from "./mode";
6
5
  import { usePhiConfig } from "../../../../../components/root/phi-config-provider";
7
6
  import { usePhiSiteBrand } from "../../../../../components/root/phi-root-live-theme-provider";
8
7
  import { resolvePhiWidgetFontFamily } from "../../../../../components/widgets/helpers/font-family";
9
8
  import { resolvePhiWidgetFontSize } from "../../../../../components/widgets/helpers/font-size";
10
- import { localizePath } from "../../../../../helpers/locale";
9
+ import { PhiLink } from "../../../../../components/navigation/phi-link";
11
10
  /** What a line shows in front of itself where the Brand names no icon for it. */
12
11
  export const PHI_BRAND_LINE_FALLBACK_ICONS = {
13
12
  slogan: "antd:star",
@@ -20,7 +19,7 @@ export const PHI_BRAND_LINE_FALLBACK_ICONS = {
20
19
  * because the Theme workspace preview shows the same Brand without being in a frame and without
21
20
  * leading anywhere.
22
21
  */
23
- export function PhiBrandWidgetClient({ config, locale, fallbackTitle, interactive = true, }) {
22
+ export function PhiBrandWidgetClient({ config, home, fallbackTitle, interactive = true, }) {
24
23
  const brand = usePhiSiteBrand();
25
24
  // The Theme's mode, not the Widget's: one picks the Logo's picture, the other what is drawn at all.
26
25
  const { fonts, token, mode: themeMode } = usePhiConfig();
@@ -54,13 +53,6 @@ export function PhiBrandWidgetClient({ config, locale, fallbackTitle, interactiv
54
53
  if (phiBrandControlIsEmpty(presentation)) {
55
54
  return null;
56
55
  }
57
- /*
58
- * The front page in the language being read. `/` alone is answered with a redirect to the visitor's
59
- * preferred language, so a reader of `/de` could be led to `/en`; and Next's prefetch of it, which loses
60
- * its `_rsc` on that redirect, came back with the static tree's own address and a 404
61
- * (AUDIT-2026-10-13, PM15).
62
- */
63
- const homeHref = brand?.homeHref?.trim() || (locale ? localizePath(locale, "/") : "/");
64
56
  const linkStyle = {
65
57
  color: "inherit",
66
58
  textDecoration: "none",
@@ -73,5 +65,5 @@ export function PhiBrandWidgetClient({ config, locale, fallbackTitle, interactiv
73
65
  if (!interactive) {
74
66
  return (_jsx("span", { "aria-disabled": "true", style: linkStyle, children: _jsx(PhiBrandControl, { ...presentation }) }));
75
67
  }
76
- return (_jsx(Link, { href: homeHref, style: linkStyle, children: _jsx(PhiBrandControl, { ...presentation }) }));
68
+ return (_jsx(PhiLink, { href: home?.href ?? "/", external: home?.external, newTab: home?.newTab, style: linkStyle, children: _jsx(PhiBrandControl, { ...presentation }) }));
77
69
  }
@@ -0,0 +1,15 @@
1
+ import type { PhiBlockRuntime } from "../../../../../types";
2
+ import { type PhiResolvedLinkTargets } from "../../../../../types/references";
3
+ import type { PhiBrandWidgetHome } from "./client";
4
+ /**
5
+ * Where the Brand leads, as an address: the Theme's target, resolved in this render's batch of link
6
+ * targets (`resolvePhiCmsTreeLinkTargets` collects the Brand's beside the tree's), or the Site root.
7
+ *
8
+ * The root is the root in the language being read. `/` alone is answered with a redirect to the
9
+ * visitor's preferred language, so a reader of `/de` could be led to `/en`; and Next's prefetch of it,
10
+ * which loses its `_rsc` on that redirect, came back with the static tree's own address and a 404
11
+ * (AUDIT-2026-10-13, PM15). A target that does not resolve -- a Page deleted, unpublished, or brought by
12
+ * a Module that is switched off -- leads there too: the Brand is the way home and always leads somewhere.
13
+ * A resolved Page path is already locale- and Area-correct (`REFERENCES.md`).
14
+ */
15
+ export declare function resolvePhiBrandWidgetHome(runtime: Pick<PhiBlockRuntime, "site" | "locale">, links: PhiResolvedLinkTargets | null | undefined): PhiBrandWidgetHome;
@@ -0,0 +1,18 @@
1
+ import { resolvePhiLinkHref } from "../../../../../helpers/link-target";
2
+ import { localizePath } from "../../../../../helpers/locale";
3
+ import { readPhiLinkTarget } from "../../../../../types/references";
4
+ /**
5
+ * Where the Brand leads, as an address: the Theme's target, resolved in this render's batch of link
6
+ * targets (`resolvePhiCmsTreeLinkTargets` collects the Brand's beside the tree's), or the Site root.
7
+ *
8
+ * The root is the root in the language being read. `/` alone is answered with a redirect to the
9
+ * visitor's preferred language, so a reader of `/de` could be led to `/en`; and Next's prefetch of it,
10
+ * which loses its `_rsc` on that redirect, came back with the static tree's own address and a 404
11
+ * (AUDIT-2026-10-13, PM15). A target that does not resolve -- a Page deleted, unpublished, or brought by
12
+ * a Module that is switched off -- leads there too: the Brand is the way home and always leads somewhere.
13
+ * A resolved Page path is already locale- and Area-correct (`REFERENCES.md`).
14
+ */
15
+ export function resolvePhiBrandWidgetHome(runtime, links) {
16
+ return resolvePhiLinkHref(readPhiLinkTarget(runtime.site.theme?.brand?.homeLinkTarget), links)
17
+ ?? { href: localizePath(runtime.locale.current, "/"), newTab: false, external: false };
18
+ }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { resolvePhiBrandWordmarkText } from "../../../../../helpers/brand-wordmark";
3
+ import { resolvePhiBrandWidgetHome } from "./home";
3
4
  import { PhiBrandWidget } from "./server";
4
5
  import { PHI_BRAND_WIDGET_DEFINITION, PHI_BRAND_WIDGET_PLUGIN_TYPE, } from "./config";
5
6
  /*
@@ -13,7 +14,7 @@ import { PHI_BRAND_WIDGET_DEFINITION, PHI_BRAND_WIDGET_PLUGIN_TYPE, } from "./co
13
14
  */
14
15
  export const PHI_BRAND_WIDGET_PLUGIN = {
15
16
  ...PHI_BRAND_WIDGET_DEFINITION,
16
- render: ({ widget, config, runtime }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime), locale: runtime.locale.current, mode: config.mode, fontFamily: config.fontFamily, fontSize: config.fontSize }, `widget-${widget.id}`)),
17
- renderPreview: ({ widget, config, runtime }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime), locale: runtime.locale.current, mode: config.mode, fontFamily: config.fontFamily, fontSize: config.fontSize, interactive: false }, `widget-${widget.id}`)),
17
+ render: ({ widget, config, runtime, links }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime), home: resolvePhiBrandWidgetHome(runtime, links), mode: config.mode, fontFamily: config.fontFamily, fontSize: config.fontSize }, `widget-${widget.id}`)),
18
+ renderPreview: ({ widget, config, runtime, links }) => (_jsx(PhiBrandWidget, { fallbackTitle: resolvePhiBrandWordmarkText(runtime), home: resolvePhiBrandWidgetHome(runtime, links), mode: config.mode, fontFamily: config.fontFamily, fontSize: config.fontSize, interactive: false }, `widget-${widget.id}`)),
18
19
  };
19
20
  export { PHI_BRAND_WIDGET_PLUGIN_TYPE };
@@ -1,3 +1,3 @@
1
1
  import type { PhiBrandWidgetClientProps, PhiBrandWidgetConfig } from "./client";
2
- export type PhiBrandWidgetProps = Pick<PhiBrandWidgetClientProps, "fallbackTitle" | "interactive" | "locale"> & PhiBrandWidgetConfig;
3
- export declare function PhiBrandWidget({ fallbackTitle, interactive, locale, mode, fontFamily, fontSize, }: PhiBrandWidgetProps): import("react").JSX.Element;
2
+ export type PhiBrandWidgetProps = Pick<PhiBrandWidgetClientProps, "fallbackTitle" | "interactive" | "home"> & PhiBrandWidgetConfig;
3
+ export declare function PhiBrandWidget({ fallbackTitle, interactive, home, mode, fontFamily, fontSize, }: PhiBrandWidgetProps): import("react").JSX.Element;
@@ -5,12 +5,12 @@ import { PhiRuntimeModuleRenderClientHost } from "../../../../../components/runt
5
5
  * The Brand itself is not read here. It comes from the root on the Client (`usePhiSiteBrand`), which
6
6
  * holds it with the Theme Set's Logo folded in and follows a live Theme draft.
7
7
  */
8
- export function PhiBrandWidget({ fallbackTitle, interactive, locale, mode, fontFamily, fontSize, }) {
8
+ export function PhiBrandWidget({ fallbackTitle, interactive, home, mode, fontFamily, fontSize, }) {
9
9
  const config = { mode, fontFamily, fontSize };
10
10
  return (_jsx(PhiRuntimeModuleRenderClientHost, { type: PhiCmsWidgetType.Brand, componentProps: {
11
11
  config,
12
12
  fallbackTitle,
13
- locale,
13
+ home,
14
14
  interactive,
15
15
  } }));
16
16
  }
@@ -2,7 +2,7 @@ import type { PhiCmsConfigField } from "../../../../../types/cms-plugins";
2
2
  import type { PhiImageAssetVariantKeyValue } from "../../../../../types/media";
3
3
  import { type PhiLinkTarget } from "../../../../../types/references";
4
4
  import { type PhiBulletListEntry, type PhiBulletListStyle } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
5
- import { type PhiCardBillingPeriod, type PhiCardHeadingLevel, type PhiCardHoverEffect, type PhiCardIconPlacement, type PhiCardTextAlign, type PhiCardVariant, type PhiCardWidgetBody } from "../../../../../components/widgets/shared/card-vocabulary";
5
+ import { type PhiCardBillingPeriod, type PhiCardHeadingLevel, type PhiCardHoverEffect, type PhiCardIconPlacement, type PhiCardTextAlign, type PhiCardBadgeStyle, type PhiCardVariant, type PhiCardWidgetBody } from "../../../../../components/widgets/shared/card-vocabulary";
6
6
  import { type PhiSignalRouteSet } from "../../../../../types/signals";
7
7
  import { type PhiCmsWidgetConfigBase } from "../../../../../components/widgets/config/parser-primitives";
8
8
  type PhiCardImageSource = {
@@ -27,6 +27,8 @@ export type PhiCmsCardWidgetConfig = PhiCmsWidgetConfigBase & PhiCardImageSource
27
27
  badge?: string;
28
28
  /** The band's colour; absent, the Theme's primary. */
29
29
  badgeColor?: string;
30
+ /** How the badge is drawn (`PHI_CARD_BADGE_STYLES`); absent, `none`. */
31
+ badgeStyle?: PhiCardBadgeStyle;
30
32
  /** A `plan` body's prices, never translated, and what each is paid per (`/ month`), which is. */
31
33
  price?: string;
32
34
  pricePeriod?: string;
@@ -106,6 +108,7 @@ export declare const PHI_CARD_WIDGET_DEFINITION: {
106
108
  imageOpacity: number;
107
109
  headingLevel: "h3";
108
110
  textAlign: "start";
111
+ badgeStyle: "none";
109
112
  };
110
113
  creationConfig: {
111
114
  surface: {
@@ -130,6 +133,7 @@ export declare const PHI_CARD_WIDGET_DEFINITION: {
130
133
  imageOpacity: number;
131
134
  headingLevel: "h3";
132
135
  textAlign: "start";
136
+ badgeStyle: "none";
133
137
  };
134
138
  fields: (({
135
139
  key: string;
@@ -142,6 +146,7 @@ export declare const PHI_CARD_WIDGET_DEFINITION: {
142
146
  } & {
143
147
  type: "string" | "url" | "icon";
144
148
  emptyValue?: "";
149
+ placeholder?: string;
145
150
  }) | ({
146
151
  key: string;
147
152
  label: string;
@@ -4,7 +4,7 @@ import { readPhiLinkTarget } from "../../../../../types/references";
4
4
  import { readPhiMediaImageSourceConfig } from "../../../../../components/widgets/config/image-source-parser";
5
5
  import { PHI_SURFACE_CARD } from "../../../../../helpers/surface-presets";
6
6
  import { buildPhiBulletListFields, readPhiBulletListEntries, readPhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
7
- import { PHI_CARD_HEADING_LEVELS, PHI_CARD_HOVER_EFFECTS, PHI_CARD_ICON_PLACEMENTS, PHI_CARD_TEXT_ALIGNS, PHI_CARD_VARIANTS, readPhiCardBillingPeriod, } from "../../../../../components/widgets/shared/card-vocabulary";
7
+ import { PHI_CARD_HEADING_LEVELS, PHI_CARD_HOVER_EFFECTS, PHI_CARD_ICON_PLACEMENTS, PHI_CARD_TEXT_ALIGNS, PHI_CARD_BADGE_STYLES, PHI_CARD_VARIANTS, readPhiCardBillingPeriod, } from "../../../../../components/widgets/shared/card-vocabulary";
8
8
  import { readPhiControlLabel } from "../../../../../components/widgets/config/common-action-field";
9
9
  import { PHI_CARD_SIGNALS } from "../../../../../components/widgets/signals/control-signal-capabilities";
10
10
  import { readPhiSignalRouteSet } from "../../../../../types/signals";
@@ -27,6 +27,7 @@ function parsePhiCmsCardWidgetConfig(config) {
27
27
  value: readString(config.value),
28
28
  badge: readString(config.badge),
29
29
  badgeColor: readString(config.badgeColor),
30
+ badgeStyle: readChoice(readString(config.badgeStyle), PHI_CARD_BADGE_STYLES),
30
31
  price: readString(config.price),
31
32
  pricePeriod: readString(config.pricePeriod),
32
33
  priceYearly: readString(config.priceYearly),
@@ -68,6 +69,7 @@ const PHI_CARD_DEFAULT_CONFIG = {
68
69
  imageOpacity: 100,
69
70
  headingLevel: "h3",
70
71
  textAlign: "start",
72
+ badgeStyle: "none",
71
73
  };
72
74
  export const PHI_CARD_WIDGET_DEFINITION = {
73
75
  kind: "widget",
@@ -259,12 +261,26 @@ export const PHI_CARD_WIDGET_DEFINITION = {
259
261
  ],
260
262
  },
261
263
  { key: "highlight", type: "boolean", label: "Highlight" },
262
- /* The band's words are written on the card; its colour is the one thing asked here. */
264
+ /* The band's words are written on the card; how it is drawn and its colour are asked here. */
265
+ {
266
+ key: "badgeStyle",
267
+ type: "choice",
268
+ heading: "Badge",
269
+ label: "Style",
270
+ presentation: "segmented",
271
+ description: "Corner: a band in the card's top corner. Tag: a rounded mark a little down the edge. Ribbon: hangs over the card's edge with a fold, and needs room beside the card -- a Layout that clips at the card's edge cuts it off. None: hides the badge and keeps its words.",
272
+ options: [
273
+ { value: "none", label: "None" },
274
+ { value: "corner", label: "Corner" },
275
+ { value: "tag", label: "Tag" },
276
+ { value: "ribbon", label: "Ribbon" },
277
+ ],
278
+ },
263
279
  {
264
280
  key: "badgeColor",
265
281
  type: "color",
266
- heading: "Badge",
267
282
  label: "Color",
283
+ visibleWhen: { field: "badgeStyle", notEquals: "none" },
268
284
  mode: "single",
269
285
  defaultToken: "colorPrimary",
270
286
  },
@@ -36,7 +36,11 @@ const PHI_CARD_BODY_TEXT_SLOTS = {
36
36
  * Slots a card often goes without. Empty, they stand on the canvas only while the card is under the
37
37
  * pointer or being written in (`styles/layout.css`), so an empty card does not read as four empty lines.
38
38
  */
39
- const PHI_CARD_OPTIONAL_TEXT_SLOTS = new Set(["eyebrow", "meta", "badge"]);
39
+ /*
40
+ * Not the badge: it is drawn only once a style is chosen for it, and a band or ribbon that disappeared
41
+ * whenever the pointer left the card left nothing to see of the choice just made.
42
+ */
43
+ const PHI_CARD_OPTIONAL_TEXT_SLOTS = new Set(["eyebrow", "meta"]);
40
44
  /**
41
45
  * One of the card's words, edited where it stands: underlined, in the type of the text it edits.
42
46
  *
@@ -55,7 +59,12 @@ function PhiCardInlineText({ slot, text, onCommit, }) {
55
59
  * into beside them, and its wrapping is the card's own anyway. The text still wraps and aligns as
56
60
  * the card sets it.
57
61
  */
58
- style: { width: "100%" },
62
+ /*
63
+ * The ground stays the card's, here as well as on the text: with a clear button the field is a
64
+ * wrapper around the text, and Ant Design's underlined wrapper paints the container ground -- white
65
+ * over the badge's band, whose light words then vanished on it.
66
+ */
67
+ style: { width: "100%", backgroundColor: "transparent" },
59
68
  /* The text's own type, and the page showing through: an edit in place reads like what it edits. */
60
69
  inputStyle: {
61
70
  paddingInline: 0,
@@ -116,7 +125,7 @@ export function PhiCardWidgetEditor({ config, onChange }) {
116
125
  && presentation.width != null
117
126
  && canvasWidth != null
118
127
  && presentation.width < canvasWidth;
119
- return (_jsxs("div", { ref: canvasRef, className: "phi-card-editor", style: { position: "relative", width: "100%" }, children: [_jsx(PhiCardWidgetClient, { labels: {
128
+ return (_jsxs("div", { ref: canvasRef, className: "phi-card-editor", style: { position: "relative", display: "flex", width: "100%" }, children: [_jsx(PhiCardWidgetClient, { labels: {
120
129
  eyebrow: config.eyebrow,
121
130
  title: config.title,
122
131
  description: config.description,
@@ -165,6 +174,7 @@ export function PhiCardWidgetEditor({ config, onChange }) {
165
174
  body: config.body,
166
175
  highlight: config.highlight,
167
176
  badgeColor: config.badgeColor,
177
+ badgeStyle: config.badgeStyle,
168
178
  billingPeriod: config.billingPeriod,
169
179
  hoverEffect: config.linkTarget ? config.hoverEffect : undefined,
170
180
  }, ...(config.body === "plan" ? {
@@ -113,6 +113,7 @@ export async function PhiCardWidget({ labels, config, runtime, links, translate,
113
113
  body: config?.body,
114
114
  highlight: config?.highlight,
115
115
  badgeColor: config?.badgeColor,
116
+ badgeStyle: config?.badgeStyle,
116
117
  billingPeriod: config?.billingPeriod,
117
118
  // Only a card that leads somewhere answers the pointer; the preview has no link but shows it.
118
119
  hoverEffect: config?.linkTarget ? config.hoverEffect : undefined,
@@ -8,7 +8,6 @@ import { PhiBulletListControl } from "../../../../../components/controls/phi-bul
8
8
  import { buildPhiBulletListTree, resolvePhiBulletListStyle, } from "../../../../../components/widgets/shared/bullet-list-vocabulary";
9
9
  import { resolvePhiTextPlaceholders } from "../../../../../helpers/text-placeholders";
10
10
  const PHI_LINE_HEIGHT_BASE = 1.5;
11
- const PHI_LINE_HEIGHT_LG = 1.6;
12
11
  const PHI_FONT_WEIGHT_STRONG = 600;
13
12
  export function PhiDescriptionWidgetClient({ labels, config, }) {
14
13
  const { token } = usePhiConfig();
@@ -18,6 +17,7 @@ export function PhiDescriptionWidgetClient({ labels, config, }) {
18
17
  borderRadius: 999,
19
18
  paddingInline: token.paddingSM,
20
19
  paddingBlock: token.paddingXXS,
20
+ fontSize: token.fontSizeSM,
21
21
  fontWeight: PHI_FONT_WEIGHT_STRONG,
22
22
  letterSpacing: "0.04em",
23
23
  color: token.colorTextTertiary,
@@ -26,8 +26,8 @@ export function PhiDescriptionWidgetClient({ labels, config, }) {
26
26
  }, children: labels.eyebrow })) : null, labels.title || labels.description ? (_jsxs("div", { children: [labels.title ? (_jsx(PhiTypographyControl, { presentation: "title", level: 2, style: { marginBottom: labels.description ? token.paddingXS : 0 }, children: labels.title })) : null, labels.description ? (_jsx(PhiTypographyControl, { presentation: "paragraph", type: config?.tone, style: {
27
27
  marginBottom: 0,
28
28
  color: config?.tone ? undefined : token.colorTextSecondary,
29
- fontSize: token.fontSizeLG,
30
- lineHeight: PHI_LINE_HEIGHT_LG,
29
+ fontSize: token.fontSize,
30
+ lineHeight: PHI_LINE_HEIGHT_BASE,
31
31
  }, children: labels.description })) : null] })) : null, labels.asideTitle || asideItems.length > 0 ? (_jsxs(PhiFlexControl, { vertical: true, gap: token.paddingMD, style: { width: "100%" }, children: [labels.asideTitle ? (_jsx(PhiTypographyControl, { strong: true, style: {
32
32
  fontSize: token.fontSize,
33
33
  lineHeight: PHI_LINE_HEIGHT_BASE,
@@ -39,6 +39,7 @@ export declare const PHI_DESCRIPTION_WIDGET_DEFINITION: {
39
39
  } & {
40
40
  type: "string" | "url" | "icon";
41
41
  emptyValue?: "";
42
+ placeholder?: string;
42
43
  }) | ({
43
44
  key: string;
44
45
  label: string;
@@ -60,6 +60,7 @@ export function PhiDescriptionWidgetEditor({ config, onChange, }) {
60
60
  borderRadius: 999,
61
61
  paddingInline: "var(--ant-padding-sm)",
62
62
  paddingBlock: "var(--ant-padding-xxs)",
63
+ fontSize: "var(--ant-font-size-sm)",
63
64
  fontWeight: 600,
64
65
  letterSpacing: "0.04em",
65
66
  color: "var(--ant-color-text-tertiary)",
@@ -74,8 +75,8 @@ export function PhiDescriptionWidgetEditor({ config, onChange, }) {
74
75
  } }), _jsx(PhiTextControl, { presentation: "textarea", value: draft.description, readOnly: !onChange, variant: "borderless", placeholder: "Description", autoSize: { minRows: 2, maxRows: 8 }, onChange: (value) => updateField("description", value ?? ""), onBlur: () => commitField("description"), style: {
75
76
  ...EDITOR_TEXTAREA_STYLE,
76
77
  color: "var(--ant-color-text-secondary)",
77
- fontSize: "1.125rem",
78
- lineHeight: 1.6,
78
+ fontSize: "var(--ant-font-size)",
79
+ lineHeight: 1.5,
79
80
  } })] })) : null, sections.aside ? (_jsxs(PhiFlexControl, { vertical: true, gap: 12, style: { width: "100%" }, children: [_jsx(PhiTextControl, { presentation: "textarea", value: draft.asideTitle, readOnly: !onChange, variant: "borderless", placeholder: "Aside title", autoSize: { minRows: 1, maxRows: 3 }, onChange: (value) => updateField("asideTitle", value ?? ""), onBlur: () => commitField("asideTitle"), style: {
80
81
  ...EDITOR_TEXTAREA_STYLE,
81
82
  color: "var(--ant-color-text-heading)",
@@ -64,6 +64,7 @@ export async function PhiDescriptionWidget({ labels, config, runtime, translate,
64
64
  borderRadius: "9999px",
65
65
  paddingInline: PHI_SPACE.sm,
66
66
  paddingBlock: PHI_SPACE.xxs,
67
+ fontSize: PHI_FONT_SIZE.sm,
67
68
  fontWeight: 600,
68
69
  letterSpacing: "0.04em",
69
70
  color: PHI_COLOR.textTertiary,
@@ -82,8 +83,8 @@ export async function PhiDescriptionWidget({ labels, config, runtime, translate,
82
83
  margin: 0,
83
84
  marginBottom: 0,
84
85
  color: resolveDescriptionToneColor(config?.tone),
85
- fontSize: PHI_FONT_SIZE.lg,
86
- lineHeight: PHI_LINE_HEIGHT.lg,
86
+ fontSize: PHI_FONT_SIZE.base,
87
+ lineHeight: PHI_LINE_HEIGHT.base,
87
88
  }, children: description })) : null] })) : null, asideTitle || resolvedAsideItems.length > 0 ? (_jsxs("div", { style: subStackStyle, children: [asideTitle ? (_jsx("span", { style: {
88
89
  display: "block",
89
90
  fontSize: PHI_FONT_SIZE.base,
@@ -83,6 +83,7 @@ export declare const PHI_ENTRY_LIST_WIDGET_DEFINITION: {
83
83
  } & {
84
84
  type: "string" | "url" | "icon";
85
85
  emptyValue?: "";
86
+ placeholder?: string;
86
87
  }) | ({
87
88
  key: string;
88
89
  label: string;
@@ -45,6 +45,7 @@ export declare const PHI_GALLERY_WIDGET_DEFINITION: {
45
45
  } & {
46
46
  type: "string" | "url" | "icon";
47
47
  emptyValue?: "";
48
+ placeholder?: string;
48
49
  }) | ({
49
50
  key: string;
50
51
  label: string;