@jxsuite/studio 3.0.0 → 4.1.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.
- package/README.md +5 -5
- package/dist/chunks/{2020-xp8vnwma.js → 2020-n6tfwnda.js} +2 -2
- package/dist/chunks/Overlay.dev-etqjwyf9.js +23 -0
- package/dist/chunks/cf-account-picker-en86v7x7.js +163 -0
- package/dist/chunks/{cf-settings-zj2cj1ar.js → cf-settings-45fgyx8y.js} +6 -5
- package/dist/chunks/{components-z6cnkyy1.js → components-zmx10t62.js} +6 -5
- package/dist/chunks/content-source-48xg86wj.js +49 -0
- package/dist/chunks/{convert-to-repeater-6vvx96n2.js → convert-to-repeater-43tfn7cs.js} +33 -25
- package/dist/chunks/entry-commands-e63f1xc0.js +46 -0
- package/dist/chunks/{extension-sections-kwzvpemr.js → extension-sections-txv08xxg.js} +34 -26
- package/dist/chunks/file-ops-wbh4hhsx.js +48 -0
- package/dist/chunks/files-3kfkwhk4.js +93 -0
- package/dist/chunks/{focus-trap.esm-ggawf1hy.js → focus-trap.esm-9t2drp0g.js} +6 -5
- package/dist/chunks/{format-host-8fr3545e.js → format-host-w16q81tf.js} +3 -3
- package/dist/chunks/fs-events-hrrg328g.js +21 -0
- package/dist/chunks/{git-panel-g01d93vc.js → git-panel-532whr1y.js} +22 -16
- package/dist/chunks/{index-nj44jpzj.js → index-wcnt3r9g.js} +2 -2
- package/dist/chunks/{jx-validate-jj9rnt4n.js → jx-validate-8er6gbxy.js} +6 -4
- package/dist/chunks/layers-panel-07hm7g5j.js +67 -0
- package/dist/chunks/layers-q75tj2mf.js +45 -0
- package/dist/chunks/lit-html-j055mkh8.js +22 -0
- package/dist/chunks/monaco-buffer-dqw0xxpx.js +25 -0
- package/dist/chunks/{monaco-setup-a5wkmbgh.js → monaco-setup-vx9bhp1d.js} +4 -4
- package/dist/chunks/notify-yjm79n4s.js +30 -0
- package/dist/chunks/parse-24d0gay3.js +19 -0
- package/dist/chunks/platform-ba1j1gbh.js +16 -0
- package/dist/chunks/publish-panel-v9st96ja.js +29 -0
- package/dist/chunks/quick-search-zccjp6gv.js +66 -0
- package/dist/chunks/references-3qx8fkyp.js +29 -0
- package/dist/chunks/right-panel-vm3b6kwy.js +46 -0
- package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +18 -0
- package/dist/chunks/sp-popover.dev-n2etmqdp.js +9 -0
- package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +15 -0
- package/dist/chunks/sp-tooltip.dev-4sastdnw.js +15 -0
- package/dist/chunks/{sp-tray.dev-64v4pxw8.js → sp-tray.dev-tkbscm06.js} +5 -5
- package/dist/chunks/{studio-wdt9y92a.js → studio-0bq6eh7v.js} +2 -2
- package/dist/chunks/studio-1ngbejft.js +181 -0
- package/dist/chunks/studio-1wagb550.js +118 -0
- package/dist/chunks/{studio-j595e748.js → studio-2b946z26.js} +172 -95
- package/dist/chunks/{studio-pkp61mh8.js → studio-3bqpr0zh.js} +25 -17
- package/dist/chunks/{studio-k4jvjcfx.js → studio-4b5z1x43.js} +46 -12
- package/dist/chunks/{studio-ry1vz5sv.js → studio-4n2b5gzb.js} +15 -13
- package/dist/chunks/{studio-p5dja96s.js → studio-4r1pv6zf.js} +2 -2
- package/dist/chunks/{studio-kvfx9z2c.js → studio-4rzyrtjz.js} +70 -31
- package/dist/chunks/{studio-tzx3xmvx.js → studio-5fx3g9jg.js} +33 -52
- package/dist/chunks/{studio-rh1cnamv.js → studio-5myknm0a.js} +565 -135
- package/dist/chunks/studio-6yw4y3my.js +91 -0
- package/dist/chunks/{studio-0rqqhrdy.js → studio-7nte2hj4.js} +2 -2
- package/dist/chunks/studio-92ab2jqe.js +282 -0
- package/dist/chunks/{studio-6z5eyjwn.js → studio-950k0dx6.js} +72 -135
- package/dist/chunks/{studio-sas7mt3k.js → studio-9ab8gqsm.js} +2 -2
- package/dist/chunks/studio-9n4mrmnb.js +184 -0
- package/dist/chunks/{studio-hcxzyk3x.js → studio-ahn3pdgx.js} +6 -6
- package/dist/chunks/studio-b55y6ewx.js +498 -0
- package/dist/chunks/{studio-s519zc99.js → studio-bgxt93ga.js} +3 -3
- package/dist/chunks/studio-cft9r4r7.js +119 -0
- package/dist/chunks/{studio-dc8s8heb.js → studio-cmb03bg5.js} +6904 -8321
- package/dist/chunks/studio-dm19q43r.js +195 -0
- package/dist/chunks/{studio-2x9zbxmx.js → studio-ef49pnzx.js} +11 -9
- package/dist/chunks/{studio-ay8wcr2b.js → studio-eqq1w638.js} +7 -9
- package/dist/chunks/{studio-5efrtn1m.js → studio-ew0djmhg.js} +23 -19
- package/dist/chunks/{studio-tr20m8g8.js → studio-f3wqjtk5.js} +39 -3
- package/dist/chunks/{studio-xntfdy1x.js → studio-fbf92ckf.js} +36 -23
- package/dist/chunks/{studio-4q0wznzf.js → studio-gcye7qka.js} +442 -126
- package/dist/chunks/{studio-7p35254k.js → studio-gfgb8pe6.js} +36 -193
- package/dist/chunks/studio-jc994dnr.js +1066 -0
- package/dist/chunks/{studio-hj2se583.js → studio-jkftxyvz.js} +69 -3423
- package/dist/chunks/{studio-5x5774ak.js → studio-kryvsw6j.js} +3 -3
- package/dist/chunks/{studio-kfpek536.js → studio-kyh9avvv.js} +2 -2
- package/dist/chunks/{studio-twsafr1e.js → studio-m3hwn7y1.js} +3 -3
- package/dist/chunks/studio-msd847zn.js +1315 -0
- package/dist/chunks/{studio-g5gdqnyd.js → studio-mtfnaqs9.js} +19 -19
- package/dist/chunks/{studio-7ejemhej.js → studio-n6j9t10q.js} +6 -6
- package/dist/chunks/{studio-vhk5fmfv.js → studio-n8b9wqwy.js} +5 -5
- package/dist/chunks/{studio-cjm7fdjf.js → studio-p4ngc046.js} +8 -104
- package/dist/chunks/studio-q43a5y9h.js +394 -0
- package/dist/chunks/studio-q7v10cg2.js +106 -0
- package/dist/chunks/{studio-6kv3apa9.js → studio-q9jd1a60.js} +27 -11
- package/dist/chunks/studio-qcy05dp8.js +1743 -0
- package/dist/chunks/{studio-a0yp7syn.js → studio-qjy2z9bv.js} +29 -132
- package/dist/chunks/{studio-2w11grav.js → studio-qq6jym04.js} +3 -3
- package/dist/chunks/{studio-3fk1ac9z.js → studio-rdx8jcxy.js} +4 -4
- package/dist/chunks/{studio-4mqb9a0j.js → studio-rn9ta2mz.js} +4 -4
- package/dist/chunks/studio-t7qb9db8.js +665 -0
- package/dist/chunks/studio-tmc5emrw.js +8 -0
- package/dist/chunks/{studio-r5xbtctf.js → studio-trxrabqf.js} +292 -2297
- package/dist/chunks/studio-wcy8trfx.js +263 -0
- package/dist/chunks/{studio-3dkcd4c5.js → studio-x4qkdat5.js} +62 -57
- package/dist/chunks/studio-x813zp48.js +195 -0
- package/dist/chunks/{studio-qpfq7xhm.js → studio-xrapxwwc.js} +22 -285
- package/dist/chunks/{studio-xm3dbxvn.js → studio-zczps70q.js} +2 -2
- package/dist/chunks/{studio-8ezdegcx.js → studio-zqazsct2.js} +40 -36
- package/dist/chunks/workspace-fjx52ryv.js +89 -0
- package/dist/iframe-entry.js +141 -56
- package/dist/manifest.json +7 -0
- package/dist/studio.js +2605 -1950
- package/favicon.ico +0 -0
- package/index.html +1 -0
- package/package.json +20 -18
- package/src/about/about-modal.ts +4 -3
- package/src/browse/library-pane.ts +57 -3
- package/src/canvas/canvas-render.ts +23 -1
- package/src/canvas/iframe-patch.ts +47 -21
- package/src/canvas/iframe-render.ts +22 -6
- package/src/commands/app-commands.ts +4 -0
- package/src/commands/command-args.ts +29 -0
- package/src/commands/context.ts +15 -1
- package/src/commands/defaults.ts +29 -1
- package/src/commands/levels.ts +16 -0
- package/src/commands/live-context.ts +3 -0
- package/src/content/collection-match.ts +267 -0
- package/src/content/entry-commands.ts +38 -29
- package/src/content/entry-fields.ts +6 -0
- package/src/editor/shortcuts.ts +5 -0
- package/src/files/file-ops.ts +10 -42
- package/src/files/files.ts +487 -59
- package/src/files/fs-events.ts +9 -0
- package/src/files/gitignore.ts +432 -0
- package/src/files/media-paths.ts +13 -42
- package/src/files/media-usage.ts +41 -131
- package/src/files/serialize-document.ts +60 -0
- package/src/format/convert-file.ts +511 -0
- package/src/format/format-choices.ts +199 -0
- package/src/grid/grid-view.ts +16 -2
- package/src/hosting/document.ts +2 -0
- package/src/hosting/layout.ts +8 -0
- package/src/media/media-open.ts +58 -0
- package/src/media/media-pane.ts +310 -0
- package/src/new-project/import-tab.ts +357 -163
- package/src/new-project/location-fields.ts +8 -5
- package/src/new-project/new-project-modal.ts +27 -34
- package/src/packages/ensure-deps.ts +14 -7
- package/src/packages/jxsuite-update.ts +11 -11
- package/src/page-params.ts +10 -46
- package/src/panels/activity-bar.ts +45 -16
- package/src/panels/ai-chat/attached-context.ts +6 -1
- package/src/panels/ai-chat/chat-view.ts +255 -13
- package/src/panels/ai-chat/composer.ts +31 -97
- package/src/panels/ai-panel.ts +116 -2
- package/src/panels/navigator-panels.ts +7 -6
- package/src/panels/settings-menu.ts +799 -0
- package/src/panels/toolbar.ts +135 -15
- package/src/platforms/cloud.ts +261 -70
- package/src/platforms/devserver.ts +67 -33
- package/src/preview/preview-overlay.ts +226 -0
- package/src/publish/publish-panel.ts +62 -2
- package/src/services/ai-ask.ts +247 -0
- package/src/services/ai-import-tools.ts +532 -0
- package/src/services/ai-models.ts +108 -1
- package/src/services/ai-project-tools.ts +20 -26
- package/src/services/ai-system-prompt.ts +88 -2
- package/src/services/ai-turn-signal.ts +70 -0
- package/src/services/connection.ts +74 -0
- package/src/services/context-manager.ts +101 -1
- package/src/services/document-assistant.ts +45 -4
- package/src/services/import-client.ts +33 -6
- package/src/services/import-run.ts +158 -0
- package/src/services/import-seed.ts +120 -0
- package/src/services/jx-validate.ts +58 -6
- package/src/services/project-adoption.ts +71 -0
- package/src/services/references.ts +0 -0
- package/src/services/settings/definitions.ts +17 -0
- package/src/services/tool-executor.ts +45 -2
- package/src/settings/contexts-section.ts +45 -3
- package/src/settings/dependencies-editor.ts +43 -23
- package/src/settings/general-settings.ts +30 -12
- package/src/settings/preferences-accounts.ts +193 -12
- package/src/settings/preferences-dialog.ts +93 -59
- package/src/settings/preferences-sections.ts +57 -0
- package/src/settings/settings-document.ts +65 -9
- package/src/studio.ts +33 -6
- package/src/tabs/transact.ts +42 -19
- package/src/types.ts +129 -7
- package/src/ui/ai-managed-connect.ts +62 -13
- package/src/ui/ai-model-picker.ts +189 -0
- package/src/ui/cf-account-picker.ts +200 -0
- package/src/ui/choice-field.ts +72 -0
- package/src/ui/layers.ts +83 -5
- package/src/utils/studio-utils.ts +22 -52
- package/styles/forced-colors.css +7 -0
- package/styles/overlays.css +256 -0
- package/styles/panels.css +13 -1
- package/styles/shell.css +147 -0
- package/dist/chunks/Overlay.dev-d2ajy6sj.js +0 -23
- package/dist/chunks/content-source-7fy9qrm0.js +0 -40
- package/dist/chunks/files-xcybanxe.js +0 -70
- package/dist/chunks/layers-panel-27essgkc.js +0 -60
- package/dist/chunks/publish-panel-09sy2bab.js +0 -22
- package/dist/chunks/quick-search-sefb61v8.js +0 -55
- package/dist/chunks/right-panel-9r32jd5j.js +0 -39
- package/dist/chunks/sp-overlay.dev-s18hymn9.js +0 -18
- package/dist/chunks/sp-popover.dev-dj9x7e7a.js +0 -9
- package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +0 -15
- package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +0 -15
- package/dist/chunks/studio-bw5dz6km.js +0 -608
- package/dist/chunks/studio-c31yb4t6.js +0 -55
- package/src/canvas/site-style-css.ts +0 -117
|
@@ -27,10 +27,26 @@ import type { ToolRegistry } from "@jxsuite/ai/tools";
|
|
|
27
27
|
|
|
28
28
|
import type { Tab } from "../tabs/tab";
|
|
29
29
|
import { batchTab, beginBatch, endBatch } from "../tabs/transact";
|
|
30
|
+
import { ensureProxyProbe, resetModelCache } from "./ai-models";
|
|
30
31
|
import { beginTurn, endTurn } from "./ai-writes";
|
|
32
|
+
import { beginToolCall, beginTurnSignal, endTurnSignal } from "./ai-turn-signal";
|
|
31
33
|
|
|
32
34
|
const MAX_ROUNDS = 5;
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Hard ceiling on rounds of every kind, so the loop terminates whatever the model does.
|
|
38
|
+
*
|
|
39
|
+
* {@link MAX_ROUNDS} bounds AUTONOMOUS work, and a round that ends by blocking on a person is the
|
|
40
|
+
* opposite of runaway — it cannot advance without them, which is the property the cap was ever a
|
|
41
|
+
* proxy for. So an interactive round does not spend that budget. This one is the backstop that
|
|
42
|
+
* keeps `runAgentLoop` provably terminating anyway; a human answering ten questions will have
|
|
43
|
+
* stopped it long before.
|
|
44
|
+
*/
|
|
45
|
+
const MAX_TOTAL_ROUNDS = 25;
|
|
46
|
+
|
|
47
|
+
/** Tools that suspend the turn on a human rather than doing work. */
|
|
48
|
+
const INTERACTIVE_TOOLS = new Set(["ask_user"]);
|
|
49
|
+
|
|
34
50
|
interface RunAgentLoopOptions {
|
|
35
51
|
chatState: ReturnType<typeof createChatState>;
|
|
36
52
|
streamingClient: StreamingClient;
|
|
@@ -59,6 +75,11 @@ export async function runAgentLoop({
|
|
|
59
75
|
const turnId = () => chatState.messages.at(-1)?.id ?? "";
|
|
60
76
|
beginTurn(`turn:${chatState.messages.length}`);
|
|
61
77
|
|
|
78
|
+
/* The signal, published for the tools rather than passed to them: `ToolRegistry.execute` takes
|
|
79
|
+
none. Without it a tool that waits for a human never learns the turn was stopped, and the
|
|
80
|
+
`await` below is the whole loop. See services/ai-turn-signal.ts. */
|
|
81
|
+
beginTurnSignal(signal);
|
|
82
|
+
|
|
62
83
|
// Batch all tool-call mutations into a single undo step, anchored on the tab being edited.
|
|
63
84
|
if (getTab) {
|
|
64
85
|
beginBatch(getTab());
|
|
@@ -83,13 +104,16 @@ export async function runAgentLoop({
|
|
|
83
104
|
};
|
|
84
105
|
|
|
85
106
|
try {
|
|
86
|
-
|
|
107
|
+
/* Rounds that DID something, which is what MAX_ROUNDS bounds — see MAX_TOTAL_ROUNDS. */
|
|
108
|
+
let workRounds = 0;
|
|
109
|
+
for (let round = 1; round <= MAX_TOTAL_ROUNDS && workRounds < MAX_ROUNDS; round++) {
|
|
87
110
|
const messages = chatState.toMessagesArray();
|
|
88
111
|
const tools = toolRegistry.listForLLM();
|
|
89
112
|
|
|
90
113
|
const toolCalls = new Map<string, { name: string; arguments: string }>();
|
|
91
114
|
let stopReason = "stop";
|
|
92
115
|
let streamError = null;
|
|
116
|
+
let streamErrorCode: string | undefined;
|
|
93
117
|
|
|
94
118
|
for await (const event of streamingClient.streamChat(
|
|
95
119
|
messages,
|
|
@@ -125,6 +149,7 @@ export async function runAgentLoop({
|
|
|
125
149
|
}
|
|
126
150
|
case "error": {
|
|
127
151
|
streamError = event.message;
|
|
152
|
+
streamErrorCode = event.code;
|
|
128
153
|
break;
|
|
129
154
|
}
|
|
130
155
|
default: {
|
|
@@ -136,6 +161,14 @@ export async function runAgentLoop({
|
|
|
136
161
|
chatState.finishStream(stopReason);
|
|
137
162
|
|
|
138
163
|
if (streamError) {
|
|
164
|
+
/* A lapsed hosted grant is the one stream error that makes the app's OWN reading wrong.
|
|
165
|
+
The probe settles once at boot, so without this every gate keeps offering an assistant
|
|
166
|
+
that cannot answer, and the send that just failed is the only evidence anyone has. Drop
|
|
167
|
+
the reading and re-probe, so the next paint carries the Reconnect CTA instead. */
|
|
168
|
+
if (streamErrorCode === "cf_reconnect_required") {
|
|
169
|
+
resetModelCache();
|
|
170
|
+
ensureProxyProbe();
|
|
171
|
+
}
|
|
139
172
|
chatState.setError(streamError);
|
|
140
173
|
return;
|
|
141
174
|
}
|
|
@@ -144,8 +177,11 @@ export async function runAgentLoop({
|
|
|
144
177
|
return;
|
|
145
178
|
}
|
|
146
179
|
|
|
180
|
+
let didWork = false;
|
|
147
181
|
for (const [id, call] of toolCalls) {
|
|
148
182
|
let result;
|
|
183
|
+
// Published, not passed: `ToolRegistry.execute` takes the args and nothing else.
|
|
184
|
+
beginToolCall(id);
|
|
149
185
|
try {
|
|
150
186
|
const args = call.arguments ? (JSON.parse(call.arguments) as object) : {};
|
|
151
187
|
result = await toolRegistry.execute(call.name, args);
|
|
@@ -155,6 +191,9 @@ export async function runAgentLoop({
|
|
|
155
191
|
error: `Failed to parse arguments: ${(error as Error).message}`,
|
|
156
192
|
};
|
|
157
193
|
}
|
|
194
|
+
if (!INTERACTIVE_TOOLS.has(call.name)) {
|
|
195
|
+
didWork = true;
|
|
196
|
+
}
|
|
158
197
|
reanchorBatch();
|
|
159
198
|
if (!result.success && result.error) {
|
|
160
199
|
allErrors.push(result.error);
|
|
@@ -166,7 +205,10 @@ export async function runAgentLoop({
|
|
|
166
205
|
chatState.pushToolResultMessage(id, JSON.stringify(result));
|
|
167
206
|
}
|
|
168
207
|
|
|
169
|
-
if (
|
|
208
|
+
if (didWork) {
|
|
209
|
+
workRounds += 1;
|
|
210
|
+
}
|
|
211
|
+
if (workRounds < MAX_ROUNDS && round < MAX_TOTAL_ROUNDS) {
|
|
170
212
|
chatState.beginAssistantTurn();
|
|
171
213
|
}
|
|
172
214
|
}
|
|
@@ -196,6 +238,7 @@ export async function runAgentLoop({
|
|
|
196
238
|
}
|
|
197
239
|
chatState.setError(tail);
|
|
198
240
|
} finally {
|
|
241
|
+
endTurnSignal();
|
|
199
242
|
endBatch();
|
|
200
243
|
endTurn(turnId());
|
|
201
244
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* alone), parks any rejection under the control that caused it, and re-renders. §7.1's third tier:
|
|
31
31
|
* a bad value belongs at its control, not in a toast that expires.
|
|
32
32
|
*
|
|
33
|
-
* @docs studio/projects/
|
|
33
|
+
* @docs studio/projects/settings
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import { html, render as litRender, nothing } from "lit-html";
|
|
@@ -140,6 +140,15 @@ type ErrorTarget = string;
|
|
|
140
140
|
*/
|
|
141
141
|
const errors = new WeakMap<HTMLElement, { target: ErrorTarget; message: string }>();
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Schema errors the file already had when this section last wrote to it.
|
|
145
|
+
*
|
|
146
|
+
* Reported at section level rather than under a control, because they belong to no control — and
|
|
147
|
+
* kept per container for the same reason {@link errors} is. Populated by the first write, since
|
|
148
|
+
* validating on every render would compile a schema for a section nobody is editing.
|
|
149
|
+
*/
|
|
150
|
+
const inheritedErrors = new WeakMap<HTMLElement, string[]>();
|
|
151
|
+
|
|
143
152
|
/** Read the current failure for a container — exported for tests and for re-render helpers. */
|
|
144
153
|
export function contextsError(container: HTMLElement): { target: string; message: string } | null {
|
|
145
154
|
return errors.get(container) ?? null;
|
|
@@ -171,12 +180,26 @@ export function renderContextsSection(container: HTMLElement): void {
|
|
|
171
180
|
* shape, and the disk can refuse the write. The predecessor surfaces (`Properties › Media` and
|
|
172
181
|
* the CSS-variables "Enable dark scheme") did `void updateSiteConfig(...)` and dropped the
|
|
173
182
|
* rejection, so a failed write read as the field snapping back for no reason.
|
|
183
|
+
*
|
|
184
|
+
* **Only the errors THIS edit introduces block it.** The candidate is the whole `project.json`,
|
|
185
|
+
* so a violation anywhere else in the file used to refuse every context edit and park the reason
|
|
186
|
+
* under whichever control had been touched — typing `1280px` into Base width reported three
|
|
187
|
+
* unevaluated-property errors about `title`, `description` and `$style`, none of which is a
|
|
188
|
+
* width. The baseline is validated first and subtracted, the same way `services/ai-tools.ts`
|
|
189
|
+
* subtracts it before blaming the model for a document it inherited. A pre-existing problem is
|
|
190
|
+
* still worth saying, so it is reported once at section level, where a whole-file problem belongs
|
|
191
|
+
* — and it is reported rather than repaired, because `project.json` is the author's file and a
|
|
192
|
+
* settings screen that silently drops keys it did not recognise is worse than one that names
|
|
193
|
+
* them.
|
|
174
194
|
*/
|
|
175
195
|
const persistMedia = async (next: Record<string, string>, target: ErrorTarget) => {
|
|
176
196
|
const candidate = { ...config, $media: next } as unknown;
|
|
177
197
|
let schemaErrors: string[] = [];
|
|
198
|
+
let inherited: string[] = [];
|
|
178
199
|
try {
|
|
179
|
-
|
|
200
|
+
inherited = await validateProjectConfig(config);
|
|
201
|
+
const found = await validateProjectConfig(candidate);
|
|
202
|
+
schemaErrors = found.filter((error) => !inherited.includes(error));
|
|
180
203
|
} catch (error) {
|
|
181
204
|
/* A validator that cannot compile must not block the edit — but it must not be silent
|
|
182
205
|
either, so the message rides the same inline slot the schema errors would have used. */
|
|
@@ -187,6 +210,7 @@ export function renderContextsSection(container: HTMLElement): void {
|
|
|
187
210
|
reject(target, schemaErrors.join("; "));
|
|
188
211
|
return;
|
|
189
212
|
}
|
|
213
|
+
inheritedErrors.set(container, inherited);
|
|
190
214
|
try {
|
|
191
215
|
await updateSiteConfig({ $media: next });
|
|
192
216
|
errors.delete(container);
|
|
@@ -199,6 +223,24 @@ export function renderContextsSection(container: HTMLElement): void {
|
|
|
199
223
|
renderContextsSection(container);
|
|
200
224
|
};
|
|
201
225
|
|
|
226
|
+
/**
|
|
227
|
+
* What is already wrong with `project.json`, said once and not blamed on a control.
|
|
228
|
+
*
|
|
229
|
+
* It does not block anything — the edit that surfaced it went through. It is here because a file
|
|
230
|
+
* that fails its own schema will keep failing it, and until this notice existed the only place
|
|
231
|
+
* that fact appeared was as an unexplained refusal of an unrelated field.
|
|
232
|
+
*/
|
|
233
|
+
const inheritedNotice = () => {
|
|
234
|
+
const inherited = inheritedErrors.get(container) ?? [];
|
|
235
|
+
return inherited.length === 0
|
|
236
|
+
? nothing
|
|
237
|
+
: html`<p class="settings-section-notice" role="status">
|
|
238
|
+
project.json has ${inherited.length} pre-existing schema
|
|
239
|
+
${inherited.length === 1 ? "problem" : "problems"} that this section did not cause:
|
|
240
|
+
${inherited.join("; ")}
|
|
241
|
+
</p>`;
|
|
242
|
+
};
|
|
243
|
+
|
|
202
244
|
/** The error line for one control, when that is where the current failure belongs. */
|
|
203
245
|
const errorFor = (target: ErrorTarget) =>
|
|
204
246
|
shown?.target === target
|
|
@@ -370,7 +412,7 @@ export function renderContextsSection(container: HTMLElement): void {
|
|
|
370
412
|
The conditions this project's pages are rendered under. Define them once here; choose
|
|
371
413
|
between them on the pane's context control while you edit.
|
|
372
414
|
</p>
|
|
373
|
-
${errorFor("section")}
|
|
415
|
+
${errorFor("section")} ${inheritedNotice()}
|
|
374
416
|
|
|
375
417
|
<div class="settings-field">
|
|
376
418
|
<label class="settings-field-label">Base width</label>
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* Dependencies settings section —
|
|
3
|
+
* Dependencies settings section — a registry-aware table of the project's npm dependencies. Shows
|
|
4
4
|
* current vs latest version, with per-row update/remove, an add-package field, "update all", and
|
|
5
|
-
* reinstall.
|
|
6
|
-
*
|
|
5
|
+
* reinstall.
|
|
6
|
+
*
|
|
7
|
+
* **Every row's Latest is that package's own newest version on npm.** Two assumptions used to sit
|
|
8
|
+
* between this table and the registry, and both are gone:
|
|
9
|
+
*
|
|
10
|
+
* 1. `@jxsuite/*` rows targeted `VERSION`, the version this Studio build embeds — right only while the
|
|
11
|
+
* whole suite released as one number. The packages release on their own cadences now, so that
|
|
12
|
+
* proposed a version that may never have been published, for a package whose real latest the
|
|
13
|
+
* table had not looked at. The same correction `packages/jxsuite-update.ts` already made.
|
|
14
|
+
* 2. The backend was asked for _outdated_ packages, so a package already at its latest arrived as an
|
|
15
|
+
* absence and the column read `—` — the registry's answer was known and thrown away. It now asks
|
|
16
|
+
* `platform.packageVersions()`, which reports every dependency's latest either way, and the
|
|
17
|
+
* comparison that decides whether an update button appears happens here.
|
|
18
|
+
*
|
|
19
|
+
* A row shows `—` only where there is genuinely nothing to show: a `workspace:`/`file:`/git spec, a
|
|
20
|
+
* package the registry does not answer for, or a host with no registry lookup at all.
|
|
7
21
|
*/
|
|
8
22
|
|
|
9
23
|
import { html, render as litRender } from "lit-html";
|
|
10
24
|
import { getPlatform } from "../platform";
|
|
11
|
-
import { VERSION } from "../version";
|
|
12
25
|
import { notify } from "../services/notify";
|
|
13
26
|
import { showProgressModal } from "../ui/progress-modal";
|
|
14
|
-
import {
|
|
27
|
+
import { isUpgrade, stripRange } from "../packages/semver";
|
|
15
28
|
import type { PackageInfo } from "../types";
|
|
16
29
|
|
|
17
|
-
const JXSUITE_PREFIX = "@jxsuite/";
|
|
18
|
-
|
|
19
30
|
interface Update {
|
|
20
31
|
name: string;
|
|
21
32
|
version: string;
|
|
@@ -28,13 +39,21 @@ let _latest = new Map<string, string>();
|
|
|
28
39
|
let _busy = false;
|
|
29
40
|
let _addName = "";
|
|
30
41
|
|
|
31
|
-
/**
|
|
42
|
+
/** This package's newest published version, or null when the registry did not answer for it. */
|
|
32
43
|
function latestFor(p: PackageInfo): string | null {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
return _latest.get(p.name) ?? null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The version a package can be updated TO, or null when it is current or ahead.
|
|
49
|
+
*
|
|
50
|
+
* `isUpgrade`, not "differs from latest": a project deliberately pinned ahead of the registry — a
|
|
51
|
+
* prerelease, or a range bumped before the publish landed — would otherwise be offered a downgrade
|
|
52
|
+
* with an update button.
|
|
53
|
+
*/
|
|
54
|
+
function upgradeFor(p: PackageInfo): string | null {
|
|
55
|
+
const latest = latestFor(p);
|
|
56
|
+
return latest && isUpgrade(p.version, latest) ? latest : null;
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
async function load() {
|
|
@@ -45,10 +64,10 @@ async function load() {
|
|
|
45
64
|
_packages = [];
|
|
46
65
|
}
|
|
47
66
|
_latest = new Map();
|
|
48
|
-
if (platform.
|
|
67
|
+
if (platform.packageVersions) {
|
|
49
68
|
try {
|
|
50
|
-
for (const
|
|
51
|
-
_latest.set(
|
|
69
|
+
for (const info of await platform.packageVersions()) {
|
|
70
|
+
_latest.set(info.name, info.latest);
|
|
52
71
|
}
|
|
53
72
|
} catch {
|
|
54
73
|
/* Registry lookups are best-effort */
|
|
@@ -118,9 +137,9 @@ async function onUpdateAll() {
|
|
|
118
137
|
}
|
|
119
138
|
const updates = (_packages ?? [])
|
|
120
139
|
.map((p): Update | null => {
|
|
121
|
-
const
|
|
122
|
-
return
|
|
123
|
-
? { dev: Boolean(p.dev), name: p.name, version: `^${stripRange(
|
|
140
|
+
const target = upgradeFor(p);
|
|
141
|
+
return target
|
|
142
|
+
? { dev: Boolean(p.dev), name: p.name, version: `^${stripRange(target)}` }
|
|
124
143
|
: null;
|
|
125
144
|
})
|
|
126
145
|
.filter((u): u is Update => u !== null);
|
|
@@ -152,6 +171,7 @@ async function onReinstall() {
|
|
|
152
171
|
|
|
153
172
|
function row(p: PackageInfo) {
|
|
154
173
|
const latest = latestFor(p);
|
|
174
|
+
const upgrade = upgradeFor(p);
|
|
155
175
|
return html`
|
|
156
176
|
<sp-table-row>
|
|
157
177
|
<sp-table-cell>
|
|
@@ -163,13 +183,13 @@ function row(p: PackageInfo) {
|
|
|
163
183
|
<sp-table-cell>${latest ?? "—"}</sp-table-cell>
|
|
164
184
|
<sp-table-cell>
|
|
165
185
|
${
|
|
166
|
-
|
|
186
|
+
upgrade
|
|
167
187
|
? html`<sp-action-button
|
|
168
188
|
size="s"
|
|
169
189
|
quiet
|
|
170
190
|
?disabled=${_busy}
|
|
171
|
-
title="Update to ${
|
|
172
|
-
@click=${() => onUpdate(p,
|
|
191
|
+
title="Update to ${upgrade}"
|
|
192
|
+
@click=${() => onUpdate(p, upgrade)}
|
|
173
193
|
>
|
|
174
194
|
<sp-icon-refresh slot="icon"></sp-icon-refresh>
|
|
175
195
|
</sp-action-button>`
|
|
@@ -194,7 +214,7 @@ function render() {
|
|
|
194
214
|
return;
|
|
195
215
|
}
|
|
196
216
|
const pkgs = _packages ?? [];
|
|
197
|
-
const hasUpdates = pkgs.some((p) =>
|
|
217
|
+
const hasUpdates = pkgs.some((p) => upgradeFor(p) !== null);
|
|
198
218
|
|
|
199
219
|
const tpl = html`
|
|
200
220
|
<div class="settings-section">
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
import { html, render as litRender, nothing } from "lit-html";
|
|
13
13
|
import { live } from "lit-html/directives/live.js";
|
|
14
14
|
import { errorMessage } from "@jxsuite/schema/parse";
|
|
15
|
-
import { projectState
|
|
15
|
+
import { projectState } from "../store";
|
|
16
16
|
import { updateSiteConfig } from "../site-context";
|
|
17
|
-
import {
|
|
17
|
+
import { activeRegistry } from "../commands/active-registry";
|
|
18
18
|
import { getPlatform } from "../platform";
|
|
19
19
|
|
|
20
20
|
import type { JxHeadEntry, ProjectConfig } from "@jxsuite/schema/types";
|
|
@@ -165,16 +165,27 @@ export function renderGeneralSettings(container: HTMLElement) {
|
|
|
165
165
|
};
|
|
166
166
|
|
|
167
167
|
/*
|
|
168
|
-
* Project Styles is the SAME document in a different editor,
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
168
|
+
* Project Styles is the SAME document in a different editor, and `styles.open` is the one place
|
|
169
|
+
* that says so. This used to write `session.ui.canvasMode` here directly — a second
|
|
170
|
+
* implementation of a capability, §12.5's defect in miniature: the button and the command could
|
|
171
|
+
* disagree about what "open Project Styles" means, and only one of them was reachable by name.
|
|
172
|
+
* The mode is still a fact about the TAB, and there is exactly one `project.json` tab, so
|
|
173
|
+
* `revealTab`'s `focusPane` puts the keyboard on whichever pane holds it.
|
|
174
|
+
*
|
|
175
|
+
* Rendered FROM the record, so the button is disabled with its `requires` sentence rather than
|
|
176
|
+
* absent when it cannot act (§12.3), and it cannot drift from the palette row or the gear menu.
|
|
172
177
|
*/
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
const registry = activeRegistry();
|
|
179
|
+
const stylesCommand = registry?.get("styles.open");
|
|
180
|
+
/* The record's TITLE, and nothing else off the registry.
|
|
181
|
+
`disabledReason` would be the §12.3 shape for a control that can be inapplicable — but this one
|
|
182
|
+
cannot be. `styles.open` is gated on `ctx.project.open`, and Overview is a section of the
|
|
183
|
+
project's own configuration document, so by the time this renders the answer is always yes.
|
|
184
|
+
Asking anyway would also be a FOCUS read inside a function that was handed a container, which
|
|
185
|
+
is what `scripts/check-pane-singletons.ts` rule 4 forbids: a pane-scoped surface must not report
|
|
186
|
+
the focused pane's state. A project-level verb happens to read the same in every pane, but the
|
|
187
|
+
way to be right about that is not to ask. The one thing that can genuinely be missing is the
|
|
188
|
+
registry itself (bootstrap order, a test), and that is an existence check, not a state read. */
|
|
178
189
|
|
|
179
190
|
const currentFavicon = config.favicon;
|
|
180
191
|
|
|
@@ -266,7 +277,14 @@ export function renderGeneralSettings(container: HTMLElement) {
|
|
|
266
277
|
Design tokens and the default element styles that apply across every page. Opens Project
|
|
267
278
|
Styles over this same document.
|
|
268
279
|
</p>
|
|
269
|
-
<sp-action-button
|
|
280
|
+
<sp-action-button
|
|
281
|
+
size="s"
|
|
282
|
+
?disabled=${!stylesCommand}
|
|
283
|
+
title=${stylesCommand?.title ?? ""}
|
|
284
|
+
@click=${() => {
|
|
285
|
+
void registry?.run("styles.open");
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
270
288
|
Edit Global Styles
|
|
271
289
|
</sp-action-button>
|
|
272
290
|
</div>
|
|
@@ -17,12 +17,31 @@
|
|
|
17
17
|
*
|
|
18
18
|
* **No secret is ever returned.** A record says whether something is stored and what it is for;
|
|
19
19
|
* printing a token into a list is how a screenshot leaks one.
|
|
20
|
+
*
|
|
21
|
+
* **Cloudflare has two spellings, and this file used to know only one.** On a platform that brokers
|
|
22
|
+
* the connection (Jx Cloud), there is no local token to enumerate — `getCfToken()` is empty by
|
|
23
|
+
* design — so the row said "Not connected" to every hosted user, named no account, and offered a
|
|
24
|
+
* Disconnect that cleared nothing server-side. The hosted state comes from the PAL instead, cached
|
|
25
|
+
* here because a template must not start a fetch on every repaint.
|
|
20
26
|
*/
|
|
21
27
|
|
|
22
28
|
import { clearGithubToken, githubTokenLocation, githubTokenStored } from "../github/github-auth";
|
|
23
29
|
import { clearCfConnection, getCfAccountId, getCfToken } from "../services/cf-settings";
|
|
24
30
|
import { clearAiProvider, getBaseUrl, hasOpenAiKey } from "../services/ai-settings";
|
|
25
|
-
import { preferredModel } from "../services/ai-models";
|
|
31
|
+
import { preferredModel, resetModelCache } from "../services/ai-models";
|
|
32
|
+
import { getPlatform, hasPlatform } from "../platform";
|
|
33
|
+
import type { CfConnection } from "../types";
|
|
34
|
+
|
|
35
|
+
/** One thing a row can do besides existing. */
|
|
36
|
+
export interface AccountAction {
|
|
37
|
+
/** Stable key — the button's `data-action`, so a test names an intent rather than a label. */
|
|
38
|
+
id: string;
|
|
39
|
+
label: string;
|
|
40
|
+
/** Spectrum `variant`; omitted for the default treatment. */
|
|
41
|
+
variant?: string;
|
|
42
|
+
/** Do it. Async ones repaint the sheet when they settle. */
|
|
43
|
+
run: () => void | Promise<void>;
|
|
44
|
+
}
|
|
26
45
|
|
|
27
46
|
/** One stored credential, as the Accounts section renders it. */
|
|
28
47
|
export interface AccountRecord {
|
|
@@ -35,6 +54,178 @@ export interface AccountRecord {
|
|
|
35
54
|
connected: boolean;
|
|
36
55
|
/** Forget it. Idempotent: revoking a disconnected account is a no-op, not an error. */
|
|
37
56
|
revoke: () => void;
|
|
57
|
+
/**
|
|
58
|
+
* What this row offers, when a plain Disconnect is not the whole story.
|
|
59
|
+
*
|
|
60
|
+
* Absent on the rows whose only verb is "forget it", which is every locally stored credential. A
|
|
61
|
+
* brokered Cloudflare connection has three more — reconnect a lapsed grant, choose an account,
|
|
62
|
+
* connect for the first time — and none of them is expressible as `revoke`.
|
|
63
|
+
*/
|
|
64
|
+
actions?: AccountAction[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── The brokered Cloudflare connection ──────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/** What the broker last said, or "checking" before it has been asked. */
|
|
70
|
+
let _cfState: CfConnection | null | "checking" = "checking";
|
|
71
|
+
|
|
72
|
+
/** The in-flight read, so a repaint during one does not start a second. */
|
|
73
|
+
let _cfInFlight: Promise<void> | null = null;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Whether the platform brokers the Cloudflare connection rather than holding a pasted token.
|
|
77
|
+
*
|
|
78
|
+
* Keyed on `cfConnect` — the hosted OAuth flow — because that is the member whose presence means
|
|
79
|
+
* "there is a connection this app did not store locally".
|
|
80
|
+
*/
|
|
81
|
+
export function platformBrokersCf(): boolean {
|
|
82
|
+
return hasPlatform() && Boolean(getPlatform().cfConnect);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Re-read the brokered connection and announce it. Always refetches; the Accounts section runs it
|
|
87
|
+
* when it is shown and after every action, because the answer changes server-side.
|
|
88
|
+
*/
|
|
89
|
+
export async function refreshCfConnection(): Promise<void> {
|
|
90
|
+
if (!platformBrokersCf()) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
_cfInFlight ??= (async () => {
|
|
94
|
+
try {
|
|
95
|
+
_cfState = (await getPlatform().cfConnection?.()) ?? null;
|
|
96
|
+
} catch {
|
|
97
|
+
/* An unreachable broker is not a connection. The row says "not connected", which is what the
|
|
98
|
+
app can act on; the error itself belongs to whatever surface made the call the user asked
|
|
99
|
+
for, not to a list that repaints itself. */
|
|
100
|
+
_cfState = null;
|
|
101
|
+
}
|
|
102
|
+
_cfInFlight = null;
|
|
103
|
+
notifyCredentialsChanged();
|
|
104
|
+
})();
|
|
105
|
+
await _cfInFlight;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Kick the first read, once. Safe to call from a template — later paints are no-ops. */
|
|
109
|
+
export function ensureCfConnection(): void {
|
|
110
|
+
if (platformBrokersCf() && _cfState === "checking" && !_cfInFlight) {
|
|
111
|
+
void refreshCfConnection();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Forget the cached broker answer.
|
|
117
|
+
*
|
|
118
|
+
* Called when Preferences opens, and that is the point: the row would otherwise paint the answer
|
|
119
|
+
* from the LAST visit — an hour-old "Connected — Acme" over a grant that has since lapsed — and
|
|
120
|
+
* only correct itself when the re-read landed. Saying "checking" while it checks is the honest
|
|
121
|
+
* version of the same half-second.
|
|
122
|
+
*/
|
|
123
|
+
export function resetCfConnectionCache(): void {
|
|
124
|
+
_cfState = "checking";
|
|
125
|
+
_cfInFlight = null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Run the hosted flow, then re-read. The models cache goes because the AI gate keys on it too. */
|
|
129
|
+
async function hostedConnect(): Promise<void> {
|
|
130
|
+
const outcome = await getPlatform().cfConnect?.();
|
|
131
|
+
if (outcome?.status === "connected" && !outcome.connection.accountId) {
|
|
132
|
+
/* Dynamically, and this is the load-bearing half: the picker imports THIS module for
|
|
133
|
+
`notifyCredentialsChanged`, so a static import here would close the cycle. */
|
|
134
|
+
const { openCfAccountPicker } = await import("../ui/cf-account-picker");
|
|
135
|
+
await openCfAccountPicker();
|
|
136
|
+
}
|
|
137
|
+
resetModelCache();
|
|
138
|
+
await refreshCfConnection();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The brokered row — five states, and each one names the next move. */
|
|
142
|
+
function brokeredCloudflareRecord(): AccountRecord {
|
|
143
|
+
const disconnect: AccountAction = {
|
|
144
|
+
id: "disconnect",
|
|
145
|
+
label: "Disconnect",
|
|
146
|
+
run: async () => {
|
|
147
|
+
await getPlatform().cfDisconnect?.();
|
|
148
|
+
resetModelCache();
|
|
149
|
+
await refreshCfConnection();
|
|
150
|
+
},
|
|
151
|
+
variant: "negative",
|
|
152
|
+
};
|
|
153
|
+
const connect = (label: string): AccountAction => ({
|
|
154
|
+
id: label === "Connect" ? "connect" : "reconnect",
|
|
155
|
+
label,
|
|
156
|
+
run: hostedConnect,
|
|
157
|
+
variant: "accent",
|
|
158
|
+
});
|
|
159
|
+
const base = { id: "cloudflare", label: "Cloudflare", revoke: () => {} };
|
|
160
|
+
|
|
161
|
+
if (_cfState === "checking") {
|
|
162
|
+
return { ...base, actions: [], connected: false, detail: "Checking your connection…" };
|
|
163
|
+
}
|
|
164
|
+
if (!_cfState?.connected) {
|
|
165
|
+
return {
|
|
166
|
+
...base,
|
|
167
|
+
actions: [connect("Connect")],
|
|
168
|
+
connected: false,
|
|
169
|
+
detail: "Not connected. Connecting publishes this site and runs the assistant on Workers AI.",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const revoke = () => {
|
|
173
|
+
void disconnect.run();
|
|
174
|
+
};
|
|
175
|
+
if (_cfState.needsReconnect) {
|
|
176
|
+
return {
|
|
177
|
+
...base,
|
|
178
|
+
actions: [connect("Reconnect"), disconnect],
|
|
179
|
+
connected: true,
|
|
180
|
+
detail: "Connection expired. Reconnect to keep publishing and the assistant working.",
|
|
181
|
+
revoke,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (_cfState.needsAccount || !_cfState.accountId) {
|
|
185
|
+
return {
|
|
186
|
+
...base,
|
|
187
|
+
actions: [
|
|
188
|
+
{
|
|
189
|
+
id: "choose-account",
|
|
190
|
+
label: "Choose account",
|
|
191
|
+
run: async () => {
|
|
192
|
+
const { openCfAccountPicker } = await import("../ui/cf-account-picker");
|
|
193
|
+
if (await openCfAccountPicker()) {
|
|
194
|
+
resetModelCache();
|
|
195
|
+
await refreshCfConnection();
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
variant: "accent",
|
|
199
|
+
},
|
|
200
|
+
disconnect,
|
|
201
|
+
],
|
|
202
|
+
connected: true,
|
|
203
|
+
detail: "Connected, but no account is chosen yet — publishing and the assistant need one.",
|
|
204
|
+
revoke,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
...base,
|
|
209
|
+
actions: [disconnect],
|
|
210
|
+
connected: true,
|
|
211
|
+
detail: `Connected — ${_cfState.accountName ?? _cfState.accountId}.`,
|
|
212
|
+
revoke,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** The locally stored row: a pasted API token, on desktop and the dev server. */
|
|
217
|
+
function localCloudflareRecord(): AccountRecord {
|
|
218
|
+
const cfToken = getCfToken();
|
|
219
|
+
const cfAccount = getCfAccountId();
|
|
220
|
+
return {
|
|
221
|
+
connected: Boolean(cfToken),
|
|
222
|
+
detail: cfToken
|
|
223
|
+
? `Connected${cfAccount ? ` — account ${cfAccount}` : ""}.`
|
|
224
|
+
: "Not connected. Publishing will ask when it needs it.",
|
|
225
|
+
id: "cloudflare",
|
|
226
|
+
label: "Cloudflare",
|
|
227
|
+
revoke: clearCfConnection,
|
|
228
|
+
};
|
|
38
229
|
}
|
|
39
230
|
|
|
40
231
|
/**
|
|
@@ -45,8 +236,6 @@ export interface AccountRecord {
|
|
|
45
236
|
*/
|
|
46
237
|
export function listAccounts(): AccountRecord[] {
|
|
47
238
|
const githubToken = githubTokenStored();
|
|
48
|
-
const cfToken = getCfToken();
|
|
49
|
-
const cfAccount = getCfAccountId();
|
|
50
239
|
const model = preferredModel();
|
|
51
240
|
const endpoint = getBaseUrl();
|
|
52
241
|
return [
|
|
@@ -70,15 +259,7 @@ export function listAccounts(): AccountRecord[] {
|
|
|
70
259
|
connected: hasOpenAiKey(),
|
|
71
260
|
revoke: clearAiProvider,
|
|
72
261
|
},
|
|
73
|
-
|
|
74
|
-
id: "cloudflare",
|
|
75
|
-
label: "Cloudflare",
|
|
76
|
-
detail: cfToken
|
|
77
|
-
? `Connected${cfAccount ? ` — account ${cfAccount}` : ""}.`
|
|
78
|
-
: "Not connected. Publishing will ask when it needs it.",
|
|
79
|
-
connected: Boolean(cfToken),
|
|
80
|
-
revoke: clearCfConnection,
|
|
81
|
-
},
|
|
262
|
+
platformBrokersCf() ? brokeredCloudflareRecord() : localCloudflareRecord(),
|
|
82
263
|
];
|
|
83
264
|
}
|
|
84
265
|
|