@salsita-ai/sdk 0.49.0-develop.cbee856178 → 0.49.0-develop.d4bea2382c

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 (44) hide show
  1. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  2. package/dist/application-spec/generate-application/code/templates/index.d.ts +1 -1
  3. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  4. package/dist/application-spec/generate-application/code/templates/phoenix-feedback-skill.js +14 -2
  5. package/dist/application-spec/generate-application/code/templates/phoenix-skill.d.ts +13 -1
  6. package/dist/application-spec/generate-application/code/templates/phoenix-skill.js +1 -1
  7. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-control-system.d.ts +7 -3
  8. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-control-system.js +1 -1
  9. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-system.d.ts +24 -1
  10. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-system.js +1 -1
  11. package/dist/docs/3d-scene/cameras/custom.mdx +4 -1
  12. package/dist/docs/3d-scene/cameras/fit-entity.mdx +2 -0
  13. package/dist/docs/3d-scene/cameras/fit-root.mdx +2 -0
  14. package/dist/docs/3d-scene/cameras/fit-selection.mdx +2 -0
  15. package/dist/docs/3d-scene/cameras/locks.mdx +439 -0
  16. package/dist/docs/3d-scene/cameras/static.mdx +2 -0
  17. package/dist/docs/3d-scene/cameras/transitions.mdx +2 -0
  18. package/dist/docs/index.json +12 -1
  19. package/dist/docs/phoenix/phoenix-cli.mdx +1 -1
  20. package/dist/docs/phoenix/project-structure.mdx +1 -1
  21. package/dist/docs/phoenix/sdk-builder-workflow.mdx +4 -3
  22. package/dist/domain/projections/common/camera-orbit.d.ts +37 -9
  23. package/dist/domain/projections/common/camera-orbit.js +1 -1
  24. package/dist/domain/projections/common/index.d.ts +1 -1
  25. package/dist/domain/projections/common/index.js +1 -1
  26. package/dist/domain/system-components/fit-entity-orthographic-camera-component.d.ts +8 -0
  27. package/dist/domain/system-components/fit-entity-orthographic-camera-component.js +1 -1
  28. package/dist/domain/system-components/fit-entity-perspective-camera-component.d.ts +8 -0
  29. package/dist/domain/system-components/fit-entity-perspective-camera-component.js +1 -1
  30. package/dist/domain/system-components/fit-root-orthographic-camera-component.d.ts +8 -0
  31. package/dist/domain/system-components/fit-root-orthographic-camera-component.js +1 -1
  32. package/dist/domain/system-components/fit-root-perspective-camera-component.d.ts +8 -0
  33. package/dist/domain/system-components/fit-root-perspective-camera-component.js +1 -1
  34. package/dist/domain/system-components/fit-selection-orthographic-camera-component.d.ts +8 -0
  35. package/dist/domain/system-components/fit-selection-orthographic-camera-component.js +1 -1
  36. package/dist/domain/system-components/fit-selection-perspective-camera-component.d.ts +8 -0
  37. package/dist/domain/system-components/fit-selection-perspective-camera-component.js +1 -1
  38. package/dist/domain/system-components/static-orthographic-camera-component.d.ts +8 -0
  39. package/dist/domain/system-components/static-orthographic-camera-component.js +1 -1
  40. package/dist/domain/system-components/static-perspective-camera-component.d.ts +8 -0
  41. package/dist/domain/system-components/static-perspective-camera-component.js +1 -1
  42. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +16 -0
  43. package/package.json +3 -3
  44. package/schema/domain-spec.json +208 -0
@@ -1,6 +1,6 @@
1
- import{GeneratedApplicationFile as e}from"../../core/index.js";import{generateAgentsMd as t,generateApplicationSpecEnvCi as n,generateApplicationSpecEnvExample as r,generateApplicationSpecEslintConfig as o,generateApplicationSpecFavicon as i,generateApplicationSpecGitignore as a,generateApplicationSpecNvmrc as s,generateApplicationSpecPackageJson as c,generateApplicationSpecPrettierConfig as l,generateApplicationSpecPrettierIgnore as p,generateApplicationSpecPrivateAssetsReadme as m,generateApplicationSpecPublicAssetsReadme as f,generateApplicationSpecReadme as d,generateApplicationSpecRobotsTxt as g,generateApplicationSpecTsconfig as u,generateApplicationSpecWebhookImplementation as x,generateLinguiRcFile as v,generatePhoenixCliSkill as h,generatePhoenixCreateConfiguratorSkill as j,generatePhoenixCreateConfiguratorSkillReferences as k,generatePhoenixFeedbackSkill as b,generatePhoenixReviewSkill as E,generatePhoenixSkill as F,stripSdkMarkerContent as M}from"./templates/index.js";export const agentSkillRoots=[".claude/skills",".agents/skills"];let P=[{path:"CLAUDE.md",generatedPrefix:""},{path:".cursor/rules/phoenix.mdc",generatedPrefix:`---
1
+ import{GeneratedApplicationFile as e}from"../../core/index.js";import{followsCanaryBuilds as t,generateAgentsMd as n,generateApplicationSpecEnvCi as r,generateApplicationSpecEnvExample as o,generateApplicationSpecEslintConfig as i,generateApplicationSpecFavicon as a,generateApplicationSpecGitignore as s,generateApplicationSpecNvmrc as c,generateApplicationSpecPackageJson as l,generateApplicationSpecPrettierConfig as p,generateApplicationSpecPrettierIgnore as m,generateApplicationSpecPrivateAssetsReadme as f,generateApplicationSpecPublicAssetsReadme as d,generateApplicationSpecReadme as g,generateApplicationSpecRobotsTxt as u,generateApplicationSpecTsconfig as x,generateApplicationSpecWebhookImplementation as h,generateLinguiRcFile as v,generatePhoenixCliSkill as j,generatePhoenixCreateConfiguratorSkill as k,generatePhoenixCreateConfiguratorSkillReferences as b,generatePhoenixFeedbackSkill as E,generatePhoenixReviewSkill as F,generatePhoenixSkill as M,stripSdkMarkerContent as P}from"./templates/index.js";export const agentSkillRoots=[".claude/skills",".agents/skills"];let w=[{path:"CLAUDE.md",generatedPrefix:""},{path:".cursor/rules/phoenix.mdc",generatedPrefix:`---
2
2
  description: Phoenix SDK project rules
3
3
  globs:
4
4
  alwaysApply: true
5
5
  ---
6
- `}],w=[".claude/commands/review.md",".cursor/commands/review.md"],A=(e,t)=>e.projectFiles.find(e=>e.file.name===t)?.file.content;export const generateProjectScaffolding=S=>{let D,{applicationSpec:R,categorizedFiles:$,sdkVersion:C,cliUseWorkspace:L,name:y,description:K}=S,G=A($,"package.json"),I=A($,".gitignore"),N=void 0!==L?L:R.appMetadata.workspace,T=(t=>{let n=[],r=[...w];for(let{path:o,generatedPrefix:i}of P){let a=A(t,o);if(void 0===a)continue;let s=M(a,i);void 0===s?r.push(o):s!==a&&n.push(e(o,s,{format:!1}))}return{strippedFiles:n,removedFiles:r}})($);return{generatedProjectFiles:[e("package.json",c({sdkVersion:C,useWorkspace:N,useClerk:R.appMetadata.useClerk??!1,name:y,description:K,existingContent:G})),e(".nvmrc",s(),{format:!1}),e(".linguirc",v(R.configurator.ui.availableLocales),{format:!1}),e("tsconfig.json",u()),e("eslint.config.js",o()),e(".gitignore",a(I)),...(D=[{name:"phoenix",content:F(),references:[]},{name:"phoenix-cli",content:h(),references:[]},{name:"phoenix-create-configurator",content:j(),references:k()},{name:"phoenix-feedback",content:b(),references:[]},{name:"phoenix-review",content:E(),references:[]}],agentSkillRoots.flatMap(t=>D.flatMap(n=>[e(`${t}/${n.name}/SKILL.md`,n.content),...n.references.map(r=>e(`${t}/${n.name}/${r.path}`,r.content))]))),...T.strippedFiles],initialProjectFiles:[e("README.md",d(y)),e("AGENTS.md",t()),e(".env.ci",n()),e(".env.example",r()),e("public/favicon.ico",i(),{binary:!0}),e("public/robots.txt",g(),{format:!1}),e("assets/public/README.md",f()),e("assets/private/README.md",m()),e("src/webhooks/index.ts",x())],prettierFiles:[e("prettier.config.js",l()),e(".prettierignore",p(),{format:!1})],removedProjectFiles:T.removedFiles}};
6
+ `}],A=[".claude/commands/review.md",".cursor/commands/review.md"],S=(e,t)=>e.projectFiles.find(e=>e.file.name===t)?.file.content;export const generateProjectScaffolding=C=>{let D,{applicationSpec:L,categorizedFiles:R,sdkVersion:$,cliUseWorkspace:y,name:K,description:G}=C,I=S(R,"package.json"),N=S(R,".gitignore"),T=void 0!==y?y:L.appMetadata.workspace,U=(t=>{let n=[],r=[...A];for(let{path:o,generatedPrefix:i}of w){let a=S(t,o);if(void 0===a)continue;let s=P(a,i);void 0===s?r.push(o):s!==a&&n.push(e(o,s,{format:!1}))}return{strippedFiles:n,removedFiles:r}})(R);return{generatedProjectFiles:[e("package.json",l({sdkVersion:$,useWorkspace:T,useClerk:L.appMetadata.useClerk??!1,name:K,description:G,existingContent:I})),e(".nvmrc",c(),{format:!1}),e(".linguirc",v(L.configurator.ui.availableLocales),{format:!1}),e("tsconfig.json",x()),e("eslint.config.js",i()),e(".gitignore",s(N)),...(D=[{name:"phoenix",content:M({checkLatestCanary:t({useWorkspace:T,sdkVersion:$})}),references:[]},{name:"phoenix-cli",content:j(),references:[]},{name:"phoenix-create-configurator",content:k(),references:b()},{name:"phoenix-feedback",content:E(),references:[]},{name:"phoenix-review",content:F(),references:[]}],agentSkillRoots.flatMap(t=>D.flatMap(n=>[e(`${t}/${n.name}/SKILL.md`,n.content),...n.references.map(r=>e(`${t}/${n.name}/${r.path}`,r.content))]))),...U.strippedFiles],initialProjectFiles:[e("README.md",g(K)),e("AGENTS.md",n()),e(".env.ci",r()),e(".env.example",o()),e("public/favicon.ico",a(),{binary:!0}),e("public/robots.txt",u(),{format:!1}),e("assets/public/README.md",d()),e("assets/private/README.md",f()),e("src/webhooks/index.ts",h())],prettierFiles:[e("prettier.config.js",p()),e(".prettierignore",m(),{format:!1})],removedProjectFiles:U.removedFiles}};
@@ -46,7 +46,7 @@ export { generatePhoenixCliSkill } from './phoenix-cli-skill.js';
46
46
  export { generatePhoenixCreateConfiguratorSkill, generatePhoenixCreateConfiguratorSkillReferences, } from './phoenix-create-configurator-skill.js';
47
47
  export { generatePhoenixFeedbackSkill } from './phoenix-feedback-skill.js';
48
48
  export { generatePhoenixReviewSkill } from './phoenix-review-skill.js';
49
- export { generatePhoenixSkill } from './phoenix-skill.js';
49
+ export { type PhoenixSkillOptions, followsCanaryBuilds, generatePhoenixSkill } from './phoenix-skill.js';
50
50
  export { generateApplicationSpecPrettierConfig } from './prettier-config.js';
51
51
  export { generateApplicationSpecPrettierIgnore } from './prettier-ignore.js';
52
52
  export { generateApplicationSpecPrivateAssetsReadme } from './private-assets-readme.js';
@@ -1 +1 @@
1
- export{stripSdkMarkerContent}from"./common/strip-sdk-marker-content.js";export{generateAgentsMd}from"./agents-md.js";export{generateApplicationSpecAdminConfig}from"./admin-config.js";export{generateApplicationSpecAdminCode}from"./admin-domain-code.js";export{generateApplicationSpecAdminFrontendCode}from"./admin-frontend-code.js";export{generateAdminGlobalBindingBlock,generateApplicationSpecAdminGlobals}from"./admin-globals.js";export{generateApplicationSpecTypesModule}from"./application-types.js";export{generateApplicationSpecAdminFile}from"./admin.js";export{generateApplicationSpecConfiguratorConfig}from"./configurator-config.js";export{generateApplicationSpecConfiguratorCode}from"./configurator-domain-code.js";export{generateApplicationSpecConfiguratorFrontendCode}from"./configurator-frontend-code.js";export{generateApplicationSpecConfiguratorGlobals,generateConfiguratorGlobalBindingBlock}from"./configurator-globals.js";export{generateApplicationSpecConfiguratorFile}from"./configurator.js";export{generateApplicationSpecDomainSpecFile}from"./domain-spec.js";export{generateImportedStateConstants,generatePhxExportsBlock,generatePhxGlobalsBlock,generateStateConstants}from"./domain-spec/index.js";export{generateApplicationSpecDomainFile}from"./domain.js";export{generateApplicationSpecEditorFile}from"./editor.js";export{generateApplicationSpecEmbeddedAdmin}from"./embedded-admin.js";export{generateApplicationSpecEmbeddedConfigurator}from"./embedded-configurator.js";export{generateApplicationSpecEnvCi}from"./env-ci.js";export{generateApplicationSpecEnvExample}from"./env-example.js";export{generateApplicationSpecEslintConfig}from"./eslint-config.js";export{generateApplicationSpecFavicon}from"./favicon.js";export{generateApplicationSpecFrontendConfig}from"./frontend-config.js";export{generateApplicationSpecGitignore}from"./gitignore.js";export{generateApplicationSpecGlobals}from"./globals.js";export{generateApplicationSpecHtmlAdminIndex}from"./html-admin-index.js";export{generateApplicationSpecHtmlIndex}from"./html-index.js";export{generateLinguiRcFile}from"./lingui-rc.js";export{generateApplicationSpecNvmrc}from"./nvmrc.js";export{generateApplicationSpecPackageJson}from"./package-json.js";export{generatePhoenixCliSkill}from"./phoenix-cli-skill.js";export{generatePhoenixCreateConfiguratorSkill,generatePhoenixCreateConfiguratorSkillReferences}from"./phoenix-create-configurator-skill.js";export{generatePhoenixFeedbackSkill}from"./phoenix-feedback-skill.js";export{generatePhoenixReviewSkill}from"./phoenix-review-skill.js";export{generatePhoenixSkill}from"./phoenix-skill.js";export{generateApplicationSpecPrettierConfig}from"./prettier-config.js";export{generateApplicationSpecPrettierIgnore}from"./prettier-ignore.js";export{generateApplicationSpecPrivateAssetsReadme}from"./private-assets-readme.js";export{generateApplicationSpecPublicAssetsReadme}from"./public-assets-readme.js";export{generateApplicationSpecReadme}from"./readme.js";export{generateApplicationSpecRobotsTxt}from"./robots-txt.js";export{generateApplicationSpecStoryDevDeclaration,generateApplicationSpecStoryPhxModule,generateApplicationSpecStoryRawProjectFiles,generateApplicationSpecStoryWrapper}from"./story/index.js";export{generateApplicationSpecTsconfig}from"./tsconfig.js";export{generateApplicationSpecWebhookBuilders}from"./webhook-builders.js";export{generateApplicationSpecWebhookImplementation}from"./webhook-implementation.js";export{generateApplicationSpecWebhookIndex}from"./webhook-index.js";export{generateApplicationSpecWebhooksConfig}from"./webhooks-config.js";
1
+ export{stripSdkMarkerContent}from"./common/strip-sdk-marker-content.js";export{generateAgentsMd}from"./agents-md.js";export{generateApplicationSpecAdminConfig}from"./admin-config.js";export{generateApplicationSpecAdminCode}from"./admin-domain-code.js";export{generateApplicationSpecAdminFrontendCode}from"./admin-frontend-code.js";export{generateAdminGlobalBindingBlock,generateApplicationSpecAdminGlobals}from"./admin-globals.js";export{generateApplicationSpecTypesModule}from"./application-types.js";export{generateApplicationSpecAdminFile}from"./admin.js";export{generateApplicationSpecConfiguratorConfig}from"./configurator-config.js";export{generateApplicationSpecConfiguratorCode}from"./configurator-domain-code.js";export{generateApplicationSpecConfiguratorFrontendCode}from"./configurator-frontend-code.js";export{generateApplicationSpecConfiguratorGlobals,generateConfiguratorGlobalBindingBlock}from"./configurator-globals.js";export{generateApplicationSpecConfiguratorFile}from"./configurator.js";export{generateApplicationSpecDomainSpecFile}from"./domain-spec.js";export{generateImportedStateConstants,generatePhxExportsBlock,generatePhxGlobalsBlock,generateStateConstants}from"./domain-spec/index.js";export{generateApplicationSpecDomainFile}from"./domain.js";export{generateApplicationSpecEditorFile}from"./editor.js";export{generateApplicationSpecEmbeddedAdmin}from"./embedded-admin.js";export{generateApplicationSpecEmbeddedConfigurator}from"./embedded-configurator.js";export{generateApplicationSpecEnvCi}from"./env-ci.js";export{generateApplicationSpecEnvExample}from"./env-example.js";export{generateApplicationSpecEslintConfig}from"./eslint-config.js";export{generateApplicationSpecFavicon}from"./favicon.js";export{generateApplicationSpecFrontendConfig}from"./frontend-config.js";export{generateApplicationSpecGitignore}from"./gitignore.js";export{generateApplicationSpecGlobals}from"./globals.js";export{generateApplicationSpecHtmlAdminIndex}from"./html-admin-index.js";export{generateApplicationSpecHtmlIndex}from"./html-index.js";export{generateLinguiRcFile}from"./lingui-rc.js";export{generateApplicationSpecNvmrc}from"./nvmrc.js";export{generateApplicationSpecPackageJson}from"./package-json.js";export{generatePhoenixCliSkill}from"./phoenix-cli-skill.js";export{generatePhoenixCreateConfiguratorSkill,generatePhoenixCreateConfiguratorSkillReferences}from"./phoenix-create-configurator-skill.js";export{generatePhoenixFeedbackSkill}from"./phoenix-feedback-skill.js";export{generatePhoenixReviewSkill}from"./phoenix-review-skill.js";export{followsCanaryBuilds,generatePhoenixSkill}from"./phoenix-skill.js";export{generateApplicationSpecPrettierConfig}from"./prettier-config.js";export{generateApplicationSpecPrettierIgnore}from"./prettier-ignore.js";export{generateApplicationSpecPrivateAssetsReadme}from"./private-assets-readme.js";export{generateApplicationSpecPublicAssetsReadme}from"./public-assets-readme.js";export{generateApplicationSpecReadme}from"./readme.js";export{generateApplicationSpecRobotsTxt}from"./robots-txt.js";export{generateApplicationSpecStoryDevDeclaration,generateApplicationSpecStoryPhxModule,generateApplicationSpecStoryRawProjectFiles,generateApplicationSpecStoryWrapper}from"./story/index.js";export{generateApplicationSpecTsconfig}from"./tsconfig.js";export{generateApplicationSpecWebhookBuilders}from"./webhook-builders.js";export{generateApplicationSpecWebhookImplementation}from"./webhook-implementation.js";export{generateApplicationSpecWebhookIndex}from"./webhook-index.js";export{generateApplicationSpecWebhooksConfig}from"./webhooks-config.js";
@@ -12,8 +12,20 @@ When to ask is your judgement:
12
12
  - **At the end of the task** — everything else. Collect the details while you work and show all drafts together in your final message.
13
13
  - **When the user asks** you to send feedback — draft it and show it before sending.
14
14
 
15
- The draft is exactly what would be sent: the type, the message, the project if you name one with \`--projectSlug\`, and the contact if the user gave one. Send it unchanged after a yes; if the user edits it, send their version. If they decline, drop it and do not raise that problem again.`,n=`## What Never Goes Into Feedback
15
+ The draft is exactly what would be sent: the type, the message, the project if you name one with \`--projectSlug\`, and the contact if the user gave one. Send it unchanged after a yes; if the user edits it, send their version. If they decline, drop it and do not raise that problem again.`,a=`## Ask for a Rating After a Deploy
16
+ When \`phoenix deploy\` reports that the configurator is live, tell the user the live URL first, then ask them to rate building with Phoenix so far:
17
+ - Ask for a score from 0 to 10, where 10 is the best, and one optional sentence on what would have made it better. Say in the same question that the answer is sent to the Phoenix team as feedback.
18
+ - Ask once per conversation, after the first deploy that goes live. Do not ask again after later deploys, and do not ask after a deploy that failed.
19
+ - Never choose or guess the score yourself. If the user skips the question or declines, send nothing and do not ask again.
20
+
21
+ A rating is feedback like any other, so the rules of this skill apply. Write the comment from the user's words, and take out anything the last section forbids: replace this project's product, customer and people names with generic words. Then show the user the three lines below exactly as they would be sent and send them only after a yes; if they edit the text, send their version. Use \`none\` when they gave no comment:
22
+ \`\`\`bash
23
+ pnpm phoenix feedback 'Rating: <score>/10
24
+ Comment: <their comment, or none>
25
+ Stage: after a deploy' --type other --agent '<your agent name>'
26
+ \`\`\`
27
+ Keep the three labels and their order, so the ratings can be read out of the feedback list. A rating is not a bug report: when the comment names a concrete problem, offer to draft it as its own feedback as the sections above describe.`,n=`## What Never Goes Into Feedback
16
28
  The message leaves this project and is read by the Phoenix team. Never include:
17
29
  - secrets: API tokens, keys, passwords or \`.env\` values;
18
30
  - customer or end-user data, or personal data such as names and email addresses, other than a contact the user asked you to add;
19
- - this project's source code, specs or business rules. Describe the shape of the problem in words, or reduce it to a generic example that contains nothing of the project.`;export const generatePhoenixFeedbackSkill=()=>e({name:"phoenix-feedback",description:"Drafting feedback about the Phoenix SDK, CLI, documentation or Studio for the Phoenix team and sending it with the phoenix feedback command once the user approves the draft, covering when to raise it, how to write it and what must never be included. Use when the SDK or CLI misbehaves, a doc or skill misled you, a missing feature forced a workaround, or the user asks to send feedback.",sections:[t,o,"## How to Send It\n```bash\npnpm phoenix feedback '<message>' --type bug --agent '<your agent name>'\n```\n- Put the message in single quotes, so the shell passes backticks, `$` and double quotes through unchanged. In bash or zsh, write a single quote inside the message as `'\\''`; in PowerShell, double it instead.\n- `--type` is `bug`, `idea` or `other` (default `other`). Use `idea` for a missing feature or an improvement.\n- `--agent` names you, the coding agent, so the team can tell agent feedback from a person's.\n- `--contact '<email>'` only when the user wants a reply and gave you the contact to use.\n- The workspace, user, SDK version and the spec's `project` are attached automatically. `--projectSlug <slug>` names another project of the workspace.\n- A message holds at most 5000 characters.\n\nWrite the message for someone who cannot see this project: what you ran or called, what you expected, what happened instead with the error text, and the workaround if you found one. Before quoting an error, replace this project's names, file paths and values in it with generic placeholders, as the list below requires.\n\nThe command needs `phoenix login`. If it answers that you are not signed in, tell the user once and continue; do not sign in yourself, because the sign-in is approved in a browser.\n\nAfter sending, confirm it to the user in one line.",n]});
31
+ - this project's source code, specs or business rules. Describe the shape of the problem in words, or reduce it to a generic example that contains nothing of the project.`;export const generatePhoenixFeedbackSkill=()=>e({name:"phoenix-feedback",description:"Drafting feedback about the Phoenix SDK, CLI, documentation or Studio for the Phoenix team and sending it with the phoenix feedback command once the user approves the draft, covering when to raise it, how to write it, the rating asked for after a deploy and what must never be included. Use when the SDK or CLI misbehaves, a doc or skill misled you, a missing feature forced a workaround, the user asks to send feedback, or phoenix deploy has just gone live and the user should be asked to rate the experience from 0 to 10.",sections:[t,o,"## How to Send It\n```bash\npnpm phoenix feedback '<message>' --type bug --agent '<your agent name>'\n```\n- Put the message in single quotes, so the shell passes backticks, `$` and double quotes through unchanged. In bash or zsh, write a single quote inside the message as `'\\''`; in PowerShell, double it instead.\n- `--type` is `bug`, `idea` or `other` (default `other`). Use `idea` for a missing feature or an improvement.\n- `--agent` names you, the coding agent, so the team can tell agent feedback from a person's.\n- `--contact '<email>'` only when the user wants a reply and gave you the contact to use.\n- The workspace, user, SDK version and the spec's `project` are attached automatically. `--projectSlug <slug>` names another project of the workspace.\n- A message holds at most 5000 characters.\n\nWrite the message for someone who cannot see this project: what you ran or called, what you expected, what happened instead with the error text, and the workaround if you found one. Before quoting an error, replace this project's names, file paths and values in it with generic placeholders, as the list below requires.\n\nThe command needs `phoenix login`. If it answers that you are not signed in, tell the user once and continue; do not sign in yourself, because the sign-in is approved in a browser.\n\nAfter sending, confirm it to the user in one line.",a,n]});
@@ -1 +1,13 @@
1
- export declare const generatePhoenixSkill: () => string;
1
+ export interface PhoenixSkillOptions {
2
+ readonly checkLatestCanary: boolean;
3
+ }
4
+ /**
5
+ * Whether a project follows the canary builds: it installs the SDK from npm, and the build it was generated by is
6
+ * itself a canary. A workspace project follows the local SDK, a stable release stays on releases, and a PR beta
7
+ * (`<version>-pr-<n>.<sha>`) is the build under test, so none of them may be moved onto canary.
8
+ */
9
+ export declare const followsCanaryBuilds: (project: {
10
+ readonly useWorkspace: boolean;
11
+ readonly sdkVersion: string;
12
+ }) => boolean;
13
+ export declare const generatePhoenixSkill: ({ checkLatestCanary }: PhoenixSkillOptions) => string;
@@ -1 +1 @@
1
- import{renderAgentSkill as e}from"./common/agent-skill.js";import{sdkGuidanceSections as o}from"./common/sdk-content.js";export const generatePhoenixSkill=()=>e({name:"phoenix",description:"Phoenix SDK project conventions, covering project structure, architecture principles, key patterns, file and export rules, webhook membership, and how to search the SDK documentation. Use before writing or changing specs or source code in this Phoenix configurator project.",sections:[o.projectStructure,o.architecturePrinciples,o.keyPatterns,o.fileConventions,o.exportRules,o.webhookMembership,o.doNotEdit,"## Code Review\nUse the `phoenix-review` skill to review code changes before committing.",o.sdkDocumentation]});
1
+ import{renderAgentSkill as e}from"./common/agent-skill.js";import{sdkGuidanceSections as t}from"./common/sdk-content.js";export const followsCanaryBuilds=e=>!e.useWorkspace&&e.sdkVersion.includes("-develop.");export const generatePhoenixSkill=({checkLatestCanary:n})=>e({name:"phoenix",description:`Phoenix SDK project conventions, covering project structure, architecture principles, key patterns, file and export rules, webhook membership, and how to search the SDK documentation${n?", and the internal-beta check for the latest canary SDK build":""}. Use before writing or changing specs or source code in this Phoenix configurator project.`,sections:[...n?["## Stay on the Latest Canary\nDuring the internal beta this project uses the `canary` build of the SDK, which is published from every change to the platform. Once per conversation, before you change anything, compare the installed version with the newest canary:\n```bash\npnpm list @salsita-ai/sdk --depth 0\nnpm view @salsita-ai/sdk@canary version\n```\nWhen they differ, tell the user which version you are moving to, then update the project and regenerate its files:\n```bash\npnpm add @salsita-ai/sdk@canary\npnpm phoenix sync\n```\n`sync` rewrites the Phoenix skills, so read the ones you use again after it. When the user runs a globally installed `phoenix`, update that too with `npm install -g @salsita-ai/sdk@canary`. If the project no longer builds after the update, stop and tell the user; the `phoenix-feedback` skill covers reporting it."]:[],t.projectStructure,t.architecturePrinciples,t.keyPatterns,t.fileConventions,t.exportRules,t.webhookMembership,t.doNotEdit,"## Code Review\nUse the `phoenix-review` skill to review code changes before committing.",t.sdkDocumentation]});
@@ -34,8 +34,12 @@ type ScheduledCameraTransition<T extends CameraControlType, C extends Perspectiv
34
34
  abortSignal: AbortSignal;
35
35
  firstLoad: boolean;
36
36
  typeChanged: boolean;
37
+ bypassViewLocks: boolean;
37
38
  }) => Promise<void>;
38
39
  };
40
+ interface CameraTransitionOptions {
41
+ readonly bypassViewLocks?: boolean;
42
+ }
39
43
  type SchedulingResult = {
40
44
  finished: Promise<void>;
41
45
  abort: () => void;
@@ -88,9 +92,9 @@ export declare class CameraControlSystem {
88
92
  readonly resetCamera: () => void;
89
93
  readonly setTransitionState: (state: 'starting' | 'active' | 'rest' | 'sleep') => void;
90
94
  readonly reportRender: () => void;
91
- scheduleCameraTransition(transition: ScheduledCameraTransition<typeof CameraControlType.Perspective, PerspectiveCamera>): SchedulingResult;
92
- scheduleCameraTransition(transition: ScheduledCameraTransition<typeof CameraControlType.Orthographic, OrthographicCamera>): SchedulingResult;
93
- scheduleCameraTransition(transition: ScheduledCameraTransition<typeof CameraControlType.Perspective | typeof CameraControlType.Orthographic, PerspectiveCamera | OrthographicCamera>): SchedulingResult;
95
+ scheduleCameraTransition(transition: ScheduledCameraTransition<typeof CameraControlType.Perspective, PerspectiveCamera>, options?: CameraTransitionOptions): SchedulingResult;
96
+ scheduleCameraTransition(transition: ScheduledCameraTransition<typeof CameraControlType.Orthographic, OrthographicCamera>, options?: CameraTransitionOptions): SchedulingResult;
97
+ scheduleCameraTransition(transition: ScheduledCameraTransition<typeof CameraControlType.Perspective | typeof CameraControlType.Orthographic, PerspectiveCamera | OrthographicCamera>, options?: CameraTransitionOptions): SchedulingResult;
94
98
  readonly setCameraControlState: (active: 'starting' | 'moving' | 'idle') => void;
95
99
  /**
96
100
  * Adapt both cameras to a new canvas size. With `manual: true` R3F no longer
@@ -1 +1 @@
1
- import t from"promise-queue";import{BehaviorSubject as e,Subject as r,debounceTime as a,map as i,of as o,shareReplay as s,startWith as n,switchMap as h}from"rxjs";import{OrthographicCamera as l,PerspectiveCamera as p}from"three";import{match as C}from"ts-pattern";import{DEFAULT_DIMENSION_UNIT as c,distanceUnitToDefault as m}from"#package/domain";import{createCameraControls as g}from"./create-camera-controls.js";export const CameraControlType={Perspective:"perspective",Orthographic:"orthographic"};let d=t=>Number.isFinite(t.width)&&Number.isFinite(t.height)&&t.width>0&&t.height>0;export class CameraControlSystem{_context;perspectiveCamera=Object.assign(new p,{manual:!0});orthographicCamera=Object.assign(new l,{manual:!0});perspectiveCameraControls=g(this.perspectiveCamera);orthographicCameraControls=g(this.orthographicCamera);_transitionState$=new e("still");_rendered$=new r;_transitionQueue=new t(1,1/0);_activeCameraControls$=new e(void 0);activeCameraControls$=this._activeCameraControls$;_canvasPadding$=new e({top:0,right:0,bottom:0,left:0});canvasPadding$=this._canvasPadding$;_generation=null;_pendingAbortController=void 0;_lastTransition=void 0;_cameraControlState$=new e("idle");cameraControlState$=this._cameraControlState$;transitionState$=this._transitionState$.pipe(h(t=>"sleep"===t?this._rendered$.pipe(a(200),i(()=>"still"),n("sleep")):o(t)),s(1));constructor(t){this._context=t,this.perspectiveCamera.fov=50,this.perspectiveCamera.near=m(500,c),this.perspectiveCamera.far=m(4e4,c),this.perspectiveCamera.position.set(0,1,0),this.orthographicCamera.left=m(-500,c),this.orthographicCamera.bottom=m(-500,c),this.orthographicCamera.right=m(500,c),this.orthographicCamera.top=m(500,c),this.orthographicCamera.far=m(4e4,c),this.orthographicCamera.near=m(500,c),this.orthographicCamera.position.set(0,1,0),this._context.configuratorExtensionSelectors.getCanvasSystem().interactiveViewport$.subscribe(t=>{null!==t&&this.handleCanvasResize(t)})}_seedCameraControls=()=>{this.perspectiveCameraControls.enabled=!0,this.orthographicCameraControls.enabled=!0,this.perspectiveCameraControls.rotateTo(0,1.2,!1),this.orthographicCameraControls.rotateTo(0,1.2,!1),this.orthographicCameraControls.dollyTo(m(1e4,c),!1)};resetCamera=()=>{this._lastTransition&&this.scheduleCameraTransition(this._lastTransition)};setTransitionState=t=>{this._transitionState$.next(t)};reportRender=()=>{this._rendered$.next()};scheduleCameraTransition(t){this._pendingAbortController?.abort();let e=new AbortController;this._pendingAbortController=e;let r=this._generation;return{finished:this._transitionQueue.add(async()=>{let a=e.signal;if(a.aborted||!r?.active)return;let i=C(t.cameraType).with(CameraControlType.Perspective,()=>this.perspectiveCameraControls).with(CameraControlType.Orthographic,()=>this.orthographicCameraControls).exhaustive(),o=null==this._activeCameraControls$.value,s=this._activeCameraControls$.value!==i;s&&this._activeCameraControls$.next(i),this._lastTransition=t,await t.run({controls:i,camera:i.camera,abortSignal:a,firstLoad:o,typeChanged:s})}),abort:()=>{e.abort()}}}setCameraControlState=t=>{this._cameraControlState$.next(t)};handleCanvasResize=t=>{if(!d(t))return;let e=t.width/t.height;this.perspectiveCamera.aspect=e,this.perspectiveCamera.updateProjectionMatrix();let r=this.orthographicCamera,a=(r.left+r.right)/2,i=(r.top-r.bottom)/2*e;r.left=a-i,r.right=a+i,r.updateProjectionMatrix()};setCanvasPadding=t=>{let e=this._canvasPadding$.value;(e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left)&&this._canvasPadding$.next(t)};enable=()=>{this.perspectiveCameraControls.enabled=!0,this.orthographicCameraControls.enabled=!0};disable=()=>{this.perspectiveCameraControls.enabled=!1,this.orthographicCameraControls.enabled=!1};fitRootCamera=()=>{let t=this._context.configuratorExtensionSelectors.getCanvasSystem().ready$.value?.scene;t&&this.perspectiveCameraControls.fitToSphere(t,!1)};fitEntity=t=>{let e=this._context.configuratorExtensionSelectors.getCanvasSystem().ready$.value?.scene;if(!e)return;let r=e.getObjectByName(t.toString());null!=r&&this.perspectiveCameraControls.fitToBox(r,!1)};setCameraOffset=t=>{let{padding:e,viewPort:r,camera:a}=t;if(!d(r))return;if(![e.top,e.right,e.bottom,e.left].every(Number.isFinite)||e.left+e.right>=r.width||e.top+e.bottom>=r.height)return void a.clearViewOffset();let i=(e.right-e.left)/2,o=(e.bottom-e.top)/2;a.setViewOffset(r.width,r.height,i,o,r.width,r.height)};initialize=()=>{null!==this._generation&&(this._generation.active=!1),this._pendingAbortController?.abort(),this._pendingAbortController=void 0,this._lastTransition=void 0,this._transitionState$.next("still"),this._cameraControlState$.next("idle"),this._activeCameraControls$.next(void 0),this._generation={active:!0},this._seedCameraControls()}}
1
+ import t from"promise-queue";import{BehaviorSubject as e,Subject as r,debounceTime as a,map as i,of as o,shareReplay as s,startWith as n,switchMap as h}from"rxjs";import{OrthographicCamera as l,PerspectiveCamera as p}from"three";import{match as c}from"ts-pattern";import{DEFAULT_DIMENSION_UNIT as C,distanceUnitToDefault as m}from"#package/domain";import{createCameraControls as g}from"./create-camera-controls.js";export const CameraControlType={Perspective:"perspective",Orthographic:"orthographic"};let d=t=>Number.isFinite(t.width)&&Number.isFinite(t.height)&&t.width>0&&t.height>0;export class CameraControlSystem{_context;perspectiveCamera=Object.assign(new p,{manual:!0});orthographicCamera=Object.assign(new l,{manual:!0});perspectiveCameraControls=g(this.perspectiveCamera);orthographicCameraControls=g(this.orthographicCamera);_transitionState$=new e("still");_rendered$=new r;_transitionQueue=new t(1,1/0);_activeCameraControls$=new e(void 0);activeCameraControls$=this._activeCameraControls$;_canvasPadding$=new e({top:0,right:0,bottom:0,left:0});canvasPadding$=this._canvasPadding$;_generation=null;_pendingAbortController=void 0;_lastTransition=void 0;_cameraControlState$=new e("idle");cameraControlState$=this._cameraControlState$;transitionState$=this._transitionState$.pipe(h(t=>"sleep"===t?this._rendered$.pipe(a(200),i(()=>"still"),n("sleep")):o(t)),s(1));constructor(t){this._context=t,this.perspectiveCamera.fov=50,this.perspectiveCamera.near=m(500,C),this.perspectiveCamera.far=m(4e4,C),this.perspectiveCamera.position.set(0,1,0),this.orthographicCamera.left=m(-500,C),this.orthographicCamera.bottom=m(-500,C),this.orthographicCamera.right=m(500,C),this.orthographicCamera.top=m(500,C),this.orthographicCamera.far=m(4e4,C),this.orthographicCamera.near=m(500,C),this.orthographicCamera.position.set(0,1,0),this._context.configuratorExtensionSelectors.getCanvasSystem().interactiveViewport$.subscribe(t=>{null!==t&&this.handleCanvasResize(t)})}_seedCameraControls=()=>{this.perspectiveCameraControls.enabled=!0,this.orthographicCameraControls.enabled=!0,this.perspectiveCameraControls.rotateTo(0,1.2,!1),this.orthographicCameraControls.rotateTo(0,1.2,!1),this.orthographicCameraControls.dollyTo(m(1e4,C),!1)};resetCamera=()=>{this._lastTransition&&this.scheduleCameraTransition(this._lastTransition,{bypassViewLocks:!0})};setTransitionState=t=>{this._transitionState$.next(t)};reportRender=()=>{this._rendered$.next()};scheduleCameraTransition(t,e={}){this._pendingAbortController?.abort();let r=new AbortController;this._pendingAbortController=r;let a=this._generation;return{finished:this._transitionQueue.add(async()=>{let i=r.signal;if(i.aborted||!a?.active)return;let o=c(t.cameraType).with(CameraControlType.Perspective,()=>this.perspectiveCameraControls).with(CameraControlType.Orthographic,()=>this.orthographicCameraControls).exhaustive(),s=null==this._activeCameraControls$.value,n=this._activeCameraControls$.value!==o;n&&this._activeCameraControls$.next(o),this._lastTransition=t,await t.run({controls:o,camera:o.camera,abortSignal:i,firstLoad:s,typeChanged:n,bypassViewLocks:!0===e.bypassViewLocks})}),abort:()=>{r.abort()}}}setCameraControlState=t=>{this._cameraControlState$.next(t)};handleCanvasResize=t=>{if(!d(t))return;let e=t.width/t.height;this.perspectiveCamera.aspect=e,this.perspectiveCamera.updateProjectionMatrix();let r=this.orthographicCamera,a=(r.left+r.right)/2,i=(r.top-r.bottom)/2*e;r.left=a-i,r.right=a+i,r.updateProjectionMatrix()};setCanvasPadding=t=>{let e=this._canvasPadding$.value;(e.top!==t.top||e.right!==t.right||e.bottom!==t.bottom||e.left!==t.left)&&this._canvasPadding$.next(t)};enable=()=>{this.perspectiveCameraControls.enabled=!0,this.orthographicCameraControls.enabled=!0};disable=()=>{this.perspectiveCameraControls.enabled=!1,this.orthographicCameraControls.enabled=!1};fitRootCamera=()=>{let t=this._context.configuratorExtensionSelectors.getCanvasSystem().ready$.value?.scene;t&&this.perspectiveCameraControls.fitToSphere(t,!1)};fitEntity=t=>{let e=this._context.configuratorExtensionSelectors.getCanvasSystem().ready$.value?.scene;if(!e)return;let r=e.getObjectByName(t.toString());null!=r&&this.perspectiveCameraControls.fitToBox(r,!1)};setCameraOffset=t=>{let{padding:e,viewPort:r,camera:a}=t;if(!d(r))return;if(![e.top,e.right,e.bottom,e.left].every(Number.isFinite)||e.left+e.right>=r.width||e.top+e.bottom>=r.height)return void a.clearViewOffset();let i=(e.right-e.left)/2,o=(e.bottom-e.top)/2;a.setViewOffset(r.width,r.height,i,o,r.width,r.height)};initialize=()=>{null!==this._generation&&(this._generation.active=!1),this._pendingAbortController?.abort(),this._pendingAbortController=void 0,this._lastTransition=void 0,this._transitionState$.next("still"),this._cameraControlState$.next("idle"),this._activeCameraControls$.next(void 0),this._generation={active:!0},this._seedCameraControls()}}
@@ -32,6 +32,7 @@ type ObjectFitConfig = {
32
32
  readonly distance: number;
33
33
  };
34
34
  interface CommonCameraConfig {
35
+ readonly rotationLockGroup: string | null | undefined;
35
36
  readonly priority: number;
36
37
  readonly animateOnLoad: boolean;
37
38
  readonly animateTransition: boolean;
@@ -61,6 +62,7 @@ interface CommonCameraConfig {
61
62
  }
62
63
  interface PerspectiveCameraConfig {
63
64
  readonly cameraType: 'perspective';
65
+ readonly distanceLockGroup: string | null | undefined;
64
66
  readonly fov: number;
65
67
  readonly far: number;
66
68
  readonly near: number;
@@ -68,6 +70,7 @@ interface PerspectiveCameraConfig {
68
70
  }
69
71
  interface OrthographicCameraConfig {
70
72
  readonly cameraType: 'orthographic';
73
+ readonly zoomLockGroup: string | null | undefined;
71
74
  readonly left: number;
72
75
  readonly bottom: number;
73
76
  readonly frustumSize: number;
@@ -81,6 +84,11 @@ export interface CameraConfig {
81
84
  readonly commonConfig: CommonCameraConfig;
82
85
  readonly typeSpecificConfig: PerspectiveCameraConfig | OrthographicCameraConfig;
83
86
  }
87
+ interface PreservedCameraView {
88
+ readonly distance?: number;
89
+ readonly zoom?: number;
90
+ readonly rotation?: Pick<CommonCameraConfig, 'azimuthAngle' | 'polarAngle'>;
91
+ }
84
92
  export type BoundingShapeDebugInfo = {
85
93
  readonly shape: 'box';
86
94
  readonly worldBox: Box3;
@@ -97,6 +105,7 @@ export declare class CameraSystem<ENTITIES extends DomainEntity<string>> {
97
105
  readonly activeCameraConfig$: Observable<CameraConfig>;
98
106
  private _useDefaultCamera;
99
107
  private _cachedDefaultCameraComponent;
108
+ private _lastAppliedView;
100
109
  private readonly _initializationGeneration$;
101
110
  constructor(_context: CameraSystemContext);
102
111
  getAllBoundingShapes(state: DomainState<ENTITIES>): readonly BoundingShapeDebugInfo[];
@@ -109,7 +118,21 @@ export declare class CameraSystem<ENTITIES extends DomainEntity<string>> {
109
118
  private _resolveOrthographicCameraConfig;
110
119
  private _resolvePerspectiveCameraConfig;
111
120
  private _resolveTypeSpecificCameraConfig;
112
- applyConfig(cameraConfig: CameraConfig, controls: CameraControls, animate: boolean, viewport: CameraFitViewport): Promise<void>;
121
+ applyConfig(cameraConfig: CameraConfig, controls: CameraControls, animate: boolean, viewport: CameraFitViewport, preservedView?: PreservedCameraView): Promise<void>;
122
+ /**
123
+ * Samples the live view aspects the next application may preserve. Each aspect is carried over
124
+ * only when both cameras name the same explicit lock group and the live value is a chosen view
125
+ * (see `isLiveAspectSettled`). Locks never cross a projection change, and the live values are
126
+ * read from the controls that are still configured for the previous camera — the caller samples
127
+ * before `applyConfig` rewrites their limits.
128
+ */
129
+ private _capturePreservedView;
130
+ /**
131
+ * The live view a scheduled transition may preserve. The first application of a lifecycle has no
132
+ * history, and Reset Camera bypasses the locks for its own run only: the next transition consults
133
+ * the history as usual, which by then records the reset's destination as a settled view.
134
+ */
135
+ private _captureTransitionView;
113
136
  private _clampOrthographicGoalTargetToBoundary;
114
137
  private _clampPerspectiveGoalTargetToBoundary;
115
138
  initialize: () => void;
@@ -1 +1 @@
1
- import{BehaviorSubject as t,EMPTY as e,catchError as o,debounceTime as n,distinctUntilChanged as a,filter as i,from as r,map as s,of as m,shareReplay as l,skip as c,startWith as d,switchMap as p,take as u}from"rxjs";import{Box3 as g,OrthographicCamera as h,PerspectiveCamera as f,Vector3 as C}from"three";import{match as y}from"ts-pattern";import{applicationConfigComponent as x,boundingBoxComponent as S,boundingSphereComponent as _,distanceUnitToDefault as T,fitEntityOrthographicCameraComponent as v,fitEntityPerspectiveCameraComponent as b,fitRootOrthographicCameraComponent as w,fitRootPerspectiveCameraComponent as A,fitSelectionOrthographicCameraComponent as D,fitSelectionPerspectiveCameraComponent as z,isBoundingBoxComponent as M,isBoundingSphereComponent as E,isDomainEntityId as I,isFitEntityOrthographicCameraComponentSnapshot as j,isFitEntityPerspectiveCameraComponentSnapshot as P,isFitRootOrthographicCameraComponent as F,isFitRootPerspectiveCameraComponent as R,isFitSelectionOrthographicCameraComponent as k,isFitSelectionPerspectiveCameraComponent as O,isStaticOrthographicCameraComponent as B,isStaticPerspectiveCameraComponent as L,isVirtualEntityId as $,resolveBoundingBox as U,resolveBoundingSphere as Z,resolveCameraOrbit as G,staticOrthographicCameraComponent as q,staticPerspectiveCameraComponent as W}from"#package/domain";import{CanvasInitializationCancellationError as X}from"../canvas-system.js";import{computeMorphedBoundingBox as Y,computeMorphedBoundingSphere as N}from"./bounding-box.js";import{orthographicBoxGoal as H,orthographicSphereGoal as J,orthographicStaticGoal as K,perspectiveBoxGoal as Q,perspectiveSphereGoal as V,perspectiveStaticGoal as tt,resolveEffectiveFieldOfView as te,resolveEffectiveFitFrustum as to}from"./camera-goal.js";let tn=t=>F(t)||R(t)||P(t)||j(t)||O(t)||k(t);export class CameraSystem{_context;activeCameraConfig$;_useDefaultCamera=!0;_cachedDefaultCameraComponent=null;_initializationGeneration$=new t(0);constructor(t){this._context=t,this.activeCameraConfig$=this._initializationGeneration$.pipe(p(()=>this._context.configuratorExtensionSelectors.getCanvasSystem().firstLoadFinished$.pipe(p(t=>t?this._context.domainContextSelectors.getStoreSystem().stateSlices.committed:e),s(t=>{let e=this._readCameraComponents(t.state);return 0===e.length&&this._useDefaultCamera&&(e=[this._getDefaultCameraComponent(t.state)]),this._parseCameraData(e,t.state)}),i(t=>null!==t),a((t,e)=>t.cameraComponent===e.cameraComponent&&t.boundingBoxSnapshot===e.boundingBoxSnapshot&&t.boundingSphereSnapshot===e.boundingSphereSnapshot&&t.worldMatrix.equals(e.worldMatrix)&&t.dimensionUnit===e.dimensionUnit&&t.fitEntityId===e.fitEntityId&&t.fitShape===e.fitShape&&t.fitMode===e.fitMode))),p(t=>r(this._context.configuratorExtensionSelectors.getCanvasSystem().waitForFlush()).pipe(s(()=>t),o(t=>{if(t instanceof X)return e;throw t}))),p(t=>{let e=m({cameraData:t,objectToFit:null}),o=t.fitEntityId;return null===o?e:y(t.fitMode).with(null,()=>e).with("bounding",()=>e).with("mesh-dynamic",()=>this._context.configuratorExtensionSelectors.getCanvasSystem().findEntity3d$({entityIds:[o],entityRoots:!0}).pipe(s(e=>({cameraData:t,objectToFit:e[0]??null})))).with("mesh-static",()=>m({cameraData:t,objectToFit:this._context.configuratorExtensionSelectors.getCanvasSystem().findEntity3d({entityIds:[o],entityRoots:!0})[0]??null})).exhaustive()}),s(({cameraData:t,objectToFit:e})=>{let o=G({state:t.cameraComponent.state,dimensionUnit:t.dimensionUnit,worldMatrix:t.worldMatrix});return{objectFitConfig:this._getObjectFitConfig(t,e),commonConfig:this._resolveCommonCameraConfig(t.cameraComponent,o),typeSpecificConfig:this._resolveTypeSpecificCameraConfig(t.cameraComponent,t.dimensionUnit)}})).pipe(p(t=>this._context.configuratorExtensionSelectors.getCameraControlSystem().canvasPadding$.pipe(c(1),n(100),s(()=>t),d(t))),p(t=>this._context.configuratorExtensionSelectors.getDragAndDropSystem().draggingState$.pipe(i(t=>null===t),u(1),s(()=>t))),l(1)),this.activeCameraConfig$.subscribe(t=>{t&&this._context.configuratorExtensionSelectors.getCameraControlSystem().scheduleCameraTransition({cameraType:t.typeSpecificConfig.cameraType,run:async({controls:e,firstLoad:o,typeChanged:n})=>{let a=this._context.configuratorExtensionSelectors.getCanvasSystem().r3fHandle$.value;if(!a)return void this._context.domainServicesSelectors.getLogger().debug("Camera transition skipped: canvas not ready");let i=y({firstLoad:o,typeChanged:n}).with({firstLoad:!0},()=>t.commonConfig.animateOnLoad).with({firstLoad:!1,typeChanged:!1},()=>t.commonConfig.animateTransition).with({firstLoad:!1,typeChanged:!0},()=>!1).exhaustive(),r=a.get().size,s={width:r.width,height:r.height,padding:this._context.configuratorExtensionSelectors.getCameraControlSystem().canvasPadding$.value},m=new Promise(t=>setTimeout(t,300));Promise.race([this.applyConfig(t,e,i,s),m]).catch(t=>{this._context.domainServicesSelectors.getLogger().error("Camera transition failed",{},t)})}})})}getAllBoundingShapes(t){let e=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[S,_]},t),o=this._context.domainContextSelectors.getDimensionSystem().getDataDistanceUnit(t);return e.map(e=>{let n=e.componentInstance.entityId;if(!I(n))return null;let a=this._context.domainContextSelectors.getPositionProjections().getEntityWorldMatrix(n,t,null);return M(e)?{shape:"box",worldBox:U(e.state,a,o),componentSnapshot:e}:E(e)?{shape:"sphere",worldSphere:Z(e.state,a,o),componentSnapshot:e}:null}).filter(t=>null!==t)}_readCameraComponents(t){return this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[A,b,v,W,z,D,w,q]},t)}_parseCameraData(t,e){let o=t.filter(t=>{if(O(t)||k(t)){let o=this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(e);return t.componentInstance.entityId===o}return!0});if(0===o.length)return null;let n=o.reduce((t,e)=>(e.state.priority??1)>(t.state.priority??1)?e:t),a=n.componentInstance.entityId;if($(a))throw Error("Cameras should not be attached to virtual entities");let i=tn(n)?n.state.fitToBox?"box":"sphere":null,r=e.rootEntityId,s=F(n)||R(n)?r:a,m=i?s:null,l=m?this._context.domainContextSelectors.getComponentReader().readOneComponent({componentType:S,entityId:s},e):null,c=m?this._context.domainContextSelectors.getComponentReader().readOneComponent({componentType:_,entityId:s},e):null,d=this._context.domainContextSelectors.getPositionProjections().getEntityWorldMatrix(a,e,null),p=this._context.domainContextSelectors.getDimensionSystem().getDataDistanceUnit(e),u=tn(n)&&!0===n.state.fitMeshChanges?"mesh-dynamic":"mesh-static",g=y(i).with("box",()=>null!==l?"bounding":u).with("sphere",()=>null!==c?"bounding":u).with(null,()=>null).exhaustive();return{cameraComponent:n,boundingBoxSnapshot:l,boundingSphereSnapshot:c,worldMatrix:d,dimensionUnit:p,fitEntityId:m,fitShape:i,fitMode:g}}_getDefaultCameraComponent(t){return null!==this._cachedDefaultCameraComponent&&this._cachedDefaultCameraComponent.componentInstance.entityId===t.rootEntityId||(this._cachedDefaultCameraComponent={componentInstance:{entityId:t.rootEntityId,instanceName:"--default-camera--",componentType:A},state:{animateOnLoad:!0,animateTransition:!0,azimuthAngle:-.2,polarAngle:Math.PI/2-.2}}),this._cachedDefaultCameraComponent}_getObjectFitConfig(t,e){let{cameraComponent:o,boundingBoxSnapshot:n,boundingSphereSnapshot:a,worldMatrix:i,fitShape:r,fitEntityId:s,dimensionUnit:m}=t,l={fitMode:"static",lookAtX:0,lookAtY:0,lookAtZ:0,distance:1e3};return y(r).with(null,()=>{if(L(o)||B(o)){let t=new C(T(o.state.lookAtX,m)??0,T(o.state.lookAtY,m)??0,T(o.state.lookAtZ,m)??0);return!0===o.state.relative&&t.applyMatrix4(i),{fitMode:"static",lookAtX:t.x,lookAtY:t.y,lookAtZ:t.z,distance:T(o.state.distance,m)??0}}throw Error("Static cameras are required when fitting is not specified")}).with("box",()=>n?{objectToFit:U(n.state,i,m),fitMode:"box",distance:T(o.state.distance,m)}:null==e?(this._context.domainServicesSelectors.getLogger()?.warn("Entity object not found",{entityId:s}),l):(e.updateMatrix(),e.updateMatrixWorld(),{objectToFit:Y(e),fitMode:"box",distance:T(o.state.distance,m)})).with("sphere",()=>a?{objectToFit:Z(a.state,i,m),fitMode:"sphere",distance:T(o.state.distance,m)}:null==e?(this._context.domainServicesSelectors.getLogger()?.warn("Entity object not found",{entityId:s}),l):(e.updateMatrix(),e.updateMatrixWorld(),{objectToFit:N(e),fitMode:"sphere",distance:T(o.state.distance,m)})).exhaustive()}getActiveCameraConfig(t){let e=this._parseCameraData(this._readCameraComponents(t),t);if(null===e)return null;let o=("mesh-static"===e.fitMode||"mesh-dynamic"===e.fitMode)&&null!==e.fitEntityId?this._context.configuratorExtensionSelectors.getCanvasSystem().findEntity3d({entityIds:[e.fitEntityId],entityRoots:!0}):[],n=G({state:e.cameraComponent.state,dimensionUnit:e.dimensionUnit,worldMatrix:e.worldMatrix});return{objectFitConfig:this._getObjectFitConfig(e,o[0]??null),commonConfig:this._resolveCommonCameraConfig(e.cameraComponent,n),typeSpecificConfig:this._resolveTypeSpecificCameraConfig(e.cameraComponent,e.dimensionUnit)}}_resolveCommonCameraConfig(t,e){let o=t.state;return{priority:o.priority??1,animateOnLoad:o.animateOnLoad??!0,animateTransition:o.animateTransition??!0,minDistance:e.minDistance,maxDistance:e.maxDistance,polarAngle:e.polarAngle,minPolarAngle:e.minPolarAngle,maxPolarAngle:e.maxPolarAngle,azimuthAngle:e.azimuthAngle,minAzimuthAngle:e.minAzimuthAngle,maxAzimuthAngle:e.maxAzimuthAngle,smoothTime:o.smoothTime??.25,draggingSmoothTime:o.draggingSmoothTime??.125,maxSpeed:o.maxSpeed??1/0,azimuthRotateSpeed:o.azimuthRotateSpeed??1,polarRotateSpeed:o.polarRotateSpeed??1,dollySpeed:o.dollySpeed??1,dollyDragInverted:o.dollyDragInverted??!1,truckSpeed:o.truckSpeed??2,mouseButtons:o.mouseButtons??{left:1,middle:8,right:2,wheel:8},touches:o.touches??{one:32,two:1024,three:64},dollyToCursor:o.dollyToCursor??!1,dragToOffset:o.dragToOffset??!1,verticalDragToForward:o.verticalDragToForward??!1,boundaryEnclosesCamera:o.boundaryEnclosesCamera??!1,boundary:o.boundary??new g(new C(-1/0,-1/0,-1/0),new C(1/0,1/0,1/0))}}_resolveOrthographicCameraConfig(t,e){let o=t.state;return{cameraType:"orthographic",left:T(o.left,e)??0,bottom:T(o.bottom,e)??0,frustumSize:T(o.frustumSize,e)??1e4,far:T(o.far,e)??1e4,near:T(o.near,e)??100,minZoom:o.minZoom??.01,maxZoom:o.maxZoom??1/0}}_resolvePerspectiveCameraConfig(t,e){let o=t.state;return{cameraType:"perspective",fov:o.fov??50,far:T(o.far,e)??5e4,near:T(o.near,e)??100,focus:T(o.focus,e)??10}}_resolveTypeSpecificCameraConfig(t,e){if(B(t)||F(t)||j(t)||k(t))return this._resolveOrthographicCameraConfig(t,e);if(L(t)||R(t)||P(t)||O(t))return this._resolvePerspectiveCameraConfig(t,e);throw Error("Unsupported camera component")}async applyConfig(t,e,o,n){let{objectFitConfig:a,commonConfig:i,typeSpecificConfig:r}=t,{fullAspectRatio:s,effectiveAspectRatio:m,effectiveHeightRatio:l}=to(n);if("orthographic"===r.cameraType&&e.camera instanceof h){let t=e.camera;t.far=r.far,t.near=r.near,t.left=r.left-r.frustumSize*s/2,t.right=r.left+r.frustumSize*s/2,t.bottom=r.bottom-r.frustumSize/2,t.top=r.bottom+r.frustumSize/2,e.minZoom=r.minZoom,e.maxZoom=r.maxZoom}else if("perspective"===r.cameraType&&e.camera instanceof f){let t=e.camera;t.fov=r.fov,t.focus=r.focus,t.far=r.far,t.near=r.near,t.aspect=s}else throw Error("Camera does not match the config type");e.camera.updateProjectionMatrix(),e.minDistance=i.minDistance,e.maxDistance=i.maxDistance,e.minPolarAngle=i.minPolarAngle,e.maxPolarAngle=i.maxPolarAngle,e.minAzimuthAngle=i.minAzimuthAngle,e.maxAzimuthAngle=i.maxAzimuthAngle,e.smoothTime=i.smoothTime,e.draggingSmoothTime=i.draggingSmoothTime,e.maxSpeed=i.maxSpeed,e.azimuthRotateSpeed=i.azimuthRotateSpeed,e.polarRotateSpeed=i.polarRotateSpeed,e.dollySpeed=i.dollySpeed,e.truckSpeed=i.truckSpeed,e.dollyDragInverted=i.dollyDragInverted,e.mouseButtons=i.mouseButtons,e.touches=i.touches,e.boundaryEnclosesCamera=i.boundaryEnclosesCamera,e.dollyToCursor=i.dollyToCursor,e.dragToOffset=i.dragToOffset,e.verticalDragToForward=i.verticalDragToForward,e.setBoundary(i.boundary);let c={minDistance:i.minDistance,maxDistance:i.maxDistance};if("orthographic"===r.cameraType){let t=r.frustumSize*l,n=y(a).with({fitMode:"static"},t=>K(t,i,c)).with({fitMode:"sphere"},e=>J(e.objectToFit,t,m,e.distance,i,c)).with({fitMode:"box"},e=>H(e.objectToFit,t,m,e.distance,i,c)).exhaustive(),s=this._clampOrthographicGoalTargetToBoundary(n,a.fitMode,i.boundary);e.normalizeRotations(),await Promise.all([e.setLookAt(s.position.x,s.position.y,s.position.z,s.target.x,s.target.y,s.target.z,o),e.zoomTo(s.zoom,o)])}else{let t=te(r.fov,l),n=y(a).with({fitMode:"static"},t=>tt(t,i,c)).with({fitMode:"sphere"},e=>V(e.objectToFit,t,m,e.distance,i,c)).with({fitMode:"box"},e=>Q(e.objectToFit,t,m,e.distance,i,c)).exhaustive(),s=this._clampPerspectiveGoalTargetToBoundary(n,a.fitMode,i.boundary);e.normalizeRotations(),await e.setLookAt(s.position.x,s.position.y,s.position.z,s.target.x,s.target.y,s.target.z,o)}}_clampOrthographicGoalTargetToBoundary(t,e,o){if("static"===e)return t;let n=t.target.clone();return(o.clampPoint(n,n),n.equals(t.target))?t:{position:t.position.clone().add(n.clone().sub(t.target)),target:n,zoom:t.zoom}}_clampPerspectiveGoalTargetToBoundary(t,e,o){if("static"===e)return t;let n=t.target.clone();return(o.clampPoint(n,n),n.equals(t.target))?t:{position:t.position.clone().add(n.clone().sub(t.target)),target:n}}initialize=()=>{let t=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[x]},this._context.domainContextSelectors.getStoreSystem().stateSlices.committed.value.state);this._useDefaultCamera=t?.state.disableDefaultCamera!==!0,this._initializationGeneration$.next(this._initializationGeneration$.value+1)}}
1
+ import{BehaviorSubject as t,EMPTY as e,catchError as o,debounceTime as i,distinctUntilChanged as n,filter as a,from as r,map as s,of as m,shareReplay as c,skip as l,startWith as d,switchMap as p,take as u}from"rxjs";import{Box3 as g,OrthographicCamera as h,PerspectiveCamera as f,Vector3 as C}from"three";import{match as y}from"ts-pattern";import{applicationConfigComponent as x,boundingBoxComponent as S,boundingSphereComponent as v,distanceUnitToDefault as _,fitEntityOrthographicCameraComponent as T,fitEntityPerspectiveCameraComponent as w,fitRootOrthographicCameraComponent as b,fitRootPerspectiveCameraComponent as A,fitSelectionOrthographicCameraComponent as z,fitSelectionPerspectiveCameraComponent as D,isBoundingBoxComponent as M,isBoundingSphereComponent as E,isDomainEntityId as I,isFitEntityOrthographicCameraComponentSnapshot as k,isFitEntityPerspectiveCameraComponentSnapshot as P,isFitRootOrthographicCameraComponent as L,isFitRootPerspectiveCameraComponent as j,isFitSelectionOrthographicCameraComponent as F,isFitSelectionPerspectiveCameraComponent as G,isStaticOrthographicCameraComponent as R,isStaticPerspectiveCameraComponent as O,isVirtualEntityId as B,prepareCameraOrbit as Z,resolveBoundingBox as $,resolveBoundingSphere as U,resolvePreparedCameraRotation as V,staticOrthographicCameraComponent as q,staticPerspectiveCameraComponent as W}from"#package/domain";import{CanvasInitializationCancellationError as X}from"../canvas-system.js";import{computeMorphedBoundingBox as Y,computeMorphedBoundingSphere as N}from"./bounding-box.js";import{orthographicBoxGoal as H,orthographicSphereGoal as J,orthographicStaticGoal as K,perspectiveBoxGoal as Q,perspectiveSphereGoal as tt,perspectiveStaticGoal as te,resolveEffectiveFieldOfView as to,resolveEffectiveFitFrustum as ti}from"./camera-goal.js";let tn=(t,e)=>null!=t&&t===e,ta=t=>L(t)||j(t)||P(t)||k(t)||G(t)||F(t);export class CameraSystem{_context;activeCameraConfig$;_useDefaultCamera=!0;_cachedDefaultCameraComponent=null;_lastAppliedView=null;_initializationGeneration$=new t(0);constructor(t){this._context=t,this.activeCameraConfig$=this._initializationGeneration$.pipe(p(()=>this._context.configuratorExtensionSelectors.getCanvasSystem().firstLoadFinished$.pipe(p(t=>t?this._context.domainContextSelectors.getStoreSystem().stateSlices.committed:e),s(t=>{let e=this._readCameraComponents(t.state);return 0===e.length&&this._useDefaultCamera&&(e=[this._getDefaultCameraComponent(t.state)]),this._parseCameraData(e,t.state)}),a(t=>null!==t),n((t,e)=>t.cameraComponent===e.cameraComponent&&t.boundingBoxSnapshot===e.boundingBoxSnapshot&&t.boundingSphereSnapshot===e.boundingSphereSnapshot&&t.worldMatrix.equals(e.worldMatrix)&&t.dimensionUnit===e.dimensionUnit&&t.fitEntityId===e.fitEntityId&&t.fitShape===e.fitShape&&t.fitMode===e.fitMode))),p(t=>r(this._context.configuratorExtensionSelectors.getCanvasSystem().waitForFlush()).pipe(s(()=>t),o(t=>{if(t instanceof X)return e;throw t}))),p(t=>{let e=m({cameraData:t,objectToFit:null}),o=t.fitEntityId;return null===o?e:y(t.fitMode).with(null,()=>e).with("bounding",()=>e).with("mesh-dynamic",()=>this._context.configuratorExtensionSelectors.getCanvasSystem().findEntity3d$({entityIds:[o],entityRoots:!0}).pipe(s(e=>({cameraData:t,objectToFit:e[0]??null})))).with("mesh-static",()=>m({cameraData:t,objectToFit:this._context.configuratorExtensionSelectors.getCanvasSystem().findEntity3d({entityIds:[o],entityRoots:!0})[0]??null})).exhaustive()}),s(({cameraData:t,objectToFit:e})=>{let o=Z({state:t.cameraComponent.state,dimensionUnit:t.dimensionUnit,worldMatrix:t.worldMatrix});return{objectFitConfig:this._getObjectFitConfig(t,e),commonConfig:this._resolveCommonCameraConfig(t.cameraComponent,o),typeSpecificConfig:this._resolveTypeSpecificCameraConfig(t.cameraComponent,t.dimensionUnit)}})).pipe(p(t=>this._context.configuratorExtensionSelectors.getCameraControlSystem().canvasPadding$.pipe(l(1),i(100),s(()=>t),d(t))),p(t=>this._context.configuratorExtensionSelectors.getDragAndDropSystem().draggingState$.pipe(a(t=>null===t),u(1),s(()=>t))),c(1)),this.activeCameraConfig$.subscribe(t=>{t&&this._context.configuratorExtensionSelectors.getCameraControlSystem().scheduleCameraTransition({cameraType:t.typeSpecificConfig.cameraType,run:async({controls:e,firstLoad:o,typeChanged:i,bypassViewLocks:n})=>{let a=this._context.configuratorExtensionSelectors.getCanvasSystem().r3fHandle$.value;if(!a)return void this._context.domainServicesSelectors.getLogger().debug("Camera transition skipped: canvas not ready");let r=y({firstLoad:o,typeChanged:i}).with({firstLoad:!0},()=>t.commonConfig.animateOnLoad).with({firstLoad:!1,typeChanged:!1},()=>t.commonConfig.animateTransition).with({firstLoad:!1,typeChanged:!0},()=>!1).exhaustive(),s=a.get().size,m={width:s.width,height:s.height,padding:this._context.configuratorExtensionSelectors.getCameraControlSystem().canvasPadding$.value},c=this._captureTransitionView(t,e,o,n),l=this.applyConfig(t,e,r,m,c),d={config:t,preservedView:c,settled:!1},p=()=>{d.settled=!0};this._lastAppliedView=d,l.then(p,p),Promise.race([l,new Promise(t=>setTimeout(t,300))]).catch(t=>{this._context.domainServicesSelectors.getLogger().error("Camera transition failed",{},t)})}})})}getAllBoundingShapes(t){let e=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[S,v]},t),o=this._context.domainContextSelectors.getDimensionSystem().getDataDistanceUnit(t);return e.map(e=>{let i=e.componentInstance.entityId;if(!I(i))return null;let n=this._context.domainContextSelectors.getPositionProjections().getEntityWorldMatrix(i,t,null);return M(e)?{shape:"box",worldBox:$(e.state,n,o),componentSnapshot:e}:E(e)?{shape:"sphere",worldSphere:U(e.state,n,o),componentSnapshot:e}:null}).filter(t=>null!==t)}_readCameraComponents(t){return this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[A,w,T,W,D,z,b,q]},t)}_parseCameraData(t,e){let o=t.filter(t=>{if(G(t)||F(t)){let o=this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(e);return t.componentInstance.entityId===o}return!0});if(0===o.length)return null;let i=o.reduce((t,e)=>(e.state.priority??1)>(t.state.priority??1)?e:t),n=i.componentInstance.entityId;if(B(n))throw Error("Cameras should not be attached to virtual entities");let a=ta(i)?i.state.fitToBox?"box":"sphere":null,r=e.rootEntityId,s=L(i)||j(i)?r:n,m=a?s:null,c=m?this._context.domainContextSelectors.getComponentReader().readOneComponent({componentType:S,entityId:s},e):null,l=m?this._context.domainContextSelectors.getComponentReader().readOneComponent({componentType:v,entityId:s},e):null,d=this._context.domainContextSelectors.getPositionProjections().getEntityWorldMatrix(n,e,null),p=this._context.domainContextSelectors.getDimensionSystem().getDataDistanceUnit(e),u=ta(i)&&!0===i.state.fitMeshChanges?"mesh-dynamic":"mesh-static",g=y(a).with("box",()=>null!==c?"bounding":u).with("sphere",()=>null!==l?"bounding":u).with(null,()=>null).exhaustive();return{cameraComponent:i,boundingBoxSnapshot:c,boundingSphereSnapshot:l,worldMatrix:d,dimensionUnit:p,fitEntityId:m,fitShape:a,fitMode:g}}_getDefaultCameraComponent(t){return null!==this._cachedDefaultCameraComponent&&this._cachedDefaultCameraComponent.componentInstance.entityId===t.rootEntityId||(this._cachedDefaultCameraComponent={componentInstance:{entityId:t.rootEntityId,instanceName:"--default-camera--",componentType:A},state:{animateOnLoad:!0,animateTransition:!0,azimuthAngle:-.2,polarAngle:Math.PI/2-.2}}),this._cachedDefaultCameraComponent}_getObjectFitConfig(t,e){let{cameraComponent:o,boundingBoxSnapshot:i,boundingSphereSnapshot:n,worldMatrix:a,fitShape:r,fitEntityId:s,dimensionUnit:m}=t,c={fitMode:"static",lookAtX:0,lookAtY:0,lookAtZ:0,distance:1e3};return y(r).with(null,()=>{if(O(o)||R(o)){let t=new C(_(o.state.lookAtX,m)??0,_(o.state.lookAtY,m)??0,_(o.state.lookAtZ,m)??0);return!0===o.state.relative&&t.applyMatrix4(a),{fitMode:"static",lookAtX:t.x,lookAtY:t.y,lookAtZ:t.z,distance:_(o.state.distance,m)??0}}throw Error("Static cameras are required when fitting is not specified")}).with("box",()=>i?{objectToFit:$(i.state,a,m),fitMode:"box",distance:_(o.state.distance,m)}:null==e?(this._context.domainServicesSelectors.getLogger()?.warn("Entity object not found",{entityId:s}),c):(e.updateMatrix(),e.updateMatrixWorld(),{objectToFit:Y(e),fitMode:"box",distance:_(o.state.distance,m)})).with("sphere",()=>n?{objectToFit:U(n.state,a,m),fitMode:"sphere",distance:_(o.state.distance,m)}:null==e?(this._context.domainServicesSelectors.getLogger()?.warn("Entity object not found",{entityId:s}),c):(e.updateMatrix(),e.updateMatrixWorld(),{objectToFit:N(e),fitMode:"sphere",distance:_(o.state.distance,m)})).exhaustive()}getActiveCameraConfig(t){let e=this._parseCameraData(this._readCameraComponents(t),t);if(null===e)return null;let o=("mesh-static"===e.fitMode||"mesh-dynamic"===e.fitMode)&&null!==e.fitEntityId?this._context.configuratorExtensionSelectors.getCanvasSystem().findEntity3d({entityIds:[e.fitEntityId],entityRoots:!0}):[],i=Z({state:e.cameraComponent.state,dimensionUnit:e.dimensionUnit,worldMatrix:e.worldMatrix});return{objectFitConfig:this._getObjectFitConfig(e,o[0]??null),commonConfig:this._resolveCommonCameraConfig(e.cameraComponent,i),typeSpecificConfig:this._resolveTypeSpecificCameraConfig(e.cameraComponent,e.dimensionUnit)}}_resolveCommonCameraConfig(t,e){let o=t.state;return{rotationLockGroup:o.rotationLockGroup,priority:o.priority??1,animateOnLoad:o.animateOnLoad??!0,animateTransition:o.animateTransition??!0,minDistance:e.minDistance,maxDistance:e.maxDistance,polarAngle:e.polarAngle,minPolarAngle:e.minPolarAngle,maxPolarAngle:e.maxPolarAngle,azimuthAngle:e.azimuthAngle,minAzimuthAngle:e.minAzimuthAngle,maxAzimuthAngle:e.maxAzimuthAngle,smoothTime:o.smoothTime??.25,draggingSmoothTime:o.draggingSmoothTime??.125,maxSpeed:o.maxSpeed??1/0,azimuthRotateSpeed:o.azimuthRotateSpeed??1,polarRotateSpeed:o.polarRotateSpeed??1,dollySpeed:o.dollySpeed??1,dollyDragInverted:o.dollyDragInverted??!1,truckSpeed:o.truckSpeed??2,mouseButtons:o.mouseButtons??{left:1,middle:8,right:2,wheel:8},touches:o.touches??{one:32,two:1024,three:64},dollyToCursor:o.dollyToCursor??!1,dragToOffset:o.dragToOffset??!1,verticalDragToForward:o.verticalDragToForward??!1,boundaryEnclosesCamera:o.boundaryEnclosesCamera??!1,boundary:o.boundary??new g(new C(-1/0,-1/0,-1/0),new C(1/0,1/0,1/0))}}_resolveOrthographicCameraConfig(t,e){let o=t.state;return{cameraType:"orthographic",zoomLockGroup:o.zoomLockGroup,left:_(o.left,e)??0,bottom:_(o.bottom,e)??0,frustumSize:_(o.frustumSize,e)??1e4,far:_(o.far,e)??1e4,near:_(o.near,e)??100,minZoom:o.minZoom??.01,maxZoom:o.maxZoom??1/0}}_resolvePerspectiveCameraConfig(t,e){let o=t.state;return{cameraType:"perspective",distanceLockGroup:o.distanceLockGroup,fov:o.fov??50,far:_(o.far,e)??5e4,near:_(o.near,e)??100,focus:_(o.focus,e)??10}}_resolveTypeSpecificCameraConfig(t,e){if(R(t)||L(t)||k(t)||F(t))return this._resolveOrthographicCameraConfig(t,e);if(O(t)||j(t)||P(t)||G(t))return this._resolvePerspectiveCameraConfig(t,e);throw Error("Unsupported camera component")}async applyConfig(t,e,o,i,n){let{objectFitConfig:a,commonConfig:r,typeSpecificConfig:s}=t,m=n?.rotation?{...r,...n.rotation}:r,c={...m,...V(m)},{fullAspectRatio:l,effectiveAspectRatio:d,effectiveHeightRatio:p}=ti(i);if("orthographic"===s.cameraType&&e.camera instanceof h){let t=e.camera;t.far=s.far,t.near=s.near,t.left=s.left-s.frustumSize*l/2,t.right=s.left+s.frustumSize*l/2,t.bottom=s.bottom-s.frustumSize/2,t.top=s.bottom+s.frustumSize/2,e.minZoom=s.minZoom,e.maxZoom=s.maxZoom}else if("perspective"===s.cameraType&&e.camera instanceof f){let t=e.camera;t.fov=s.fov,t.focus=s.focus,t.far=s.far,t.near=s.near,t.aspect=l}else throw Error("Camera does not match the config type");e.camera.updateProjectionMatrix(),e.minDistance=c.minDistance,e.maxDistance=c.maxDistance,e.minPolarAngle=c.minPolarAngle,e.maxPolarAngle=c.maxPolarAngle,e.minAzimuthAngle=c.minAzimuthAngle,e.maxAzimuthAngle=c.maxAzimuthAngle,e.smoothTime=c.smoothTime,e.draggingSmoothTime=c.draggingSmoothTime,e.maxSpeed=c.maxSpeed,e.azimuthRotateSpeed=c.azimuthRotateSpeed,e.polarRotateSpeed=c.polarRotateSpeed,e.dollySpeed=c.dollySpeed,e.truckSpeed=c.truckSpeed,e.dollyDragInverted=c.dollyDragInverted,e.mouseButtons=c.mouseButtons,e.touches=c.touches,e.boundaryEnclosesCamera=c.boundaryEnclosesCamera,e.dollyToCursor=c.dollyToCursor,e.dragToOffset=c.dragToOffset,e.verticalDragToForward=c.verticalDragToForward,e.setBoundary(c.boundary);let u={minDistance:c.minDistance,maxDistance:c.maxDistance};if("orthographic"===s.cameraType){let t=s.frustumSize*p,i=y(a).with({fitMode:"static"},t=>K(t,c,u)).with({fitMode:"sphere"},e=>J(e.objectToFit,t,d,e.distance,c,u)).with({fitMode:"box"},e=>H(e.objectToFit,t,d,e.distance,c,u)).exhaustive(),r=this._clampOrthographicGoalTargetToBoundary(i,a.fitMode,c.boundary),m=n?.zoom===void 0?r:{...r,zoom:Math.min(Math.max(n.zoom,s.minZoom),s.maxZoom)};e.normalizeRotations(),await Promise.all([e.setLookAt(m.position.x,m.position.y,m.position.z,m.target.x,m.target.y,m.target.z,o),e.zoomTo(m.zoom,o)])}else{let t=to(s.fov,p),i=n?.distance===void 0?a:{...a,distance:n.distance},r=y(i).with({fitMode:"static"},t=>te(t,c,u)).with({fitMode:"sphere"},e=>tt(e.objectToFit,t,d,e.distance,c,u)).with({fitMode:"box"},e=>Q(e.objectToFit,t,d,e.distance,c,u)).exhaustive(),m=this._clampPerspectiveGoalTargetToBoundary(r,i.fitMode,c.boundary);e.normalizeRotations(),await e.setLookAt(m.position.x,m.position.y,m.position.z,m.target.x,m.target.y,m.target.z,o)}}_capturePreservedView(t,e,o){let i;if(null===t||t.config.typeSpecificConfig.cameraType!==e.typeSpecificConfig.cameraType)return;let n=t.config,a=tn(n.commonConfig.rotationLockGroup,e.commonConfig.rotationLockGroup)&&(i=t.preservedView?.rotation!==void 0,t.settled||i)?{azimuthAngle:o.azimuthAngle,polarAngle:o.polarAngle}:void 0;if("perspective"===n.typeSpecificConfig.cameraType&&"perspective"===e.typeSpecificConfig.cameraType){let i;return{distance:tn(n.typeSpecificConfig.distanceLockGroup,e.typeSpecificConfig.distanceLockGroup)&&(i=t.preservedView?.distance!==void 0,t.settled||i)?o.distance:void 0,rotation:a}}if("orthographic"===n.typeSpecificConfig.cameraType&&"orthographic"===e.typeSpecificConfig.cameraType&&o.camera instanceof h){let i;return{zoom:tn(n.typeSpecificConfig.zoomLockGroup,e.typeSpecificConfig.zoomLockGroup)&&(i=t.preservedView?.zoom!==void 0,t.settled||i)?o.camera.zoom:void 0,rotation:a}}return{rotation:a}}_captureTransitionView(t,e,o,i){return o||i?void 0:this._capturePreservedView(this._lastAppliedView,t,e)}_clampOrthographicGoalTargetToBoundary(t,e,o){if("static"===e)return t;let i=t.target.clone();return(o.clampPoint(i,i),i.equals(t.target))?t:{position:t.position.clone().add(i.clone().sub(t.target)),target:i,zoom:t.zoom}}_clampPerspectiveGoalTargetToBoundary(t,e,o){if("static"===e)return t;let i=t.target.clone();return(o.clampPoint(i,i),i.equals(t.target))?t:{position:t.position.clone().add(i.clone().sub(t.target)),target:i}}initialize=()=>{let t=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[x]},this._context.domainContextSelectors.getStoreSystem().stateSlices.committed.value.state);this._useDefaultCamera=t?.state.disableDefaultCamera!==!0,this._lastAppliedView=null,this._initializationGeneration$.next(this._initializationGeneration$.value+1)}}
@@ -118,7 +118,7 @@ export default createOnConfiguratorReadyProvider(({ children }) => {
118
118
 
119
119
  ## Camera Control API
120
120
 
121
- ### `scheduleCameraTransition(transition)`
121
+ ### `scheduleCameraTransition(transition, options?)`
122
122
 
123
123
  Schedules a camera transition. Aborts any previously scheduled transition.
124
124
 
@@ -133,10 +133,13 @@ Schedules a camera transition. Aborts any previously scheduled transition.
133
133
  abortSignal: AbortSignal // signals when to stop
134
134
  firstLoad: boolean // true if no camera was active before
135
135
  typeChanged: boolean // true if switching perspective <-> orthographic
136
+ bypassViewLocks: boolean // true when Reset Camera re-runs the transition
136
137
  }) => Promise<void>
137
138
  }
138
139
  ```
139
140
 
141
+ The optional `options` object accepts `bypassViewLocks`. Reset Camera sets it to re-run the last transition without the [camera locks](?path=/docs/3d-scene-cameras-locks--docs); the built-in camera components read the matching `run` prop, and a custom transition may ignore it.
142
+
140
143
  **Returns:**
141
144
 
142
145
  ```typescript
@@ -182,6 +182,8 @@ Notice that:
182
182
  - Bounding shapes use `relative: true`, so they follow each entity's position offset. The Box is at `position: 0` and the BuckyBall at `position: 2` (meters, via `distanceDataUnit: m`) -- their bounding shapes are correctly centered on each entity despite using zero-centered coordinates
183
183
  - Each entity has independent camera angles: Box uses `azimuthAngle: -1` (from the left), BuckyBall uses `azimuthAngle: 1` (from the right)
184
184
 
185
+ To retain the user's live rotation, distance, or zoom when a different fit-entity camera activates, see the [Camera Locks example](?path=/docs/3d-scene-cameras-locks--docs).
186
+
185
187
  > **Debug visualization**: registered bounding shapes (blue), the active camera fit target (green), and perspective/orthographic camera frustums can be highlighted in 3D — open the Editor's **Debug** panel (eye icon in the left sidebar) and flip the **Camera Debug** switch on to see what the camera is fitting to.
186
188
 
187
189
  ## Test cases
@@ -711,6 +711,8 @@ Pay particular attention to the torus examples: the "Torus Box" view demonstrate
711
711
 
712
712
  Each camera configuration maintains smooth user interaction within appropriate constraints while providing intelligent automatic positioning that adapts to the specific characteristics of each content type and fitting algorithm, making this system ideal for responsive configurators with diverse content requirements and precise camera control needs.
713
713
 
714
+ To retain the user's live rotation, distance, or zoom when switching between a fit-root camera and a related target, see the [Camera Locks example](?path=/docs/3d-scene-cameras-locks--docs).
715
+
714
716
  > **Debug visualization**: registered bounding shapes (blue), the active camera fit target (green), and perspective/orthographic camera frustums can be highlighted in 3D — open the Editor's **Debug** panel (eye icon in the left sidebar) and flip the **Camera Debug** switch on to see what the camera is fitting to.
715
717
 
716
718
  ## Complete Implementation
@@ -709,6 +709,8 @@ The sphere fitting algorithm (`fitToBox: false`) provides natural, organic camer
709
709
 
710
710
  **Dual Projection Modes**: Use the view controls to switch between perspective and orthographic projection modes. In perspective mode, cameras animate smoothly between selections for natural transitions. In orthographic mode, cameras are configured with `animateTransition: false` for immediate, technical switching that's ideal for precise examination and measurement tasks. This demonstrates how the same multi-layered selection system can provide both cinematic user experiences and professional technical workflows.
711
711
 
712
+ To retain the user's live rotation, distance, or zoom when a different selected camera activates, see the [Camera Locks example](?path=/docs/3d-scene-cameras-locks--docs).
713
+
712
714
  > **Debug visualization**: registered bounding shapes (blue), the active camera fit target (green), and perspective/orthographic camera frustums can be highlighted in 3D — open the Editor's **Debug** panel (eye icon in the left sidebar) and flip the **Camera Debug** switch on to see which shape is being framed.
713
715
 
714
716
  ## Complete Implementation