@salsita-ai/sdk 0.50.0-develop.ef06ca4ac1 → 0.50.0-develop.f101f4c3c9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configurator/common/configurator-interface.d.ts +52 -2
- package/dist/configurator/common/configurator-interface.js +1 -1
- package/dist/configurator/configurator-context/configurator-hooks/layout-hooks.d.ts +4 -0
- package/dist/configurator/index.d.ts +1 -0
- package/dist/configurator/index.js +1 -1
- package/dist/configurator/pages/default-configurator-elements.d.ts +3 -2
- package/dist/configurator/pages/default-configurator-elements.js +1 -1
- package/dist/configurator/pages/default-elements/index.d.ts +1 -0
- package/dist/configurator/pages/default-elements/index.js +1 -1
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInAddEntityFab.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInAddEntityFab.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInDeleteEntityFab.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInDeleteEntityFab.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInNavigationHotspot.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInNavigationHotspot.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInSelectionHotspot.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInSelectionHotspot.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInToolMenuFab.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInToolMenuFab.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/index.d.ts +6 -0
- package/dist/configurator/pages/default-elements/scene-controls/index.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/scene-control-elements.d.ts +6 -0
- package/dist/configurator/pages/default-elements/scene-controls/scene-control-elements.js +1 -0
- package/dist/configurator/scene/index.d.ts +1 -0
- package/dist/configurator/scene/index.js +1 -1
- package/dist/configurator/scene/scene-controls/ToolFab.d.ts +4 -0
- package/dist/configurator/scene/scene-controls/ToolFab.js +1 -1
- package/dist/configurator/scene/scene-controls/use-scene-control-tooltip.d.ts +2 -1
- package/dist/configurator/scene/scene-entity/scene-controls/SceneEntityHtmlHotspots.js +1 -1
- package/dist/configurator/toolbar-actions/index.d.ts +3 -0
- package/dist/configurator/toolbar-actions/index.js +1 -0
- package/dist/configurator/toolbar-actions/use-assistant-panel.d.ts +18 -0
- package/dist/configurator/toolbar-actions/use-assistant-panel.js +1 -0
- package/dist/configurator/toolbar-actions/use-measurements.d.ts +35 -0
- package/dist/configurator/toolbar-actions/use-measurements.js +1 -0
- package/dist/configurator/toolbar-actions/use-toolbar-actions.d.ts +100 -0
- package/dist/configurator/toolbar-actions/use-toolbar-actions.js +1 -0
- package/dist/configurator/toolbar-actions/use-toolbar-help.d.ts +7 -0
- package/dist/configurator/toolbar-actions/use-toolbar-help.js +1 -0
- package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +1 -1
- package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -1
- package/dist/configurator/ui/assistant/alert/AssistantAlert.d.ts +8 -0
- package/dist/configurator/ui/assistant/alert/AssistantAlert.js +1 -0
- package/dist/configurator/ui/assistant/alert/index.d.ts +2 -0
- package/dist/configurator/ui/assistant/alert/index.js +1 -0
- package/dist/configurator/ui/assistant/alert/remark-alerts.d.ts +10 -0
- package/dist/configurator/ui/assistant/alert/remark-alerts.js +1 -0
- package/dist/configurator/ui/assistant/form/AssistantForm.js +1 -1
- package/dist/configurator/ui/assistant/form/built-in-form-registry.js +1 -1
- package/dist/configurator/ui/assistant/form/compose-answer.d.ts +2 -1
- package/dist/configurator/ui/assistant/form/compose-answer.js +1 -1
- package/dist/configurator/ui/assistant/form/form-model.d.ts +3 -2
- package/dist/configurator/ui/assistant/form/form-registry.d.ts +1 -1
- package/dist/configurator/ui/assistant/form/form-registry.js +2 -2
- package/dist/configurator/ui/assistant/form/parse-form-block.d.ts +2 -1
- package/dist/configurator/ui/assistant/form/parse-form-block.js +1 -1
- package/dist/configurator/ui/assistant/form/read-form-attributes.js +1 -1
- package/dist/configurator/ui/assistant/form-elements/FormCard.d.ts +9 -0
- package/dist/configurator/ui/assistant/form-elements/FormCard.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormConfirm.d.ts +9 -0
- package/dist/configurator/ui/assistant/form-elements/FormConfirm.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormGroup.d.ts +4 -1
- package/dist/configurator/ui/assistant/form-elements/FormNumber.d.ts +9 -0
- package/dist/configurator/ui/assistant/form-elements/FormNumber.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormOptions.d.ts +4 -1
- package/dist/configurator/ui/assistant/form-elements/FormReply.d.ts +4 -1
- package/dist/configurator/ui/assistant/form-elements/FormSwatch.d.ts +10 -0
- package/dist/configurator/ui/assistant/form-elements/FormSwatch.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormText.d.ts +4 -1
- package/dist/configurator/ui/assistant/form-elements/FormText.js +1 -1
- package/dist/configurator/ui/assistant/form-elements/clamp-form-number.d.ts +10 -0
- package/dist/configurator/ui/assistant/form-elements/clamp-form-number.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/form-element-props.d.ts +17 -13
- package/dist/configurator/ui/assistant/form-elements/index.d.ts +4 -0
- package/dist/configurator/ui/assistant/form-elements/index.js +1 -1
- package/dist/configurator/ui/assistant/resource-image/index.d.ts +1 -1
- package/dist/configurator/ui/assistant/resource-image/index.js +1 -1
- package/dist/configurator/ui/assistant/resource-image/resource-path.d.ts +0 -9
- package/dist/configurator/ui/assistant/resource-image/resource-path.js +1 -1
- package/dist/configurator/ui/assistant/streamed-markdown.d.ts +2 -2
- package/dist/configurator/ui/assistant/streamed-markdown.js +2 -2
- package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/LanguageMenuAction.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/LanguageToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/MeasurementsMenuAction.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/MeasurementsToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/ShareToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/measurements/DimensionUnitSelector.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/measurements/MeasurementsIndicatorsSwitch.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/share/ShareModal.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/share/use-share.d.ts +7 -8
- package/dist/configurator/ui/phoenix-layout-elements/share/use-share.js +1 -1
- package/dist/docs/complex-use-cases/focus-mode.mdx +2 -0
- package/dist/docs/fabs/basic.mdx +4 -0
- package/dist/docs/index.json +1 -1
- package/dist/docs/layouts/custom-layout.mdx +3 -1
- package/dist/docs/other-features/navigation-hotspot.mdx +2 -0
- package/dist/docs/phoenix/configurator-interface.mdx +68 -1
- package/dist/docs/phoenix/sdk-builder-workflow.mdx +1 -1
- package/dist/docs/selection-and-highlight/hotspots.mdx +2 -0
- package/dist/domain/application/assistant/form/assistant-form-elements.d.ts +163 -0
- package/dist/domain/application/assistant/form/assistant-form-elements.js +1 -0
- package/dist/domain/application/assistant/form/assistant-resource-path.d.ts +10 -0
- package/dist/domain/application/assistant/form/assistant-resource-path.js +1 -0
- package/dist/domain/application/assistant/form/get-assistant-form-value-attribute.d.ts +6 -0
- package/dist/domain/application/assistant/form/get-assistant-form-value-attribute.js +1 -0
- package/dist/domain/application/assistant/form/index.d.ts +4 -0
- package/dist/domain/application/assistant/form/index.js +1 -0
- package/dist/domain/application/assistant/generate-assistant-prompt.d.ts +3 -0
- package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
- package/dist/domain/application/assistant/index.d.ts +1 -2
- package/dist/domain/application/assistant/index.js +1 -1
- package/dist/i18n/locales/cs/messages.js +1 -1
- package/dist/i18n/locales/da/messages.js +1 -1
- package/dist/i18n/locales/de/messages.js +1 -1
- package/dist/i18n/locales/de-AT/messages.js +1 -1
- package/dist/i18n/locales/en/messages.js +1 -1
- package/dist/i18n/locales/en-AU/messages.js +1 -1
- package/dist/i18n/locales/en-GB/messages.js +1 -1
- package/dist/i18n/locales/es/messages.js +1 -1
- package/dist/i18n/locales/fr/messages.js +1 -1
- package/dist/i18n/locales/hu/messages.js +1 -1
- package/dist/i18n/locales/it/messages.js +1 -1
- package/dist/i18n/locales/nl/messages.js +1 -1
- package/dist/i18n/locales/pl/messages.js +1 -1
- package/dist/i18n/locales/pt/messages.js +1 -1
- package/dist/i18n/locales/ro/messages.js +1 -1
- package/dist/i18n/locales/sk/messages.js +1 -1
- package/dist/i18n/locales/tr/messages.js +1 -1
- package/dist/react-sdk/domain-context/index.d.ts +3 -0
- package/dist/react-sdk/domain-context/index.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/generate-share-link-hooks.d.ts +8 -0
- package/dist/react-sdk/domain-context/system-hooks/generate-share-link-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/locale-switch-hooks.d.ts +24 -0
- package/dist/react-sdk/domain-context/system-hooks/locale-switch-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/share-links-hooks.d.ts +32 -0
- package/dist/react-sdk/domain-context/system-hooks/share-links-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/undo-redo-hooks.js +1 -1
- package/dist/react-sdk/index.d.ts +1 -0
- package/dist/react-sdk/index.js +1 -1
- package/dist/react-sdk/scene-control-elements/AddEntityFabElement.d.ts +36 -0
- package/dist/react-sdk/scene-control-elements/AddEntityFabElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/DeleteEntityFabElement.d.ts +44 -0
- package/dist/react-sdk/scene-control-elements/DeleteEntityFabElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/NavigationHotspotElement.d.ts +38 -0
- package/dist/react-sdk/scene-control-elements/NavigationHotspotElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/SelectionHotspotElement.d.ts +30 -0
- package/dist/react-sdk/scene-control-elements/SelectionHotspotElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/ToolMenuFabElement.d.ts +30 -0
- package/dist/react-sdk/scene-control-elements/ToolMenuFabElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-add-entity-fab.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-add-entity-fab.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-delete-entity-fab.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-delete-entity-fab.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-navigation-hotspot.d.ts +10 -0
- package/dist/react-sdk/scene-control-elements/create-navigation-hotspot.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-selection-hotspot.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-selection-hotspot.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-tool-menu-fab.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-tool-menu-fab.js +1 -0
- package/dist/react-sdk/scene-control-elements/index.d.ts +11 -0
- package/dist/react-sdk/scene-control-elements/index.js +1 -0
- package/dist/react-sdk/scene-control-elements/scene-control-tooltip-visibility.d.ts +5 -0
- package/dist/react-sdk/scene-control-elements/scene-control-tooltip-visibility.js +1 -0
- package/dist/ui-sdk/icons/__generated/Lightbulb.d.ts +3 -0
- package/dist/ui-sdk/icons/__generated/Lightbulb.js +1 -0
- package/dist/ui-sdk/icons/__generated/XCircle.d.ts +3 -0
- package/dist/ui-sdk/icons/__generated/XCircle.js +1 -0
- package/dist/ui-sdk/icons/__generated/index.d.ts +4 -0
- package/dist/ui-sdk/icons/__generated/index.js +1 -1
- package/dist/ui-sdk/icons/icons-registry.js +1 -1
- package/dist/ui-sdk/theme/types.d.ts +1 -1
- package/package.json +3 -3
- package/dist/configurator/ui/phoenix-layout-elements/language/use-locale-options.d.ts +0 -4
- package/dist/configurator/ui/phoenix-layout-elements/language/use-locale-options.js +0 -1
- package/dist/configurator/ui/phoenix-layout-elements/measurements/use-measurements-visibility.d.ts +0 -4
- package/dist/configurator/ui/phoenix-layout-elements/measurements/use-measurements-visibility.js +0 -1
- package/dist/domain/application/assistant/assistant-form-elements.d.ts +0 -28
- package/dist/domain/application/assistant/assistant-form-elements.js +0 -1
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
import type { AccessLevel } from '@salsita-ai/contracts/api';
|
|
2
|
-
export interface GenerateShareLinkOptions {
|
|
3
|
-
access: AccessLevel;
|
|
4
|
-
expiration?: number;
|
|
5
|
-
}
|
|
6
|
-
export declare const useGenerateShareLink: () => (options: GenerateShareLinkOptions) => Promise<{
|
|
7
|
-
readonly link: string;
|
|
8
|
-
}>;
|
|
9
1
|
interface UseShareDataParams {
|
|
10
2
|
/**
|
|
11
3
|
* Expiration time in hours. When undefined, links do not expire.
|
|
@@ -14,6 +6,9 @@ interface UseShareDataParams {
|
|
|
14
6
|
ownerLinkDisabled: boolean;
|
|
15
7
|
readRevisionLinkDisabled: boolean;
|
|
16
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated Use useShareLinks() instead. This API will be removed in a future version.
|
|
11
|
+
*/
|
|
17
12
|
export declare const useShareData: (params: UseShareDataParams) => {
|
|
18
13
|
isLoadingOwnerData: boolean;
|
|
19
14
|
isLoadingRevisionData: boolean;
|
|
@@ -24,6 +19,10 @@ export declare const useShareData: (params: UseShareDataParams) => {
|
|
|
24
19
|
readonly link: string;
|
|
25
20
|
}> | undefined;
|
|
26
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated Use useShareLinks() instead, whose `available`, `ownerLinkAvailable` and `readLinkAvailable` say which
|
|
24
|
+
* links can be shared. This API will be removed in a future version.
|
|
25
|
+
*/
|
|
27
26
|
export declare const useShareSettings: () => {
|
|
28
27
|
disabled: boolean;
|
|
29
28
|
ownerLinkDisabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useQuery as e}from"@tanstack/react-query";import{
|
|
1
|
+
import{useQuery as e}from"@tanstack/react-query";import{applicationConfigComponent as a}from"#package/domain";import{useConfigurationId as t,useDomain as r,useFirstRootComponent as s,useGenerateShareLink as i}from"#package/react-sdk";export const useShareData=a=>{let{expiration:s,ownerLinkDisabled:n,readRevisionLinkDisabled:o}=a,d=i(),{storeSystem:c}=r(),l=t(),u=c.stateSlices.committed.value.state,{data:m,isLoading:h}=e({queryKey:["share-owner-link",l],staleTime:1/0,queryFn:()=>d({access:"o",expiration:s}),enabled:!n}),{data:k,isLoading:y,isFetchedAfterMount:p}=e({queryKey:["share-revision-link",l,u.entities,u.rootEntityId],queryFn:()=>d({access:"rr",expiration:s}),enabled:!o});return{isLoadingOwnerData:h,isLoadingRevisionData:y,ownerData:m,revisionData:p?k:void 0}};export const useShareSettings=()=>{let e=s(a),t=e?.state.shareDisabled??!1,r=t||(e?.state.shareOwnerLinkDisabled??!1),i=t||(e?.state.shareReadRevisionLinkDisabled??!1);return{disabled:r&&i,ownerLinkDisabled:r,readRevisionLinkDisabled:i}};
|
|
@@ -69,6 +69,8 @@ components:
|
|
|
69
69
|
|
|
70
70
|
The toolMenuFab is scene HTML rendered at the entity's position in the 3D scene. It supports positioning properties (`offsetX`, `offsetY`, `offsetZ`) and appearance properties like opacity and visibility.
|
|
71
71
|
|
|
72
|
+
The FAB and its menu are the entity element `ToolMenuFab`. A project replaces them by registering its own with `phx.frontend.createToolMenuFab`, which receives the `options` and `onSelectTool(toolState)`. See [Configurator Interface → In-Scene Controls](?path=/docs/phoenix-configurator-interface--docs#in-scene-controls).
|
|
73
|
+
|
|
72
74
|
### Step 3: Add exitToolModeFab component
|
|
73
75
|
|
|
74
76
|
The `exitToolModeFab` component renders a button in the call to action layout slot, providing a clear way to exit the current tool mode. This appears in the UI chrome, not in the scene:
|
package/dist/docs/fabs/basic.mdx
CHANGED
|
@@ -131,6 +131,10 @@ components:
|
|
|
131
131
|
- non-existent
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
### Your own FABs
|
|
135
|
+
|
|
136
|
+
The FABs are the entity elements `DeleteEntityFab` and `AddEntityFab`. A project replaces their look by registering its own with `phx.frontend.createDeleteEntityFab` or `phx.frontend.createAddEntityFab`; the spec's settings and positions above still apply. The delete FAB's replacement gets `onDelete`, `disabled` and `disabledHint`; the add FAB's reads its recipes with `phx.frontend.useGroupedRecipeOptions`. See [Configurator Interface → In-Scene Controls](?path=/docs/phoenix-configurator-interface--docs#in-scene-controls).
|
|
137
|
+
|
|
134
138
|
## Example
|
|
135
139
|
|
|
136
140
|
Here you can see the live example!
|
package/dist/docs/index.json
CHANGED
|
@@ -1032,6 +1032,8 @@ export default phx.frontend.createCustomPhoenixEntityElement('EdgeFinishHotspot'
|
|
|
1032
1032
|
|
|
1033
1033
|
[Html Hotspots](?path=/docs/controls-scene-html-hotspots--docs) describes the component and its other properties.
|
|
1034
1034
|
|
|
1035
|
+
The SDK's own controls on the product, such as `sceneEntityAddFab` and `sceneEntitySelectHotspot`, keep the SDK's look in a project layout too. Each is rendered through an entity element of its own name, so a project gives it the brand's look by registering that name, for example with `phx.frontend.createAddEntityFab`; see [Configurator Interface → In-Scene Controls](?path=/docs/phoenix-configurator-interface--docs#in-scene-controls). `htmlHotspot` is for a control the SDK has none of, like this one.
|
|
1036
|
+
|
|
1035
1037
|
## Control the scene
|
|
1036
1038
|
|
|
1037
1039
|
The four finishes differ by a few millimetres on a table two metres long, so from the default distance the difference is hard to judge. The layout offers two views, **Whole table** and **Edge close-up**, a close and low look at a corner of the top, and a **Reset view** button that brings back the framing after the customer has orbited, panned or zoomed.
|
|
@@ -1268,7 +1270,7 @@ The same layout in a frame 390 pixels wide, as on a phone. Opened on its own fro
|
|
|
1268
1270
|
A project layout has only what it renders. This one leaves out the following, each of which a product may need. [Layout Slots](?path=/docs/phoenix-layout-slots--docs) describes every `LayoutSlots` member named here.
|
|
1269
1271
|
|
|
1270
1272
|
- **Messages that stay snackbars.** The toast shows violations, side effects, errors and failed saves, and the finalize button shows its own error. Two kinds of message stay snackbars. An error of a built-in action, such as one from `LayoutSlots.DesktopToolbar`, renders into `LayoutSlots.DesktopSnackbarsRoot`; this layout mounts neither, and a layout that mounts such an action mounts the root too, or the snackbar appears outside the configurator. A failure to start from a preset appears on the preset screen, before this layout mounts; a project replaces that screen with `phx.frontend.createPresetUi`, see [Configurator Interface → Preset UI](?path=/docs/phoenix-configurator-interface--docs#preset-ui).
|
|
1271
|
-
- **Toolbar actions.** Undo and redo, measurements, sharing and language switching are not offered. A layout can mount `LayoutSlots.DesktopToolbar` for the toolbar the spec configures, or build its own buttons
|
|
1273
|
+
- **Toolbar actions.** Undo and redo, measurements, sharing and language switching are not offered. A layout can mount `LayoutSlots.DesktopToolbar` for the toolbar the spec configures, or build its own buttons: `phx.frontend.useToolbarActions()` returns the spec's toolbar as runnable entries, and `phx.frontend.useUndoRedo()`, `useMeasurements()`, `useShareLinks()` and `useLocaleSwitch()` serve single actions, pushing the same analytics events as the built-in buttons; see [Configurator Interface → Hooks for a Project Layout](?path=/docs/phoenix-configurator-interface--docs#hooks-for-a-project-layout).
|
|
1272
1274
|
- **The spec's view controls, tool controls and leaving a tool mode.** A spec can configure view controls and tool controls, and a tool mode has a button that leaves it. The built-in layout places them with `LayoutSlots.DesktopViewControls`, `LayoutSlots.DesktopToolControls` and `LayoutSlots.DesktopCallToAction`; this layout mounts none of them, so the spec's `viewControls` and `toolControls` do not appear. Its views come from its own buttons instead, see [Control the scene](#control-the-scene), and it has no tool modes.
|
|
1273
1275
|
- **A project's layout dialog.** A dialog the project registers with `phx.frontend.createLayoutDialog` is shown by `LayoutDialog`, which the built-in layout mounts. This layout does not; a layout that needs the dialog mounts `LayoutDialog` from `@salsita-ai/sdk/configurator`.
|
|
1274
1276
|
- **Selection.** The table has no parts to select. A product with selectable parts reads `phx.frontend.useSelectedDomainEntityId()` and shows the controls of the selected part.
|
|
@@ -96,6 +96,8 @@ Key observations:
|
|
|
96
96
|
- With `pushToStack: true` and no `canStackOnto`, the new state is always pushed on top
|
|
97
97
|
- With `canStackOnto: [Legs]`, the stack is "rewound" to `Legs` before pushing - any states after `Legs` are removed
|
|
98
98
|
|
|
99
|
+
The hotspot is the entity element `NavigationHotspot`. A project replaces its look by registering its own with `phx.frontend.createNavigationHotspot`, which receives `navigationState` and `onNavigate`, the navigation with the stack rules above. See [Configurator Interface → In-Scene Controls](?path=/docs/phoenix-configurator-interface--docs#in-scene-controls).
|
|
100
|
+
|
|
99
101
|
## Example
|
|
100
102
|
|
|
101
103
|
This example demonstrates navigation hotspots with stack behavior. The sidebar shows breadcrumbs displaying the current navigation stack for easier understanding.
|
|
@@ -455,6 +455,61 @@ export default phx.frontend.createAppLoader(({ phases }) => <span>{phases[0]?.la
|
|
|
455
455
|
- The top-level `appLoader: legacy` key in the spec turns the overlay off.
|
|
456
456
|
- The built-in content is the default `AppLoaderContent` registration in `defaultConfiguratorElements`, see below.
|
|
457
457
|
|
|
458
|
+
### In-Scene Controls
|
|
459
|
+
|
|
460
|
+
The spec's built-in controls on the product are rendered through entity elements, one name per control. A project element registered under one of the names replaces that control everywhere the spec places it, in the built-in layouts and in a project layout. Without it, Phoenix renders its built-in control, which is the default registration of the name in `defaultConfiguratorElements`.
|
|
461
|
+
|
|
462
|
+
| Element name | Rendered for | Props besides `entityId` | Registration helper |
|
|
463
|
+
| ------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
|
464
|
+
| `AddEntityFab` | `sceneEntityAddFab` | `instanceNames`, `fixableViolations`, `icon`, `label`, `title`, `tooltipVisibility`, `skipMenuWhenOnlyOne`, `hideWhenNone` | `phx.frontend.createAddEntityFab` |
|
|
465
|
+
| `DeleteEntityFab` | `sceneEntityDeleteFab` | `icon`, `label`, `title`, `tooltipVisibility`, `onDelete`, `disabled`, `disabledHint`, `trace` | `phx.frontend.createDeleteEntityFab` |
|
|
466
|
+
| `SelectionHotspot` | `sceneEntitySelectHotspot` | `title`, `tooltipVisibility`, `onSelect` | `phx.frontend.createSelectionHotspot` |
|
|
467
|
+
| `NavigationHotspot` | `sceneEntityNavigationHotspot` | `navigationState`, `title`, `tooltipVisibility`, `onNavigate` | `phx.frontend.createNavigationHotspot` |
|
|
468
|
+
| `ToolMenuFab` | `toolMenuFab` | `options`, `icon`, `title`, `tooltipVisibility`, `onSelectTool` | `phx.frontend.createToolMenuFab` |
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
// src/elements/SceneControls.tsx
|
|
472
|
+
import { LocalizableMessage } from '@salsita-ai/sdk/react'
|
|
473
|
+
|
|
474
|
+
const AddEntityButtons = phx.frontend.createAddEntityFab(({ entityId, instanceNames, fixableViolations }) => {
|
|
475
|
+
const recipes = phx.frontend
|
|
476
|
+
.useGroupedRecipeOptions(entityId, instanceNames, fixableViolations)
|
|
477
|
+
.flatMap(group => group.recipes)
|
|
478
|
+
|
|
479
|
+
return (
|
|
480
|
+
<div className="brand-scene-buttons">
|
|
481
|
+
{recipes.map((recipe, index) => (
|
|
482
|
+
<button
|
|
483
|
+
key={index}
|
|
484
|
+
disabled={recipe.disabled}
|
|
485
|
+
title={recipe.tooltipTitle}
|
|
486
|
+
onClick={event => {
|
|
487
|
+
event.stopPropagation()
|
|
488
|
+
recipe.handleClick()
|
|
489
|
+
}}
|
|
490
|
+
>
|
|
491
|
+
<LocalizableMessage>{recipe.title}</LocalizableMessage>
|
|
492
|
+
</button>
|
|
493
|
+
))}
|
|
494
|
+
</div>
|
|
495
|
+
)
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
const DeleteEntityButton = phx.frontend.createDeleteEntityFab(({ onDelete, disabled, disabledHint }) => (
|
|
499
|
+
<button className="brand-scene-button" onClick={onDelete} disabled={disabled} title={disabledHint}>
|
|
500
|
+
Remove
|
|
501
|
+
</button>
|
|
502
|
+
))
|
|
503
|
+
|
|
504
|
+
export default [AddEntityButtons, DeleteEntityButton]
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
- Each helper registers the element under its name with the props typed by `AddEntityFabElementProps`, `DeleteEntityFabElementProps`, `SelectionHotspotElementProps`, `NavigationHotspotElementProps` or `ToolMenuFabElementProps`, exported with the helpers from `@salsita-ai/sdk/react` and `@salsita-ai/sdk/configurator`. `createCustomPhoenixEntityElement` with the same name works too, with only `entityId` and `children` typed. The component may return `null` to show nothing.
|
|
508
|
+
- The props are the component's settings from the spec, with `tooltipVisibility` already defaulted to `on-hover`, and the actions the built-in control performs. `onDelete` deletes the entity, `onSelect` selects it and `onNavigate` goes to `navigationState`; each pushes the same analytics event as the built-in control, and each can be passed straight to `onClick`: given the click, it stops its propagation, so the click does not also reach the scene. `onSelectTool(toolState)` switches to the tool state of one of the `options`. `disabled` and `disabledHint` say whether deleting would cause a violation the component's `hiddenViolations` do not allow, and why. `trace` logs the deletion's dry run to the console.
|
|
509
|
+
- The add FAB gets no recipes: it reads them with `phx.frontend.useGroupedRecipeOptions(entityId, instanceNames, fixableViolations)`. Each recipe has `handleClick`, which adds it, `disabled` and `tooltipTitle`. Every recipe is dry-run to tell whether it is allowed, so the built-in FAB reads them only while its menu is open, or up front when `skipMenuWhenOnlyOne` applies; a replacement with a menu does the same. Its `handleClick` does not stop the click's propagation, so the replacement does.
|
|
510
|
+
- Where and when the control shows stays with the spec and the scene: the offsets, `showWhen`, the camera limits, `hideWhileDragging` and `occlude` apply to the element as they did to the built-in control. The element is rendered into the scene's HTML layer, with the theme and every `phx.frontend` hook.
|
|
511
|
+
- A project's own control, one the SDK has no built-in for, is an `htmlHotspot` that names an element of the project, as the edge hotspot of [Custom Layout](?path=/docs/layouts-custom-layout--docs#put-a-control-on-the-product) is.
|
|
512
|
+
|
|
458
513
|
### Loaded Configuration UI
|
|
459
514
|
|
|
460
515
|
A layout element registered under the name `ConfiguratorLayout` replaces the whole UI of a loaded configuration, in owner and in read mode, whatever `layout` the spec sets. Without it, Phoenix renders the layout the spec selects. [Custom Layout](?path=/docs/layouts-custom-layout--docs) is a worked example: a fully custom product page with its panel on the left, built from the scene, the safe-area hook and state hooks.
|
|
@@ -494,7 +549,7 @@ export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout
|
|
|
494
549
|
- Messages for the user come one of two ways. `LayoutSlots.Services` shows violations of a rejected change, side-effect hints, errors and failed saves as the built-in snackbars, which render into `LayoutSlots.DesktopSnackbarsRoot` or `LayoutSlots.MobileSnackbarsRoot` wherever the layout mounts it. `phx.frontend.useConfiguratorMessages(onMessage)` hands the same messages to the layout's own UI instead, with their text resolved, as the toast in [Custom Layout](?path=/docs/layouts-custom-layout--docs#show-messages) does; mount one of the two, not both, or every message is shown twice. A failure to save the configuration, and the first successful save after it, are messages too, so a layout that mounts neither shows no sign of a failed save. An error of the built-in finalize and other actions stays a snackbar either way. It renders into the snackbar root, so a layout that mounts none shows it outside the configurator, in the page's `body`. A failure to start a configuration from a preset is not a message of the hook either: it happens on the preset screen, before any `ConfiguratorLayout` mounts. In production with more than one preset it is a snackbar there, also on a preset screen replaced with `createPresetUi` (see [Preset UI](#preset-ui)), which reads the error from `phx.frontend.usePresetSelection().selectionError` and mounts `PresetSnackbarsRoot` to keep the snackbar inside the configurator. With a single preset it is the [Configurator Error Page](#configurator-error-page). A finalize action the layout runs with `phx.frontend.useFinalizeActions` or `phx.frontend.useFinalizeAction` reports its error as the action's `error` instead, neither as a message nor as a snackbar.
|
|
495
550
|
- Any project layout element named `ConfiguratorLayout` takes over the page.
|
|
496
551
|
|
|
497
|
-
`defaultConfiguratorElements`, exported from `@salsita-ai/sdk/configurator`, holds the built-in UI as a `ConfiguratorLayout` registration that renders the layout the spec selects, the built-in not-found page as a `ConfigurationNotFoundPage` registration, the built-in not-authenticated page as a `NotAuthenticatedPage` registration, the built-in preset screen as a `PresetUi` registration, the built-in error page as a `ConfiguratorErrorPage` registration,
|
|
552
|
+
`defaultConfiguratorElements`, exported from `@salsita-ai/sdk/configurator`, holds the built-in UI as a `ConfiguratorLayout` registration that renders the layout the spec selects, the built-in not-found page as a `ConfigurationNotFoundPage` registration, the built-in not-authenticated page as a `NotAuthenticatedPage` registration, the built-in preset screen as a `PresetUi` registration, the built-in error page as a `ConfiguratorErrorPage` registration, the app loader's built-in content as an `AppLoaderContent` registration, and the built-in in-scene controls under the names of [In-Scene Controls](#in-scene-controls). Generated code registers these defaults with `withDefaultElements`, apart from the project's elements, so the built-in UI is a registration like any other; a project element registered under the same name wins over a default. The list also holds the configurator's built-in controls and layout elements under the kinds the spec names them by, such as `attributeSelect` or `undoToolbarButton`, so a project element registered under one of those names replaces the built-in one wherever the spec names that kind. A config assembled by hand, without generated code, calls `withDefaultElements(defaultConfiguratorElements)` too; the lists are the only way the built-in elements are registered, so without it the page shows a "not found" box instead of the configurator, and a project layout shows one in place of every built-in control. The admin mounts a full configurator, so its list, `defaultAdminElements` from `@salsita-ai/sdk/admin`, holds the configurator's defaults followed by the admin's built-in pages, sidebars and actions; generated admin code passes it to `withDefaultElements` in place of `defaultConfiguratorElements`, because each call replaces the defaults passed before.
|
|
498
553
|
|
|
499
554
|
#### Hooks for a Project Layout
|
|
500
555
|
|
|
@@ -573,6 +628,18 @@ export const Wizard = () => {
|
|
|
573
628
|
}
|
|
574
629
|
```
|
|
575
630
|
|
|
631
|
+
**Toolbar**
|
|
632
|
+
|
|
633
|
+
A layout builds its toolbar from `phx.frontend.useToolbarActions()`, which returns the entries of the spec's `toolbar`, or of the default toolbar without one, as data with the function that runs each; or it places single actions with the per-action hooks below. Each hook does what the built-in toolbar button of the same action does and pushes the same analytics events; opening and closing the layout's own dialogs and popovers push none. `useToolbarActions`, `useMeasurements` and `useAssistantPanel` are exported from `@salsita-ai/sdk/configurator` besides `phx.frontend`, not from `@salsita-ai/sdk/react`. `useToolbarState()` from `@salsita-ai/sdk/configurator` is deprecated in favour of `useToolbarActions()`, or of mounting `LayoutSlots.DesktopToolbar` to keep project element overrides, and `useShareData()` and `useShareSettings()` in favour of `useShareLinks()`. Undo and redo for a single button are `phx.frontend.useUndoRedo()` above, fullscreen is `useIsFullscreenActive()` with `useSetFullscreen()`, and the camera reset is `useResetCamera()`.
|
|
634
|
+
|
|
635
|
+
| Hook | Returns |
|
|
636
|
+
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
637
|
+
| `phx.frontend.useToolbarActions(placement?)` | `{ groups, actions }` for `placement` `'desktop'` (the default), `'mobile'` or `'mobileMenu'`, the `toolbar` component's list of that name. `groups` follows the spec's `toolbarButtonGroup`s, an element outside a group being a group of one, and `actions` is the same entries flattened. Each entry has `key`, `kind`, `label` (the built-in button's title in the current language), and `icon` and `showTitle` from the element's props. By `kind`: `undo` and `redo` with `disabled` and `run`; `fullscreen` with `active` and `run`, which toggles the fullscreen flag; `resetCamera` with `run`; `share`, for which the layout opens its own dialog over `useShareLinks`; `measurements` with `measurements`, the result of `useMeasurements` with the element's `showIndicatorSwitch` applied, and `icons`, the element's icon per unit; `language` with `language`, the result of `useLocaleSwitch`; `help` with `Content`, a component that renders the element's children or else the help registered with `createToolbarHelp`; `assistant` with `assistant`, the result of `useAssistantPanel`, and `run`, which opens or closes the panel; `custom` with `name` and `Element`, which renders the project element a `customLayoutElement` names. It leaves out an entry with nothing to offer, such as share with sharing disabled or language with one locale, and an element that is not a toolbar entry. A project element registered under a built-in kind, such as `undoToolbarButton`, replaces that button in `LayoutSlots.DesktopToolbar` but not here. As with `LayoutSlots.DesktopToolbar`, the default toolbar leaves out share, undo and redo for a viewer, and a configured `toolbar` is returned as written. Four differences from the built-in toolbar: the entries keep the spec's order for every placement, where `LayoutSlots.MobileToolbar` renders the `mobile` list reversed; a `toolbarButtonGroup` nested in a group is flattened into it, where the built-in toolbar nests it; the assistant's `showTitle` is the spec's, where the built-in button also hides its title on a phone or tablet-sized layout; and a `helpMenuAction` without children shows the registered help, where the built-in menu action shows an empty dialog. |
|
|
638
|
+
| `phx.frontend.useMeasurements(options?)` | `{ units, unit, setUnit, hasIndicatorSwitch, indicatorsShown, toggleIndicators, available }`: the units the dimension configuration offers, the one dimensions are shown in and a function that changes it; whether the dimension indicators can be switched (`options.showIndicatorSwitch`, else `applicationConfig.indicatorSwitch`, else `true`), whether they show and a function that switches them; and whether there is anything to set, more than one unit or the switch. |
|
|
639
|
+
| `phx.frontend.useLocaleSwitch()` | `{ locales, locale, setLocale, available }`: the spec's `availableLocales` as `{ locale, displayName }`, each named in its own language, the active locale, a function that activates one, and whether there is more than one to choose from. Besides `phx.frontend`, `@salsita-ai/sdk/react` exports it. |
|
|
640
|
+
| `phx.frontend.useShareLinks(options?)` | `{ available, ownerLinkAvailable, readLinkAvailable, ownerLink, readLink, isLoading }`: whether sharing is offered and which kinds of link the application config enables, a link that opens the configuration for editing and one that opens its current revision for viewing, each `null` until generated, and whether one is still being generated. While enabled, each commit of the configuration generates a new read link. Generating a link saves the configuration, so a layout passes `{ enabled: open }` with whether its share dialog is open; `enabled` is `true` by default. Besides `phx.frontend`, `@salsita-ai/sdk/react` exports it. |
|
|
641
|
+
| `phx.frontend.useAssistantPanel()` | `{ available, open, hasUnreadReply, setOpen }`: whether the spec has an `assistant`, whether its panel is open, whether it replied while closed, and a function that opens or closes it. The panel renders only inside `LayoutSlots.DesktopSidebar`, so a layout without that slot does not offer it. |
|
|
642
|
+
|
|
576
643
|
**Violations**
|
|
577
644
|
|
|
578
645
|
| Hook | Returns |
|
|
@@ -95,7 +95,7 @@ Tell us what is broken, confusing or missing, from wherever you are:
|
|
|
95
95
|
|
|
96
96
|
- In Studio, **Send beta feedback** in the sidebar opens a short form.
|
|
97
97
|
- In the terminal, `pnpm phoenix feedback "<message>" --type bug|idea|other` sends it with your sign-in, and `--file <path>` sends a text file instead of the argument. See the [CLI reference](?path=/docs/phoenix-phoenix-cli--docs).
|
|
98
|
-
- On a build's page in Studio, **Download
|
|
98
|
+
- On a build's page in Studio, **Download** → **Sources** downloads the archive `phoenix deploy` uploaded for that build, so a report only needs to name the build.
|
|
99
99
|
|
|
100
100
|
Coding agents working in your project use the same command, guided by the generated `phoenix-feedback` skill. They never send on their own: an agent shows you a draft, right away when the problem is crucial and otherwise at the end of the task, and sends it only after you approve it. A report names the build with its Studio page link and the live configurator, says what the agent was doing, what happened and what it expected, quotes the exact error and ends with a condensed conversation log. Agents are told never to include secrets, credentials or personal data; parts of your project and your own words go in only through a draft you approved. When you are done with the configurator, the agent also asks you, once per conversation, to evaluate the run: minutes spent, then scores from 0 to 10 for the agent's guidance, functionality, UX, 3D quality, UI visuals and the overall result, and your comments. It asks one question at a time, never picks a score for you, tells you the answers go to the Phoenix team, shows the message and sends it only after you approve it.
|
|
101
101
|
|
|
@@ -56,6 +56,8 @@ sceneEntitySelectHotspot:
|
|
|
56
56
|
|
|
57
57
|
The `title` supplies the tooltip text. Tooltips appear on hover by default; `tooltipVisibility` can instead keep them visible continuously, while the camera is still, or hide them. The `showWhen` property controls visibility based on selection state. The default `['unselected']` hides hotspots once entities are selected, keeping the view clean. Setting it to `['selected', 'unselected']` keeps hotspots always visible.
|
|
58
58
|
|
|
59
|
+
The hotspot is the entity element `SelectionHotspot`. A project replaces its look by registering its own with `phx.frontend.createSelectionHotspot`, which receives `onSelect`, the selection with its analytics event; the settings above still apply. See [Configurator Interface → In-Scene Controls](?path=/docs/phoenix-configurator-interface--docs#in-scene-controls).
|
|
60
|
+
|
|
59
61
|
## System Component Interfaces
|
|
60
62
|
|
|
61
63
|
```typescript
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an element of an assistant form collects: a `group` all its children, a `choice` exactly one
|
|
3
|
+
* of its options, a `choices` any number of them; an `option` is something to pick and an `input` a
|
|
4
|
+
* free value.
|
|
5
|
+
*/
|
|
6
|
+
export type AssistantFormRole = 'group' | 'choice' | 'choices' | 'option' | 'input';
|
|
7
|
+
/** The type an attribute of a form element is validated and coerced to; the model writes every attribute as a string. */
|
|
8
|
+
export interface AssistantFormAttribute {
|
|
9
|
+
readonly type: 'string' | 'number' | 'integer' | 'boolean';
|
|
10
|
+
readonly enum?: readonly string[];
|
|
11
|
+
/** A JSON Schema `pattern` a string attribute must match, such as a resource path or a `#rrggbb` color. */
|
|
12
|
+
readonly pattern?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An element the assistant can write inside a ```` ```form ```` fence of its reply to ask the user
|
|
16
|
+
* something. The panel renders it, and the SDK prompt lists it, so the model and the panel agree on
|
|
17
|
+
* one set of elements.
|
|
18
|
+
*/
|
|
19
|
+
export interface AssistantFormElementDescriptor {
|
|
20
|
+
/** The tag: lowercase, and never the name of an HTML element. */
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly role: AssistantFormRole;
|
|
23
|
+
/** What the element is for, printed in the prompt after its signature. */
|
|
24
|
+
readonly description: string;
|
|
25
|
+
/** Attributes beyond the built-in `label` and, on an option or an input, `value`. */
|
|
26
|
+
readonly attributes: Readonly<Record<string, AssistantFormAttribute>>;
|
|
27
|
+
readonly required?: readonly string[];
|
|
28
|
+
/**
|
|
29
|
+
* The type the built-in `value` of an option or an input is validated and coerced to: a string
|
|
30
|
+
* by default, or `none` for an element that takes no `value`.
|
|
31
|
+
*/
|
|
32
|
+
readonly valueType?: 'number' | 'none';
|
|
33
|
+
/**
|
|
34
|
+
* An input the user answers with one action, such as a `confirm`: a form whose only question it
|
|
35
|
+
* is sends on that action, without a submit button, as a lone `choice` sends on the first pick.
|
|
36
|
+
*/
|
|
37
|
+
readonly quick?: true;
|
|
38
|
+
/**
|
|
39
|
+
* The attributes an option sends, tried in order, when it has neither `value` nor `label`. An
|
|
40
|
+
* option that declares them and has none of them, nor `value` or `label`, makes its form fail;
|
|
41
|
+
* its component shows its `value` as its name when it has no `label`.
|
|
42
|
+
*/
|
|
43
|
+
readonly identity?: readonly string[];
|
|
44
|
+
}
|
|
45
|
+
/** What an attribute's declaration makes of its value: one of its words, a string, a number or a boolean. */
|
|
46
|
+
export type AssistantFormAttributeValue<ATTRIBUTE extends AssistantFormAttribute> = ATTRIBUTE extends {
|
|
47
|
+
readonly enum: readonly (infer WORD)[];
|
|
48
|
+
} ? WORD : ATTRIBUTE['type'] extends 'string' ? string : ATTRIBUTE['type'] extends 'boolean' ? boolean : number;
|
|
49
|
+
type RequiredAttributeName<DESCRIPTOR> = DESCRIPTOR extends {
|
|
50
|
+
readonly required: readonly (infer NAME)[];
|
|
51
|
+
} ? NAME : never;
|
|
52
|
+
type StartingValueAttribute<DESCRIPTOR extends AssistantFormElementDescriptor> = DESCRIPTOR extends {
|
|
53
|
+
readonly role: 'input';
|
|
54
|
+
} ? DESCRIPTOR extends {
|
|
55
|
+
readonly valueType: 'none';
|
|
56
|
+
} ? Record<never, never> : {
|
|
57
|
+
readonly value?: DESCRIPTOR extends {
|
|
58
|
+
readonly valueType: 'number';
|
|
59
|
+
} ? number : string;
|
|
60
|
+
} : Record<never, never>;
|
|
61
|
+
/**
|
|
62
|
+
* The attributes a form element's component gets, typed from its descriptor: the declared ones,
|
|
63
|
+
* optional unless `required`, and an input's starting `value`. The form validates the markup
|
|
64
|
+
* against the same descriptor, so the component can rely on these types.
|
|
65
|
+
*/
|
|
66
|
+
export type AssistantFormElementAttributes<DESCRIPTOR extends AssistantFormElementDescriptor> = StartingValueAttribute<DESCRIPTOR> & {
|
|
67
|
+
readonly [NAME in keyof DESCRIPTOR['attributes'] as NAME extends RequiredAttributeName<DESCRIPTOR> ? NAME : never]: AssistantFormAttributeValue<DESCRIPTOR['attributes'][NAME]>;
|
|
68
|
+
} & {
|
|
69
|
+
readonly [NAME in keyof DESCRIPTOR['attributes'] as NAME extends RequiredAttributeName<DESCRIPTOR> ? never : NAME]?: AssistantFormAttributeValue<DESCRIPTOR['attributes'][NAME]>;
|
|
70
|
+
};
|
|
71
|
+
/** The elements every assistant form understands, in the order the prompt lists them. */
|
|
72
|
+
export declare const ASSISTANT_BUILT_IN_FORM_ELEMENTS: readonly [{
|
|
73
|
+
readonly name: 'group';
|
|
74
|
+
readonly role: 'group';
|
|
75
|
+
readonly description: 'Puts related questions together under its label.';
|
|
76
|
+
readonly attributes: {};
|
|
77
|
+
}, {
|
|
78
|
+
readonly name: 'choice';
|
|
79
|
+
readonly role: 'choice';
|
|
80
|
+
readonly description: 'The user picks exactly one of its options.';
|
|
81
|
+
readonly attributes: {};
|
|
82
|
+
}, {
|
|
83
|
+
readonly name: 'choices';
|
|
84
|
+
readonly role: 'choices';
|
|
85
|
+
readonly description: 'The user picks any number of its options.';
|
|
86
|
+
readonly attributes: {};
|
|
87
|
+
}, {
|
|
88
|
+
readonly name: 'reply';
|
|
89
|
+
readonly role: 'option';
|
|
90
|
+
readonly description: 'A short answer shown as a button.';
|
|
91
|
+
readonly attributes: {};
|
|
92
|
+
}, {
|
|
93
|
+
readonly name: 'card';
|
|
94
|
+
readonly role: 'option';
|
|
95
|
+
readonly description: 'A tile with an optional picture over its label and a one-line `description`. `image` is a path you read on an `imageUrl` field. Without `value` or `label` it sends its `description`, or else its `image`.';
|
|
96
|
+
readonly attributes: {
|
|
97
|
+
readonly description: {
|
|
98
|
+
readonly type: 'string';
|
|
99
|
+
};
|
|
100
|
+
readonly image: {
|
|
101
|
+
readonly type: 'string';
|
|
102
|
+
readonly pattern: string;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
readonly identity: readonly ["description", "image"];
|
|
106
|
+
}, {
|
|
107
|
+
readonly name: 'swatch';
|
|
108
|
+
readonly role: 'option';
|
|
109
|
+
readonly description: 'A small tile of a color or a material, without text: its label shows on hover. `color` is `#rrggbb`, as on the `color` field of an option, and `image` a path you read on an `imageUrl` field. Without `value` or `label` it sends its `color`, or else its `image`.';
|
|
110
|
+
readonly attributes: {
|
|
111
|
+
readonly color: {
|
|
112
|
+
readonly type: 'string';
|
|
113
|
+
readonly pattern: '^#[0-9a-fA-F]{6}$';
|
|
114
|
+
};
|
|
115
|
+
readonly image: {
|
|
116
|
+
readonly type: 'string';
|
|
117
|
+
readonly pattern: string;
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
readonly identity: readonly ["color", "image"];
|
|
121
|
+
}, {
|
|
122
|
+
readonly name: 'text';
|
|
123
|
+
readonly role: 'input';
|
|
124
|
+
readonly description: 'A single line the user types.';
|
|
125
|
+
readonly attributes: {
|
|
126
|
+
readonly placeholder: {
|
|
127
|
+
readonly type: 'string';
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
}, {
|
|
131
|
+
readonly name: 'number';
|
|
132
|
+
readonly role: 'input';
|
|
133
|
+
readonly description: 'A number the user types, with a slider when both `min` and `max` are given. The answer carries the `unit`, as in `Length: 240 cm`.';
|
|
134
|
+
readonly attributes: {
|
|
135
|
+
readonly min: {
|
|
136
|
+
readonly type: 'number';
|
|
137
|
+
};
|
|
138
|
+
readonly max: {
|
|
139
|
+
readonly type: 'number';
|
|
140
|
+
};
|
|
141
|
+
readonly step: {
|
|
142
|
+
readonly type: 'number';
|
|
143
|
+
};
|
|
144
|
+
readonly unit: {
|
|
145
|
+
readonly type: 'string';
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
readonly valueType: 'number';
|
|
149
|
+
}, {
|
|
150
|
+
readonly name: 'confirm';
|
|
151
|
+
readonly role: 'input';
|
|
152
|
+
readonly description: "A yes or no question, shown as two buttons. It answers with the user's own words for yes and no, such as `Yes` or `Ano`, and alone in a form it sends on the click.";
|
|
153
|
+
readonly attributes: {};
|
|
154
|
+
readonly valueType: 'none';
|
|
155
|
+
readonly quick: true;
|
|
156
|
+
}];
|
|
157
|
+
/** The name of a built-in form element. */
|
|
158
|
+
export type AssistantBuiltInFormElementName = (typeof ASSISTANT_BUILT_IN_FORM_ELEMENTS)[number]['name'];
|
|
159
|
+
/** The attributes the component of a built-in form element gets, typed from its descriptor. */
|
|
160
|
+
export type AssistantBuiltInFormElementAttributes<NAME extends AssistantBuiltInFormElementName> = AssistantFormElementAttributes<Extract<(typeof ASSISTANT_BUILT_IN_FORM_ELEMENTS)[number], {
|
|
161
|
+
readonly name: NAME;
|
|
162
|
+
}>>;
|
|
163
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ASSISTANT_RESOURCE_PATH_PATTERN as e}from"./assistant-resource-path.js";export const ASSISTANT_BUILT_IN_FORM_ELEMENTS=[{name:"group",role:"group",description:"Puts related questions together under its label.",attributes:{}},{name:"choice",role:"choice",description:"The user picks exactly one of its options.",attributes:{}},{name:"choices",role:"choices",description:"The user picks any number of its options.",attributes:{}},{name:"reply",role:"option",description:"A short answer shown as a button.",attributes:{}},{name:"card",role:"option",description:"A tile with an optional picture over its label and a one-line `description`. `image` is a path you read on an `imageUrl` field. Without `value` or `label` it sends its `description`, or else its `image`.",attributes:{description:{type:"string"},image:{type:"string",pattern:e}},identity:["description","image"]},{name:"swatch",role:"option",description:"A small tile of a color or a material, without text: its label shows on hover. `color` is `#rrggbb`, as on the `color` field of an option, and `image` a path you read on an `imageUrl` field. Without `value` or `label` it sends its `color`, or else its `image`.",attributes:{color:{type:"string",pattern:"^#[0-9a-fA-F]{6}$"},image:{type:"string",pattern:e}},identity:["color","image"]},{name:"text",role:"input",description:"A single line the user types.",attributes:{placeholder:{type:"string"}}},{name:"number",role:"input",description:"A number the user types, with a slider when both `min` and `max` are given. The answer carries the `unit`, as in `Length: 240 cm`.",attributes:{min:{type:"number"},max:{type:"number"},step:{type:"number"},unit:{type:"string"}},valueType:"number"},{name:"confirm",role:"input",description:"A yes or no question, shown as two buttons. It answers with the user's own words for yes and no, such as `Yes` or `Ano`, and alone in a form it sends on the click.",attributes:{},valueType:"none",quick:!0}];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A path into the project's resources, the kind stored on `imageUrl` fields: no scheme and no host,
|
|
3
|
+
* so resolving it never leaves the resources origin. A backslash, a control character or leading
|
|
4
|
+
* whitespace is refused too, because the URL parser reads a backslash as a slash and strips the
|
|
5
|
+
* others, which would turn `\\host` or ` //host` into an address on another host.
|
|
6
|
+
*
|
|
7
|
+
* Written as a JSON Schema `pattern`, so the `image` attribute of a form element applies the same
|
|
8
|
+
* rule as the images of a reply.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ASSISTANT_RESOURCE_PATH_PATTERN: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ASSISTANT_RESOURCE_PATH_PATTERN=String.raw`^(?![a-zA-Z][a-zA-Z0-9+.-]*:)(?!//)[^\s\\\x00-\x1f][^\\\x00-\x1f]*$`;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AssistantFormAttribute, AssistantFormElementDescriptor } from './assistant-form-elements.js';
|
|
2
|
+
/**
|
|
3
|
+
* The built-in `value` attribute an element takes, or `undefined` when it takes none: options and
|
|
4
|
+
* inputs take one, a string unless the element says otherwise.
|
|
5
|
+
*/
|
|
6
|
+
export declare const getAssistantFormValueAttribute: ({ role, valueType, }: AssistantFormElementDescriptor) => AssistantFormAttribute | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const getAssistantFormValueAttribute=({role:t,valueType:n})=>{if(("option"===t||"input"===t)&&"none"!==n)return{type:n??"string"}};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ASSISTANT_BUILT_IN_FORM_ELEMENTS } from './assistant-form-elements.js';
|
|
2
|
+
export type { AssistantBuiltInFormElementAttributes, AssistantBuiltInFormElementName, AssistantFormAttribute, AssistantFormAttributeValue, AssistantFormElementAttributes, AssistantFormElementDescriptor, AssistantFormRole, } from './assistant-form-elements.js';
|
|
3
|
+
export { ASSISTANT_RESOURCE_PATH_PATTERN } from './assistant-resource-path.js';
|
|
4
|
+
export { getAssistantFormValueAttribute } from './get-assistant-form-value-attribute.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{ASSISTANT_BUILT_IN_FORM_ELEMENTS}from"./assistant-form-elements.js";export{ASSISTANT_RESOURCE_PATH_PATTERN}from"./assistant-resource-path.js";export{getAssistantFormValueAttribute}from"./get-assistant-form-value-attribute.js";
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { AssistantManifest } from './assistant-manifest.js';
|
|
2
|
+
import { type AssistantFormElementDescriptor } from './form/index.js';
|
|
3
|
+
/** How the prompt writes an element: its tag with each attribute and what it takes. Exported for tests. */
|
|
4
|
+
export declare const formElementSignature: (element: AssistantFormElementDescriptor) => string;
|
|
2
5
|
/** SDK-version conventions for using the manifest's capabilities and reading their results. */
|
|
3
6
|
export declare const generateAssistantPrompt: (assistant: AssistantManifest) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ASSISTANT_BUILT_IN_FORM_ELEMENTS as e}from"./
|
|
1
|
+
import{ASSISTANT_BUILT_IN_FORM_ELEMENTS as e,getAssistantFormValueAttribute as t}from"./form/index.js";let a=[["Containers",["group","choice","choices"]],["Options",["option"]],["Inputs",["input"]]],n=(e,{type:t,enum:a})=>a?`${e}="${a.join("|")}"`:"string"===t?e:`${e}="<${t}>"`;export const formElementSignature=e=>{let{name:a,role:s,attributes:i}=e,o=t(e),r=["label",...o?[n("value",o)]:[],...Object.entries(i).map(([e,t])=>n(e,t))];return"option"===s||"input"===s?`<${a} ${r.join(" ")} />`:`<${a} ${r.join(" ")}>…</${a}>`};export const generateAssistantPrompt=t=>{let n=Object.values(t.childRecipes??{}).some(e=>e.length>0),s=Object.values(t.replaceRecipes??{}).some(e=>e.length>0),i=Object.values(t.fieldOptions??{}).some(e=>e.length>0),o=Object.keys(t.actions??{}).length>0,r=(t.deletableTypes?.length??0)>0||Object.values(t.writableFields??{}).some(e=>Object.keys(e).length>0)||n||s||o,h=n||s||i,l=[...n?["`childRecipes` is what can be added to it"]:[],...s?["`replaceRecipes` what it can be replaced with"]:[],...i?["`<field>Options` the values a field accepts"]:[]],d=["`selected`",...(t.navigationFields??[]).map(e=>`\`${e}\``)].join(", ");return[["Tools","- Put related operations in one call and select only the fields you need.",...r?[`- Use a mutation inside \`query\` to answer a what-if about a change ${h?"the lists under `meta` do not show":"before making it"}.`,"- Use `apply` only for what the user asked for."]:["- Nothing in this configurator can be changed through you. Say so when the user asks for a change."],"- When an operation fails, its `errors` say why and what to send instead."],...r?[["After a change","- A change is made only when the `apply` result has `committed: true`. Otherwise `errors` or `message` says what happened; without either, nothing changed. Never say a change was made when it was not.","- `sideEffects` names the major or unexpected things the configurator changed along with your change. Tell the user about them when committed; otherwise they describe what would happen. Smaller adjustments are not listed.","- A mutation returns the state after the change: use the ids and values it returned, not the ones you sent.",...o?["- When one mutation does the whole of what the user asked, use it instead of building the same change from several others."]:[]]]:[],...h?[["What can be done to an entity",`- \`meta\` lists what is possible right now: ${l.join(", ")}. Read the list before such a change and before telling the user what is available.`,`- You can select \`dryRun\` on ${[...n||s?["recipes"]:[],...i?["options"]:[]].join(" or ")} to look ahead and pick one that meets what the user asked for. It is optional before a change.`,"- Read the lists again after a change, unless their descriptions say they stay the same."]]:[],["The `[changes]` block","- Every user message starts with it: a glimpse of what the user did in the configurator since your last turn.",`- \`[changes: none]\`, or only ${d} lines: what you know still holds. Do not query it again.`,"- Other lines are a hint at what to read again: `A` added, `D` deleted, `M` modified with the fields. Text after `#` says what caused the line; `reset: undo/redo` says the user undid or redid changes.","- Read computed fields again when a listed change may affect them.","- `[changes: unavailable]` and `[changes: too many (n)]`: read again before relying on anything you read earlier.",...r?["- Your applied changes are not listed, except those waiting for user approval: they appear once approved."]:[]],["Reading the schema","- Take ids from results.",...(t.deletableTypes?.length??0)>0||s?["- The id of a deleted or replaced entity is gone and may later be given to another entity."]:[],`- A field whose name starts with \`_\` is computed: read it${r?", never write it":""}.`,"- Numbers are in the unit the field description names. Convert when the user uses another unit."],["Links","- You may link an entity of the current configuration when you speak about that very entity: `[the left burner](entity:CookingTop-1)`. It helps the user find it in the configurator.",...h?["- Whenever you name recipes or options you read from `meta`, in a list, a table or a sentence, link each one: `[Single gas burner](recipe:KitchenRange-1/top/gas_single)` or `[Oak](option:Cabinet-2/finishOptions/oak)`. The link shows the user its picture or color and its description, so never draw previews yourself. The target is the entity id, the recipe group id or the options field you read, and the key or value, each encoded as in a URL."]:[]],["Replies","- Write Markdown only, never HTML: the panel does not render it.","- An image, ``, is shown only when its path is relative.","- To make a warning or a key note stand out, write a GitHub alert: a blockquote whose first line is only `[!WARNING]` or `[!NOTE]`. Use it sparingly."],["Forms","- To ask the user something they answer by picking or typing, end your reply with a form: a fenced code block with the language `form`. It holds only elements, written as lowercase tags with string attributes, without Markdown or text.","- Every element takes `label`, the text the user sees; on a container or an input it also names the answer. An option or an input whose signature lists `value` takes it: an option sends it instead of its label, and an input starts with it.",'- A bare attribute in a signature takes any text, `name="<type>"` takes a value of that type written as text, such as `min="<number>"`, and `name="a|b"` takes one of the words listed.',"- Options can also stand on their own, outside a `choice` or `choices`.","- An element or attribute not listed here, or an element other than an option inside a `choice` or `choices`, makes the whole form fail.","- The answer is the user's next message. Each container or input gives a line `Label: value`, or the bare value without a label; the lines of a labelled container are indented by two spaces under it, a `choices` lists each picked option on its own line, and `—` marks what was left unanswered.",'- Example: `<choice label="Plinth"><reply label="Black" /><reply label="Oak" /></choice>` is answered with `Plinth: Oak`.',"- The chat shows the answer as the user's own message, so a `value` reads as natural words, never as an id, key or code. Give `value` only when the label alone would be unclear as an answer, and map the answer's labels back to ids yourself.",...a.flatMap(([t,a])=>{let n=e.filter(({role:e})=>a.includes(e));return 0===n.length?[]:[`- ${t}:`,...n.map(e=>` - \`${formElementSignature(e)}\`: ${e.description}`)]})],...t.lenses?.declarations?[["Built-in fragments","- These fragments are already defined for every operation. Spread one by name on a field of its type, as in `field { ...FragmentName }`, instead of listing its fields.","- A fragment is a starting selection: add the other fields you need next to the spread, by name from the schema.","- Never define a fragment with one of these names.","",t.lenses.declarations]]:[]].map(e=>e.join("\n")).join("\n\n")};
|
|
@@ -7,5 +7,4 @@ export type { AssistantFieldSource, AssistantRecipeGroup, AssistantFieldOptions,
|
|
|
7
7
|
export { generateAssistantPrompt } from './generate-assistant-prompt.js';
|
|
8
8
|
export * from './session-setup/index.js';
|
|
9
9
|
export { describeAssistantValues, mergeAssistantFields, resolveAssistantField } from './assistant-fields.js';
|
|
10
|
-
export
|
|
11
|
-
export type { AssistantFormAttribute, AssistantFormElementDescriptor, AssistantFormRole, } from './assistant-form-elements.js';
|
|
10
|
+
export * from './form/index.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{ASSISTANT_HINTS}from"./assistant-resolved-manifest.js";export{buildAssistantManifest}from"./build-assistant-manifest.js";export{AssistantFieldClashError,createAssistantExecutionSchema}from"./create-assistant-execution-schema.js";export{generateAssistantPrompt}from"./generate-assistant-prompt.js";export*from"./session-setup/index.js";export{describeAssistantValues,mergeAssistantFields,resolveAssistantField}from"./assistant-fields.js";export
|
|
1
|
+
export{ASSISTANT_HINTS}from"./assistant-resolved-manifest.js";export{buildAssistantManifest}from"./build-assistant-manifest.js";export{AssistantFieldClashError,createAssistantExecutionSchema}from"./create-assistant-execution-schema.js";export{generateAssistantPrompt}from"./generate-assistant-prompt.js";export*from"./session-setup/index.js";export{describeAssistantValues,mergeAssistantFields,resolveAssistantField}from"./assistant-fields.js";export*from"./form/index.js";
|