@salsita-ai/sdk 0.50.0-develop.10b1931917 → 0.50.0-develop.2772b27a05
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-create-configurator-skill.js +3 -1
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-feedback-skill.js +39 -16
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
- package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +133 -14
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/index.d.ts +1 -1
- package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts +2 -0
- package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.js +1 -1
- package/dist/configurator/common/frontend-analytics.d.ts +21 -21
- package/dist/configurator/index.d.ts +0 -1
- package/dist/configurator/index.js +1 -1
- package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
- package/dist/configurator/scene/scene-analytics/scene-analytics.d.ts +2 -2
- package/dist/configurator/ui/assistant/AssistantChips.d.ts +11 -0
- package/dist/configurator/ui/assistant/AssistantChips.js +1 -0
- package/dist/configurator/ui/assistant/AssistantComposer.d.ts +6 -0
- package/dist/configurator/ui/assistant/AssistantComposer.js +1 -0
- package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +7 -0
- package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -0
- package/dist/configurator/ui/assistant/AssistantPanel.d.ts +11 -0
- package/dist/configurator/ui/assistant/AssistantPanel.js +1 -0
- package/dist/configurator/ui/assistant/AssistantThread.d.ts +6 -0
- package/dist/configurator/ui/assistant/AssistantThread.js +1 -0
- package/dist/configurator/ui/assistant/assistant-failure.d.ts +18 -0
- package/dist/configurator/ui/assistant/assistant-failure.js +1 -0
- package/dist/configurator/ui/assistant/index.d.ts +2 -0
- package/dist/configurator/ui/assistant/index.js +1 -0
- package/dist/configurator/ui/assistant/streamed-markdown.d.ts +6 -0
- package/dist/configurator/ui/assistant/streamed-markdown.js +2 -0
- package/dist/configurator/ui/assistant/theme.d.ts +2 -0
- package/dist/configurator/ui/assistant/theme.js +1 -0
- package/dist/configurator/ui/assistant/thread-items.d.ts +37 -0
- package/dist/configurator/ui/assistant/thread-items.js +1 -0
- package/dist/configurator/ui/assistant/tool-chip.d.ts +37 -0
- package/dist/configurator/ui/assistant/tool-chip.js +1 -0
- package/dist/configurator/ui/assistant/use-follow-stream.d.ts +17 -0
- package/dist/configurator/ui/assistant/use-follow-stream.js +1 -0
- package/dist/configurator/ui/assistant/use-is-assistant-panel-open.d.ts +5 -0
- package/dist/configurator/ui/assistant/use-is-assistant-panel-open.js +1 -0
- package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.js +5 -5
- package/dist/configurator/ui/configurator-layout/universal/MobileLayout.d.ts +2 -0
- package/dist/configurator/ui/configurator-layout/universal/MobileLayout.js +4 -4
- package/dist/configurator/ui/legacy/mobile-controls/MobileControls.d.ts +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.d.ts +10 -0
- package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.js +1 -0
- package/dist/configurator/ui/phoenix-layout-elements/index.d.ts +1 -0
- package/dist/configurator/ui/phoenix-layout-elements/index.js +1 -1
- package/dist/docs/index.json +1 -1
- package/dist/docs/layouts/toolbar.mdx +6 -0
- package/dist/docs/phoenix/admin.mdx +2 -2
- package/dist/docs/phoenix/finalization.mdx +1 -1
- package/dist/docs/phoenix/integration/headless-mode-external.mdx +5 -5
- package/dist/docs/phoenix/integration/widget-mode-external.mdx +5 -5
- package/dist/docs/phoenix/phoenix-cli.mdx +16 -12
- package/dist/docs/phoenix/sdk-builder-workflow.mdx +3 -2
- package/dist/domain/domain-context/index.d.ts +1 -1
- package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.d.ts +44 -0
- package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.js +1 -0
- package/dist/domain/system-components/phoenix-layout-elements/index.d.ts +1 -0
- package/dist/domain/system-components/phoenix-layout-elements/index.js +1 -1
- package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.d.ts +2 -1
- package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.js +1 -1
- package/dist/i18n/locales/cs/messages.js +1 -1
- package/dist/i18n/locales/da/messages.js +1 -1
- package/dist/i18n/locales/de/messages.js +1 -1
- package/dist/i18n/locales/de-AT/messages.js +1 -1
- package/dist/i18n/locales/en/messages.js +1 -1
- package/dist/i18n/locales/en-AU/messages.js +1 -1
- package/dist/i18n/locales/en-GB/messages.js +1 -1
- package/dist/i18n/locales/es/messages.js +1 -1
- package/dist/i18n/locales/fr/messages.js +1 -1
- package/dist/i18n/locales/hu/messages.js +1 -1
- package/dist/i18n/locales/it/messages.js +1 -1
- package/dist/i18n/locales/nl/messages.js +1 -1
- package/dist/i18n/locales/pl/messages.js +1 -1
- package/dist/i18n/locales/pt/messages.js +1 -1
- package/dist/i18n/locales/ro/messages.js +1 -1
- package/dist/i18n/locales/sk/messages.js +1 -1
- package/dist/i18n/locales/tr/messages.js +1 -1
- package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +6 -0
- package/dist/phoenix/cli.js +1 -1
- package/dist/phoenix/commands/deploy.js +2 -2
- package/dist/phoenix/commands/feedback.d.ts +2 -1
- package/dist/phoenix/commands/feedback.js +1 -1
- package/dist/react-sdk/application-runtime/common-services/analytics/analytics-service.d.ts +2 -2
- package/dist/react-sdk/application-runtime/common-services/analytics/domain-controls-analytics.d.ts +21 -21
- package/dist/react-sdk/application-runtime/hooks/data/use-published-data.d.ts +1 -1
- package/dist/ui-sdk/common/use-viewport-size.d.ts +4 -1
- package/dist/ui-sdk/common/use-viewport-size.js +1 -1
- package/dist/ui-sdk/components/mobile-controls/index.d.ts +1 -1
- package/dist/ui-sdk/theme/types.d.ts +4 -0
- package/package.json +5 -8
- package/schema/domain-spec.json +24 -0
- package/dist/configurator/assistant/index.d.ts +0 -1
- package/dist/configurator/assistant/index.js +0 -1
- package/dist/configurator/assistant/use-assistant-chat.d.ts +0 -20
- package/dist/configurator/assistant/use-assistant-chat.js +0 -1
- package/dist/extras/assistant/AssistantChat.d.ts +0 -6
- package/dist/extras/assistant/AssistantChat.js +0 -1
- package/dist/extras/assistant/index.d.ts +0 -1
- package/dist/extras/assistant/index.js +0 -1
package/README.md
CHANGED
|
@@ -42,17 +42,17 @@ Generated code exposes the `phx` global in project files: `phx.domain` for the m
|
|
|
42
42
|
|
|
43
43
|
Run the CLI through the project's package manager so its version matches the installed SDK.
|
|
44
44
|
|
|
45
|
-
| Command | What it does
|
|
46
|
-
| ------------------------------ |
|
|
47
|
-
| `phoenix init [folder]` | Starts a project: `package.json` when missing, and a starter spec.
|
|
48
|
-
| `phoenix sync` | Regenerates project files and aligns dependencies and scripts.
|
|
49
|
-
| `phoenix dev` | Development server with hot reload, webhook tunnel and the editor.
|
|
50
|
-
| `phoenix build` | Production build of the frontend and the webhooks.
|
|
51
|
-
| `phoenix check` / `fix` | Type check, lint and format; `fix` applies the automatic fixes.
|
|
52
|
-
| `phoenix login <workspace>` | Signs in through Studio; the token is kept in `~/.phoenix/credentials.json`.
|
|
53
|
-
| `phoenix deploy` | Uploads the project sources for a platform build.
|
|
54
|
-
| `phoenix docs` | Searches and reads the SDK documentation bundled with the package.
|
|
55
|
-
| `phoenix feedback "<message>"` | Sends feedback about the SDK, CLI, documentation or Studio to Salsita.
|
|
45
|
+
| Command | What it does |
|
|
46
|
+
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
|
47
|
+
| `phoenix init [folder]` | Starts a project: `package.json` when missing, and a starter spec. |
|
|
48
|
+
| `phoenix sync` | Regenerates project files and aligns dependencies and scripts. |
|
|
49
|
+
| `phoenix dev` | Development server with hot reload, webhook tunnel and the editor. |
|
|
50
|
+
| `phoenix build` | Production build of the frontend and the webhooks. |
|
|
51
|
+
| `phoenix check` / `fix` | Type check, lint and format; `fix` applies the automatic fixes. |
|
|
52
|
+
| `phoenix login <workspace>` | Signs in through Studio; the token is kept in `~/.phoenix/credentials.json`. |
|
|
53
|
+
| `phoenix deploy` | Uploads the project sources for a platform build. |
|
|
54
|
+
| `phoenix docs` | Searches and reads the SDK documentation bundled with the package. |
|
|
55
|
+
| `phoenix feedback "<message>"` | Sends feedback about the SDK, CLI, documentation or Studio to Salsita; `--file <path>` sends a text file instead. |
|
|
56
56
|
|
|
57
57
|
Every frontend `.js` file `phoenix build` emits starts with a comment naming the application version from `spec/application.yaml`, the git commit when the build runs in a checkout, and the SDK version. `--banner <text>` replaces the application line with your own text. The line is public in every bundle:
|
|
58
58
|
|
|
@@ -71,7 +71,9 @@ Walk through the whole journey as the intended user: start, explore and change o
|
|
|
71
71
|
|
|
72
72
|
Check representative valid and invalid combinations, boundaries, dependent changes, independent options on repeated parts, and hand-calculated prices where applicable. Include persistence/reload, undo/redo, exports, and integrations when implemented. Invalid configurations should produce the agreed handling, not an assumed universal blocking pattern.
|
|
73
73
|
|
|
74
|
-
Fix gaps and regressions, update the specification, and present the complete experience for the client's final review. State what is implemented, what remains provisional or unverified, and whether it is a working draft or ready for its intended users. Publishing and connecting live systems remain separate actions governed by the client's authorization and the environment's rules; obtain any required approval against the concrete result
|
|
74
|
+
Fix gaps and regressions, update the specification, and present the complete experience for the client's final review. State what is implemented, what remains provisional or unverified, and whether it is a working draft or ready for its intended users. Publishing and connecting live systems remain separate actions governed by the client's authorization and the environment's rules; obtain any required approval against the concrete result.
|
|
75
|
+
|
|
76
|
+
Once the client says they are done, or the configurator is live and they confirm the work is finished, follow the \`phoenix-feedback\` skill to interview them for an evaluation of the run. Ask for it once, and send it only with their approval.`,c=`## Verification evidence
|
|
75
77
|
|
|
76
78
|
Use the project's documented verification commands and available tools throughout. Distinguish automated checks, your actual preview observations, and client feedback. A successful build does not prove preview access or interaction quality. Missing browser automation need not prevent the client from using a preview: offer a focused check and record their result as client feedback. Disclose anything you cannot test; a generic “OK” is not evidence that an untested interaction works.`,d=`# Phoenix UX guidance
|
|
77
79
|
|
package/dist/application-spec/generate-application/code/templates/skills/phoenix-feedback-skill.js
CHANGED
|
@@ -4,7 +4,9 @@ Draft feedback for the Phoenix team when one of these happens while you work in
|
|
|
4
4
|
- **Misleading guidance** — a doc page, a Phoenix skill or an error message sent you the wrong way, or did not say what you needed.
|
|
5
5
|
- **Missing feature** — something the SDK does not support forced a workaround, or a routine task took far more steps than it should.
|
|
6
6
|
|
|
7
|
-
Do not draft feedback for problems in this project's own specs or code, for a suspicion you have not checked, or twice for the same problem. Write one draft per distinct problem
|
|
7
|
+
Do not draft feedback for problems in this project's own specs or code, for a suspicion you have not checked, or twice for the same problem. Write one draft per distinct problem.
|
|
8
|
+
|
|
9
|
+
The evaluation of a finished run is feedback too; the section "Evaluate the Run" says when to ask for it.`,o=`## Ask Before You Send
|
|
8
10
|
Never send feedback on your own. Show the user a draft and send it only after they approve it.
|
|
9
11
|
|
|
10
12
|
When to ask is your judgement:
|
|
@@ -12,20 +14,41 @@ When to ask is your judgement:
|
|
|
12
14
|
- **At the end of the task** — everything else. Collect the details while you work and show all drafts together in your final message.
|
|
13
15
|
- **When the user asks** you to send feedback — draft it and show it before sending.
|
|
14
16
|
|
|
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.`,
|
|
16
|
-
When
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
The draft is exactly what would be sent: the type, the full 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=`## Evaluate the Run
|
|
18
|
+
When the work on the configurator is finished, interview the user for an evaluation of the run and send it as feedback. The run is finished when the user says they are done, or when \`phoenix deploy\` reported the configurator live and the user confirms they have nothing more to build; ask that once after a deploy goes live, and do not start the interview while they want to continue.
|
|
19
|
+
|
|
20
|
+
Say first that the answers are sent to the Phoenix team as feedback, and that the user can decline. Then ask one question at a time, in this order, and wait for each answer before asking the next. Give the short explanation with each question. Every score is a whole number from 0 to 10, where 0 is catastrophic, 5 is passable and 10 is amazing:
|
|
21
|
+
1. **Minutes spent** — roughly how many minutes the user spent on the run, from the first prompt to now. This one is a number of minutes, not a score.
|
|
22
|
+
2. **Agent guidance** — how clear it was what to expect and what to do, how clearly you explained yourself, and how well they understood the consequences of their choices.
|
|
23
|
+
3. **Functionality** — how complete the configurator is: whether it configures the full product with all its options, keeps the user from creating or submitting a wrong configuration, and offers a way to finalize one.
|
|
24
|
+
4. **UX** — how easy the configurator is to understand and operate, how clear the choices are, and how understandable incompatibilities and the ways to resolve them are.
|
|
25
|
+
5. **3D quality** — how realistic and attractive the configured object and its scene are: shapes, textures, surroundings, lighting and shadows.
|
|
26
|
+
6. **UI visuals** — how good the user interface looks.
|
|
27
|
+
7. **Overall** — the user's subjective verdict on the configurator as a whole.
|
|
28
|
+
8. **Comments** — anything else about the experience, in their own words. This one is free text and may be empty.
|
|
29
|
+
|
|
30
|
+
Rules of the interview:
|
|
31
|
+
- Ask once per conversation. Do not run it again after a later deploy, and do not run it after a deploy that failed.
|
|
32
|
+
- Never choose, guess or adjust a score, and never answer a question for the user. When they skip one, write \`skipped\` for it.
|
|
33
|
+
- When the user declines the evaluation, send nothing and do not ask again.
|
|
34
|
+
|
|
35
|
+
Compose the evaluation from the answers with exactly these labels, one per line, in this order. Write \`none\` when there are no comments, and fill \`Build\` and \`Configurator\` as the report template describes:
|
|
36
|
+
\`\`\`text
|
|
37
|
+
Evaluation
|
|
38
|
+
Minutes spent: <minutes>
|
|
39
|
+
Agent guidance: <score>/10
|
|
40
|
+
Functionality: <score>/10
|
|
41
|
+
UX: <score>/10
|
|
42
|
+
3D quality: <score>/10
|
|
43
|
+
UI visuals: <score>/10
|
|
44
|
+
Overall: <score>/10
|
|
45
|
+
Comments: <their comments, or none>
|
|
46
|
+
Build: <build id> <Studio build page link>
|
|
47
|
+
Configurator: <live configurator link>
|
|
26
48
|
\`\`\`
|
|
27
|
-
Keep the
|
|
49
|
+
Keep the labels and their order, so the scores can be read out of the feedback list and its export. Show the user the text exactly as it would be sent, and send it with \`--type other\` only after a yes; if they edit it, send their version. An evaluation is not a bug report: when a comment names a concrete problem, offer to draft it as its own report.`,a=`## What Never Goes Into Feedback
|
|
28
50
|
The message leaves this project and is read by the Phoenix team. Never include:
|
|
29
|
-
- secrets: API tokens, keys, passwords or \`.env\` values
|
|
30
|
-
-
|
|
31
|
-
|
|
51
|
+
- secrets and credentials: API tokens, keys, passwords or \`.env\` values, also when they appear inside an error or a log you quote. Replace each with \`<redacted>\`;
|
|
52
|
+
- personal data, or data of the project's customers and end users, such as names, email addresses and orders, other than a contact the user asked you to add.
|
|
53
|
+
|
|
54
|
+
Parts of this project may go into a message: file names and paths, snippets of its specs and code, and the user's own words. They are sent only as part of a draft the user saw and approved, which is why the draft you show is always the full text.`;export const generatePhoenixFeedbackSkill=()=>e({name:"phoenix-feedback",description:"Evaluating a finished run and drafting feedback about the Phoenix SDK, CLI, documentation or Studio for the Phoenix team, sent with the phoenix feedback command once the user approves the draft. Use right after phoenix deploy reports the configurator live or when the user says they are done, to offer the evaluation of the run, and 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,"## Report Template\nWrite a report as plain text with the labels below, in this order, so the Phoenix team reads every report the same way. Keep every label; write `none` or `not deployed yet` where you have nothing to put.\n```text\nBuild: <build id> <Studio build page link>\nConfigurator: <live configurator link>\n\nWhat I was doing:\n<the task and the step you were on>\n\nWhat happened and what I expected:\n<what you ran or called, the result you got, and the result you expected>\n\nExact error or agent output:\n<the error text or the output, copied unchanged>\n\nWorkaround:\n<what you did instead, or none>\n\nConversation log:\n<the conversation condensed to the turns that matter: what the user asked, what you tried, what failed>\n```\n- **Build** is the latest build of this project, the one whose sources show the problem. `pnpm phoenix deploy` prints its id as `Build <id> started` and its page as `Follow the build in Studio: <url>`. When that line is missing, compose the link as `https://<workspace>.<studio host>/builder/deployments/<build id>`: the workspace is the one named at `phoenix login`, and the host is `studio.eu.salsita.ai` for the `eu` region, `studio.salsita.ai` for `us` and `studio-staging.salsita.app` for staging. A Studio address passed to login with `--studioUrl` replaces the workspace and host. Never invent a build id: when the project was not deployed, write `not deployed yet`.\n- **Configurator** is the live address `pnpm phoenix deploy` prints once the build is live.\n- The team downloads the sources of the build from its Studio page, so do not paste whole files; quote only the lines that show the problem.\n- Quote errors and output exactly. A report that paraphrases an error cannot be searched for.\n- Condense the conversation log, do not dump it: keep the user's requests, your decisions and the failures, and leave out tool output that the other sections already carry.",n,"## How to Send It\nWrite a report or an evaluation to a text file and send the file, so the shell changes nothing in it:\n```bash\npnpm phoenix feedback --file <path> --type bug --agent '<your agent name>'\n```\nPut the file outside the project, in the system's temporary folder, and delete it after sending. A note of a line or two can go in as an argument instead:\n```bash\npnpm phoenix feedback '<message>' --type idea --agent '<your agent name>'\n```\n- Give either `--file` or the message argument, never both.\n- Put a message argument 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, and `other` for an evaluation.\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 100,000 characters. Shorten the conversation log first when a report is longer.\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]});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{renderAgentSkill as e}from"./agent-skill.js";import{sdkGuidanceSections as t}from"./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, versioning the domain model once it is deployed, showing the user the dev preview and the build of a deploy, 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,t.modelVersioning,"## Show the Preview While You Work\nThe user should see the configurator change as you build it. Before your first change in a conversation, make sure `pnpm phoenix dev` is running (start it in the background when it is not) and open the address it prints as `Preview: <url>` (also written to `.phoenix/phoenix-dev.log`) in the user's browser: `open '<url>'` on macOS, `xdg-open '<url>'` on Linux, `Start-Process '<url>'` in PowerShell, `start \"\" \"<url>\"` in cmd. Keep the quotes, the shell would otherwise read a `?` in the address as a pattern. The log keeps the lines of earlier runs, so when you have just started dev, wait for the `Preview:` line written after your start and use the last one. Open it once; the preview reloads on every change, so tell the user to keep that tab open instead of opening it again.",
|
|
1
|
+
import{renderAgentSkill as e}from"./agent-skill.js";import{sdkGuidanceSections as t}from"./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, versioning the domain model once it is deployed, showing the user the dev preview and the build of a deploy, the run evaluation that follows a live deploy, 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,t.modelVersioning,"## Show the Preview While You Work\nThe user should see the configurator change as you build it. Before your first change in a conversation, make sure `pnpm phoenix dev` is running (start it in the background when it is not) and open the address it prints as `Preview: <url>` (also written to `.phoenix/phoenix-dev.log`) in the user's browser: `open '<url>'` on macOS, `xdg-open '<url>'` on Linux, `Start-Process '<url>'` in PowerShell, `start \"\" \"<url>\"` in cmd. Keep the quotes, the shell would otherwise read a `?` in the address as a pattern. The log keeps the lines of earlier runs, so when you have just started dev, wait for the `Preview:` line written after your start and use the last one. Open it once; the preview reloads on every change, so tell the user to keep that tab open instead of opening it again.",'## Show the Build After a Deploy\n`pnpm phoenix deploy` starts a build on the server and waits for it to go live. Right after `Build <id> started` it prints `Follow the build in Studio: <url>`. Run the deploy so that you can read its output while it runs, and open that link in the user\'s browser, quoted as above, as soon as it appears, so they can follow the build they just triggered. When the line is missing, the sign-in is older than the link: tell the user the build is listed under Deployments in Studio, and that `pnpm phoenix login <workspace>` brings the link back.\n\nWhen the deploy reports the configurator live, load the `phoenix-feedback` skill before you write your final message and follow its "Evaluate the Run" section, so the user is asked about the run while they are still there. It is asked once per conversation, and only once the user has nothing more to build.',"## Code Review\nUse the `phoenix-review` skill to review code changes before committing.",t.sdkDocumentation]});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AnimationSystem as e,AssistantSystem as t,CameraControlSystem as n,CameraSystem as o,CanvasSystem as r,DragAndDropSystem as s,ExportSystem as g,GraphQLSystem as i,ImageRenderingSystem as a,LayoutSystem as m,SketchingSystem as S}from"./systems/index.js";export const createConfiguratorExtensionSelectors=y=>{let{domainContextSelectors:u,specSelectors:w,domainServicesSelectors:C,accessSelectors:c,commonServicesSelectors:f}=y,
|
|
1
|
+
import{AnimationSystem as e,AssistantSystem as t,CameraControlSystem as n,CameraSystem as o,CanvasSystem as r,DragAndDropSystem as s,ExportSystem as g,GraphQLSystem as i,ImageRenderingSystem as a,LayoutSystem as m,SketchingSystem as S}from"./systems/index.js";export const createConfiguratorExtensionSelectors=y=>{let{domainContextSelectors:u,specSelectors:w,domainServicesSelectors:C,accessSelectors:c,commonServicesSelectors:f}=y,d={getConfigurationId:()=>c.getConfiguratorConfigurationId(),getAuthorizedClient:()=>f.getConfiguratorAuthorizedClient(),domainContextSelectors:u,domainServicesSelectors:C,specSelectors:w},l={getSystems:()=>G,getCanvasSystem:()=>p,getCameraControlSystem:()=>A,getAnimationSystem:()=>E,getDragAndDropSystem:()=>h,getImageRenderingSystem:()=>v,getLayoutSystem:()=>L,getSketchingSystem:()=>j,getExportSystem:()=>I,getCameraSystem:()=>k,getGraphQLSystem:()=>z,getAssistantSystem:()=>D},x={...d,configuratorExtensionSelectors:l},p=new r(d),A=new n({configuratorExtensionSelectors:l}),h=new s(x),E=new e(d),I=new g(x),z=new i(d),D=new t(x),L=new m(d),j=new S(x),k=new o(x),v=new a(x),G={cameraControlSystem:A,layoutSystem:L,sketchingSystem:j,dragAndDropSystem:h,canvasSystem:p,cameraSystem:k,exportSystem:I,imageRenderingSystem:v,animationSystem:E,graphQLSystem:z,assistantSystem:D};return l};
|
|
@@ -1,35 +1,154 @@
|
|
|
1
1
|
import type { AnyClientTool } from '@tanstack/ai';
|
|
2
|
-
import
|
|
2
|
+
import { type UIMessage } from '@tanstack/ai-client';
|
|
3
|
+
import { type ObservableWithLastValue } from '@salsita-ai/common/utils';
|
|
4
|
+
import type { AuthorizedPhoenixApiClient } from '#package/client';
|
|
5
|
+
import type { DomainContextSelectors, DomainServicesSelectors, InitializableSystem } from '#package/domain';
|
|
3
6
|
import type { ConfiguratorExtensionSelectors } from '../../selectors.js';
|
|
4
7
|
/** A tool of the assistant that the model calls and the browser runs, as the chat client accepts it. */
|
|
5
8
|
export type AssistantClientTool = AnyClientTool;
|
|
6
9
|
/**
|
|
7
10
|
* Constructor context of an `AssistantSystem`: whole selector objects, like its peers take them, so
|
|
8
|
-
* the tools reach the store, the session mode and the `GraphQLSystem` when they are called
|
|
11
|
+
* the tools reach the store, the session mode and the `GraphQLSystem` when they are called, and the
|
|
12
|
+
* chat reaches the server of the current session when it sends a request.
|
|
9
13
|
*/
|
|
10
14
|
export interface AssistantSystemContext {
|
|
11
15
|
readonly configuratorExtensionSelectors: ConfiguratorExtensionSelectors;
|
|
12
16
|
readonly domainContextSelectors: DomainContextSelectors;
|
|
13
|
-
readonly
|
|
17
|
+
readonly domainServicesSelectors: DomainServicesSelectors;
|
|
18
|
+
readonly getConfigurationId: () => string;
|
|
19
|
+
readonly getAuthorizedClient: () => AuthorizedPhoenixApiClient<any, any, any>;
|
|
14
20
|
}
|
|
15
21
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
* The conversation with the assistant as the panel paints it. One value is published per change, so
|
|
23
|
+
* it changes with every chunk of a streaming reply.
|
|
24
|
+
*/
|
|
25
|
+
export interface AssistantChatSnapshot {
|
|
26
|
+
/** The thread as painted: hydrated from the server, then streamed. */
|
|
27
|
+
readonly messages: ReadonlyArray<UIMessage>;
|
|
28
|
+
/** True while a round of a reply streams or its browser tools run; false in the pause before the next round. */
|
|
29
|
+
readonly isLoading: boolean;
|
|
30
|
+
readonly error: Error | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* HTTP status of the latest request the server answered with a failure, for telling a refusal from
|
|
33
|
+
* a dropped connection when `error` is set. Nothing when the latest answered request succeeded or
|
|
34
|
+
* the server was never reached.
|
|
35
|
+
*/
|
|
36
|
+
readonly failedStatus: number | undefined;
|
|
37
|
+
/** False when the server said this session may read the thread but not add to it. */
|
|
38
|
+
readonly isContinuable: boolean;
|
|
39
|
+
/** False until the first request of the chat, the thread read, was answered or failed. */
|
|
40
|
+
readonly isLoaded: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The built-in assistant panel as a layout and the toolbar button see it: whether it is open, and
|
|
44
|
+
* the few facts about it they render. It changes only when one of those does.
|
|
45
|
+
*/
|
|
46
|
+
export interface AssistantPanelSnapshot {
|
|
47
|
+
readonly open: boolean;
|
|
48
|
+
/** True when a reply finished while the panel was closed and the panel was not opened since. */
|
|
49
|
+
readonly hasUnreadReply: boolean;
|
|
50
|
+
/** True while the message input has focus; a phone layout gives the panel the whole screen then. */
|
|
51
|
+
readonly isInputFocused: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Owns the conversational assistant of a configurator: its client tools (`getBrowserDetails`,
|
|
55
|
+
* `query` and `apply`), the chat with the server, and the state of the built-in panel.
|
|
56
|
+
*
|
|
57
|
+
* The server owns the thread, so the chat keeps nothing in browser storage: its client hydrates
|
|
58
|
+
* from the server and rejoins a reply that is still streaming.
|
|
59
|
+
*
|
|
60
|
+
* WORKAROUND: the runtime cannot tell a system that its configuration was left, and a system cannot
|
|
61
|
+
* observe the application spec. Until it can do both, the chat is started and ended from outside,
|
|
62
|
+
* through {@link AssistantSystem.activate}; see there for what that costs.
|
|
20
63
|
*/
|
|
21
64
|
export declare class AssistantSystem implements InitializableSystem {
|
|
22
65
|
private readonly _context;
|
|
23
66
|
private readonly _tools;
|
|
24
|
-
private
|
|
67
|
+
private readonly _chat;
|
|
68
|
+
private readonly _panel;
|
|
69
|
+
/** Identity of the current `activate()` call; nothing while the chat is not active. */
|
|
70
|
+
private _activation;
|
|
71
|
+
/** The chat of the current activation; nothing while no chat is active. */
|
|
72
|
+
private _current;
|
|
73
|
+
/**
|
|
74
|
+
* Chats that have ended while their reply was still running and are finishing it in the
|
|
75
|
+
* background. Each leaves the set when its reply ends or when a new chat takes its thread over.
|
|
76
|
+
*/
|
|
77
|
+
private readonly _finishing;
|
|
25
78
|
constructor(_context: AssistantSystemContext);
|
|
79
|
+
/** The conversation. It never completes, and holds the empty chat whenever no chat is active. */
|
|
80
|
+
readonly chat$: ObservableWithLastValue<AssistantChatSnapshot>;
|
|
81
|
+
/** The state of the built-in panel. It never completes. */
|
|
82
|
+
readonly panel$: ObservableWithLastValue<AssistantPanelSnapshot>;
|
|
83
|
+
/**
|
|
84
|
+
* A new configuration load: the chat of the previous one is deactivated and both observables go
|
|
85
|
+
* back to their empty values. An activation that is still held outlives it and gets the chat of
|
|
86
|
+
* the new load, because the page that holds it can be mounted before this runs: a configuration
|
|
87
|
+
* that is entered again renders its page at once and initializes from an effect after the page's.
|
|
88
|
+
*
|
|
89
|
+
* Accepted cost: on such a re-entry the thread is read twice. The page's `activate()` has already
|
|
90
|
+
* created a client and started its read when this runs, and that client is replaced here, because
|
|
91
|
+
* the system cannot tell that it was created for the load that is only now being initialized.
|
|
92
|
+
*/
|
|
26
93
|
initialize: () => void;
|
|
27
94
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
95
|
+
* Starts the chat of the loaded configuration: creates the client, which reads the thread from the
|
|
96
|
+
* server and rejoins a reply that is still running. The returned function ends it.
|
|
97
|
+
*
|
|
98
|
+
* WORKAROUND, to be replaced once the runtime has teardown and spec observation for systems. Two
|
|
99
|
+
* things a system cannot learn by itself are decided by the caller, the loaded configurator page:
|
|
100
|
+
*
|
|
101
|
+
* - Whether the application spec has an `assistant` section, also after editor hot reload added or
|
|
102
|
+
* removed it. The page calls this only while the section is there; the system does not check.
|
|
103
|
+
* - That the user left the configuration. No `initialize()` follows a route change, so without the
|
|
104
|
+
* returned function the chat would go on working on a configuration nobody looks at.
|
|
105
|
+
*
|
|
106
|
+
* Ending the chat does not cut a running reply off: it finishes in the background, where every
|
|
107
|
+
* tool call it still makes is answered with a refusal that tells the model to stop, and it no
|
|
108
|
+
* longer reaches `chat$` or `panel$`. A tool call that is already running when the chat ends is
|
|
109
|
+
* not interrupted, so an `apply` that has started may still commit. The background reply keeps
|
|
110
|
+
* the configuration and the authorized client it started with.
|
|
111
|
+
*
|
|
112
|
+
* The background reply ends early when a new chat starts for the same configuration, which is
|
|
113
|
+
* what opening the configuration again does: see `_takeOverThread`.
|
|
114
|
+
*/
|
|
115
|
+
activate: () => (() => void);
|
|
116
|
+
/** Sends a user message stamped with its send time, which the model is told. */
|
|
117
|
+
send: (content: string) => Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Stops the reply on both sides. Closing the stream alone keeps the server run going, because a
|
|
120
|
+
* closed connection is also how a reload looks and a reload must be able to rejoin the run.
|
|
121
|
+
*/
|
|
122
|
+
stop: () => Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Loads the thread from the server again, rejoining a reply that is still running there. It never
|
|
125
|
+
* sends a message again, because the server may already hold it; a message that did not reach the
|
|
126
|
+
* server is therefore gone from the thread and has to be written again.
|
|
127
|
+
*/
|
|
128
|
+
reloadThread: () => void;
|
|
129
|
+
setOpen: (open: boolean) => void;
|
|
130
|
+
setInputFocused: (isInputFocused: boolean) => void;
|
|
131
|
+
private _patchPanel;
|
|
132
|
+
/**
|
|
133
|
+
* Disconnects every chat that is still finishing a reply on the thread of the given configuration,
|
|
134
|
+
* so the chat that is about to start is the only one on it.
|
|
135
|
+
*
|
|
136
|
+
* Two clients on one thread would both follow the same server run: the new one rejoins it when it
|
|
137
|
+
* reads the thread. Both would then handle the run's next tool call, the old one with a refusal
|
|
138
|
+
* and the new one for real, and both would send the result back, of which the server accepts one.
|
|
139
|
+
* The model could be told that nothing was run about a change that was made.
|
|
140
|
+
*
|
|
141
|
+
* Stopping a client only closes its stream in the browser. Nothing is cancelled on the server, so
|
|
142
|
+
* the run goes on there and the new chat rejoins it. A chat that finishes a reply for another
|
|
143
|
+
* configuration is on another thread and is left alone.
|
|
144
|
+
*/
|
|
145
|
+
private _takeOverThread;
|
|
146
|
+
private _startClient;
|
|
147
|
+
/**
|
|
148
|
+
* Lets go of the current chat and puts the empty chat in its place. The client itself is left as
|
|
149
|
+
* it is: whether it finishes its reply or is cut off is the caller's decision.
|
|
33
150
|
*/
|
|
34
|
-
|
|
151
|
+
private _retireChat;
|
|
152
|
+
/** Ends the current chat without cutting its reply off. */
|
|
153
|
+
private _endChat;
|
|
35
154
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ChatClient as t,fetchServerSentEvents as e}from"@tanstack/ai-client";import{BehaviorSubject as i}from"rxjs";import{isShallowEqual as s}from"@salsita-ai/common/utils";import{createBrowserDetailsClientTool as a}from"./browser-details.js";import{createApplyClientTool as n,createQueryClientTool as r}from"./graphql-tools.js";let o=Object.freeze({messages:Object.freeze([]),isLoading:!1,error:void 0,failedStatus:void 0,isContinuable:!0,isLoaded:!1}),h=Object.freeze({open:!1,hasUnreadReply:!1,isInputFocused:!1}),l=Object.freeze({results:Object.freeze([]),committed:!1,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."}),c=t=>{if(t.getError())return!0;let e=t.getMessages().at(-1)?.parts.at(-1);return e?.type!=="tool-call"&&e?.type!=="tool-result"};export class AssistantSystem{_context;_tools;_chat=new i(o);_panel=new i(h);_activation=null;_current=null;_finishing=new Set;constructor(t){this._context=t;let{configuratorExtensionSelectors:e,domainContextSelectors:i}=t,s={getGraphQLSystem:()=>e.getGraphQLSystem(),getStoreSystem:()=>i.getStoreSystem(),getActiveMode:()=>i.getActiveMode()};this._tools=[a(),r(s),n(s)]}chat$=this._chat;panel$=this._panel;initialize=()=>{this._endChat(),this._patchPanel(h),this._activation&&this._startClient()};activate=()=>{this._endChat();let t={};return this._activation=t,this._startClient(),()=>{this._activation===t&&(this._activation=null,this._endChat())}};send=async t=>{let e={sentAt:new Date().toISOString()};await this._current?.client.sendMessage({content:t,metadata:e})};stop=async()=>{let t=this._current;if(!t)return;let e=t.client.getCurrentRunId();if(t.client.stop(),null!=e)try{let i=t.getConnection();await fetch(`${i.url}/runs/${encodeURIComponent(e)}/cancel`,{method:"POST",headers:await i.headers()})}catch(t){this._context.domainServicesSelectors.getLogger().warn("Could not cancel the assistant run on the server",{error:t})}};reloadThread=()=>{if(!this._activation)return;let t=this._retireChat();t?.client.stop(),t?.dispose(),this._startClient()};setOpen=t=>{this._patchPanel(t?{open:t,hasUnreadReply:!1}:{open:t,isInputFocused:!1})};setInputFocused=t=>{this._patchPanel({isInputFocused:t})};_patchPanel=t=>{let e={...this._panel.value,...t};s(e,this._panel.value)||this._panel.next(e)};_takeOverThread=t=>{for(let e of this._finishing)e.configurationId===t&&(e.client.stop(),e.dispose())};_startClient=()=>{let i={active:!0},a=this._context.getConfigurationId();this._takeOverThread(a);let n=this._context.getAuthorizedClient(),r=()=>n.getAssistantChatConnection(a),o=t=>{if(!i.active)return;let e={...this._chat.value,...t};s(e,this._chat.value)||this._chat.next(e)},h=async(t,e)=>{let i=await fetch(t,e).finally(()=>o({isLoaded:!0})),s=i.headers.get("x-assistant-thread-continuable");return o({failedStatus:i.ok?void 0:i.status,...null!=s&&{isContinuable:"false"!==s}}),i},u=async()=>({headers:await r().headers(),fetchClient:h}),d=e(()=>r().url,u),_=e(()=>`${r().url}/runs`,u),g=this._tools.map(t=>({...t,execute:(e,s)=>i.active?t.execute?.(e,s):l})),p=new t({threadId:`assistant:${a}`,connection:{...d,joinRun:_.joinRun},persistence:!0,history:{pageSize:50},tools:g,onMessagesChange:t=>o({messages:t}),onLoadingChange:t=>{let e=i.active&&this._chat.value.isLoading;o({isLoading:t}),!t&&c(p)&&(i.active?e&&!this._panel.value.open&&this._patchPanel({hasUnreadReply:!0}):v())},onErrorChange:t=>{i.active&&t!==this._chat.value.error&&(o({error:t}),t&&this._context.domainServicesSelectors.getLogger().error("The assistant request failed",{status:this._chat.value.failedStatus},t))}}),f=!1,v=()=>{f||(f=!0,this._finishing.delete(m),p.dispose())},m={client:p,configurationId:a,generation:i,getConnection:r,dispose:v};this._current=m,p.attach()};_retireChat=()=>{let t=this._current;return this._current=null,t&&(t.generation.active=!1),this._chat.value!==o&&this._chat.next(o),t};_endChat=()=>{let t=this._retireChat();if(t){if(!t.client.getIsLoading()&&c(t.client))return void t.dispose();this._finishing.add(t)}}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AnimationSystem, type AnimationSystemContext } from './animation-system.js';
|
|
2
|
-
export { type AssistantClientTool, AssistantSystem, type AssistantSystemContext, } from './assistant-system/assistant-system.js';
|
|
2
|
+
export { type AssistantChatSnapshot, type AssistantClientTool, type AssistantPanelSnapshot, AssistantSystem, type AssistantSystemContext, } from './assistant-system/assistant-system.js';
|
|
3
3
|
export { CameraControlSystem, CameraControlType } from './camera-control-system/camera-control-system.js';
|
|
4
4
|
export { type AnyCameraSystem, CameraSystem, type CameraSystemContext } from './camera-control-system/camera-system.js';
|
|
5
5
|
export { createCameraControls } from './camera-control-system/create-camera-controls.js';
|
package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export declare class LayoutSystem<ENTITIES extends DomainEntity<string>> {
|
|
|
25
25
|
* whatever was true when it was created.
|
|
26
26
|
*/
|
|
27
27
|
private get _toolbarHelpExists();
|
|
28
|
+
/** Whether the application spec has an `assistant` section; read on demand for the same reason. */
|
|
29
|
+
private get _hasAssistant();
|
|
28
30
|
initialize: () => void;
|
|
29
31
|
getCleanUpPropertyPageStateUpdates: (entityId: DomainEntityId<string>, state: DomainState<ENTITIES>) => import("#package/domain").ComponentModification<import("#package/domain").SystemComponentType<"layoutStateComponent", import("./layout-state.js").LayoutStateComponentState, "propertyPageStates">>[];
|
|
30
32
|
getLayoutConfig: (state: DomainState<ENTITIES>) => {
|
package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{P as t,match as e}from"ts-pattern";import{SystemState as o,applicationConfigComponent as n,customSummaryPageComponent as a,entityHeaderComponent as i,navigationStepsComponent as r,propertyPageComponent as s,sidebarTabsComponent as l,simpleSummaryComponent as d,toolbarComponent as c}from"#package/domain";import{layoutStateComponent as g}from"./layout-state.js";import{toolbarHelpElementName as u}from"./toolbar-help-element.js";export class LayoutSystem{_context;_toolbarCache=new WeakMap;state;constructor(t){this._context=t,this.state=new o({...this._context,selectors:this._context.domainContextSelectors,componentType:g,getInitialState:()=>({propertyPageStates:{}})})}get _toolbarHelpExists(){return void 0!==this._context.specSelectors.getElementSlots().layoutElements[u]}initialize=()=>{this.state.initialize()};getCleanUpPropertyPageStateUpdates=(t,e)=>this.state.getUpdates(e=>{if(!e.propertyPageStates[t])return null;let o={...e.propertyPageStates};return delete o[t],{...e,propertyPageStates:o}},e);getLayoutConfig=t=>{let e=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[n]},t),o=e?.state.shareDisabled??!1,a=o||(e?.state.shareOwnerLinkDisabled??!1),i=o||(e?.state.shareReadRevisionLinkDisabled??!1);return{layout:e?.state.layout??"universal",ignoreSelectionInSidebar:e?.state.ignoreSelectionInSidebar??!1,miniSidebar:e?.state.miniSidebar??!1,splitSidebar:e?.state.splitSidebar??!1,showSelectionBackButton:e?.state.showSelectionBackButton??!0,shareDisabled:a&&i,availableLocales:e?.state.availableLocales??["en"],indicatorSwitch:e?.state.indicatorSwitch??!0}};getSidebarSummaryPage=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveNavigationState(t),o=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[a,d]},t),n=e===o?.state.summaryNavigationState,i=o?.state.summaryNavigationState;return{page:o,isSummaryActive:n,summaryNavigationState:i}};getEnterSidebarSummaryUpdates=t=>{let{summaryNavigationState:e}=this.getSidebarSummaryPage(t);return e?[this._context.domainContextSelectors.getNavigationSystem().getPushNavigationUpdate(e,t)]:[]};getLeaveSidebarSummaryUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);if(e)return[e];let o=this._context.domainContextSelectors.getNavigationStates().at(0);return o?(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop, falling back to the initial navigation state. Check whether the history stack is not broken by some custom navigation logic."),[this._context.domainContextSelectors.getNavigationSystem().getNavigationUpdate(o)]):(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop and no initial navigation state to fallback to, this should not happen"),[])};getPushSidebarStackPageUpdates=(t,e,o,n)=>this.state.getUpdates(n=>{let a=n.propertyPageStates[t]??{customState:{},stackPages:[]};return{...n,propertyPageStates:{...n.propertyPageStates,[t]:{...a,stackPages:[...a.stackPages,{props:o,component:e}]}}}},n);getPopSidebarStackPageUpdates=(t,e)=>this.state.getUpdates(e=>{let o=e.propertyPageStates[t]??{customState:{},stackPages:[]};return{...e,propertyPageStates:{...e.propertyPageStates,[t]:{...o,stackPages:o.stackPages.slice(0,-1)}}}},e);getSidebarBackUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getHasStackToPop(t),o=this._context.domainContextSelectors.getNavigationSystem().getIsRootSelected(t),{renderedStackedPage:n,summaryPage:a,activePropertyPageId:i}=this.getSidebarContent(t);if(a)return this.getLeaveSidebarSummaryUpdates(t);if(n&&i)return this.getPopSidebarStackPageUpdates(i,t);if(e){let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);return e?[e]:[]}return o?[]:[this._context.domainContextSelectors.getNavigationSystem().getDeselectUpdate()]};getSidebarContent=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),o=this.state.get(t),n=this.getLayoutConfig(t),a=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[l],entityIds:[e]},t).some(t=>t.state.tabs.length>0),r=n.miniSidebar&&!a;r&&a&&this._context.domainServicesSelectors.getLogger().warn("Mini sidebar is not active because the layout contains tabs. Only layouts without tabs can have mini sidebar.");let d=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[s]},t),{isSummaryActive:c,page:g}=this.getSidebarSummaryPage(t),u=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[i]},t),p=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(e);if(!p)throw Error(`Selected entity ${e} not found in composition tree`);let m=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(t.rootEntityId);if(!m)throw Error(`Root entity ${t.rootEntityId} not found in composition tree`);let
|
|
1
|
+
import{P as t,match as e}from"ts-pattern";import{SystemState as o,applicationConfigComponent as n,customSummaryPageComponent as a,entityHeaderComponent as i,navigationStepsComponent as r,propertyPageComponent as s,sidebarTabsComponent as l,simpleSummaryComponent as d,toolbarComponent as c}from"#package/domain";import{layoutStateComponent as g}from"./layout-state.js";import{toolbarHelpElementName as u}from"./toolbar-help-element.js";export class LayoutSystem{_context;_toolbarCache=new WeakMap;state;constructor(t){this._context=t,this.state=new o({...this._context,selectors:this._context.domainContextSelectors,componentType:g,getInitialState:()=>({propertyPageStates:{}})})}get _toolbarHelpExists(){return void 0!==this._context.specSelectors.getElementSlots().layoutElements[u]}get _hasAssistant(){return void 0!==this._context.specSelectors.getAssistant()}initialize=()=>{this.state.initialize()};getCleanUpPropertyPageStateUpdates=(t,e)=>this.state.getUpdates(e=>{if(!e.propertyPageStates[t])return null;let o={...e.propertyPageStates};return delete o[t],{...e,propertyPageStates:o}},e);getLayoutConfig=t=>{let e=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[n]},t),o=e?.state.shareDisabled??!1,a=o||(e?.state.shareOwnerLinkDisabled??!1),i=o||(e?.state.shareReadRevisionLinkDisabled??!1);return{layout:e?.state.layout??"universal",ignoreSelectionInSidebar:e?.state.ignoreSelectionInSidebar??!1,miniSidebar:e?.state.miniSidebar??!1,splitSidebar:e?.state.splitSidebar??!1,showSelectionBackButton:e?.state.showSelectionBackButton??!0,shareDisabled:a&&i,availableLocales:e?.state.availableLocales??["en"],indicatorSwitch:e?.state.indicatorSwitch??!0}};getSidebarSummaryPage=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveNavigationState(t),o=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[a,d]},t),n=e===o?.state.summaryNavigationState,i=o?.state.summaryNavigationState;return{page:o,isSummaryActive:n,summaryNavigationState:i}};getEnterSidebarSummaryUpdates=t=>{let{summaryNavigationState:e}=this.getSidebarSummaryPage(t);return e?[this._context.domainContextSelectors.getNavigationSystem().getPushNavigationUpdate(e,t)]:[]};getLeaveSidebarSummaryUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);if(e)return[e];let o=this._context.domainContextSelectors.getNavigationStates().at(0);return o?(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop, falling back to the initial navigation state. Check whether the history stack is not broken by some custom navigation logic."),[this._context.domainContextSelectors.getNavigationSystem().getNavigationUpdate(o)]):(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop and no initial navigation state to fallback to, this should not happen"),[])};getPushSidebarStackPageUpdates=(t,e,o,n)=>this.state.getUpdates(n=>{let a=n.propertyPageStates[t]??{customState:{},stackPages:[]};return{...n,propertyPageStates:{...n.propertyPageStates,[t]:{...a,stackPages:[...a.stackPages,{props:o,component:e}]}}}},n);getPopSidebarStackPageUpdates=(t,e)=>this.state.getUpdates(e=>{let o=e.propertyPageStates[t]??{customState:{},stackPages:[]};return{...e,propertyPageStates:{...e.propertyPageStates,[t]:{...o,stackPages:o.stackPages.slice(0,-1)}}}},e);getSidebarBackUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getHasStackToPop(t),o=this._context.domainContextSelectors.getNavigationSystem().getIsRootSelected(t),{renderedStackedPage:n,summaryPage:a,activePropertyPageId:i}=this.getSidebarContent(t);if(a)return this.getLeaveSidebarSummaryUpdates(t);if(n&&i)return this.getPopSidebarStackPageUpdates(i,t);if(e){let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);return e?[e]:[]}return o?[]:[this._context.domainContextSelectors.getNavigationSystem().getDeselectUpdate()]};getSidebarContent=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),o=this.state.get(t),n=this.getLayoutConfig(t),a=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[l],entityIds:[e]},t).some(t=>t.state.tabs.length>0),r=n.miniSidebar&&!a;r&&a&&this._context.domainServicesSelectors.getLogger().warn("Mini sidebar is not active because the layout contains tabs. Only layouts without tabs can have mini sidebar.");let d=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[s]},t),{isSummaryActive:c,page:g}=this.getSidebarSummaryPage(t),u=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[i]},t),p=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(e);if(!p)throw Error(`Selected entity ${e} not found in composition tree`);let m=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(t.rootEntityId);if(!m)throw Error(`Root entity ${t.rootEntityId} not found in composition tree`);let h=(n.ignoreSelectionInSidebar?[m]:[p,...p.ancestors()]).map(t=>{let e=n.ignoreSelectionInSidebar?d:d.filter(e=>e.componentInstance.entityId===t.id),o=u.filter(e=>e.componentInstance.entityId===t.id);return{id:t.id,propertyPages:e,header:o[0]??null}}),S=h[0]?.id,b=(S?o.propertyPageStates[S]?.stackPages??[]:[]).at(-1)??null,k=this._context.domainContextSelectors.getNavigationSystem().getHasStackToPop(t),x=this._context.domainContextSelectors.getNavigationSystem().getIsRootSelected(t),y=c&&k||k||!!b||!x&&n.showSelectionBackButton&&!n.ignoreSelectionInSidebar;return c?{summaryPage:g,propertyPages:[],isBackAvailable:y,hasMiniSidebar:r,hasSplitSidebar:n.splitSidebar}:{summaryPage:null,propertyPages:h,renderedStackedPage:b,activePropertyPageId:S,isBackAvailable:y,hasMiniSidebar:r,hasSplitSidebar:n.splitSidebar}};getToolbarState=o=>{let n=this._toolbarHelpExists,a=this._hasAssistant,i=this._toolbarCache.get(o);if(i&&i.toolbarHelpExists===n&&i.hasAssistant===a)return i.result;let r=this._context.domainContextSelectors.getExpressionContext(),s=this._context.domainContextSelectors.getNavigationSystem().getActiveFullscreenState(o),l="read"===r.mode,d=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[c]},o),g=this._context.domainContextSelectors.getDimensionSystem().getDimensionConfig(o),u=this.getLayoutConfig(o),p="read"===this._context.domainContextSelectors.getExpressionContext().mode,m=u.availableLocales.length>1,h=!p&&!u.shareDisabled,S=u.indicatorSwitch,b=g.availableDimensions.length>1||S,k=a?[{kind:"toolbarButtonGroup",children:[{kind:"assistantToolbarButton"}]}]:[],x=e(d).returnType().with(t.nonNullable,t=>t.state).with(t.nullish,()=>e([s,l]).returnType().with([!1,!1],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},...m?[{kind:"toolbarButtonGroup",children:[{kind:"languageToolbarButton"}]}]:[],...h||b?[{kind:"toolbarButtonGroup",children:[...h?[{kind:"shareToolbarButton"}]:[],...b?[{kind:"measurementsToolbarButton"}]:[],...n?[{kind:"helpToolbarButton"}]:[]]}]:[],{kind:"toolbarButtonGroup",children:[{kind:"undoToolbarButton"},{kind:"redoToolbarButton"}]},...k],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"undoToolbarButton"},{kind:"redoToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},...k],mobileMenu:[...h?[{kind:"shareMenuAction"}]:[],...b?[{kind:"measurementsMenuAction"}]:[],...m?[{kind:"languageMenuAction"}]:[],...n?[{kind:"helpMenuAction"}]:[]]})).with([!0,!1],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobileMenu:[]})).with([!1,!0],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},...m?[{kind:"toolbarButtonGroup",children:[{kind:"languageToolbarButton"}]}]:[],...b?[{kind:"toolbarButtonGroup",children:[...b?[{kind:"measurementsToolbarButton"}]:[]]}]:[],...n?[{kind:"helpToolbarButton"}]:[],...k],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},...k],mobileMenu:[...b?[{kind:"measurementsMenuAction"}]:[],...m?[{kind:"languageMenuAction"}]:[],...n?[{kind:"helpMenuAction"}]:[]]})).with([!0,!0],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobileMenu:[]})).exhaustive()).exhaustive();return this._toolbarCache.set(o,{toolbarHelpExists:n,hasAssistant:a,result:x}),x};getNavigationSteps=t=>{let e=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[r]},t);return e?.state.steps??[]}}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { type EntityId } from '#package/domain';
|
|
2
|
-
declare const CloseConfiguratorClicked: () => import("
|
|
3
|
-
declare const PreviousStepButtonClicked: () => import("
|
|
4
|
-
declare const NextStepButtonClicked: () => import("
|
|
5
|
-
declare const FinalizeButtonClicked: () => import("
|
|
6
|
-
declare const SummaryButtonClicked: () => import("
|
|
7
|
-
declare const TopNavigationStepClicked: (step: string) => import("
|
|
8
|
-
declare const HorizontalTabClicked: (tab: string) => import("
|
|
9
|
-
declare const GoToProductsClicked: () => import("
|
|
10
|
-
declare const ExpandedViewClosed: () => import("
|
|
11
|
-
declare const ProductSelected: (productType: string, productName: string) => import("
|
|
12
|
-
declare const SelectionHotspotClicked: (id: string) => import("
|
|
13
|
-
declare const RedoButtonClicked: () => import("
|
|
14
|
-
declare const UndoButtonClicked: () => import("
|
|
15
|
-
declare const GlobalDimensionsButtonClicked: (dimensionUnit: string) => import("
|
|
16
|
-
declare const MeasurementsMenuToggled: () => import("
|
|
17
|
-
declare const MeasurementsMenuClosed: () => import("
|
|
18
|
-
declare const DimensionIndicatorsToggled: () => import("
|
|
19
|
-
declare const FullScreenModeToggled: () => import("
|
|
20
|
-
declare const HelpDialogOpened: () => import("
|
|
21
|
-
declare const HelpDialogClosed: () => import("
|
|
22
|
-
declare const EntityDeleteHotspotClicked: (id: EntityId) => import("
|
|
2
|
+
declare const CloseConfiguratorClicked: () => import("#package/domain").UserActionEvent;
|
|
3
|
+
declare const PreviousStepButtonClicked: () => import("#package/domain").UserActionEvent;
|
|
4
|
+
declare const NextStepButtonClicked: () => import("#package/domain").UserActionEvent;
|
|
5
|
+
declare const FinalizeButtonClicked: () => import("#package/domain").UserActionEvent;
|
|
6
|
+
declare const SummaryButtonClicked: () => import("#package/domain").UserActionEvent;
|
|
7
|
+
declare const TopNavigationStepClicked: (step: string) => import("#package/domain").UserActionEvent;
|
|
8
|
+
declare const HorizontalTabClicked: (tab: string) => import("#package/domain").UserActionEvent;
|
|
9
|
+
declare const GoToProductsClicked: () => import("#package/domain").UserActionEvent;
|
|
10
|
+
declare const ExpandedViewClosed: () => import("#package/domain").UserActionEvent;
|
|
11
|
+
declare const ProductSelected: (productType: string, productName: string) => import("#package/domain").UserActionEvent;
|
|
12
|
+
declare const SelectionHotspotClicked: (id: string) => import("#package/domain").UserActionEvent;
|
|
13
|
+
declare const RedoButtonClicked: () => import("#package/domain").UserActionEvent;
|
|
14
|
+
declare const UndoButtonClicked: () => import("#package/domain").UserActionEvent;
|
|
15
|
+
declare const GlobalDimensionsButtonClicked: (dimensionUnit: string) => import("#package/domain").UserActionEvent;
|
|
16
|
+
declare const MeasurementsMenuToggled: () => import("#package/domain").UserActionEvent;
|
|
17
|
+
declare const MeasurementsMenuClosed: () => import("#package/domain").UserActionEvent;
|
|
18
|
+
declare const DimensionIndicatorsToggled: () => import("#package/domain").UserActionEvent;
|
|
19
|
+
declare const FullScreenModeToggled: () => import("#package/domain").UserActionEvent;
|
|
20
|
+
declare const HelpDialogOpened: () => import("#package/domain").UserActionEvent;
|
|
21
|
+
declare const HelpDialogClosed: () => import("#package/domain").UserActionEvent;
|
|
22
|
+
declare const EntityDeleteHotspotClicked: (id: EntityId) => import("#package/domain").UserActionEvent;
|
|
23
23
|
export declare const FrontendAnalyticsFactory: {
|
|
24
24
|
CloseConfiguratorClicked: typeof CloseConfiguratorClicked;
|
|
25
25
|
PreviousStepButtonClicked: typeof PreviousStepButtonClicked;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{ConfiguratorApp}from"./ConfiguratorApp.js";export*from"./application-runtime/index.js";export*from"./
|
|
1
|
+
export{ConfiguratorApp}from"./ConfiguratorApp.js";export*from"./application-runtime/index.js";export*from"./common/index.js";export*from"./configurator-context/index.js";export*from"./pages/index.js";export*from"./scene/index.js";export*from"./ui/index.js";export*from"./integration/index.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as
|
|
1
|
+
import{jsx as e,jsxs as o,Fragment as i}from"react/jsx-runtime";import r from"mui-modal-provider";import{memo as n,useCallback as t,useEffect as a,useMemo as d}from"react";import{match as s}from"ts-pattern";import{InvalidAccessKeyError as l}from"#package/client";import{BuiltInLoadingPhase as u,CommonAnalyticsFactory as m,DomainLifecycleBoundary as c,LoadedDataProvider as f,useAccessService as p,useAnalyticsService as g,useAuthorizedClient as v,useConfiguratorDomainFactory as w,useConfiguratorLoadStatus as y,useConfiguratorRawLoadedData as h,useExpectPhase as I,useLoadedDataActions as j,useLoadingError as x,useLoadingPhase as C,useLogger as b,useRoutingService as k,useSpecAssistant as L}from"#package/react-sdk";import{useConfigurator as U}from"../application-runtime/index.js";import{IntegrationSubscription as M,OnConfiguratorReadyProvider as S,ViewportStateSync as E,useLayoutSystemConfig as F}from"../configurator-context/index.js";import{Layout as R,Legacy as D}from"../ui/index.js";import{LayoutCustomOwnerUi as T,LayoutCustomReadUi as P,LayoutDialog as A}from"../ui/phoenix-layout-elements/index.js";import{ConfiguratorNotFoundPage as O}from"./ConfiguratorNotFoundPage.js";import{DevContextFallback as V}from"./error-boundaries/DevContextFallback.js";import{DevLoadingFallback as q}from"./error-boundaries/DevLoadingFallback.js";import{LoadingErrorBoundary as z}from"./error-boundaries/LoadingErrorBoundary.js";import{useLoad as B}from"./use-load.js";let G=n(n=>{let{viewMode:t}=n,l=F();(()=>{let{assistantSystem:e}=U(),o=null!=L();a(()=>{if(o)return e.activate()},[e,o])})();let{ownerUi:u,readUi:m}=d(()=>s(l.layout).with("simple",()=>({ownerUi:e(D.SimpleOwnerLayout,{}),readUi:e(D.SummaryReadLayout,{})})).with("tabs",()=>({ownerUi:e(D.TabsOwnerLayout,{}),readUi:e(D.SummaryReadLayout,{})})).with("steps",()=>({ownerUi:e(D.StepsOwnerLayout,{}),readUi:e(D.SummaryReadLayout,{})})).with("universal",()=>({ownerUi:e(R,{}),readUi:e(R,{})})).with("custom",()=>({ownerUi:e(T,{}),readUi:e(P,{})})).exhaustive(),[l.layout]);return e(r,{children:o(S,{children:[t?m:u,"universal"===l.layout&&o(i,{children:[e(D.CommonFinalizePage,{}),e(A,{})]})]})})});export const LoadedConfiguratorDetailPage=()=>{I(u.SceneAssets);let{configurationId:i,accessInfo:r}=p(),n=v(),a=k(),d=b(),s=g(),{startConfiguratorLoad:L,completeConfiguratorLoad:U,failConfiguratorLoad:S,resetConfiguratorLoad:F}=j(),{isLoading:R,isError:D}=y(),T=h(),P=w(),A=t(()=>r.viewMode&&void 0!==r.revisionId?n.getConfigurationRevision(i,r.revisionId):n.getConfiguration(i),[r.revisionId,r.viewMode,n,i]),N=t(()=>{L()},[L]),H=t(e=>{U({initialRevisionId:e.revisionId,initialEntities:e.data,modelVersion:e.modelVersion,rootEntityType:e.rootEntityType,schemaIdentity:P}),s.push(m.ConfigurationLoaded(e.configurationId,e.revisionId,r.viewMode?"read":"owner")),d.appendToGlobalContext({configurationId:e.configurationId,revisionId:e.revisionId,mode:r.viewMode}),d.info("Configuration loaded.")},[r.viewMode,s,U,P,d]),J=t(e=>{if(S(e),e instanceof l)return void a.rejectAccessToken(i,r.raw);let o=e instanceof Error?e:Error("Configuration loading failed with an unknown error.",{cause:e});d.error("Configuration loading failed because the API request did not succeed. Verify the configuration ID and access token.",{configurationId:i},o)},[r.raw,i,S,d,a]),K=t(()=>{F()},[F]);return(B({key:[n,i,r.revisionId,r.viewMode],load:A,onStart:N,onSuccess:H,onError:J,onCleanup:K}),C({id:u.DataFetch,label:"Loading configuration…"},R),x(D),D)?e(O,{}):null===T?null:e(f,{value:T,children:e(z,{fallbackRender:o=>e(q,{...o}),children:e(c,{type:"configurator",children:e(z,{FallbackComponent:V,children:o(M,{children:[e(E,{}),e(G,{viewMode:r.viewMode})]})})})})})};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
declare const PerspectiveCameraDragEvent: () => import("
|
|
2
|
-
declare const OrthographicCameraDragEvent: () => import("
|
|
1
|
+
declare const PerspectiveCameraDragEvent: () => import("#package/domain").UserActionEvent;
|
|
2
|
+
declare const OrthographicCameraDragEvent: () => import("#package/domain").UserActionEvent;
|
|
3
3
|
export declare const SceneAnalyticsFactory: {
|
|
4
4
|
PerspectiveCameraDragEvent: typeof PerspectiveCameraDragEvent;
|
|
5
5
|
OrthographicCameraDragEvent: typeof OrthographicCameraDragEvent;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AssistantToolItem } from './thread-items.js';
|
|
2
|
+
export interface AssistantToolChipsProps {
|
|
3
|
+
readonly tools: readonly AssistantToolItem[];
|
|
4
|
+
/** True while the reply runs and this row is its newest block: the assistant is at work on it. */
|
|
5
|
+
readonly isActive: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 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.
|
|
10
|
+
*/
|
|
11
|
+
export declare const AssistantToolChips: import("react").MemoExoticComponent<({ tools, isActive }: AssistantToolChipsProps) => import("react/jsx-runtime").JSX.Element>;
|
|
@@ -0,0 +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 n,useCallback as s}from"react";import{match as o}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 c={id:"sdk.assistant.tool.updateDesign.label",message:"Update design"},m={id:"sdk.assistant.tool.readDesign.label",message:"Read design"},p={id:"sdk.assistant.tool.mistake.hint",message:"The assistant made a mistake."},h=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)"})),g=i("span",{name:d,slot:"activity"})(({theme:{typography:t}})=>({blockSize:`calc(${t.caption.lineHeight} * 1em)`,display:"grid",placeItems:"center"})),f=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"}}}),k=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}})),u=n(({tool:e})=>{let{chip:i,input:n,result:l}=e,d=r(c),h=r(m),g=r(p),f=o(i.label).with({kind:"text"},({text:t})=>t).with({kind:"updateDesign"},()=>d).with({kind:"readDesign"},()=>h).exhaustive(),u=s(()=>{console.info(`Assistant tool call "${f}"`,{input:n,result:l})},[n,f,l]);return t(a,{title:"failed"===i.state||"flawed"===i.state?g:"",placement:"top-start",children:t(k,{onClick:u,"data-chip":i.state,children:f})})});export const AssistantToolChips=n(({tools:a,isActive:i})=>e(h,{"data-chips":i?"active":"done","aria-busy":i,children:[t(g,{children:t(f,{name:"Gear"})}),t("div",{children:a.map(e=>t(u,{tool:e},e.id))})]}));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message input of the panel. Enter sends and Shift+Enter adds a line. While a reply runs the
|
|
3
|
+
* send control is a Stop control and the input stays editable, so the next message can be written.
|
|
4
|
+
* A thread this session may not continue disables the input and says why.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AssistantComposer: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Trans as i}from"@lingui/react";import{IconButton as o,InputBase as r,Tooltip as t,styled as n}from"@mui/material";import{useCallback as s,useRef as d,useState as l}from"react";import{useEagerObservableSelector as m,useLocalizable as c}from"#package/react-sdk";import{PhxIcon as p,useResponsive as u}from"#package/ui-sdk";import{useConfigurator as b}from"../../application-runtime/index.js";import{AssistantPanelName as g}from"./theme.js";let h={id:"sdk.assistant.composer.placeholder",message:"Ask or tell me what to change…"},k={id:"sdk.assistant.composer.send.label",message:"Send"},f={id:"sdk.assistant.composer.stop.label",message:"Stop"},x=n("form",{name:g,slot:"composer"})(({theme:{palette:e,spacing:a}})=>({borderBlockStart:`1px solid ${e.divider}`,display:"grid",gap:a(.75),paddingBlock:a(1.25),paddingInline:`var(--phx-sidebar-padding, ${a(2)})`})),y=n("div",{name:g,slot:"inputFrame"})(({theme:{palette:e,spacing:a}})=>({alignItems:"flex-end",border:`1px solid ${e.divider}`,borderRadius:12,display:"flex",gap:a(1),paddingBlock:a(.5),paddingInline:`${a(1.5)} ${a(.5)}`,"&:focus-within":{borderColor:e.primary.main},"&:has(textarea:disabled)":{background:e.action.disabledBackground}})),v=n(r,{name:g,slot:"input"})(({theme:{spacing:e,typography:a}})=>({...a.body2,flex:1,paddingBlock:e(.75)})),B=n(o,{name:g,slot:"sendButton"})(({theme:{palette:e}})=>({background:e.primary.main,borderRadius:8,color:e.primary.contrastText,"&:hover":{background:e.primary.dark},"&.Mui-disabled":{background:e.action.disabledBackground,color:e.action.disabled}})),C=n(o,{name:g,slot:"stopButton"})(({theme:{palette:e}})=>({background:e.text.primary,borderRadius:8,color:e.background.paper,"&:hover":{background:e.text.secondary}})),R=n("span")({background:"currentColor",blockSize:10,borderRadius:2,inlineSize:10,margin:5}),S=n("div",{name:g,slot:"hint"})(({theme:{palette:e,typography:a}})=>({...a.caption,color:e.text.secondary}));export const AssistantComposer=()=>{let{assistantSystem:o}=b(),[r,n]=l(""),g=d(null),{isDesktop:I}=u(),$=c(h),j=c(k),w=c(f),{isReplying:z,isContinuable:F}=m(o.chat$,e=>({isReplying:e.isLoading,isContinuable:e.isContinuable}),[]),A=r.trim(),D=F&&!z&&A.length>0,P=s(()=>{D&&(n(""),o.send(A),I||g.current?.blur())},[o,D,A,I]),T=s(e=>{e.preventDefault(),P()},[P]),E=s(e=>{"Enter"!==e.key||e.shiftKey||e.nativeEvent.isComposing||(e.preventDefault(),P())},[P]),K=s(()=>{o.stop()},[o]);return a(x,{onSubmit:T,children:[!F&&e(S,{role:"status",children:e(i,{id:"sdk.assistant.composer.notContinuable",message:"This conversation can be read here, but it can no longer be continued."})}),a(y,{children:[e(v,{multiline:!0,maxRows:6,placeholder:$,value:r,disabled:!F,inputRef:g,onChange:e=>n(e.target.value),onKeyDown:E,onFocus:()=>o.setInputFocused(!0),onBlur:()=>o.setInputFocused(!1),inputProps:{"aria-label":$}}),z?e(t,{title:w,placement:"top",children:e(C,{size:"small","aria-label":w,onClick:K,children:e(R,{})})}):e(t,{title:j,placement:"top",children:e("span",{children:e(B,{type:"submit",size:"small","aria-label":j,disabled:!D,children:e(p,{name:"PaperPlaneRightBold",fontSize:"small"})})})})]}),e(S,{children:e(i,{id:"sdk.assistant.composer.disclaimer",message:"AI can be wrong. Check the result."})})]})};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface AssistantMarkdownProps {
|
|
2
|
+
readonly markdown: string;
|
|
3
|
+
/** True while the text is still arriving, so markup the model has not finished is completed. */
|
|
4
|
+
readonly isStreaming: boolean;
|
|
5
|
+
}
|
|
6
|
+
/** Assistant text of the thread: Markdown with lists, tables, links and code, without a bubble. */
|
|
7
|
+
export declare const AssistantMarkdown: import("react").MemoExoticComponent<({ markdown, isStreaming }: AssistantMarkdownProps) => import("react/jsx-runtime").JSX.Element>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as r}from"react/jsx-runtime";import{styled as e}from"@mui/material";import{memo as o}from"react";import a from"react-markdown";import i from"remark-gfm";import{completeStreamedMarkdown as n}from"./streamed-markdown.js";import{AssistantPanelName as t}from"./theme.js";let l=e("div",{name:t,slot:"assistantMessage"})(({theme:{palette:r,shape:e,spacing:o,typography:a}})=>({overflowWrap:"anywhere","& > :first-child":{marginBlockStart:0},"& > :last-child":{marginBlockEnd:0},"p, ul, ol, pre, blockquote":{marginBlock:o(1)},"h1, h2, h3, h4, h5, h6":{...a.subtitle2,marginBlock:o(1.5,.5)},"ul, ol":{paddingInlineStart:o(2.5)},a:{color:r.primary.main},code:{background:r.action.hover,borderRadius:e.borderRadius/2,fontFamily:"ui-monospace, Menlo, monospace",fontSize:"0.9em",paddingInline:o(.5)},pre:{background:r.action.hover,borderRadius:e.borderRadius,overflowX:"auto",padding:o(1),code:{background:"none",padding:0}},blockquote:{borderInlineStart:`2px solid ${r.divider}`,color:r.text.secondary,marginInline:0,paddingInlineStart:o(1.5)},"th, td":{borderBlockEnd:`1px solid ${r.divider}`,padding:o(.5,1),textAlign:"start",verticalAlign:"top"}})),d=e("div")({overflowX:"auto",table:{borderCollapse:"collapse",inlineSize:"100%"}}),m={a:({node:e,children:o,...a})=>r("a",{...a,target:"_blank",rel:"noopener noreferrer",children:o}),table:({node:e,children:o,...a})=>r(d,{children:r("table",{...a,children:o})})},s=[i],c=["img"];export const AssistantMarkdown=o(({markdown:e,isStreaming:o})=>r(l,{children:r(a,{components:m,remarkPlugins:s,disallowedElements:c,children:o?n(e):e})}));
|