@jxsuite/studio 3.0.0 → 4.0.0

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 (173) hide show
  1. package/README.md +5 -5
  2. package/dist/chunks/Overlay.dev-723bv0df.js +23 -0
  3. package/dist/chunks/{cf-settings-zj2cj1ar.js → cf-settings-45fgyx8y.js} +6 -5
  4. package/dist/chunks/{components-z6cnkyy1.js → components-zmx10t62.js} +6 -5
  5. package/dist/chunks/content-source-t88vb2dr.js +49 -0
  6. package/dist/chunks/{convert-to-repeater-6vvx96n2.js → convert-to-repeater-dhw1nrz9.js} +27 -19
  7. package/dist/chunks/entry-commands-6z6bhxg0.js +46 -0
  8. package/dist/chunks/{extension-sections-kwzvpemr.js → extension-sections-3ydgjbdy.js} +32 -24
  9. package/dist/chunks/file-ops-rskmhxyq.js +48 -0
  10. package/dist/chunks/files-0cratsr0.js +92 -0
  11. package/dist/chunks/{format-host-8fr3545e.js → format-host-tgcmfs4p.js} +3 -3
  12. package/dist/chunks/fs-events-hrrg328g.js +21 -0
  13. package/dist/chunks/{git-panel-g01d93vc.js → git-panel-0yywyk76.js} +21 -16
  14. package/dist/chunks/{jx-validate-jj9rnt4n.js → jx-validate-8q2g05x1.js} +6 -4
  15. package/dist/chunks/layers-panel-z3tqe5cd.js +67 -0
  16. package/dist/chunks/layers-q75tj2mf.js +45 -0
  17. package/dist/chunks/lit-html-j055mkh8.js +22 -0
  18. package/dist/chunks/monaco-buffer-dqw0xxpx.js +25 -0
  19. package/dist/chunks/{monaco-setup-a5wkmbgh.js → monaco-setup-rws7k4br.js} +4 -4
  20. package/dist/chunks/notify-yjm79n4s.js +30 -0
  21. package/dist/chunks/parse-24d0gay3.js +19 -0
  22. package/dist/chunks/platform-ba1j1gbh.js +16 -0
  23. package/dist/chunks/publish-panel-54jvhskq.js +28 -0
  24. package/dist/chunks/quick-search-cgfmjwq5.js +65 -0
  25. package/dist/chunks/references-3qx8fkyp.js +29 -0
  26. package/dist/chunks/right-panel-cqp8bc4v.js +46 -0
  27. package/dist/chunks/sp-overlay.dev-yztmtrs1.js +18 -0
  28. package/dist/chunks/sp-popover.dev-n2etmqdp.js +9 -0
  29. package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +15 -0
  30. package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +15 -0
  31. package/dist/chunks/{sp-tray.dev-64v4pxw8.js → sp-tray.dev-tkbscm06.js} +5 -5
  32. package/dist/chunks/{studio-8ezdegcx.js → studio-1hbqnt19.js} +36 -32
  33. package/dist/chunks/{studio-r5xbtctf.js → studio-2zkz8evn.js} +446 -2451
  34. package/dist/chunks/{studio-pkp61mh8.js → studio-4pqqxbrt.js} +23 -15
  35. package/dist/chunks/{studio-kvfx9z2c.js → studio-4rzyrtjz.js} +70 -31
  36. package/dist/chunks/{studio-3dkcd4c5.js → studio-5q69gfag.js} +60 -56
  37. package/dist/chunks/{studio-rh1cnamv.js → studio-5rywrkxz.js} +563 -133
  38. package/dist/chunks/{studio-6kv3apa9.js → studio-68n67094.js} +25 -9
  39. package/dist/chunks/{studio-0rqqhrdy.js → studio-7nte2hj4.js} +2 -2
  40. package/dist/chunks/{studio-xntfdy1x.js → studio-8swrjbny.js} +31 -18
  41. package/dist/chunks/studio-92ab2jqe.js +282 -0
  42. package/dist/chunks/{studio-sas7mt3k.js → studio-9ab8gqsm.js} +2 -2
  43. package/dist/chunks/{studio-ry1vz5sv.js → studio-9c4k4vhv.js} +15 -13
  44. package/dist/chunks/studio-9n4mrmnb.js +184 -0
  45. package/dist/chunks/{studio-tr20m8g8.js → studio-9r6j5ev1.js} +37 -2
  46. package/dist/chunks/{studio-2x9zbxmx.js → studio-ahjfdy6e.js} +9 -7
  47. package/dist/chunks/{studio-s519zc99.js → studio-bgxt93ga.js} +3 -3
  48. package/dist/chunks/{studio-dc8s8heb.js → studio-cb12yz9w.js} +8528 -8780
  49. package/dist/chunks/studio-cft9r4r7.js +119 -0
  50. package/dist/chunks/{studio-hcxzyk3x.js → studio-eenfkt4e.js} +6 -6
  51. package/dist/chunks/studio-eesa1myv.js +498 -0
  52. package/dist/chunks/{studio-ay8wcr2b.js → studio-eqq1w638.js} +7 -9
  53. package/dist/chunks/{studio-7p35254k.js → studio-ex8zyp98.js} +30 -26
  54. package/dist/chunks/studio-gv8vvshc.js +118 -0
  55. package/dist/chunks/studio-hbtp0eh1.js +1315 -0
  56. package/dist/chunks/{studio-a0yp7syn.js → studio-htsf0w6m.js} +29 -114
  57. package/dist/chunks/{studio-j595e748.js → studio-kjjsjak4.js} +139 -63
  58. package/dist/chunks/{studio-5x5774ak.js → studio-kryvsw6j.js} +3 -3
  59. package/dist/chunks/{studio-kfpek536.js → studio-kyh9avvv.js} +2 -2
  60. package/dist/chunks/{studio-g5gdqnyd.js → studio-kyk2v92a.js} +14 -14
  61. package/dist/chunks/{studio-twsafr1e.js → studio-m3hwn7y1.js} +3 -3
  62. package/dist/chunks/{studio-tzx3xmvx.js → studio-mmv72a27.js} +8 -8
  63. package/dist/chunks/{studio-vhk5fmfv.js → studio-n8b9wqwy.js} +5 -5
  64. package/dist/chunks/{studio-cjm7fdjf.js → studio-p4ngc046.js} +8 -104
  65. package/dist/chunks/{studio-p5dja96s.js → studio-p8j86wag.js} +2 -2
  66. package/dist/chunks/{studio-6z5eyjwn.js → studio-pdfjntbk.js} +66 -129
  67. package/dist/chunks/studio-q43a5y9h.js +394 -0
  68. package/dist/chunks/studio-q7v10cg2.js +106 -0
  69. package/dist/chunks/{studio-2w11grav.js → studio-qq6jym04.js} +3 -3
  70. package/dist/chunks/studio-r1r7afxx.js +1066 -0
  71. package/dist/chunks/{studio-3fk1ac9z.js → studio-rdx8jcxy.js} +4 -4
  72. package/dist/chunks/{studio-4mqb9a0j.js → studio-rn9ta2mz.js} +4 -4
  73. package/dist/chunks/studio-sf7zjgv1.js +91 -0
  74. package/dist/chunks/studio-t7qb9db8.js +665 -0
  75. package/dist/chunks/studio-tmc5emrw.js +8 -0
  76. package/dist/chunks/{studio-wdt9y92a.js → studio-txwkm6p9.js} +2 -2
  77. package/dist/chunks/{studio-k4jvjcfx.js → studio-v38hmvx1.js} +7 -7
  78. package/dist/chunks/{studio-7ejemhej.js → studio-vadzgkrz.js} +6 -6
  79. package/dist/chunks/studio-wcy8trfx.js +263 -0
  80. package/dist/chunks/{studio-5efrtn1m.js → studio-wx5609xd.js} +21 -17
  81. package/dist/chunks/{studio-qpfq7xhm.js → studio-xrapxwwc.js} +22 -285
  82. package/dist/chunks/{studio-hj2se583.js → studio-yxrkyv8a.js} +41 -31
  83. package/dist/chunks/{studio-xm3dbxvn.js → studio-zczps70q.js} +2 -2
  84. package/dist/chunks/workspace-xpres40v.js +89 -0
  85. package/dist/iframe-entry.js +108 -24
  86. package/dist/studio.js +2362 -1695
  87. package/package.json +16 -15
  88. package/src/browse/library-pane.ts +57 -3
  89. package/src/canvas/canvas-render.ts +23 -1
  90. package/src/canvas/iframe-patch.ts +47 -21
  91. package/src/canvas/iframe-render.ts +22 -6
  92. package/src/commands/app-commands.ts +4 -0
  93. package/src/commands/command-args.ts +29 -0
  94. package/src/commands/context.ts +15 -1
  95. package/src/commands/defaults.ts +29 -1
  96. package/src/commands/levels.ts +16 -0
  97. package/src/commands/live-context.ts +3 -0
  98. package/src/content/collection-match.ts +267 -0
  99. package/src/content/entry-commands.ts +38 -29
  100. package/src/content/entry-fields.ts +6 -0
  101. package/src/editor/shortcuts.ts +5 -0
  102. package/src/files/file-ops.ts +10 -42
  103. package/src/files/files.ts +485 -59
  104. package/src/files/fs-events.ts +9 -0
  105. package/src/files/gitignore.ts +432 -0
  106. package/src/files/serialize-document.ts +60 -0
  107. package/src/format/convert-file.ts +511 -0
  108. package/src/format/format-choices.ts +199 -0
  109. package/src/grid/grid-view.ts +16 -2
  110. package/src/media/media-open.ts +58 -0
  111. package/src/media/media-pane.ts +310 -0
  112. package/src/new-project/import-tab.ts +332 -160
  113. package/src/new-project/new-project-modal.ts +27 -34
  114. package/src/packages/ensure-deps.ts +14 -7
  115. package/src/packages/jxsuite-update.ts +11 -11
  116. package/src/page-params.ts +10 -46
  117. package/src/panels/activity-bar.ts +45 -16
  118. package/src/panels/ai-chat/attached-context.ts +6 -1
  119. package/src/panels/ai-chat/chat-view.ts +255 -13
  120. package/src/panels/ai-chat/composer.ts +21 -97
  121. package/src/panels/ai-panel.ts +116 -2
  122. package/src/panels/navigator-panels.ts +7 -6
  123. package/src/panels/settings-menu.ts +799 -0
  124. package/src/panels/toolbar.ts +135 -15
  125. package/src/platforms/cloud.ts +20 -0
  126. package/src/platforms/devserver.ts +67 -33
  127. package/src/preview/preview-overlay.ts +226 -0
  128. package/src/services/ai-ask.ts +247 -0
  129. package/src/services/ai-import-tools.ts +494 -0
  130. package/src/services/ai-project-tools.ts +20 -26
  131. package/src/services/ai-system-prompt.ts +88 -2
  132. package/src/services/ai-turn-signal.ts +70 -0
  133. package/src/services/connection.ts +74 -0
  134. package/src/services/context-manager.ts +89 -0
  135. package/src/services/document-assistant.ts +45 -4
  136. package/src/services/import-client.ts +33 -6
  137. package/src/services/import-run.ts +158 -0
  138. package/src/services/import-seed.ts +120 -0
  139. package/src/services/jx-validate.ts +58 -6
  140. package/src/services/project-adoption.ts +71 -0
  141. package/src/services/references.ts +0 -0
  142. package/src/services/settings/definitions.ts +17 -0
  143. package/src/services/tool-executor.ts +34 -2
  144. package/src/settings/contexts-section.ts +45 -3
  145. package/src/settings/dependencies-editor.ts +43 -23
  146. package/src/settings/general-settings.ts +30 -12
  147. package/src/settings/preferences-dialog.ts +18 -41
  148. package/src/settings/preferences-sections.ts +57 -0
  149. package/src/settings/settings-document.ts +65 -9
  150. package/src/studio.ts +33 -6
  151. package/src/tabs/transact.ts +42 -19
  152. package/src/types.ts +103 -5
  153. package/src/ui/ai-model-picker.ts +162 -0
  154. package/src/ui/choice-field.ts +72 -0
  155. package/src/ui/layers.ts +83 -5
  156. package/src/utils/studio-utils.ts +22 -52
  157. package/styles/forced-colors.css +7 -0
  158. package/styles/overlays.css +256 -0
  159. package/styles/panels.css +13 -1
  160. package/styles/shell.css +111 -0
  161. package/dist/chunks/Overlay.dev-d2ajy6sj.js +0 -23
  162. package/dist/chunks/content-source-7fy9qrm0.js +0 -40
  163. package/dist/chunks/files-xcybanxe.js +0 -70
  164. package/dist/chunks/layers-panel-27essgkc.js +0 -60
  165. package/dist/chunks/publish-panel-09sy2bab.js +0 -22
  166. package/dist/chunks/quick-search-sefb61v8.js +0 -55
  167. package/dist/chunks/right-panel-9r32jd5j.js +0 -39
  168. package/dist/chunks/sp-overlay.dev-s18hymn9.js +0 -18
  169. package/dist/chunks/sp-popover.dev-dj9x7e7a.js +0 -9
  170. package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +0 -15
  171. package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +0 -15
  172. package/dist/chunks/studio-c31yb4t6.js +0 -55
  173. package/src/canvas/site-style-css.ts +0 -117
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Ai-ask.ts — the assistant's one way to stop and put a question to the author.
3
+ *
4
+ * The agent loop `await`s `toolRegistry.execute` (`services/tool-executor.ts`), so a tool that
5
+ * returns a pending promise suspends the turn. That is the whole mechanism: `ask_user` registers a
6
+ * question, hands back a promise, and the panel resolves it when the human answers. No new chat
7
+ * status, no pre-execution hook, and nothing in the published `@jxsuite/ai` changes —
8
+ * `finishStream` has already set the state to `"idle"` by the time tools run, so a suspended turn
9
+ * is idle by construction and this store is what "waiting" means.
10
+ *
11
+ * Two decisions are load-bearing and easy to get wrong on a rewrite:
12
+ *
13
+ * - **A skip is a SUCCESS.** "You decide" is a real answer to a question; only a stopped turn is a
14
+ * failure. Reporting a skip as an error would make the model apologise for having asked.
15
+ * - **The answer never becomes a user message.** `toMessagesArray` serialises the array verbatim
16
+ * and a provider requires a `tool` reply to follow its `tool_calls` request; a user turn spliced
17
+ * between them is a 400. The answer travels as the tool result, and the chat view renders it into
18
+ * the question's own chip.
19
+ *
20
+ * Exactly one question can be outstanding, because `runAgentLoop` executes a round's tool calls
21
+ * serially. The guard below is defensive, not load-bearing.
22
+ *
23
+ * @license MIT
24
+ */
25
+
26
+ import { createToolDefinition, toolError, toolSuccess } from "@jxsuite/ai/tools";
27
+ import { reactive } from "../reactivity";
28
+ import { currentToolCallId, turnSignal } from "./ai-turn-signal";
29
+
30
+ import type { ToolRegistry } from "@jxsuite/ai/tools";
31
+
32
+ /** A question the assistant is waiting on. `id` is the tool-call id, so the chip and this agree. */
33
+ export interface PendingAsk {
34
+ id: string;
35
+ question: string;
36
+ /** Short choices rendered as buttons. Free text is always allowed alongside them. */
37
+ options: string[];
38
+ /** One line of why-I'm-asking, rendered under the question. */
39
+ context: string;
40
+ }
41
+
42
+ /** How a question was settled. `answer: null` with `skipped` is the author saying "you decide". */
43
+ export interface AskAnswer {
44
+ answer: string | null;
45
+ skipped: boolean;
46
+ }
47
+
48
+ /** Max choices rendered as buttons; beyond this the question wants free text, not a menu. */
49
+ const MAX_OPTIONS = 6;
50
+
51
+ const state = reactive<{ pending: PendingAsk | null }>({ pending: null });
52
+
53
+ /** The resolver for {@link state.pending}, kept out of the reactive object (it is not render data). */
54
+ let settle: ((answer: AskAnswer) => void) | null = null;
55
+
56
+ /** Torn down with the question so a stopped turn does not leave a listener on a dead signal. */
57
+ let detachAbort: (() => void) | null = null;
58
+
59
+ function clear() {
60
+ state.pending = null;
61
+ settle = null;
62
+ detachAbort?.();
63
+ detachAbort = null;
64
+ }
65
+
66
+ /**
67
+ * The outstanding question, or null. Reactive — read it inside an `effect()` to repaint on it.
68
+ *
69
+ * @returns {PendingAsk | null}
70
+ */
71
+ export function pendingAsk(): PendingAsk | null {
72
+ return state.pending;
73
+ }
74
+
75
+ /** Whether a turn is suspended on the author. The composer's Send becomes Answer on this. */
76
+ export function isAwaitingAnswer(): boolean {
77
+ return state.pending !== null;
78
+ }
79
+
80
+ /**
81
+ * Register a question and suspend until it is settled.
82
+ *
83
+ * @param {PendingAsk} ask
84
+ * @returns {Promise<AskAnswer>} Resolves on an answer, a skip, or the turn being stopped (`{
85
+ * answer: null, skipped: false }` — the shape the tool reports as a failure).
86
+ */
87
+ export function askUser(ask: PendingAsk): Promise<AskAnswer> {
88
+ return new Promise<AskAnswer>((resolve) => {
89
+ state.pending = ask;
90
+ settle = resolve;
91
+
92
+ /* The turn's signal, not a parameter: `ToolRegistry.execute` has nowhere to pass one. Without
93
+ this, Stop would leave the loop awaiting a promise nothing could ever resolve. */
94
+ const signal = turnSignal();
95
+ if (signal?.aborted) {
96
+ clear();
97
+ resolve({ answer: null, skipped: false });
98
+ return;
99
+ }
100
+ if (signal) {
101
+ const onAbort = () => {
102
+ cancelAsk();
103
+ };
104
+ signal.addEventListener("abort", onAbort, { once: true });
105
+ detachAbort = () => signal.removeEventListener("abort", onAbort);
106
+ }
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Answer the outstanding question.
112
+ *
113
+ * @param {string} text
114
+ * @returns {boolean} False when nothing was pending, so a caller can fall through to a normal send.
115
+ */
116
+ export function answerAsk(text: string): boolean {
117
+ if (!settle) {
118
+ return false;
119
+ }
120
+ const resolve = settle;
121
+ clear();
122
+ resolve({ answer: text, skipped: false });
123
+ return true;
124
+ }
125
+
126
+ /**
127
+ * Decline to answer — "you decide". Settles as a success, so the turn carries on.
128
+ *
129
+ * @returns {boolean} False when nothing was pending.
130
+ */
131
+ export function skipAsk(): boolean {
132
+ if (!settle) {
133
+ return false;
134
+ }
135
+ const resolve = settle;
136
+ clear();
137
+ resolve({ answer: null, skipped: true });
138
+ return true;
139
+ }
140
+
141
+ /** Settle an outstanding question as unanswered. Called from `stop()` and from the turn's abort. */
142
+ export function cancelAsk(): void {
143
+ if (!settle) {
144
+ return;
145
+ }
146
+ const resolve = settle;
147
+ clear();
148
+ resolve({ answer: null, skipped: false });
149
+ }
150
+
151
+ /** Drop any outstanding question without settling a turn. For New Chat and for tests. */
152
+ export function resetAsk(): void {
153
+ cancelAsk();
154
+ clear();
155
+ }
156
+
157
+ /**
158
+ * Register `ask_user` into a tool registry.
159
+ *
160
+ * The description carries the discipline the tool needs to be worth having: a question the model
161
+ * could have answered itself is noise, and a question about a setting the pipeline never receives
162
+ * is worse than noise, because the answer cannot be honoured.
163
+ *
164
+ * @param {Pick<ToolRegistry, "register">} registry
165
+ */
166
+ export function registerAskTool(registry: Pick<ToolRegistry, "register">): void {
167
+ registry.register(
168
+ createToolDefinition({
169
+ name: "ask_user",
170
+ description:
171
+ "Pause and put a question to the user; the turn waits for their reply and resumes with " +
172
+ "it. Use it ONLY for a judgement that is genuinely theirs — which pages matter, whether to " +
173
+ "keep the original design, whether a low-fidelity page is acceptable. Never ask something " +
174
+ "you can decide yourself or discover with another tool, and never ask about an option you " +
175
+ "cannot act on. One question at a time; offer options when the sensible answers are a " +
176
+ "short list, and the user may always reply in their own words instead.",
177
+ parameters: {
178
+ type: "object",
179
+ properties: {
180
+ question: {
181
+ type: "string",
182
+ description: "The question, in one sentence, addressed to the user.",
183
+ },
184
+ options: {
185
+ type: "array",
186
+ items: { type: "string" },
187
+ description:
188
+ `Up to ${MAX_OPTIONS} short answers, rendered as buttons. Omit when the answer is ` +
189
+ "open-ended.",
190
+ },
191
+ context: {
192
+ type: "string",
193
+ description:
194
+ "One line of why you are asking — what you found that made this a judgement call.",
195
+ },
196
+ },
197
+ required: ["question"],
198
+ },
199
+ execute: async (args) => {
200
+ /* Everything `unknown`, and every field narrowed by `typeof` rather than by `?.`. The
201
+ registry's `validate()` is "a lightweight structural check, not a JSON Schema validator",
202
+ `strict: false` skips it entirely, and this tool may be registered into someone else's
203
+ registry — so an argument that is a number is a case, not an impossibility. Optional
204
+ chaining guards null, not type: `context?.trim()` threw on one. */
205
+ const { question, options, context } = args as {
206
+ question?: unknown;
207
+ options?: unknown;
208
+ context?: unknown;
209
+ };
210
+ if (typeof question !== "string" || !question.trim()) {
211
+ return toolError('Pass "question" — the question to put to the user.');
212
+ }
213
+ if (isAwaitingAnswer()) {
214
+ return toolError("A question is already waiting for the user.");
215
+ }
216
+ const choices = Array.isArray(options)
217
+ ? options
218
+ .filter((o): o is string => typeof o === "string" && o.trim().length > 0)
219
+ .slice(0, MAX_OPTIONS)
220
+ : [];
221
+
222
+ const { answer, skipped } = await askUser({
223
+ context: typeof context === "string" ? context.trim() : "",
224
+ /* The tool-call id, so the chip the transcript draws and the question this registers are
225
+ the same thing. Without it a restored, permanently unanswered question would render
226
+ identically to the live one. */
227
+ id: currentToolCallId(),
228
+ options: choices,
229
+ question: question.trim(),
230
+ });
231
+
232
+ if (skipped) {
233
+ /* A success: the model asked, and "you decide" is an answer. Rendering it as a failure
234
+ would have the model apologise for having asked a fair question. */
235
+ return toolSuccess(
236
+ { answer: null, skipped: true },
237
+ "The user declined to answer; proceed with your best judgment.",
238
+ );
239
+ }
240
+ if (answer === null) {
241
+ return toolError("The user did not answer — the turn was stopped.");
242
+ }
243
+ return toolSuccess({ answer, skipped: false }, `The user answered: "${answer}"`);
244
+ },
245
+ }),
246
+ );
247
+ }