@salsita-ai/sdk 0.50.0-develop.2f8d91de91 → 0.50.0-develop.30ea3b5e1a

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 (92) hide show
  1. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  2. package/dist/application-spec/generate-application/code/standalone-app-files.d.ts +2 -2
  3. package/dist/application-spec/generate-application/code/standalone-app-files.js +1 -1
  4. package/dist/application-spec/generate-application/code/templates/index.d.ts +0 -1
  5. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  6. package/dist/application-spec/generate-application/code/templates/package-json.js +1 -1
  7. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +1 -1
  8. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +1 -1
  9. package/dist/application-spec/generate-application/code/templates/webhook-index.d.ts +6 -1
  10. package/dist/application-spec/generate-application/code/templates/webhook-index.js +10 -1
  11. package/dist/application-spec/generate-application/index.d.ts +2 -0
  12. package/dist/application-spec/generate-application/index.js +1 -1
  13. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.d.ts +6 -2
  14. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.js +1 -1
  15. package/dist/application-spec/generate-application/runtime/application-runtime.d.ts +8 -1
  16. package/dist/application-spec/generate-application/runtime/application-runtime.js +1 -1
  17. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +5 -1
  18. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
  19. package/dist/application-spec/generate-application/runtime/factory-data/factory-data-runtime.d.ts +43 -0
  20. package/dist/application-spec/generate-application/runtime/factory-data/factory-data-runtime.js +1 -0
  21. package/dist/application-spec/generate-application/runtime/factory-data/index.d.ts +3 -0
  22. package/dist/application-spec/generate-application/runtime/factory-data/index.js +1 -0
  23. package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.d.ts +13 -0
  24. package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.js +1 -0
  25. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.d.ts +16 -0
  26. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.js +1 -0
  27. package/dist/application-spec/generate-application/runtime/migrations/index.d.ts +1 -0
  28. package/dist/application-spec/generate-application/runtime/migrations/index.js +1 -0
  29. package/dist/application-spec/generate-application/runtime/migrations/migration-runtime.d.ts +53 -0
  30. package/dist/application-spec/generate-application/runtime/migrations/migration-runtime.js +1 -0
  31. package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +6 -0
  32. package/dist/application-spec/parse-application-files/categorize/categorize-files.js +1 -1
  33. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +29 -0
  34. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  35. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +7 -1
  36. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
  37. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +6 -0
  38. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  39. package/dist/configurator/common/configurator-interface.d.ts +10 -1
  40. package/dist/configurator/common/configurator-interface.js +1 -1
  41. package/dist/configurator/common/index.d.ts +1 -0
  42. package/dist/configurator/common/index.js +1 -1
  43. package/dist/configurator/common/use-finalize-action.d.ts +5 -1
  44. package/dist/configurator/common/use-finalize-action.js +1 -1
  45. package/dist/configurator/common/use-reset-camera.d.ts +6 -0
  46. package/dist/configurator/common/use-reset-camera.js +1 -0
  47. package/dist/configurator/ui/assistant/AssistantChips.d.ts +8 -2
  48. package/dist/configurator/ui/assistant/AssistantChips.js +1 -1
  49. package/dist/configurator/ui/assistant/AssistantThread.js +1 -1
  50. package/dist/docs/index.json +1 -1
  51. package/dist/docs/layouts/custom-layout.mdx +886 -161
  52. package/dist/docs/phoenix/configurator-interface.mdx +18 -10
  53. package/dist/docs/phoenix/layout-slots.mdx +4 -2
  54. package/dist/docs/phoenix/phoenix-cli.mdx +20 -6
  55. package/dist/docs/phoenix/project-structure.mdx +8 -3
  56. package/dist/docs/phoenix/sdk-builder-workflow.mdx +1 -1
  57. package/dist/docs/phoenix/webhooks.mdx +17 -0
  58. package/dist/domain/domain-context/index.d.ts +1 -1
  59. package/dist/domain/domain-context/undo-system.d.ts +28 -1
  60. package/dist/domain/domain-context/undo-system.js +1 -1
  61. package/dist/i18n/locales/cs/messages.js +1 -1
  62. package/dist/i18n/locales/da/messages.js +1 -1
  63. package/dist/i18n/locales/de/messages.js +1 -1
  64. package/dist/i18n/locales/de-AT/messages.js +1 -1
  65. package/dist/i18n/locales/en/messages.js +1 -1
  66. package/dist/i18n/locales/en-AU/messages.js +1 -1
  67. package/dist/i18n/locales/en-GB/messages.js +1 -1
  68. package/dist/i18n/locales/es/messages.js +1 -1
  69. package/dist/i18n/locales/fr/messages.js +1 -1
  70. package/dist/i18n/locales/hu/messages.js +1 -1
  71. package/dist/i18n/locales/it/messages.js +1 -1
  72. package/dist/i18n/locales/nl/messages.js +1 -1
  73. package/dist/i18n/locales/pl/messages.js +1 -1
  74. package/dist/i18n/locales/pt/messages.js +1 -1
  75. package/dist/i18n/locales/ro/messages.js +1 -1
  76. package/dist/i18n/locales/sk/messages.js +1 -1
  77. package/dist/i18n/locales/tr/messages.js +1 -1
  78. package/dist/phoenix/application-spec/application-spec-builder.js +1 -1
  79. package/dist/phoenix/commands/check.js +1 -1
  80. package/dist/phoenix/commands/dev.js +5 -5
  81. package/dist/phoenix/commands/fix.js +1 -1
  82. package/dist/phoenix/services/application-spec-project-watcher.d.ts +2 -0
  83. package/dist/phoenix/services/application-spec-project-watcher.js +3 -3
  84. package/dist/phoenix/services/swc.js +1 -1
  85. package/dist/react-sdk/domain-context/index.d.ts +1 -1
  86. package/dist/react-sdk/domain-context/index.js +1 -1
  87. package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.d.ts +5 -0
  88. package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.js +1 -1
  89. package/dist/ui-sdk/theme/types.d.ts +1 -1
  90. package/package.json +33 -29
  91. package/dist/application-spec/generate-application/code/templates/webhook-implementation.d.ts +0 -1
  92. package/dist/application-spec/generate-application/code/templates/webhook-implementation.js +0 -7
@@ -1 +1 @@
1
- import{ChatClient as e,fetchServerSentEvents as t}from"@tanstack/ai-client";import{BehaviorSubject as i}from"rxjs";import{skip as s}from"rxjs/operators";import{isShallowEqual as a}from"@salsita-ai/common/utils";import{AssistantRequestError as r}from"#package/client";import{createBrowserDetailsClientTool as n}from"./browser-details.js";import{ChangeRecorder as h}from"./change-recorder.js";import{createApplyClientTool as o,createQueryClientTool as l}from"./graphql-tools.js";let c=Object.freeze({messages:Object.freeze([]),isLoading:!1,error:void 0,failedStatus:void 0,isContinuable:!0,isLoaded:!1,isResuming:!1,earlierThreads:Object.freeze([]),hasEarlierThread:!1,isLoadingEarlierThread:!1}),d=Object.freeze({open:!1,hasUnreadReply:!1,isInputFocused:!1}),u=Object.freeze({}),_=Object.freeze({results:Object.freeze([]),committed:!1,sideEffects:Object.freeze([]),message:"Nothing was run, because the user closed the configurator while this reply was being written. Do not call any more tools in this reply and end it now. The tools work again as soon as the user writes the next message."}),g=e=>{if(e.getError())return!0;let t=e.getMessages().at(-1)?.parts.at(-1);return t?.type!=="tool-call"&&t?.type!=="tool-result"};export class AssistantSystem{_context;_tools;_chat=new i(c);_panel=new i(d);_activation=null;_current=null;_finishing=new Set;_olderThreadId=null;_resuming=null;_refused=null;_firstAttempt=null;_chain={};_loadingThreadId=null;_recorder;_recording=null;constructor(e){this._context=e;let{configuratorExtensionSelectors:t,domainContextSelectors:i,domainServicesSelectors:s,getAssistantSetup:a}=e;this._recorder=new h({maxEntities:30},{readers:{selectedEntityId:e=>i.getNavigationSystem().getActiveSelectionState(e),navigationState:e=>i.getNavigationSystem().getActiveNavigationState(e),viewState:e=>i.getNavigationSystem().getActiveViewState(e),toolState:e=>i.getNavigationSystem().getActiveToolState(e)},getExposedFields:()=>a().manifest?.fields??u,resolveLocalizable:e=>s.getResolveLocalizable()(e)});let r={getGraphQLSystem:()=>t.getGraphQLSystem(),getStoreSystem:()=>i.getStoreSystem(),getActiveMode:()=>i.getActiveMode(),clearChanges:()=>this._recorder.clear(this._committedState())};this._tools=[n(),l(r),o(r)]}chat$=this._chat;panel$=this._panel;initialize=()=>{let{committed:e}=this._context.domainContextSelectors.getStoreSystem().stateSlices;this._recording??=e.pipe(s(1)).subscribe(this._recorder.record),this._recorder.reset(e.value.state),this._endChat(),this._patchPanel(d),this._activation&&this._startClient()};activate=()=>{this._endChat();let e={};return this._activation=e,this._startClient(),()=>{this._activation===e&&(this._activation=null,this._endChat())}};send=async e=>{if(!this._chat.value.isContinuable&&!await this.resume())return;let t=this._current;if(!t)return;let i={changes:this._recorder.take(this._committedState())};this._firstAttempt=t;try{await this._sendOn(t,e,i);let s=this._findRefusedMessageIndex(t);if(-1===s)return;this._refused={chat:t,index:s}}finally{this._firstAttempt===t&&(this._firstAttempt=null)}if(!await this.resume())return;let s=this._current;s&&await this._sendOn(s,e,i)};resume=async()=>{let e=this._current;if(!e)return!1;if(this._chat.value.isContinuable)return!0;if(this._resuming)return this._resuming;let t=this._resume(e).finally(()=>{this._resuming===t&&(this._resuming=null)});return this._resuming=t,t};loadEarlierThread=async()=>{let e=this._current,t=this._olderThreadId;if(!e||null===t||this._loadingThreadId===t)return;let i=this._chain;this._loadingThreadId=t,this._chat.next({...this._chat.value,isLoadingEarlierThread:!0});try{let s=await e.authorizedClient.getAssistantEarlierThread(e.configurationId,t,50);if(this._chain!==i||this._olderThreadId!==t)return;this._olderThreadId=s.previousThreadId,this._chat.next({...this._chat.value,earlierThreads:[{threadId:t,messages:s.messages},...this._chat.value.earlierThreads],hasEarlierThread:null!==s.previousThreadId})}catch(e){this._context.domainServicesSelectors.getLogger().warn("Could not load the earlier assistant conversation",{error:e})}finally{this._chain===i&&this._loadingThreadId===t&&(this._loadingThreadId=null,this._chat.next({...this._chat.value,isLoadingEarlierThread:!1}))}};stop=async()=>{let e=this._current;if(!e)return;let t=e.client.getCurrentRunId();if(e.client.stop(),null!=t)try{let i=e.getConnection();await fetch(`${i.url}/runs/${encodeURIComponent(t)}/cancel`,{method:"POST",headers:await i.headers()})}catch(e){this._context.domainServicesSelectors.getLogger().warn("Could not cancel the assistant run on the server",{error:e})}};reloadThread=()=>{if(!this._activation)return;let e=this._retireChat();e?.client.stop(),e?.dispose(),this._recorder.reset(this._committedState()),this._startClient()};setOpen=e=>{this._patchPanel(e?{open:e,hasUnreadReply:!1}:{open:e,isInputFocused:!1})};setInputFocused=e=>{this._patchPanel({isInputFocused:e})};_findRefusedMessageIndex=e=>{var t;let i,{failedStatus:s,isContinuable:a}=this._chat.value;return this._firstAttempt===e&&e.generation.active&&409===s&&!a?(i=(t=e.client.getMessages()).map(e=>e.role).lastIndexOf("user"),t.slice(i+1).every(e=>0===e.parts.length)?i:-1):-1};_sendOn=async(e,t,i)=>{let{client:s}=e;s.getIsLoading()||null===s.getResumeState()||s.stop();try{await s.sendMessage({content:t,metadata:i})}catch(i){let t=i instanceof Error?i:Error(String(i));e.generation.active&&this._chat.next({...this._chat.value,error:t}),this._context.domainServicesSelectors.getLogger().error("The assistant message could not be sent",{},t)}};_resume=async e=>{this._chat.next({...this._chat.value,isResuming:!0,error:void 0,failedStatus:void 0});try{let{previousThreadId:t}=await e.authorizedClient.compactAssistantThread(e.configurationId);if(!e.generation.active)return!1;let{messages:i,earlierThreads:s,hasEarlierThread:a}=this._chat.value,r=this._refused?.chat===e?i.slice(0,this._refused.index):i;return e.generation.active=!1,this._current=null,e.client.stop(),e.dispose(),this._chat.next({...c,earlierThreads:[...s,{threadId:t,messages:r}],hasEarlierThread:a,isLoadingEarlierThread:null!==this._loadingThreadId}),this._startClient(),!0}catch(i){let t=i instanceof Error?i:Error(String(i));if(t instanceof r&&409===t.status)return e.generation.active&&this.reloadThread(),!1;return e.generation.active&&this._chat.next({...this._chat.value,isResuming:!1,error:t,failedStatus:t instanceof r?t.status:void 0}),this._context.domainServicesSelectors.getLogger().error("The assistant thread could not be resumed",{},t),!1}};_committedState=()=>this._context.domainContextSelectors.getStoreSystem().stateSlices.committed.value.state;_patchPanel=e=>{let t={...this._panel.value,...e};a(t,this._panel.value)||this._panel.next(t)};_takeOverThread=e=>{for(let t of this._finishing)t.configurationId===e&&(t.client.stop(),t.dispose())};_startClient=()=>{let i={active:!0},s=this._context.getConfigurationId();this._takeOverThread(s);let r=this._context.getAuthorizedClient(),n=()=>{if(i.active){let{manifest:e,error:t}=this._context.getAssistantSetup();if(t)throw t;if(!e)throw Error("The assistant is disabled for this session.")}return r.getAssistantChatConnection(s)},h=e=>{if(!i.active)return;let t={...this._chat.value,...e};a(t,this._chat.value)||this._chat.next(t)},o=async(e,t)=>{let s=await fetch(e,t).finally(()=>h({isLoaded:!0})),a=s.headers.get("x-assistant-thread-continuable");return null!=a&&s.ok&&i.active&&0===this._chat.value.earlierThreads.length&&(this._olderThreadId=s.headers.get("x-assistant-previous-thread")),h({failedStatus:s.ok?void 0:s.status,...null!=a&&{isContinuable:"false"!==a,hasEarlierThread:null!==this._olderThreadId}}),s},l=async()=>({headers:await n().headers(),fetchClient:o}),c=t(()=>n().url,l),d=t(()=>`${n().url}/runs`,l),u=this._tools.map(e=>({...e,execute:(t,s)=>{if(!i.active)return _;let{manifest:a,error:r}=this._context.getAssistantSetup();return r||!a?{error:r?.message??"The assistant is disabled for this session."}:e.execute?.(t,s)}})),f=new e({threadId:`assistant:${s}`,connection:{...c,joinRun:d.joinRun},persistence:!0,history:{pageSize:50},tools:u,onMessagesChange:e=>h({messages:e}),onLoadingChange:e=>{let t=i.active&&this._chat.value.isLoading;h({isLoading:e}),!e&&g(f)&&(i.active?t&&!this._panel.value.open&&this._patchPanel({hasUnreadReply:!0}):v())},onErrorChange:e=>{i.active&&e!==this._chat.value.error&&(!e||-1===this._findRefusedMessageIndex(p))&&(h({error:e}),e&&this._context.domainServicesSelectors.getLogger().error("The assistant request failed",{status:this._chat.value.failedStatus},e))}}),m=!1,v=()=>{m||(m=!0,this._finishing.delete(p),f.dispose())},p={client:f,configurationId:s,generation:i,authorizedClient:r,getConnection:n,dispose:v};this._current=p,f.attach()};_retireChat=()=>{let e=this._current;return this._current=null,e&&(e.generation.active=!1),this._chain={},this._olderThreadId=null,this._resuming=null,this._refused=null,this._loadingThreadId=null,this._chat.value!==c&&this._chat.next(c),e};_endChat=()=>{let e=this._retireChat();if(e){if(!e.client.getIsLoading()&&g(e.client))return void e.dispose();this._finishing.add(e)}}}
1
+ import{ChatClient as e,fetchServerSentEvents as t}from"@tanstack/ai-client";import{BehaviorSubject as i}from"rxjs";import{skip as s}from"rxjs/operators";import{isShallowEqual as a}from"@salsita-ai/common/utils";import{AssistantRequestError as r}from"#package/client";import{createBrowserDetailsClientTool as n}from"./browser-details.js";import{ChangeRecorder as o}from"./change-recorder.js";import{createApplyClientTool as l,createQueryClientTool as h}from"./graphql-tools.js";let d=Object.freeze({messages:Object.freeze([]),isLoading:!1,error:void 0,failedStatus:void 0,isContinuable:!0,isLoaded:!1,isResuming:!1,earlierThreads:Object.freeze([]),hasEarlierThread:!1,isLoadingEarlierThread:!1}),c=Object.freeze({open:!1,hasUnreadReply:!1,isInputFocused:!1}),u=Object.freeze({}),_=Object.freeze({results:Object.freeze([]),committed:!1,sideEffects:Object.freeze([]),message:"Nothing was run, because the user closed the configurator while this reply was being written. Do not call any more tools in this reply and end it now. The tools work again as soon as the user writes the next message."}),g=e=>{if(e.getError())return!0;let t=e.getMessages().at(-1)?.parts.at(-1);return t?.type!=="tool-call"&&t?.type!=="tool-result"};export class AssistantSystem{_context;_tools;_chat=new i(d);_panel=new i(c);_activation=null;_current=null;_finishing=new Set;_olderThreadId=null;_resuming=null;_refused=null;_firstAttempt=null;_chain={};_loadingThreadId=null;_recorder;_recording=null;_applyToolCallIds=new WeakMap;_undoTipToolCallId=new i(void 0);constructor(e){this._context=e;let{configuratorExtensionSelectors:t,domainContextSelectors:i,domainServicesSelectors:s,getAssistantSetup:a}=e;this._recorder=new o({maxEntities:30},{readers:{selectedEntityId:e=>i.getNavigationSystem().getActiveSelectionState(e),navigationState:e=>i.getNavigationSystem().getActiveNavigationState(e),viewState:e=>i.getNavigationSystem().getActiveViewState(e),toolState:e=>i.getNavigationSystem().getActiveToolState(e)},getExposedFields:()=>a().manifest?.fields??u,resolveLocalizable:e=>s.getResolveLocalizable()(e)});let r={getGraphQLSystem:()=>t.getGraphQLSystem(),getStoreSystem:()=>i.getStoreSystem(),getActiveMode:()=>i.getActiveMode(),clearChanges:()=>this._recorder.clear(this._committedState()),rememberCommit:(e,t)=>{this._applyToolCallIds.set(t,e),this._refreshUndoTip()}};this._tools=[n(),h(r),l(r)],i.getUndoSystem().tip$.subscribe(this._refreshUndoTip)}chat$=this._chat;panel$=this._panel;undoTipToolCallId$=this._undoTipToolCallId;initialize=()=>{let{committed:e}=this._context.domainContextSelectors.getStoreSystem().stateSlices;this._recording??=e.pipe(s(1)).subscribe(this._recorder.record),this._recorder.reset(e.value.state),this._endChat(),this._patchPanel(c),this._activation&&this._startClient()};activate=()=>{this._endChat();let e={};return this._activation=e,this._startClient(),()=>{this._activation===e&&(this._activation=null,this._endChat())}};send=async e=>{if(!this._chat.value.isContinuable&&!await this.resume())return;let t=this._current;if(!t)return;let i={changes:this._recorder.take(this._committedState())};this._firstAttempt=t;try{await this._sendOn(t,e,i);let s=this._findRefusedMessageIndex(t);if(-1===s)return;this._refused={chat:t,index:s}}finally{this._firstAttempt===t&&(this._firstAttempt=null)}if(!await this.resume())return;let s=this._current;s&&await this._sendOn(s,e,i)};resume=async()=>{let e=this._current;if(!e)return!1;if(this._chat.value.isContinuable)return!0;if(this._resuming)return this._resuming;let t=this._resume(e).finally(()=>{this._resuming===t&&(this._resuming=null)});return this._resuming=t,t};loadEarlierThread=async()=>{let e=this._current,t=this._olderThreadId;if(!e||null===t||this._loadingThreadId===t)return;let i=this._chain;this._loadingThreadId=t,this._chat.next({...this._chat.value,isLoadingEarlierThread:!0});try{let s=await e.authorizedClient.getAssistantEarlierThread(e.configurationId,t,50);if(this._chain!==i||this._olderThreadId!==t)return;this._olderThreadId=s.previousThreadId,this._chat.next({...this._chat.value,earlierThreads:[{threadId:t,messages:s.messages},...this._chat.value.earlierThreads],hasEarlierThread:null!==s.previousThreadId})}catch(e){this._context.domainServicesSelectors.getLogger().warn("Could not load the earlier assistant conversation",{error:e})}finally{this._chain===i&&this._loadingThreadId===t&&(this._loadingThreadId=null,this._chat.next({...this._chat.value,isLoadingEarlierThread:!1}))}};stop=async()=>{let e=this._current;if(!e)return;let t=e.client.getCurrentRunId();if(e.client.stop(),null!=t)try{let i=e.getConnection();await fetch(`${i.url}/runs/${encodeURIComponent(t)}/cancel`,{method:"POST",headers:await i.headers()})}catch(e){this._context.domainServicesSelectors.getLogger().warn("Could not cancel the assistant run on the server",{error:e})}};reloadThread=()=>{if(!this._activation)return;let e=this._retireChat();e?.client.stop(),e?.dispose(),this._recorder.reset(this._committedState()),this._startClient()};undoToolCalls=e=>{let t=this._context.domainContextSelectors.getUndoSystem(),i=()=>{let t=this._findTipToolCallId();return void 0!==t&&e.has(t)};for(;i();)t.undo()};setOpen=e=>{this._patchPanel(e?{open:e,hasUnreadReply:!1}:{open:e,isInputFocused:!1})};setInputFocused=e=>{this._patchPanel({isInputFocused:e})};_findRefusedMessageIndex=e=>{var t;let i,{failedStatus:s,isContinuable:a}=this._chat.value;return this._firstAttempt===e&&e.generation.active&&409===s&&!a?(i=(t=e.client.getMessages()).map(e=>e.role).lastIndexOf("user"),t.slice(i+1).every(e=>0===e.parts.length)?i:-1):-1};_sendOn=async(e,t,i)=>{let{client:s}=e;s.getIsLoading()||null===s.getResumeState()||s.stop();try{await s.sendMessage({content:t,metadata:i})}catch(i){let t=i instanceof Error?i:Error(String(i));e.generation.active&&this._chat.next({...this._chat.value,error:t}),this._context.domainServicesSelectors.getLogger().error("The assistant message could not be sent",{},t)}};_resume=async e=>{this._chat.next({...this._chat.value,isResuming:!0,error:void 0,failedStatus:void 0});try{let{previousThreadId:t}=await e.authorizedClient.compactAssistantThread(e.configurationId);if(!e.generation.active)return!1;let{messages:i,earlierThreads:s,hasEarlierThread:a}=this._chat.value,r=this._refused?.chat===e?i.slice(0,this._refused.index):i;return e.generation.active=!1,this._current=null,e.client.stop(),e.dispose(),this._chat.next({...d,earlierThreads:[...s,{threadId:t,messages:r}],hasEarlierThread:a,isLoadingEarlierThread:null!==this._loadingThreadId}),this._startClient(),!0}catch(i){let t=i instanceof Error?i:Error(String(i));if(t instanceof r&&409===t.status)return e.generation.active&&this.reloadThread(),!1;return e.generation.active&&this._chat.next({...this._chat.value,isResuming:!1,error:t,failedStatus:t instanceof r?t.status:void 0}),this._context.domainServicesSelectors.getLogger().error("The assistant thread could not be resumed",{},t),!1}};_findTipToolCallId=()=>{let e=this._context.domainContextSelectors.getUndoSystem(),t=e.tip$.value?.committedState;return void 0!==t&&e.canUndo()?this._applyToolCallIds.get(t):void 0};_refreshUndoTip=()=>{let e=this._findTipToolCallId();e!==this._undoTipToolCallId.value&&this._undoTipToolCallId.next(e)};_committedState=()=>this._context.domainContextSelectors.getStoreSystem().stateSlices.committed.value.state;_patchPanel=e=>{let t={...this._panel.value,...e};a(t,this._panel.value)||this._panel.next(t)};_takeOverThread=e=>{for(let t of this._finishing)t.configurationId===e&&(t.client.stop(),t.dispose())};_startClient=()=>{let i={active:!0},s=this._context.getConfigurationId();this._takeOverThread(s);let r=this._context.getAuthorizedClient(),n=()=>{if(i.active){let{manifest:e,error:t}=this._context.getAssistantSetup();if(t)throw t;if(!e)throw Error("The assistant is disabled for this session.")}return r.getAssistantChatConnection(s)},o=e=>{if(!i.active)return;let t={...this._chat.value,...e};a(t,this._chat.value)||this._chat.next(t)},l=async(e,t)=>{let s=await fetch(e,t).finally(()=>o({isLoaded:!0})),a=s.headers.get("x-assistant-thread-continuable");return null!=a&&s.ok&&i.active&&0===this._chat.value.earlierThreads.length&&(this._olderThreadId=s.headers.get("x-assistant-previous-thread")),o({failedStatus:s.ok?void 0:s.status,...null!=a&&{isContinuable:"false"!==a,hasEarlierThread:null!==this._olderThreadId}}),s},h=async()=>({headers:await n().headers(),fetchClient:l}),d=t(()=>n().url,h),c=t(()=>`${n().url}/runs`,h),u=this._tools.map(e=>({...e,execute:(t,s)=>{if(!i.active)return _;let{manifest:a,error:r}=this._context.getAssistantSetup();return r||!a?{error:r?.message??"The assistant is disabled for this session."}:e.execute?.(t,s)}})),m=new e({threadId:`assistant:${s}`,connection:{...d,joinRun:c.joinRun},persistence:!0,history:{pageSize:50},tools:u,onMessagesChange:e=>o({messages:e}),onLoadingChange:e=>{let t=i.active&&this._chat.value.isLoading;o({isLoading:e}),!e&&g(m)&&(i.active?t&&!this._panel.value.open&&this._patchPanel({hasUnreadReply:!0}):p())},onErrorChange:e=>{i.active&&e!==this._chat.value.error&&(!e||-1===this._findRefusedMessageIndex(v))&&(o({error:e}),e&&this._context.domainServicesSelectors.getLogger().error("The assistant request failed",{status:this._chat.value.failedStatus},e))}}),f=!1,p=()=>{f||(f=!0,this._finishing.delete(v),m.dispose())},v={client:m,configurationId:s,generation:i,authorizedClient:r,getConnection:n,dispose:p};this._current=v,m.attach()};_retireChat=()=>{let e=this._current;return this._current=null,e&&(e.generation.active=!1),this._chain={},this._olderThreadId=null,this._resuming=null,this._refused=null,this._loadingThreadId=null,this._chat.value!==d&&this._chat.next(d),e};_endChat=()=>{let e=this._retireChat();if(e){if(!e.client.getIsLoading()&&g(e.client))return void e.dispose();this._finishing.add(e)}}}
@@ -20,6 +20,11 @@ export interface AssistantToolDependencies {
20
20
  readonly getActiveMode: () => DomainMode;
21
21
  /** Forgets the changes recorded for the next change record, so it never lists what the agent itself changed. */
22
22
  readonly clearChanges: () => void;
23
+ /**
24
+ * Remembers the state an `apply` call committed or held for the user's approval, so the panel can
25
+ * tell while that state is the step of the undo history the configuration is at.
26
+ */
27
+ readonly rememberCommit: (toolCallId: string, state: DomainState<DomainEntity<string>>) => void;
23
28
  }
24
29
  /**
25
30
  * Browser implementation of the assistant's `query` tool: prepares the operations from the
@@ -93,7 +98,8 @@ export declare const createQueryClientTool: (dependencies: AssistantToolDependen
93
98
  * `GraphQLSystem.execute`, which commits their changes as one undo step, and returns its result,
94
99
  * including why nothing was committed when a commit was held or lost to a conflict. A commit clears
95
100
  * the recorded changes; a held one does not, so it shows in the next change record once the user
96
- * approves it. Only an owner
101
+ * approves it. The state it committed or held is remembered under the id of the call, so the chip
102
+ * row of the call can undo it. Only an owner
97
103
  * session may change the configuration; the check lives here and not in the `GraphQLSystem`, whose
98
104
  * other caller, the editor panel, has its own rules.
99
105
  */
@@ -1 +1 @@
1
- import{AssistantGraphQLToolInput as e,applyTool as t,queryTool as o}from"@salsita-ai/contracts/assistant";let a=e=>({results:[],committed:!1,message:e,sideEffects:[]}),i=e=>JSON.parse(JSON.stringify(e)),s=t=>{let o=e.safeParse(t);if(!o.success){let e=o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join("; ");return{ok:!1,refusal:a(`Nothing was run, because the tool input is not a list of operations (${e}). Send { "operations": [{ "query": "<one GraphQL operation>" }] } with at least one item.`)}}return{ok:!0,operations:o.data.operations.map(({query:e,variables:t,operationName:o})=>({document:e,variables:t??void 0,operationName:o??void 0}))}};export const createQueryClientTool=e=>o.client(async t=>{let o=s(t);if(!o.ok)return o.refusal;let n=e.getStoreSystem();if(!n.isInitialized)return a("Nothing was run, because the configuration is not loaded yet, so there is no state to query. Call the tool again once the configurator has finished loading.");let r=e.getGraphQLSystem(),l=await r.prepare(n.stateSlices.committed.value.state,o.operations);return i({results:l.map(({result:e})=>e),committed:!1,sideEffects:r.readSideEffects(l)})});export const createApplyClientTool=e=>t.client(async t=>{if("owner"!==e.getActiveMode())return a("Nothing was run, because changes cannot be applied in this session: the user may view this configuration but not change it. The query tool still works here, including mutations, to show what a change would do without keeping it.");let o=s(t);if(!o.ok)return o.refusal;let n=await e.getGraphQLSystem().execute(o.operations);return n.committed&&e.clearChanges(),i(n)});
1
+ import{AssistantGraphQLToolInput as e,applyTool as t,queryTool as o}from"@salsita-ai/contracts/assistant";let a=e=>({results:[],committed:!1,message:e,sideEffects:[]}),i=({preparedState:e,...t})=>JSON.parse(JSON.stringify(t)),s=t=>{let o=e.safeParse(t);if(!o.success){let e=o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join("; ");return{ok:!1,refusal:a(`Nothing was run, because the tool input is not a list of operations (${e}). Send { "operations": [{ "query": "<one GraphQL operation>" }] } with at least one item.`)}}return{ok:!0,operations:o.data.operations.map(({query:e,variables:t,operationName:o})=>({document:e,variables:t??void 0,operationName:o??void 0}))}};export const createQueryClientTool=e=>o.client(async t=>{let o=s(t);if(!o.ok)return o.refusal;let r=e.getStoreSystem();if(!r.isInitialized)return a("Nothing was run, because the configuration is not loaded yet, so there is no state to query. Call the tool again once the configurator has finished loading.");let n=e.getGraphQLSystem(),l=await n.prepare(r.stateSlices.committed.value.state,o.operations);return i({results:l.map(({result:e})=>e),committed:!1,sideEffects:n.readSideEffects(l)})});export const createApplyClientTool=e=>t.client(async(t,o)=>{if("owner"!==e.getActiveMode())return a("Nothing was run, because changes cannot be applied in this session: the user may view this configuration but not change it. The query tool still works here, including mutations, to show what a change would do without keeping it.");let r=s(t);if(!r.ok)return r.refusal;let n=await e.getGraphQLSystem().execute(r.operations),l=o?.toolCallId;return void 0!==l&&void 0!==n.preparedState&&e.rememberCommit(l,n.preparedState),n.committed&&e.clearChanges(),i(n)});
@@ -39,6 +39,12 @@ export interface GraphQLExecution {
39
39
  readonly message?: string;
40
40
  /** What the configurator did, or would do, beyond what the operations asked for, in applied order. */
41
41
  readonly sideEffects: readonly string[];
42
+ /**
43
+ * The state the operations prepared, when it was committed or held for the user's approval; a
44
+ * held state is the one committed if the user approves. A caller can recognize its own change in
45
+ * the undo history by it. It stays in the browser: nothing that leaves it may carry the state.
46
+ */
47
+ readonly preparedState?: AnyDomainState;
42
48
  }
43
49
  /**
44
50
  * Resolves a localizable value, or each item of a list of them, to a string in the current locale.
@@ -1 +1 @@
1
- import{GraphQLError as e,NoSchemaIntrospectionCustomRule as t,defaultFieldResolver as i,execute as o,getNamedType as n,parse as r,specifiedRules as a,validate as s,validateSchema as l}from"graphql";import{assertExistence as c,createSelector as d,isMessageDescriptor as h,isMultilingualString as u}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as m,createAssistantExecutionSchema as f,findAssistantMutation as p}from"#package/domain";import{createEntityMeta as g}from"./entity-meta.js";import{injectAssistantLenses as y}from"./inject-assistant-lenses.js";import{readSideEffects as v}from"./side-effects.js";let S={errors:[new e("The configurator does not provide this request. This is a fault in the configurator, not in your request. Tell the user this is not available.")]},_={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},x=new RegExp(m.pattern),w=[...a,t],E=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,$=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,b=e=>e instanceof Error?e.message:String(e),T=e=>/[.!?]$/.test(e)?e:`${e}.`,C=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,R=(e,t)=>{let i=C(e)?e._raw:void 0;if(E(i))return i;if(!Array.isArray(e)&&!C(e))return e;if(t.has(e))throw Error("The value contains itself.");let o=new Set([...t,e]);return Array.isArray(e)?e.map(e=>R(e,o)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,R(t,o)]))};export const localize=(e,t)=>Array.isArray(e)?e.map(e=>localize(e,t)):"string"==typeof e||h(e)||u(e)?t(e):e;let j=t=>new e(`The configurator cannot handle this request: ${T(t)} This is a fault in the configurator, not in your request. Tell the user this is not available.`),A=e=>(e.errors?.length??0)>0,M=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=d([d.leaf(e=>e)],e=>{try{let t=f(e),i=l(t);return i.length>0?j(i.map(e=>e.message).join(" ")):t}catch(e){return j(b(e))}});_selectLenses=d([d.leaf(e=>e)],e=>r(e));_selectFieldResolver=d([d.leaf(e=>e)],t=>(o,r,a,s)=>{if(s.parentType===s.schema.getQueryType())return i(o,r,a,s);if(s.parentType===s.schema.getMutationType())return this._resolveMutation(s.fieldName,String(s.path.key),r,a);if(!E(o))return i(o,r,a,s);if("meta"===s.fieldName){let e;return g({entity:o,state:("object"==typeof(e=$(o,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??a.state},{manifest:a.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e)})}let{source:l,localized:c}=t[s.parentType.name]?.[s.fieldName]??{source:s.fieldName},d=((t,i)=>{try{return $(t,i)}catch(o){throw new e(`Expression "${i}" failed on ${E(t)?t.id:"an entity"}: ${T(b(o))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:o instanceof Error?o:void 0})}})(o,l);return c?localize(d,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===n(s.returnType).name?((t,i,o)=>{try{return R(o,new Set)}catch(o){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${E(t)?t.id:"an entity"}: ${T(b(o))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:o instanceof Error?o:void 0})}})(o,l,d):d});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var o=i.rootEntityId;let n=t.filter(e=>!x.test(e));if(n.length>0)throw new e(`${n.map(e=>`"${e}"`).join(", ")} ${1===n.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${o}". Use ids as returned by the "id" field.`);return t.map(e=>x.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>E(e)?e.type:void 0;initialize=()=>{};_resolveMutation=(t,i,o,n)=>{if(void 0!==n.failedMutationField)throw new e(`Not run: the earlier field "${n.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,o,n)}catch(e){throw n.failedMutationField=i,e}};_runMutation=(t,i,o)=>{let n=p(t,o.manifest);if(!n)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. This is a fault in the configurator, not in your request. Tell the user this is not available.`);let r=o.state,a=n.implementation(i,r,{manifest:o.manifest,action:this._context.domainContextSelectors.getActionSystem().createContext(r),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});if(a.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(a.updates,r,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${T(b(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);o.emissions.push(i),o.state=i.state}return a.readResult({previousState:r,state:o.state})};_setup=()=>{let{manifest:t,error:i}=this._context.getAssistantSetup();if(i)return{ready:!1,result:{errors:[new e(i.message,{originalError:i})]}};if(!t)return{ready:!1,result:S};let o=this._selectSchema(t.sdl);return o instanceof e?{ready:!1,result:{errors:[o]}}:{ready:!0,assistant:t,schema:o,lenses:t.lenses?.definitions?this._selectLenses(t.lenses.definitions):void 0,fieldResolver:this._selectFieldResolver(t.fields)}};_run=(t,i,n,a)=>{let l;try{l=r(t)}catch(t){return{errors:[t instanceof e?t:new e(b(t))]}}let c=s(n.schema,l,w);return c.length>0?{errors:c}:o({schema:n.schema,document:l,rootValue:this._rootValue,contextValue:a,variableValues:i.variables,operationName:i.operationName,fieldResolver:n.fieldResolver,typeResolver:this._resolveType})};prepare=async(t,i)=>{let o,n=this._setup();if(!n.ready)return i.map(()=>({result:n.result,emissions:[]}));let r={manifest:n.assistant,state:t,emissions:[],failedMutationField:void 0},a=[];for(let[t,s]of i.entries())if(void 0===o){let i=r.emissions.length;r.failedMutationField=void 0;let l=n.lenses?y(s.document,n.lenses):s.document,c=l instanceof e?{errors:[l]}:await this._run(l,s,n,r);a.push({result:c,emissions:r.emissions.slice(i)}),A(c)&&(o=t)}else a.push({result:M(o),emissions:[]});return a};readSideEffects=e=>{let t=this._context.domainServicesSelectors.getResolveLocalizable();return e.flatMap(({emissions:e})=>e.flatMap(e=>v(e.updates,t)))};_prepareAndCommit=async e=>{let t,i,o,n=this._context.domainContextSelectors.getStoreSystem(),r=await this.prepare(n.stateSlices.committed.value.state,e),a=r.map(({result:e})=>e),s=this.readSideEffects(r),l=r.flatMap(e=>e.emissions);return a.some(A)||0===l.length?{results:a,sideEffects:s,outcome:"none"}:{results:a,sideEffects:s,outcome:n.commitPrepared((t=c(l[0]),i=c(l[l.length-1]),o=l.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:t.previousState,state:i.state,updates:l.flatMap(e=>e.updates),validity:i.validity,transformationLog:o?l.flatMap(e=>e.transformationLog??[]):void 0}))}};execute=async e=>{if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>_),committed:!1,sideEffects:[]};let t=await this._prepareAndCommit(e),{results:i,sideEffects:o,outcome:n}="conflict"===t.outcome?await this._prepareAndCommit(e):t;return"committed"===n?{results:i,committed:!0,sideEffects:o}:"held"===n?{results:i,committed:!1,message:"The changes wait for the user's approval and are not applied yet. Do not send them again; the next `[changes]` block lists them once approved.",sideEffects:o}:"conflict"===n?{results:i,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense.",sideEffects:o}:{results:i,committed:!1,sideEffects:o}}}
1
+ import{GraphQLError as e,NoSchemaIntrospectionCustomRule as t,defaultFieldResolver as i,execute as o,getNamedType as n,parse as r,specifiedRules as a,validate as s,validateSchema as l}from"graphql";import{assertExistence as c,createSelector as d,isMessageDescriptor as h,isMultilingualString as u}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as m,createAssistantExecutionSchema as f,findAssistantMutation as p}from"#package/domain";import{createEntityMeta as g}from"./entity-meta.js";import{injectAssistantLenses as y}from"./inject-assistant-lenses.js";import{readSideEffects as v}from"./side-effects.js";let S={errors:[new e("The configurator does not provide this request. This is a fault in the configurator, not in your request. Tell the user this is not available.")]},_={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},x=new RegExp(m.pattern),w=[...a,t],E=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,$=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,b=e=>e instanceof Error?e.message:String(e),T=e=>/[.!?]$/.test(e)?e:`${e}.`,C=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,R=(e,t)=>{let i=C(e)?e._raw:void 0;if(E(i))return i;if(!Array.isArray(e)&&!C(e))return e;if(t.has(e))throw Error("The value contains itself.");let o=new Set([...t,e]);return Array.isArray(e)?e.map(e=>R(e,o)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,R(t,o)]))};export const localize=(e,t)=>Array.isArray(e)?e.map(e=>localize(e,t)):"string"==typeof e||h(e)||u(e)?t(e):e;let j=t=>new e(`The configurator cannot handle this request: ${T(t)} This is a fault in the configurator, not in your request. Tell the user this is not available.`),A=e=>(e.errors?.length??0)>0,M=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=d([d.leaf(e=>e)],e=>{try{let t=f(e),i=l(t);return i.length>0?j(i.map(e=>e.message).join(" ")):t}catch(e){return j(b(e))}});_selectLenses=d([d.leaf(e=>e)],e=>r(e));_selectFieldResolver=d([d.leaf(e=>e)],t=>(o,r,a,s)=>{if(s.parentType===s.schema.getQueryType())return i(o,r,a,s);if(s.parentType===s.schema.getMutationType())return this._resolveMutation(s.fieldName,String(s.path.key),r,a);if(!E(o))return i(o,r,a,s);if("meta"===s.fieldName){let e;return g({entity:o,state:("object"==typeof(e=$(o,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??a.state},{manifest:a.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e)})}let{source:l,localized:c}=t[s.parentType.name]?.[s.fieldName]??{source:s.fieldName},d=((t,i)=>{try{return $(t,i)}catch(o){throw new e(`Expression "${i}" failed on ${E(t)?t.id:"an entity"}: ${T(b(o))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:o instanceof Error?o:void 0})}})(o,l);return c?localize(d,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===n(s.returnType).name?((t,i,o)=>{try{return R(o,new Set)}catch(o){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${E(t)?t.id:"an entity"}: ${T(b(o))} This is a fault in the configurator, not in your request. Tell the user this is not available.`,{originalError:o instanceof Error?o:void 0})}})(o,l,d):d});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var o=i.rootEntityId;let n=t.filter(e=>!x.test(e));if(n.length>0)throw new e(`${n.map(e=>`"${e}"`).join(", ")} ${1===n.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${o}". Use ids as returned by the "id" field.`);return t.map(e=>x.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>E(e)?e.type:void 0;initialize=()=>{};_resolveMutation=(t,i,o,n)=>{if(void 0!==n.failedMutationField)throw new e(`Not run: the earlier field "${n.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,o,n)}catch(e){throw n.failedMutationField=i,e}};_runMutation=(t,i,o)=>{let n=p(t,o.manifest);if(!n)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. This is a fault in the configurator, not in your request. Tell the user this is not available.`);let r=o.state,a=n.implementation(i,r,{manifest:o.manifest,action:this._context.domainContextSelectors.getActionSystem().createContext(r),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});if(a.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(a.updates,r,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${T(b(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);o.emissions.push(i),o.state=i.state}return a.readResult({previousState:r,state:o.state})};_setup=()=>{let{manifest:t,error:i}=this._context.getAssistantSetup();if(i)return{ready:!1,result:{errors:[new e(i.message,{originalError:i})]}};if(!t)return{ready:!1,result:S};let o=this._selectSchema(t.sdl);return o instanceof e?{ready:!1,result:{errors:[o]}}:{ready:!0,assistant:t,schema:o,lenses:t.lenses?.definitions?this._selectLenses(t.lenses.definitions):void 0,fieldResolver:this._selectFieldResolver(t.fields)}};_run=(t,i,n,a)=>{let l;try{l=r(t)}catch(t){return{errors:[t instanceof e?t:new e(b(t))]}}let c=s(n.schema,l,w);return c.length>0?{errors:c}:o({schema:n.schema,document:l,rootValue:this._rootValue,contextValue:a,variableValues:i.variables,operationName:i.operationName,fieldResolver:n.fieldResolver,typeResolver:this._resolveType})};prepare=async(t,i)=>{let o,n=this._setup();if(!n.ready)return i.map(()=>({result:n.result,emissions:[]}));let r={manifest:n.assistant,state:t,emissions:[],failedMutationField:void 0},a=[];for(let[t,s]of i.entries())if(void 0===o){let i=r.emissions.length;r.failedMutationField=void 0;let l=n.lenses?y(s.document,n.lenses):s.document,c=l instanceof e?{errors:[l]}:await this._run(l,s,n,r);a.push({result:c,emissions:r.emissions.slice(i)}),A(c)&&(o=t)}else a.push({result:M(o),emissions:[]});return a};readSideEffects=e=>{let t=this._context.domainServicesSelectors.getResolveLocalizable();return e.flatMap(({emissions:e})=>e.flatMap(e=>v(e.updates,t)))};_prepareAndCommit=async e=>{let t,i,o,n=this._context.domainContextSelectors.getStoreSystem(),r=await this.prepare(n.stateSlices.committed.value.state,e),a=r.map(({result:e})=>e),s=this.readSideEffects(r),l=r.flatMap(e=>e.emissions);if(a.some(A)||0===l.length)return{results:a,sideEffects:s,outcome:"none"};let d=(t=c(l[0]),i=c(l[l.length-1]),o=l.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:t.previousState,state:i.state,updates:l.flatMap(e=>e.updates),validity:i.validity,transformationLog:o?l.flatMap(e=>e.transformationLog??[]):void 0});return{results:a,sideEffects:s,outcome:n.commitPrepared(d),preparedState:d.state}};execute=async e=>{if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>_),committed:!1,sideEffects:[]};let t=await this._prepareAndCommit(e),{results:i,sideEffects:o,outcome:n,preparedState:r}="conflict"===t.outcome?await this._prepareAndCommit(e):t;return"committed"===n?{results:i,committed:!0,sideEffects:o,preparedState:r}:"held"===n?{results:i,committed:!1,message:"The changes wait for the user's approval and are not applied yet. Do not send them again; the next `[changes]` block lists them once approved.",sideEffects:o,preparedState:r}:"conflict"===n?{results:i,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense.",sideEffects:o}:{results:i,committed:!1,sideEffects:o}}}
@@ -1,5 +1,5 @@
1
1
  import { type $DomainEntity, type ConfiguratorBuilder, type CreateCustomPhoenixEntityElement, type CreateCustomPhoenixLayoutElement, type CreateGetThemeHandler, type CreateGetWebComponentOptionsHandler, type CreatePhoenixHandler, type DomainEntity, type IsCreateRecipeUpdate, type IsReplaceRecipeUpdate, type TransformerBuilder, type TransformerFactory, type UpdateFactory, getIdSequence } from '#package/domain';
2
- import { type CreateCollectFrontendDataHandler, type CreateFilterPublishedChunksHandler, type CreateNotAuthenticatedPage, type IsAngleDimensionUpdate, type IsAttributeSelectionUpdate, type IsAttributeSwitchUpdate, type IsDeleteEntityUpdate, type IsDistanceDimensionUpdate, type IsGenericDimensionUpdate, type IsSwatchSelectionUpdate, type IsTextValueUpdate, type UseAccessService, type UseActiveNavigationState, type UseActiveToolState, type UseActiveViewState, type UseAnalyticsService, type UseAngleDimension, type UseAttributeSelection, type UseAttributeSwitch, type UseAuthenticatedClient, type UseAuthorizedClient, type UseBackendData, type UseChildEntitySelection, type UseComponent, type UseComponents, type UseComposition, type UseConfigurationId, type UseConfiguratorMessages, type UseControlViolations, type UseCookRecipes, type UseCookReplaceRecipes, type UseCookedRecipes, type UseCookedReplaceRecipes, type UseCreateAuthorizedClient, type UseDeleteEntity, type UseDeletedEntity, type UseDeselect, type UseDistanceDimension, type UseDistanceDimensionSelection, type UseDomainApplicationContext, type UseDomainApplicationProps, type UseDomainContext, type UseDomainEntityState, type UseEntityPrice, type UseEntitySelector, type UseFinalizeViolations, type UseFirstComponent, type UseFirstRootComponent, type UseFrontendData, type UseGenericDimension, type UseGroupedRecipeOptions, type UseHoveredDomainEntityId, type UseHoveredEntity, type UseHoveredEntityId, type UseIntegrationService, type UseIntegrationState, type UseIsFullscreenActive, type UseLoadedData, type UseLoadingState, type UseMapViolationHints, type UseNavigate, type UsePopNavigate, type UsePresetEmbeddedState, type UsePublishedData, type UsePushNavigate, type UseRecipeEmbeddedState, type UseReplaceEntityRecipeOptions, type UseRootComponents, type UseRootEntityId, type UseRootEntityPrice, type UseRootEntitySelector, type UseRootEntityState, type UseSelectEntity, type UseSelectParent, type UseSelectedDomainEntityId, type UseSelectedEntity, type UseSelectedEntityId, type UseSession, type UseSessionRole, type UseSessionService, type UseSetFullscreen, type UseSlice, type UseState, type UseStateValidity, type UseSwatchSelection, type UseSynchronizedSelector, type UseSynchronizedSelector$, type UseTextValue, type UseUpdateControl, type UseUpdatedControl, useLoadingStateValue } from '#package/react-sdk';
2
+ import { type CreateCollectFrontendDataHandler, type CreateFilterPublishedChunksHandler, type CreateNotAuthenticatedPage, type IsAngleDimensionUpdate, type IsAttributeSelectionUpdate, type IsAttributeSwitchUpdate, type IsDeleteEntityUpdate, type IsDistanceDimensionUpdate, type IsGenericDimensionUpdate, type IsSwatchSelectionUpdate, type IsTextValueUpdate, type UseAccessService, type UseActiveNavigationState, type UseActiveToolState, type UseActiveViewState, type UseAnalyticsService, type UseAngleDimension, type UseAttributeSelection, type UseAttributeSwitch, type UseAuthenticatedClient, type UseAuthorizedClient, type UseBackendData, type UseChildEntitySelection, type UseComponent, type UseComponents, type UseComposition, type UseConfigurationId, type UseConfiguratorMessages, type UseControlViolations, type UseCookRecipes, type UseCookReplaceRecipes, type UseCookedRecipes, type UseCookedReplaceRecipes, type UseCreateAuthorizedClient, type UseDeleteEntity, type UseDeletedEntity, type UseDeselect, type UseDistanceDimension, type UseDistanceDimensionSelection, type UseDomainApplicationContext, type UseDomainApplicationProps, type UseDomainContext, type UseDomainEntityState, type UseEntityPrice, type UseEntitySelector, type UseFinalizeViolations, type UseFirstComponent, type UseFirstRootComponent, type UseFrontendData, type UseGenericDimension, type UseGroupedRecipeOptions, type UseHoveredDomainEntityId, type UseHoveredEntity, type UseHoveredEntityId, type UseIntegrationService, type UseIntegrationState, type UseIsFullscreenActive, type UseLoadedData, type UseLoadingState, type UseMapViolationHints, type UseNavigate, type UsePopNavigate, type UsePresetEmbeddedState, type UsePublishedData, type UsePushNavigate, type UseRecipeEmbeddedState, type UseReplaceEntityRecipeOptions, type UseRootComponents, type UseRootEntityId, type UseRootEntityPrice, type UseRootEntitySelector, type UseRootEntityState, type UseSelectEntity, type UseSelectParent, type UseSelectedDomainEntityId, type UseSelectedEntity, type UseSelectedEntityId, type UseSession, type UseSessionRole, type UseSessionService, type UseSetFullscreen, type UseSetViewState, type UseSlice, type UseState, type UseStateValidity, type UseSwatchSelection, type UseSynchronizedSelector, type UseSynchronizedSelector$, type UseTextValue, type UseUpdateControl, type UseUpdatedControl, useLoadingStateValue } from '#package/react-sdk';
3
3
  import { type CreatePhoenixIconSet } from '#package/ui-sdk';
4
4
  import { type UseConfiguratorExtensions } from '../application-runtime/index.js';
5
5
  import { type CreateOnApplicationStartProvider, type CreateOnBackendDataReadyProvider, type CreateOnConfiguratorReadyProvider, type CreateOnFrontendDataReadyProvider, type CreateOnGoToIndexHandler, type CreateOnPresetLoadedHandler, type CreateOnPublishedDataReadyProvider, type CreateOnRoutingReadyProvider, type CreateOnSessionReadyProvider, type UseConfiguratorRouting, type UsePresetSelection, usePropertyPageStack, usePropertyPageState } from '../configurator-context/index.js';
@@ -9,6 +9,7 @@ import { type CreateAppLoader, type CreateCanvasLoader, type CreateLayoutAdminUi
9
9
  import { useGenerateShareLink } from '../ui/phoenix-layout-elements/share/use-share.js';
10
10
  import { type CreatePresetDeviceNotSupported, type CreatePresetPageAction, type CreatePresetUi } from '../ui/preset-layout/PresetUi.js';
11
11
  import { type UseFinalizeAction } from './use-finalize-action.js';
12
+ import { type UseResetCamera } from './use-reset-camera.js';
12
13
  export interface ConfiguratorInterface<ENTITIES extends DomainEntity<string>, ALLOWED_ROOT_ENTITIES extends ENTITIES, $MAP extends Record<ENTITIES['type'], $DomainEntity<ENTITIES>>, PUBLISHED_DATA extends Record<string, unknown>, SESSION_DATA extends Record<string, unknown>, FRONTEND_DATA extends Record<string, unknown>, NAVIGATION_STATES extends string, VIEW_STATES extends string, TOOL_STATES extends string, EXPORT_STATES extends string, FINALIZE_DATA extends Record<string, unknown>, FINALIZE_RESULT extends Record<string, unknown>> {
13
14
  /**
14
15
  * Factory for creating update functions for entities.
@@ -470,6 +471,10 @@ export interface ConfiguratorInterface<ENTITIES extends DomainEntity<string>, AL
470
471
  * Get the currently active view state
471
472
  */
472
473
  useActiveViewState: UseActiveViewState<VIEW_STATES>;
474
+ /**
475
+ * Make the given view state the active one
476
+ */
477
+ useSetViewState: UseSetViewState<VIEW_STATES>;
473
478
  /**
474
479
  * Get the currently active tool state
475
480
  */
@@ -482,6 +487,10 @@ export interface ConfiguratorInterface<ENTITIES extends DomainEntity<string>, AL
482
487
  * Turn the fullscreen state on or off. The state is a flag the layout reads with `useIsFullscreenActive`
483
488
  */
484
489
  useSetFullscreen: UseSetFullscreen;
490
+ /**
491
+ * Move the camera back to the framing of the active camera configuration, as the toolbar's reset button does
492
+ */
493
+ useResetCamera: UseResetCamera;
485
494
  /**
486
495
  * Returns composition tree of the domain
487
496
  */
@@ -1 +1 @@
1
- import{createCustomPhoenixEntityElement as e,createCustomPhoenixLayoutElement as t,createGetThemeHandler as r,createGetWebComponentOptionsHandler as o,createPhoenixHandler as i,createTransformer as s,createTransformerBuilder as a,createUpdateFactory as n,getIdSequence as m,isCreateEntityUpdate as p,isReplaceEntityUpdate as u}from"#package/domain";import{createCollectFrontendDataHandler as c,createFilterPublishedChunksHandler as d,createNotAuthenticatedPage as f,isAngleDimensionUpdate as j,isAttributeSelectionUpdate as l,isAttributeSwitchUpdate as g,isDeleteEntityUpdate as x,isDistanceDimensionUpdate as y,isGenericDimensionUpdate as k,isSwatchSelectionUpdate as h,isTextValueUpdate as S,useAccessService as E,useActiveNavigationState as I,useActiveToolState as q,useActiveViewState as z,useAnalyticsService as A,useAngleDimension as B,useAttributeSelection as C,useAttributeSwitch as P,useAuthenticatedClient as R,useAuthorizedClient as U,useBackendData as b,useChildEntitySelection as v,useCommittedSlice as w,useCommittedState as D,useComponent as F,useComponents as G,useComposition as H,useConfigurationId as J,useConfiguratorMessages as K,useControlViolations as L,useCookRecipes as M,useCookReplaceRecipes as N,useCookedRecipes as O,useCookedReplaceRecipes as Q,useCreateAuthorizedClient as T,useDeleteEntity as V,useDeletedEntity as W,useDeselect as X,useDistanceDimension as Y,useDistanceDimensionSelection as Z,useDomain as $,useDomainApplication as _,useDomainApplicationProps as ee,useDomainEntityRenderedState as et,useDomainEntityState as er,useEntityPrice as eo,useEntitySelector as ei,useFinalizeViolations as es,useFirstComponent as ea,useFirstRenderedComponent as en,useFirstRootComponent as em,useFirstRootRenderedComponent as ep,useFrontendData as eu,useGenericDimension as ec,useGroupedRecipeOptions as ed,useHoveredDomainEntityId as ef,useHoveredEntity as ej,useHoveredEntityId as el,useIntegrationService as eg,useIntegrationState as ex,useIsFullscreenActive as ey,useLoadedData as ek,useLoadingState as eh,useLoadingStateValue as eS,useMapViolationHints as eE,useNavigate as eI,usePopNavigate as eq,usePresetEmbeddedState as ez,usePublishedData as eA,usePushNavigate as eB,useRecipeEmbeddedState as eC,useRenderedComponent as eP,useRenderedComponents as eR,useRenderedEntitySelector as eU,useRenderedRootEntitySelector as eb,useRenderedSelector as ev,useRenderedSelector$ as ew,useRenderedSlice as eD,useRenderedState as eF,useReplaceEntityRecipeOptions as eG,useRootComponents as eH,useRootEntityId as eJ,useRootEntityPrice as eK,useRootEntityRenderedId as eL,useRootEntityRenderedState as eM,useRootEntitySelector as eN,useRootEntityState as eO,useRootRenderedComponents as eQ,useSelectEntity as eT,useSelectParent as eV,useSelectedDomainEntityId as eW,useSelectedEntity as eX,useSelectedEntityId as eY,useSession as eZ,useSessionRole as e$,useSessionService as e_,useSetFullscreen as e0,useStateValidity as e1,useStoreSelector as e2,useStoreSelector$ as e3,useSwatchSelection as e4,useTextValue as e5,useUpdateControl as e6,useUpdatedControl as e7}from"#package/react-sdk";import{createPhoenixIconSet as e8}from"#package/ui-sdk";import{useConfigurator as e9}from"../application-runtime/index.js";import{createOnApplicationStartProvider as te,createOnBackendDataReadyProvider as tt,createOnConfiguratorReadyProvider as tr,createOnFrontendDataReadyProvider as to,createOnGoToIndexHandler as ti,createOnPresetLoadedHandler as ts,createOnPublishedDataReadyProvider as ta,createOnRoutingReadyProvider as tn,createOnSessionReadyProvider as tm,useConfiguratorRouting as tp,usePresetSelection as tu,usePropertyPageStack as tc,usePropertyPageState as td}from"../configurator-context/index.js";import{createGetTextureProcessingMapHandler as tf}from"../scene/index.js";import{useUpdateEntityObjectSelection as tj}from"../scene/scene-entity/drag-and-drop/use-updated-selection.js";import{createAppLoader as tl,createCanvasLoader as tg,createLayoutAdminUi as tx,createLayoutCustomOwnerUi as ty,createLayoutCustomReadUi as tk,createLayoutDialog as th,createLayoutFooter as tS,createLayoutToolbarDesktop as tE,createLayoutToolbarDesktopFullScreen as tI,createLayoutToolbarReadDesktop as tq,createLayoutToolbarReadDesktopFullScreen as tz,createLayoutUserUi as tA,createToolbarHelp as tB}from"../ui/index.js";import{useGenerateShareLink as tC}from"../ui/phoenix-layout-elements/share/use-share.js";import{createPresetDeviceNotSupported as tP,createPresetPageAction as tR,createPresetUi as tU}from"../ui/preset-layout/PresetUi.js";import{useFinalizeAction as tb}from"./use-finalize-action.js";export const createConfiguratorInterface=tv=>({updates:n(),getIdSequence:m,createGetWebComponentOptionsHandler:o,createGetThemeHandler:r,createPhoenixHandler:i,createCustomPhoenixEntityElement:e,createCustomPhoenixLayoutElement:t,createPhoenixIconSet:e8,createGetTextureProcessingMapHandler:tf,createCollectFrontendDataHandler:c,createFilterPublishedChunksHandler:d,createOnGoToIndexHandler:ti,createOnApplicationStartProvider:te,createOnSessionReadyProvider:tm,createOnPublishedDataReadyProvider:ta,createOnFrontendDataReadyProvider:to,createOnBackendDataReadyProvider:tt,createOnRoutingReadyProvider:tn,createOnConfiguratorReadyProvider:tr,createOnPresetLoadedHandler:ts,createPresetUi:tU,createPresetPageAction:tR,createPresetDeviceNotSupported:tP,createLayoutFooter:tS,createAppLoader:tl,createCanvasLoader:tg,createLayoutDialog:th,createLayoutToolbarDesktop:tE,createLayoutToolbarDesktopFullScreen:tI,createLayoutToolbarReadDesktop:tq,createLayoutToolbarReadDesktopFullScreen:tz,createLayoutCustomOwnerUi:ty,createLayoutCustomReadUi:tk,createLayoutAdminUi:tx,createLayoutUserUi:tA,createToolbarHelp:tB,createNotAuthenticatedPage:f,createTransformer:s,transformerBuilder:a(),useAnalytics:A,useAuthenticatedClient:R,useAuthorizedClient:U,useCreateAuthorizedClient:T,useAccessService:E,useConfigurationId:J,useSession:eZ,useSessionRole:e$,useSessionService:e_,useDomain:$,useDomainApplication:_,useConfigurator:e9,useConfiguratorRouting:tp,usePresetSelection:tu,usePublishedData:eA,useFrontendData:eu,useBackendData:b,useDomainApplicationProps:ee,useLoadedData:ek,useIntegrationService:eg,useIntegrationState:ex,usePresetEmbeddedState:ez,useRecipeEmbeddedState:eC,useCommittedSlice:w,useRenderedSlice:eD,useCommittedState:D,useRenderedState:eF,useStoreSelector:e2,useRenderedSelector:ev,useStoreSelector$:e3,useRenderedSelector$:ew,useEntitySelector:ei,useRenderedEntitySelector:eU,useRootEntitySelector:eN,useRenderedRootEntitySelector:eb,useStateValidity:e1,useMapViolationHints:eE,useNavigate:eI,usePopNavigate:eq,usePushNavigate:eB,useControlViolations:L,useFinalizeViolations:es,useConfiguratorMessages:K,useFinalizeAction:tb,useComponent:F,useRenderedComponent:eP,useComponents:G,useRenderedComponents:eR,useFirstComponent:ea,useFirstRenderedComponent:en,useRootComponents:eH,useRootRenderedComponents:eQ,useFirstRootComponent:em,useFirstRootRenderedComponent:ep,useEntityState:er,useEntityRenderedState:et,useRootEntityState:eO,useRootEntityRenderedState:eM,useRootEntityId:eJ,useRootEntityRenderedId:eL,useEntityPrice:eo,useRootEntityPrice:eK,useSelectedEntity:eX,useSelectedEntityId:eY,useSelectedDomainEntityId:eW,useSelectEntity:eT,useDeselect:X,useSelectParent:eV,useHoveredEntity:ej,useHoveredEntityId:el,useHoveredDomainEntityId:ef,useActiveNavigationState:I,useActiveViewState:z,useActiveToolState:q,useIsFullscreenActive:ey,useSetFullscreen:e0,useComposition:H,useUpdateControl:e6,useUpdatedControl:e7,useCookRecipes:M,useCookedRecipes:O,useCookReplaceRecipes:N,useCookedReplaceRecipes:Q,useDeleteEntity:V,useDeletedEntity:W,useSwatchSelection:e4,useTextValue:e5,useAttributeSelection:C,useAttributeSwitch:P,useDistanceDimension:Y,useDistanceDimensionSelection:Z,useGenericDimension:ec,useAngleDimension:B,useChildEntitySelection:v,useGroupedRecipeOptions:ed,useReplaceEntityRecipeOptions:eG,useUpdateEntityObjectSelection:tj,useGenerateShareLink:tC,useLoadingState:eh,useLoadingStateValue:eS,usePropertyPageStack:tc,usePropertyPageState:td,isCreateEntityUpdate:p,isReplaceEntityUpdate:u,isDeleteEntityUpdate:x,isAngleDimensionUpdate:j,isDistanceDimensionUpdate:y,isGenericDimensionUpdate:k,isAttributeSelectionUpdate:l,isAttributeSwitchUpdate:g,isTextValueUpdate:S,isSwatchSelectionUpdate:h});
1
+ import{createCustomPhoenixEntityElement as e,createCustomPhoenixLayoutElement as r,createGetThemeHandler as t,createGetWebComponentOptionsHandler as o,createPhoenixHandler as i,createTransformer as s,createTransformerBuilder as a,createUpdateFactory as m,getIdSequence as n,isCreateEntityUpdate as p,isReplaceEntityUpdate as u}from"#package/domain";import{createCollectFrontendDataHandler as c,createFilterPublishedChunksHandler as d,createNotAuthenticatedPage as f,isAngleDimensionUpdate as j,isAttributeSelectionUpdate as l,isAttributeSwitchUpdate as g,isDeleteEntityUpdate as x,isDistanceDimensionUpdate as y,isGenericDimensionUpdate as k,isSwatchSelectionUpdate as h,isTextValueUpdate as S,useAccessService as E,useActiveNavigationState as I,useActiveToolState as q,useActiveViewState as z,useAnalyticsService as A,useAngleDimension as B,useAttributeSelection as C,useAttributeSwitch as P,useAuthenticatedClient as R,useAuthorizedClient as U,useBackendData as b,useChildEntitySelection as v,useCommittedSlice as w,useCommittedState as D,useComponent as F,useComponents as G,useComposition as H,useConfigurationId as J,useConfiguratorMessages as K,useControlViolations as L,useCookRecipes as M,useCookReplaceRecipes as N,useCookedRecipes as O,useCookedReplaceRecipes as Q,useCreateAuthorizedClient as T,useDeleteEntity as V,useDeletedEntity as W,useDeselect as X,useDistanceDimension as Y,useDistanceDimensionSelection as Z,useDomain as $,useDomainApplication as _,useDomainApplicationProps as ee,useDomainEntityRenderedState as er,useDomainEntityState as et,useEntityPrice as eo,useEntitySelector as ei,useFinalizeViolations as es,useFirstComponent as ea,useFirstRenderedComponent as em,useFirstRootComponent as en,useFirstRootRenderedComponent as ep,useFrontendData as eu,useGenericDimension as ec,useGroupedRecipeOptions as ed,useHoveredDomainEntityId as ef,useHoveredEntity as ej,useHoveredEntityId as el,useIntegrationService as eg,useIntegrationState as ex,useIsFullscreenActive as ey,useLoadedData as ek,useLoadingState as eh,useLoadingStateValue as eS,useMapViolationHints as eE,useNavigate as eI,usePopNavigate as eq,usePresetEmbeddedState as ez,usePublishedData as eA,usePushNavigate as eB,useRecipeEmbeddedState as eC,useRenderedComponent as eP,useRenderedComponents as eR,useRenderedEntitySelector as eU,useRenderedRootEntitySelector as eb,useRenderedSelector as ev,useRenderedSelector$ as ew,useRenderedSlice as eD,useRenderedState as eF,useReplaceEntityRecipeOptions as eG,useRootComponents as eH,useRootEntityId as eJ,useRootEntityPrice as eK,useRootEntityRenderedId as eL,useRootEntityRenderedState as eM,useRootEntitySelector as eN,useRootEntityState as eO,useRootRenderedComponents as eQ,useSelectEntity as eT,useSelectParent as eV,useSelectedDomainEntityId as eW,useSelectedEntity as eX,useSelectedEntityId as eY,useSession as eZ,useSessionRole as e$,useSessionService as e_,useSetFullscreen as e0,useSetViewState as e1,useStateValidity as e2,useStoreSelector as e3,useStoreSelector$ as e4,useSwatchSelection as e5,useTextValue as e6,useUpdateControl as e7,useUpdatedControl as e8}from"#package/react-sdk";import{createPhoenixIconSet as e9}from"#package/ui-sdk";import{useConfigurator as re}from"../application-runtime/index.js";import{createOnApplicationStartProvider as rr,createOnBackendDataReadyProvider as rt,createOnConfiguratorReadyProvider as ro,createOnFrontendDataReadyProvider as ri,createOnGoToIndexHandler as rs,createOnPresetLoadedHandler as ra,createOnPublishedDataReadyProvider as rm,createOnRoutingReadyProvider as rn,createOnSessionReadyProvider as rp,useConfiguratorRouting as ru,usePresetSelection as rc,usePropertyPageStack as rd,usePropertyPageState as rf}from"../configurator-context/index.js";import{createGetTextureProcessingMapHandler as rj}from"../scene/index.js";import{useUpdateEntityObjectSelection as rl}from"../scene/scene-entity/drag-and-drop/use-updated-selection.js";import{createAppLoader as rg,createCanvasLoader as rx,createLayoutAdminUi as ry,createLayoutCustomOwnerUi as rk,createLayoutCustomReadUi as rh,createLayoutDialog as rS,createLayoutFooter as rE,createLayoutToolbarDesktop as rI,createLayoutToolbarDesktopFullScreen as rq,createLayoutToolbarReadDesktop as rz,createLayoutToolbarReadDesktopFullScreen as rA,createLayoutUserUi as rB,createToolbarHelp as rC}from"../ui/index.js";import{useGenerateShareLink as rP}from"../ui/phoenix-layout-elements/share/use-share.js";import{createPresetDeviceNotSupported as rR,createPresetPageAction as rU,createPresetUi as rb}from"../ui/preset-layout/PresetUi.js";import{useFinalizeAction as rv}from"./use-finalize-action.js";import{useResetCamera as rw}from"./use-reset-camera.js";export const createConfiguratorInterface=rD=>({updates:m(),getIdSequence:n,createGetWebComponentOptionsHandler:o,createGetThemeHandler:t,createPhoenixHandler:i,createCustomPhoenixEntityElement:e,createCustomPhoenixLayoutElement:r,createPhoenixIconSet:e9,createGetTextureProcessingMapHandler:rj,createCollectFrontendDataHandler:c,createFilterPublishedChunksHandler:d,createOnGoToIndexHandler:rs,createOnApplicationStartProvider:rr,createOnSessionReadyProvider:rp,createOnPublishedDataReadyProvider:rm,createOnFrontendDataReadyProvider:ri,createOnBackendDataReadyProvider:rt,createOnRoutingReadyProvider:rn,createOnConfiguratorReadyProvider:ro,createOnPresetLoadedHandler:ra,createPresetUi:rb,createPresetPageAction:rU,createPresetDeviceNotSupported:rR,createLayoutFooter:rE,createAppLoader:rg,createCanvasLoader:rx,createLayoutDialog:rS,createLayoutToolbarDesktop:rI,createLayoutToolbarDesktopFullScreen:rq,createLayoutToolbarReadDesktop:rz,createLayoutToolbarReadDesktopFullScreen:rA,createLayoutCustomOwnerUi:rk,createLayoutCustomReadUi:rh,createLayoutAdminUi:ry,createLayoutUserUi:rB,createToolbarHelp:rC,createNotAuthenticatedPage:f,createTransformer:s,transformerBuilder:a(),useAnalytics:A,useAuthenticatedClient:R,useAuthorizedClient:U,useCreateAuthorizedClient:T,useAccessService:E,useConfigurationId:J,useSession:eZ,useSessionRole:e$,useSessionService:e_,useDomain:$,useDomainApplication:_,useConfigurator:re,useConfiguratorRouting:ru,usePresetSelection:rc,usePublishedData:eA,useFrontendData:eu,useBackendData:b,useDomainApplicationProps:ee,useLoadedData:ek,useIntegrationService:eg,useIntegrationState:ex,usePresetEmbeddedState:ez,useRecipeEmbeddedState:eC,useCommittedSlice:w,useRenderedSlice:eD,useCommittedState:D,useRenderedState:eF,useStoreSelector:e3,useRenderedSelector:ev,useStoreSelector$:e4,useRenderedSelector$:ew,useEntitySelector:ei,useRenderedEntitySelector:eU,useRootEntitySelector:eN,useRenderedRootEntitySelector:eb,useStateValidity:e2,useMapViolationHints:eE,useNavigate:eI,usePopNavigate:eq,usePushNavigate:eB,useControlViolations:L,useFinalizeViolations:es,useConfiguratorMessages:K,useFinalizeAction:rv,useComponent:F,useRenderedComponent:eP,useComponents:G,useRenderedComponents:eR,useFirstComponent:ea,useFirstRenderedComponent:em,useRootComponents:eH,useRootRenderedComponents:eQ,useFirstRootComponent:en,useFirstRootRenderedComponent:ep,useEntityState:et,useEntityRenderedState:er,useRootEntityState:eO,useRootEntityRenderedState:eM,useRootEntityId:eJ,useRootEntityRenderedId:eL,useEntityPrice:eo,useRootEntityPrice:eK,useSelectedEntity:eX,useSelectedEntityId:eY,useSelectedDomainEntityId:eW,useSelectEntity:eT,useDeselect:X,useSelectParent:eV,useHoveredEntity:ej,useHoveredEntityId:el,useHoveredDomainEntityId:ef,useActiveNavigationState:I,useActiveViewState:z,useSetViewState:e1,useActiveToolState:q,useIsFullscreenActive:ey,useSetFullscreen:e0,useResetCamera:rw,useComposition:H,useUpdateControl:e7,useUpdatedControl:e8,useCookRecipes:M,useCookedRecipes:O,useCookReplaceRecipes:N,useCookedReplaceRecipes:Q,useDeleteEntity:V,useDeletedEntity:W,useSwatchSelection:e5,useTextValue:e6,useAttributeSelection:C,useAttributeSwitch:P,useDistanceDimension:Y,useDistanceDimensionSelection:Z,useGenericDimension:ec,useAngleDimension:B,useChildEntitySelection:v,useGroupedRecipeOptions:ed,useReplaceEntityRecipeOptions:eG,useUpdateEntityObjectSelection:rl,useGenerateShareLink:rP,useLoadingState:eh,useLoadingStateValue:eS,usePropertyPageStack:rd,usePropertyPageState:rf,isCreateEntityUpdate:p,isReplaceEntityUpdate:u,isDeleteEntityUpdate:x,isAngleDimensionUpdate:j,isDistanceDimensionUpdate:y,isGenericDimensionUpdate:k,isAttributeSelectionUpdate:l,isAttributeSwitchUpdate:g,isTextValueUpdate:S,isSwatchSelectionUpdate:h});
@@ -1,3 +1,4 @@
1
1
  export { type ConfiguratorInterface, createConfiguratorInterface } from './configurator-interface.js';
2
2
  export { FrontendAnalyticsFactory } from './frontend-analytics.js';
3
3
  export { type UseFinalizeAction, type UseFinalizeActionResult, useFinalizeAction } from './use-finalize-action.js';
4
+ export { type UseResetCamera, useResetCamera } from './use-reset-camera.js';
@@ -1 +1 @@
1
- export{createConfiguratorInterface}from"./configurator-interface.js";export{FrontendAnalyticsFactory}from"./frontend-analytics.js";export{useFinalizeAction}from"./use-finalize-action.js";
1
+ export{createConfiguratorInterface}from"./configurator-interface.js";export{FrontendAnalyticsFactory}from"./frontend-analytics.js";export{useFinalizeAction}from"./use-finalize-action.js";export{useResetCamera}from"./use-reset-camera.js";
@@ -4,9 +4,13 @@
4
4
  * `disabled` or `isPending`.
5
5
  * - `isPending`: the handler is running.
6
6
  * - `error`: what the handler's last run rejected with, `null` after a run that resolved and while a new run is
7
- * pending. No snackbar is raised; the layout shows it its own way.
7
+ * pending. A rejection that is not an `Error` is wrapped in one, with the rejected value as its `cause`. No snackbar
8
+ * is raised; the layout shows it its own way.
8
9
  * - `disabled` and `hints`: from `useFinalizeViolations()`; the configuration cannot be finalized while it has
9
10
  * a `warning` or worse, or while it is out of sync with the server, and `hints` says why.
11
+ *
12
+ * `isPending` and `error` belong to the mounted component: when it unmounts during a run, the handler keeps
13
+ * running, and a component mounted afterwards starts with `isPending` false and no `error`.
10
14
  */
11
15
  export interface UseFinalizeActionResult {
12
16
  readonly run: () => void;
@@ -1 +1 @@
1
- import{useMutation as r}from"@tanstack/react-query";import{useCallback as e,useRef as t}from"react";import{useAnalyticsService as o,useFinalizeViolations as n,useLogger as a,usePhoenixHandler as i}from"#package/react-sdk";import{FrontendAnalyticsFactory as c}from"./frontend-analytics.js";export const useFinalizeAction=l=>{let{handler:m}=i(l),s=o(),u=a(),{disabled:p,hints:d}=n(),f=t(!1),{mutate:k,isPending:z,error:F}=r({mutationFn:m,onError:r=>{u.error("Failed to finalize",{error:r.message})},onSettled:()=>{f.current=!1}});return{run:e(()=>{p||f.current||(f.current=!0,s.push(c.FinalizeButtonClicked()),k())},[s,p,k]),isPending:z,error:F,disabled:p,hints:d}};
1
+ import{useMutation as r}from"@tanstack/react-query";import{useCallback as t,useRef as e}from"react";import{useAnalyticsService as a,useFinalizeViolations as n,useLogger as o,usePhoenixHandler as i}from"#package/react-sdk";import{FrontendAnalyticsFactory as c}from"./frontend-analytics.js";export const useFinalizeAction=l=>{let{handler:s}=i(l),u=a(),f=o(),{disabled:m,hints:d}=n(),h=e(!1),{mutate:p,isPending:y,error:k}=r({mutationFn:async()=>{try{await s()}catch(r){throw r instanceof Error?r:Error(null==r?"The finalize handler failed without a reason.":String(r),{cause:r})}finally{h.current=!1}},onError:r=>{f.error("Failed to finalize",{error:r.message})}});return{run:t(()=>{m||h.current||(h.current=!0,u.push(c.FinalizeButtonClicked()),p())},[u,m,p]),isPending:y,error:k,disabled:m,hints:d}};
@@ -0,0 +1,6 @@
1
+ export type UseResetCamera = () => () => void;
2
+ /**
3
+ * Hook to move the camera back to the framing of the active camera configuration, undoing what the user orbited,
4
+ * panned or zoomed. It does what the built-in toolbar's reset button does
5
+ */
6
+ export declare const useResetCamera: UseResetCamera;
@@ -0,0 +1 @@
1
+ import{useCallback as e}from"react";import{useConfigurator as r}from"../application-runtime/index.js";export const useResetCamera=()=>{let{cameraControlSystem:t}=r();return e(()=>{t.resetCamera()},[t])};
@@ -3,9 +3,15 @@ export interface AssistantToolChipsProps {
3
3
  readonly tools: readonly AssistantToolItem[];
4
4
  /** True while the reply runs and this row is its newest block: the assistant is at work on it. */
5
5
  readonly isActive: boolean;
6
+ /** True when the row offers Undo: no reply runs, and the step of the undo history the configuration is at is one of its changes. */
7
+ readonly canUndo: boolean;
8
+ /** Undoes the changes of the given tool calls while one of them is the step the configuration is at. */
9
+ readonly onUndo: (toolCallIds: ReadonlySet<string>) => void;
6
10
  }
7
11
  /**
8
12
  * A row of notes on what the assistant did in a reply, one per tool call. A click on a note logs
9
- * the input and the result of its call to the console.
13
+ * the input and the result of its call to the console. While one of the row's changes is the step
14
+ * of the undo history the configuration is at, an Undo after the notes takes the row's changes
15
+ * back, up to a change the user made in between.
10
16
  */
11
- export declare const AssistantToolChips: import("react").MemoExoticComponent<({ tools, isActive }: AssistantToolChipsProps) => import("react/jsx-runtime").JSX.Element>;
17
+ export declare const AssistantToolChips: import("react").MemoExoticComponent<({ tools, isActive, canUndo, onUndo }: AssistantToolChipsProps) => import("react/jsx-runtime").JSX.Element>;
@@ -1 +1 @@
1
- import{jsx as t,jsxs as e}from"react/jsx-runtime";import{Tooltip as a,styled as i}from"@mui/material";import{memo as s,useCallback as o}from"react";import{match as n}from"ts-pattern";import{useLocalizable as r}from"#package/react-sdk";import{PhxIcon as l}from"#package/ui-sdk";import{AssistantPanelName as d}from"./theme.js";let m={id:"sdk.assistant.tool.updateDesign.label",message:"Update design"},c={id:"sdk.assistant.tool.readDesign.label",message:"Read design"},p={id:"sdk.assistant.tool.mistake.hint",message:"The assistant made a mistake."},h={id:"sdk.assistant.tool.cancelled.hint",message:"Stopped before it finished."},g={id:"sdk.assistant.tool.noResult.hint",message:"The result of this step was lost. It may or may not have been done."},f={id:"sdk.assistant.tool.toolBudget.hint",message:"Not done: the assistant reached its limit of steps for one message."},u=i("div",{name:d,slot:"chips"})(({theme:{palette:t,spacing:e,typography:a}})=>({...a.caption,alignItems:"start",color:t.text.secondary,display:"grid",gap:e(.75),gridTemplateColumns:"min-content minmax(0, 1fr)"})),k=i("span",{name:d,slot:"activity"})(({theme:{typography:t}})=>({blockSize:`calc(${t.caption.lineHeight} * 1em)`,display:"grid",placeItems:"center"})),x=i(l,{name:d,slot:"activityIcon"})({fontSize:14,'[data-chips="active"] &':{animation:"phx-assistant-turn 2.4s linear infinite","@media (prefers-reduced-motion: reduce)":{animation:"none"}},"@keyframes phx-assistant-turn":{to:{rotate:"360deg"}}}),w=i("span",{name:d,slot:"chip"})(({theme:{palette:t,spacing:e}})=>({cursor:"default",display:"inline-block",whiteSpace:"nowrap","&:not(:last-child)::after":{color:t.text.secondary,content:'"\xb7"',display:"inline-block",marginInline:e(.75),textDecoration:"none"},'&[data-chip="failed"]':{color:t.error.main},'&[data-chip="flawed"]':{textDecoration:"line-through",textDecorationColor:t.error.main}})),y=s(({tool:e})=>{let{chip:i,input:s,result:l}=e,d=r(m),u=r(c),k=r(p),x=r(h),y=r(g),b=r(f),v=n(i.label).with({kind:"text"},({text:t})=>t).with({kind:"updateDesign"},()=>d).with({kind:"readDesign"},()=>u).exhaustive(),C=o(()=>{console.info(`Assistant tool call "${v}"`,{input:s,result:l})},[s,v,l]);return t(a,{title:n(i).with({errorCode:"cancelled"},()=>x).with({errorCode:"no_result"},()=>y).with({errorCode:"tool_budget"},()=>b).with({state:"failed"},{state:"flawed"},()=>k).otherwise(()=>""),placement:"top-start",children:t(w,{onClick:C,"data-chip":i.state,children:v})})});export const AssistantToolChips=s(({tools:a,isActive:i})=>e(u,{"data-chips":i?"active":"done","aria-busy":i,children:[t(k,{children:t(x,{name:"Gear"})}),t("div",{children:a.map(e=>t(y,{tool:e},e.id))})]}));
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ButtonBase as i,Tooltip as a,styled as n}from"@mui/material";import{memo as o,useCallback as s}from"react";import{match as r}from"ts-pattern";import{useLocalizable as d}from"#package/react-sdk";import{PhxIcon as l}from"#package/ui-sdk";import{AssistantPanelName as m}from"./theme.js";let c={id:"sdk.assistant.tool.updateDesign.label",message:"Update design"},p={id:"sdk.assistant.tool.readDesign.label",message:"Read design"},h={id:"sdk.assistant.tool.mistake.hint",message:"The assistant made a mistake."},u={id:"sdk.assistant.tool.cancelled.hint",message:"Stopped before it finished."},f={id:"sdk.assistant.tool.noResult.hint",message:"The result of this step was lost. It may or may not have been done."},g={id:"sdk.assistant.tool.toolBudget.hint",message:"Not done: the assistant reached its limit of steps for one message."},k={id:"sdk.assistant.tool.undo.label",message:"Undo"},x=n("div",{name:m,slot:"chips"})(({theme:{palette:e,spacing:t,typography:i}})=>({...i.caption,alignItems:"start",color:e.text.secondary,display:"grid",gap:t(.75),gridTemplateColumns:"min-content minmax(0, 1fr)"})),y=n("span",{name:m,slot:"activity"})(({theme:{typography:e}})=>({blockSize:`calc(${e.caption.lineHeight} * 1em)`,display:"grid",placeItems:"center"})),w=n(l,{name:m,slot:"activityIcon"})({fontSize:14,'[data-chips="active"] &':{animation:"phx-assistant-turn 2.4s linear infinite","@media (prefers-reduced-motion: reduce)":{animation:"none"}},"@keyframes phx-assistant-turn":{to:{rotate:"360deg"}}}),b=n("span",{name:m,slot:"chip"})(({theme:{palette:e,spacing:t}})=>({cursor:"default",display:"inline-block",whiteSpace:"nowrap","&:not(:last-of-type)::after":{color:e.text.secondary,content:'"\xb7"',display:"inline-block",marginInline:t(.75),textDecoration:"none"},'&[data-chip="failed"]':{color:e.error.main},'&[data-chip="flawed"]':{textDecoration:"line-through",textDecorationColor:e.error.main}})),v=n(i,{name:m,slot:"undoButton"})(({theme:{palette:e,shape:t,spacing:i}})=>({alignItems:"center",animation:"phx-assistant-undo-in 150ms ease-out",borderRadius:t.borderRadius,color:e.primary.main,display:"inline-flex",font:"inherit",gap:i(.5),marginInlineStart:i(1),verticalAlign:"top","&:hover":{textDecoration:"underline"},"@media (prefers-reduced-motion: reduce)":{animation:"none"},"@keyframes phx-assistant-undo-in":{from:{opacity:0}}})),C=o(({tool:t})=>{let{chip:i,input:n,result:o}=t,l=d(c),m=d(p),k=d(h),x=d(u),y=d(f),w=d(g),v=r(i.label).with({kind:"text"},({text:e})=>e).with({kind:"updateDesign"},()=>l).with({kind:"readDesign"},()=>m).exhaustive(),C=s(()=>{console.info(`Assistant tool call "${v}"`,{input:n,result:o})},[n,v,o]);return e(a,{title:r(i).with({errorCode:"cancelled"},()=>x).with({errorCode:"no_result"},()=>y).with({errorCode:"tool_budget"},()=>w).with({state:"failed"},{state:"flawed"},()=>k).otherwise(()=>""),placement:"top-start",children:e(b,{onClick:C,"data-chip":i.state,children:v})})});export const AssistantToolChips=o(({tools:i,isActive:a,canUndo:n,onUndo:o})=>{let r=d(k),m=s(()=>{o(new Set(i.map(({id:e})=>e)))},[o,i]);return t(x,{"data-chips":a?"active":"done","aria-busy":a,children:[e(y,{children:e(w,{name:"Gear"})}),t("div",{children:[i.map(t=>e(C,{tool:t},t.id)),n&&t(v,{onClick:m,children:[e(l,{name:"ArrowCounterClockwise",fontSize:"inherit"}),r]})]})]})});
@@ -1 +1 @@
1
- import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Trans as i}from"@lingui/react";import{Button as t,styled as s}from"@mui/material";import{useCallback as r,useMemo as n}from"react";import{match as o}from"ts-pattern";import{useEagerObservableValue as l}from"#package/react-sdk";import{PhxIcon as d}from"#package/ui-sdk";import{useConfigurator as m}from"../../application-runtime/index.js";import{AssistantToolChips as c}from"./AssistantChips.js";import{AssistantMarkdown as h}from"./AssistantMarkdown.js";import{describeAssistantFailure as p}from"./assistant-failure.js";import{EntityLinkSelectionProvider as g,useCreateEntityLinkSelection as u}from"./entity-link/index.js";import{AssistantPanelName as k}from"./theme.js";import{buildEarlierThreadItems as f,buildThreadView as v}from"./thread-items.js";import{useFollowStream as w}from"./use-follow-stream.js";let b=s("div",{name:k,slot:"thread"})({display:"grid",position:"relative"}),x=s("div",{name:k,slot:"messages"})(({theme:{spacing:e,typography:a}})=>({...a.body2,display:"flex",flexDirection:"column",gap:e(1.5),overflowY:"auto",overscrollBehavior:"contain",paddingBlock:e(2),paddingInline:`var(--phx-sidebar-padding, ${e(2)})`,"& > :first-child":{marginBlockStart:"auto"}})),y=s("div",{name:k,slot:"userMessage"})(({theme:{palette:e,spacing:a}})=>({alignSelf:"flex-end",background:`hsl(from ${e.primary.main} h s l / 12%)`,borderRadius:"14px 14px 4px 14px",maxInlineSize:"85%",overflowWrap:"anywhere",paddingBlock:a(1),paddingInline:a(1.5),whiteSpace:"pre-wrap"})),S=s("div",{name:k,slot:"greeting"})({}),T=s("div",{name:k,slot:"status"})(({theme:{palette:e,typography:a}})=>({...a.caption,color:e.text.secondary})),j=s("div",{name:k,slot:"separator"})(({theme:{palette:e,spacing:a,typography:i}})=>({...i.caption,alignItems:"center",color:e.text.secondary,display:"flex",gap:a(1),"&::before, &::after":{borderBlockStart:`1px solid ${e.divider}`,content:'""',flex:1}})),B=s(t,{name:k,slot:"earlierButton"})({alignSelf:"center"}),E=s("div",{name:k,slot:"notice"})(({theme:{palette:e,shape:a,spacing:i}})=>({alignItems:"center",background:e.action.hover,borderRadius:a.borderRadius,display:"flex",flexWrap:"wrap",gap:i(1),justifyContent:"space-between",paddingBlock:i(1),paddingInline:i(1.5)})),I=s(t,{name:k,slot:"newReplyButton"})(({theme:{palette:e,spacing:a}})=>({background:e.text.primary,borderRadius:"calc(infinity * 1px)",color:e.background.paper,insetBlockEnd:a(1),insetInlineStart:"50%",position:"absolute",translate:"-50% 0","&:hover":{background:e.text.secondary}})),A=({item:a,isActive:t})=>o(a).with({kind:"user"},({text:a})=>e(y,{children:a})).with({kind:"text"},({markdown:a,isStreaming:i})=>e(h,{markdown:a,isStreaming:i})).with({kind:"tools"},({tools:a})=>e(c,{tools:a,isActive:t})).with({kind:"separator"},()=>e(j,{role:"separator",children:e(i,{id:"sdk.assistant.thread.earlierSummarized",message:"Earlier conversation summarized"})})).exhaustive();export const AssistantThread=()=>{let{assistantSystem:s}=m(),c=l(s.chat$),{messages:h,isLoading:k,isResuming:y,earlierThreads:j}=c,{items:C,isThinking:R}=n(()=>v(h,k),[h,k]),L=n(()=>f(j),[j]),z=n(()=>C.filter(({kind:e})=>"user"===e).length,[C]),$=c.error?p(c.failedStatus):void 0,{scrollerRef:D,hasUnseenContent:M,handleScroll:W,scrollToEnd:q}=w(n(()=>({messages:h,isThinking:R,isResuming:y,failure:$?.kind}),[h,R,y,$?.kind]),z),H=u(),N=r(e=>{e.target instanceof Element&&e.target.closest('a, button, input, select, textarea, [role="button"]')||window.getSelection()?.isCollapsed!==!1&&H.release()},[H]),Y=!c.isLoaded&&0===L.length&&!y&&!$;return a(b,{children:[e(g,{selection:H,children:a(x,{ref:D,onScroll:W,onClick:N,role:"log","aria-live":"polite",children:[c.isLoaded&&0===C.length&&0===L.length&&!k&&!y&&!$&&e(S,{children:e(i,{id:"sdk.assistant.thread.greeting",message:"Hi. I can answer questions about this design and change it with you. Tell me what you need."})}),Y&&e(T,{children:e(i,{id:"sdk.assistant.thread.loading",message:"Loading the conversation…"})}),c.isLoadingEarlierThread&&e(T,{children:e(i,{id:"sdk.assistant.thread.loadingEarlier",message:"Loading the earlier conversation…"})}),c.hasEarlierThread&&!c.isLoadingEarlierThread&&e(B,{size:"small",color:"inherit",onClick:s.loadEarlierThread,children:e(i,{id:"sdk.assistant.thread.showEarlier.label",message:"Show earlier conversation"})}),L.map(a=>e(A,{item:a,isActive:!1},a.id)),C.map(a=>e(A,{item:a,isActive:k&&a===C.at(-1)},a.id)),y&&e(T,{children:e(i,{id:"sdk.assistant.thread.resuming",message:"Resuming the conversation…"})}),R&&e(T,{children:e(i,{id:"sdk.assistant.thread.thinking",message:"Thinking…"})}),$&&a(E,{role:"alert",children:[e("span",{children:o($.kind).with("notAvailable",()=>e(i,{id:"sdk.assistant.failure.notAvailable",message:"The assistant is not available here."})).with("busy",()=>e(i,{id:"sdk.assistant.failure.busy",message:"The assistant is still working on an earlier message."})).with("unavailable",()=>e(i,{id:"sdk.assistant.failure.unavailable",message:"The assistant is unavailable right now."})).exhaustive()}),$.canRetry&&e(t,{size:"small",variant:"outlined",color:"inherit",onClick:s.reloadThread,children:e(i,{id:"sdk.assistant.failure.retry.label",message:"Try again"})})]})]})}),M&&e(I,{size:"small",onClick:q,endIcon:e(d,{name:"CaretDown"}),children:e(i,{id:"sdk.assistant.thread.newReply.label",message:"New reply"})})]})};
1
+ import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Trans as i}from"@lingui/react";import{Button as t,styled as s}from"@mui/material";import{useCallback as r,useMemo as n}from"react";import{match as o}from"ts-pattern";import{useEagerObservableValue as l}from"#package/react-sdk";import{PhxIcon as d}from"#package/ui-sdk";import{useConfigurator as m}from"../../application-runtime/index.js";import{AssistantToolChips as c}from"./AssistantChips.js";import{AssistantMarkdown as h}from"./AssistantMarkdown.js";import{describeAssistantFailure as p}from"./assistant-failure.js";import{EntityLinkSelectionProvider as g,useCreateEntityLinkSelection as u}from"./entity-link/index.js";import{AssistantPanelName as k}from"./theme.js";import{buildEarlierThreadItems as f,buildThreadView as v}from"./thread-items.js";import{useFollowStream as b}from"./use-follow-stream.js";let w=s("div",{name:k,slot:"thread"})({display:"grid",position:"relative"}),x=s("div",{name:k,slot:"messages"})(({theme:{spacing:e,typography:a}})=>({...a.body2,display:"flex",flexDirection:"column",gap:e(1.5),overflowY:"auto",overscrollBehavior:"contain",paddingBlock:e(2),paddingInline:`var(--phx-sidebar-padding, ${e(2)})`,"& > :first-child":{marginBlockStart:"auto"}})),y=s("div",{name:k,slot:"userMessage"})(({theme:{palette:e,spacing:a}})=>({alignSelf:"flex-end",background:`hsl(from ${e.primary.main} h s l / 12%)`,borderRadius:"14px 14px 4px 14px",maxInlineSize:"85%",overflowWrap:"anywhere",paddingBlock:a(1),paddingInline:a(1.5),whiteSpace:"pre-wrap"})),T=s("div",{name:k,slot:"greeting"})({}),C=s("div",{name:k,slot:"status"})(({theme:{palette:e,typography:a}})=>({...a.caption,color:e.text.secondary})),S=s("div",{name:k,slot:"separator"})(({theme:{palette:e,spacing:a,typography:i}})=>({...i.caption,alignItems:"center",color:e.text.secondary,display:"flex",gap:a(1),"&::before, &::after":{borderBlockStart:`1px solid ${e.divider}`,content:'""',flex:1}})),j=s(t,{name:k,slot:"earlierButton"})({alignSelf:"center"}),I=s("div",{name:k,slot:"notice"})(({theme:{palette:e,shape:a,spacing:i}})=>({alignItems:"center",background:e.action.hover,borderRadius:a.borderRadius,display:"flex",flexWrap:"wrap",gap:i(1),justifyContent:"space-between",paddingBlock:i(1),paddingInline:i(1.5)})),B=s(t,{name:k,slot:"newReplyButton"})(({theme:{palette:e,spacing:a}})=>({background:e.text.primary,borderRadius:"calc(infinity * 1px)",color:e.background.paper,insetBlockEnd:a(1),insetInlineStart:"50%",position:"absolute",translate:"-50% 0","&:hover":{background:e.text.secondary}})),E=({item:a,isActive:t,undoableToolCallId:s,onUndo:r})=>o(a).with({kind:"user"},({text:a})=>e(y,{children:a})).with({kind:"text"},({markdown:a,isStreaming:i})=>e(h,{markdown:a,isStreaming:i})).with({kind:"tools"},({tools:a})=>e(c,{tools:a,isActive:t,canUndo:a.some(({id:e})=>e===s),onUndo:r})).with({kind:"separator"},()=>e(S,{role:"separator",children:e(i,{id:"sdk.assistant.thread.earlierSummarized",message:"Earlier conversation summarized"})})).exhaustive();export const AssistantThread=()=>{let{assistantSystem:s}=m(),c=l(s.chat$),{messages:h,isLoading:k,isResuming:y,earlierThreads:S}=c,A=l(s.undoTipToolCallId$),R=k?void 0:A,{items:L,isThinking:z}=n(()=>v(h,k),[h,k]),$=n(()=>f(S),[S]),U=n(()=>L.filter(({kind:e})=>"user"===e).length,[L]),D=c.error?p(c.failedStatus):void 0,{scrollerRef:M,hasUnseenContent:W,handleScroll:q,scrollToEnd:H}=b(n(()=>({messages:h,isThinking:z,isResuming:y,failure:D?.kind}),[h,z,y,D?.kind]),U),N=u(),Y=r(e=>{e.target instanceof Element&&e.target.closest('a, button, input, select, textarea, [role="button"]')||window.getSelection()?.isCollapsed!==!1&&N.release()},[N]),F=!c.isLoaded&&0===$.length&&!y&&!D;return a(w,{children:[e(g,{selection:N,children:a(x,{ref:M,onScroll:q,onClick:Y,role:"log","aria-live":"polite",children:[c.isLoaded&&0===L.length&&0===$.length&&!k&&!y&&!D&&e(T,{children:e(i,{id:"sdk.assistant.thread.greeting",message:"Hi. I can answer questions about this design and change it with you. Tell me what you need."})}),F&&e(C,{children:e(i,{id:"sdk.assistant.thread.loading",message:"Loading the conversation…"})}),c.isLoadingEarlierThread&&e(C,{children:e(i,{id:"sdk.assistant.thread.loadingEarlier",message:"Loading the earlier conversation…"})}),c.hasEarlierThread&&!c.isLoadingEarlierThread&&e(j,{size:"small",color:"inherit",onClick:s.loadEarlierThread,children:e(i,{id:"sdk.assistant.thread.showEarlier.label",message:"Show earlier conversation"})}),$.map(a=>e(E,{item:a,isActive:!1,undoableToolCallId:R,onUndo:s.undoToolCalls},a.id)),L.map(a=>e(E,{item:a,isActive:k&&a===L.at(-1),undoableToolCallId:R,onUndo:s.undoToolCalls},a.id)),y&&e(C,{children:e(i,{id:"sdk.assistant.thread.resuming",message:"Resuming the conversation…"})}),z&&e(C,{children:e(i,{id:"sdk.assistant.thread.thinking",message:"Thinking…"})}),D&&a(I,{role:"alert",children:[e("span",{children:o(D.kind).with("notAvailable",()=>e(i,{id:"sdk.assistant.failure.notAvailable",message:"The assistant is not available here."})).with("busy",()=>e(i,{id:"sdk.assistant.failure.busy",message:"The assistant is still working on an earlier message."})).with("unavailable",()=>e(i,{id:"sdk.assistant.failure.unavailable",message:"The assistant is unavailable right now."})).exhaustive()}),D.canRetry&&e(t,{size:"small",variant:"outlined",color:"inherit",onClick:s.reloadThread,children:e(i,{id:"sdk.assistant.failure.retry.label",message:"Try again"})})]})]})}),W&&e(B,{size:"small",onClick:H,endIcon:e(d,{name:"CaretDown"}),children:e(i,{id:"sdk.assistant.thread.newReply.label",message:"New reply"})})]})};
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-10-14T17:52:28.300Z",
2
+ "generatedAt": "2026-10-15T06:23:41.238Z",
3
3
  "categories": [
4
4
  {
5
5
  "name": "Phoenix",