@jxsuite/studio 4.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/dist/chunks/{2020-xp8vnwma.js → 2020-n6tfwnda.js} +2 -2
- package/dist/chunks/{Overlay.dev-723bv0df.js → Overlay.dev-etqjwyf9.js} +4 -4
- package/dist/chunks/cf-account-picker-en86v7x7.js +163 -0
- package/dist/chunks/{content-source-t88vb2dr.js → content-source-48xg86wj.js} +17 -17
- package/dist/chunks/{convert-to-repeater-dhw1nrz9.js → convert-to-repeater-43tfn7cs.js} +24 -24
- package/dist/chunks/{entry-commands-6z6bhxg0.js → entry-commands-e63f1xc0.js} +20 -20
- package/dist/chunks/{extension-sections-3ydgjbdy.js → extension-sections-txv08xxg.js} +23 -23
- package/dist/chunks/{file-ops-rskmhxyq.js → file-ops-wbh4hhsx.js} +16 -16
- package/dist/chunks/{files-0cratsr0.js → files-3kfkwhk4.js} +25 -24
- package/dist/chunks/{focus-trap.esm-ggawf1hy.js → focus-trap.esm-9t2drp0g.js} +6 -5
- package/dist/chunks/{format-host-tgcmfs4p.js → format-host-w16q81tf.js} +2 -2
- package/dist/chunks/fs-events-hrrg328g.js +1 -1
- package/dist/chunks/{git-panel-0yywyk76.js → git-panel-532whr1y.js} +16 -15
- package/dist/chunks/{index-nj44jpzj.js → index-wcnt3r9g.js} +2 -2
- package/dist/chunks/{jx-validate-8q2g05x1.js → jx-validate-8er6gbxy.js} +3 -3
- package/dist/chunks/{layers-panel-z3tqe5cd.js → layers-panel-07hm7g5j.js} +19 -19
- package/dist/chunks/{monaco-setup-rws7k4br.js → monaco-setup-vx9bhp1d.js} +2 -2
- package/dist/chunks/{publish-panel-54jvhskq.js → publish-panel-v9st96ja.js} +11 -10
- package/dist/chunks/{quick-search-cgfmjwq5.js → quick-search-zccjp6gv.js} +26 -25
- package/dist/chunks/{right-panel-cqp8bc4v.js → right-panel-vm3b6kwy.js} +17 -17
- package/dist/chunks/{sp-overlay.dev-yztmtrs1.js → sp-overlay.dev-qgvhnxf4.js} +5 -5
- package/dist/chunks/{sp-tooltip.dev-xcvpnqr0.js → sp-tooltip.dev-4sastdnw.js} +3 -3
- package/dist/chunks/{studio-txwkm6p9.js → studio-0bq6eh7v.js} +2 -2
- package/dist/chunks/studio-1ngbejft.js +181 -0
- package/dist/chunks/{studio-gv8vvshc.js → studio-1wagb550.js} +3 -3
- package/dist/chunks/{studio-kjjsjak4.js → studio-2b946z26.js} +58 -57
- package/dist/chunks/{studio-4pqqxbrt.js → studio-3bqpr0zh.js} +16 -16
- package/dist/chunks/{studio-v38hmvx1.js → studio-4b5z1x43.js} +42 -8
- package/dist/chunks/{studio-9c4k4vhv.js → studio-4n2b5gzb.js} +4 -4
- package/dist/chunks/{studio-p8j86wag.js → studio-4r1pv6zf.js} +2 -2
- package/dist/chunks/{studio-mmv72a27.js → studio-5fx3g9jg.js} +29 -48
- package/dist/chunks/{studio-5rywrkxz.js → studio-5myknm0a.js} +42 -42
- package/dist/chunks/{studio-sf7zjgv1.js → studio-6yw4y3my.js} +3 -3
- package/dist/chunks/{studio-pdfjntbk.js → studio-950k0dx6.js} +35 -35
- package/dist/chunks/{studio-eenfkt4e.js → studio-ahn3pdgx.js} +5 -5
- package/dist/chunks/{studio-eesa1myv.js → studio-b55y6ewx.js} +9 -9
- package/dist/chunks/{studio-cb12yz9w.js → studio-cmb03bg5.js} +99 -1264
- package/dist/chunks/studio-dm19q43r.js +195 -0
- package/dist/chunks/{studio-ahjfdy6e.js → studio-ef49pnzx.js} +6 -6
- package/dist/chunks/{studio-wx5609xd.js → studio-ew0djmhg.js} +10 -10
- package/dist/chunks/{studio-9r6j5ev1.js → studio-f3wqjtk5.js} +4 -3
- package/dist/chunks/{studio-8swrjbny.js → studio-fbf92ckf.js} +14 -14
- package/dist/chunks/{studio-4q0wznzf.js → studio-gcye7qka.js} +442 -126
- package/dist/chunks/{studio-ex8zyp98.js → studio-gfgb8pe6.js} +23 -184
- package/dist/chunks/{studio-r1r7afxx.js → studio-jc994dnr.js} +24 -24
- package/dist/chunks/{studio-yxrkyv8a.js → studio-jkftxyvz.js} +52 -3416
- package/dist/chunks/{studio-hbtp0eh1.js → studio-msd847zn.js} +13 -13
- package/dist/chunks/{studio-kyk2v92a.js → studio-mtfnaqs9.js} +14 -14
- package/dist/chunks/{studio-vadzgkrz.js → studio-n6j9t10q.js} +2 -2
- package/dist/chunks/{studio-68n67094.js → studio-q9jd1a60.js} +4 -4
- package/dist/chunks/studio-qcy05dp8.js +1743 -0
- package/dist/chunks/{studio-htsf0w6m.js → studio-qjy2z9bv.js} +12 -30
- package/dist/chunks/{studio-2zkz8evn.js → studio-trxrabqf.js} +12 -12
- package/dist/chunks/{studio-5q69gfag.js → studio-x4qkdat5.js} +12 -11
- package/dist/chunks/studio-x813zp48.js +195 -0
- package/dist/chunks/{studio-1hbqnt19.js → studio-zqazsct2.js} +29 -29
- package/dist/chunks/{workspace-xpres40v.js → workspace-fjx52ryv.js} +4 -4
- package/dist/iframe-entry.js +34 -33
- package/dist/manifest.json +7 -0
- package/dist/studio.js +534 -546
- package/favicon.ico +0 -0
- package/index.html +1 -0
- package/package.json +15 -14
- package/src/about/about-modal.ts +4 -3
- package/src/files/files.ts +6 -4
- package/src/files/media-paths.ts +13 -42
- package/src/files/media-usage.ts +41 -131
- package/src/hosting/document.ts +2 -0
- package/src/hosting/layout.ts +8 -0
- package/src/new-project/import-tab.ts +37 -15
- package/src/new-project/location-fields.ts +8 -5
- package/src/panels/ai-chat/composer.ts +10 -0
- package/src/platforms/cloud.ts +241 -70
- package/src/publish/publish-panel.ts +62 -2
- package/src/services/ai-import-tools.ts +50 -12
- package/src/services/ai-models.ts +108 -1
- package/src/services/context-manager.ts +12 -1
- package/src/services/tool-executor.ts +11 -0
- package/src/settings/preferences-accounts.ts +193 -12
- package/src/settings/preferences-dialog.ts +75 -18
- package/src/types.ts +27 -3
- package/src/ui/ai-managed-connect.ts +62 -13
- package/src/ui/ai-model-picker.ts +29 -2
- package/src/ui/cf-account-picker.ts +200 -0
- package/styles/shell.css +36 -0
- package/dist/chunks/studio-bw5dz6km.js +0 -608
|
@@ -25,7 +25,9 @@ import {
|
|
|
25
25
|
isManagedProxy,
|
|
26
26
|
isProxyConfigured,
|
|
27
27
|
proxyStateCode,
|
|
28
|
+
resetModelCache,
|
|
28
29
|
} from "../services/ai-models";
|
|
30
|
+
import type { CfConnectOutcome } from "../types";
|
|
29
31
|
|
|
30
32
|
export interface ManagedConnectOptions {
|
|
31
33
|
/** Host re-render scheduler — called on connect start/finish and when the probe settles. */
|
|
@@ -52,20 +54,73 @@ export function createManagedConnect(opts: ManagedConnectOptions): ManagedConnec
|
|
|
52
54
|
let connectError = "";
|
|
53
55
|
|
|
54
56
|
/*
|
|
55
|
-
* Offer the keyless path when the proxy
|
|
56
|
-
*
|
|
57
|
-
*
|
|
57
|
+
* Offer the keyless path when the proxy is managed and the platform can run the hosted OAuth flow
|
|
58
|
+
* (the PAL seam — desktop shells can implement cfConnect later). Guarded on hasPlatform() because
|
|
59
|
+
* gates can render from modules that load before registration.
|
|
60
|
+
*
|
|
61
|
+
* `configured` alone does NOT withdraw the offer any more. A backend can answer `configured: true`
|
|
62
|
+
* over a grant it cannot actually use, and hiding the CTA there left the user with a broken
|
|
63
|
+
* assistant and no button at all — the one state in which reconnecting is the whole fix. So the
|
|
64
|
+
* lapsed code overrides `configured`, while `cf_upstream_error` (also `configured: true`, and
|
|
65
|
+
* carrying no reconnect code) still hides it: an unreachable Cloudflare is not fixed by an OAuth
|
|
66
|
+
* round trip.
|
|
58
67
|
*/
|
|
59
68
|
function canOffer(): boolean {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
if (!isManagedProxy() || !hasPlatform() || !getPlatform().cfConnect) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return !isProxyConfigured() || proxyStateCode() === "cf_reconnect_required";
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
function ensureProbe() {
|
|
66
76
|
ensureProxyProbe(opts.requestRender);
|
|
67
77
|
}
|
|
68
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Act on how the flow ended.
|
|
81
|
+
*
|
|
82
|
+
* Every branch here exists because the old code had ONE: any truthy result was success, so a
|
|
83
|
+
* connect that landed on the same lapsed row re-probed, got the same `cf_reconnect_required`
|
|
84
|
+
* back, and re-rendered a byte-identical CTA. The user clicked Reconnect and observably nothing
|
|
85
|
+
* happened — which is the half of the outage that made the other half impossible to diagnose.
|
|
86
|
+
*
|
|
87
|
+
* @param {CfConnectOutcome | null} outcome - Null where the platform had no DOM to open a popup
|
|
88
|
+
* in.
|
|
89
|
+
*/
|
|
90
|
+
async function settle(outcome: CfConnectOutcome | null): Promise<void> {
|
|
91
|
+
/* A blocked popup turns into a full-page redirect, and null is a host with nowhere to open one:
|
|
92
|
+
in both the document is on its way out, so an apology would be the last thing painted before
|
|
93
|
+
it goes. Cancellation is silent for the opposite reason — the user already knows. */
|
|
94
|
+
if (!outcome || outcome.status === "redirect" || outcome.status === "canceled") {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (outcome.status === "timeout") {
|
|
98
|
+
connectError =
|
|
99
|
+
"The Cloudflare window didn't finish. Sign in there, then reconnect — nothing was changed.";
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!outcome.connection.accountId) {
|
|
103
|
+
/* Lazily, because the picker pulls the dialog layer in and this module is imported by every
|
|
104
|
+
credentials gate — including ones that render before layers are bound. */
|
|
105
|
+
const { openCfAccountPicker } = await import("./cf-account-picker");
|
|
106
|
+
if (!(await openCfAccountPicker())) {
|
|
107
|
+
connectError =
|
|
108
|
+
"Cloudflare is connected, but no account is chosen yet — pick one to finish setting up the assistant.";
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Re-probe: /models flips to configured once the connection lands, opening the gate.
|
|
113
|
+
resetModelCache();
|
|
114
|
+
await fetchAvailableModels({ force: true });
|
|
115
|
+
/* And then CHECK, because a connect the backend does not honour must not look like one that
|
|
116
|
+
worked. This is the only place that can tell the difference. */
|
|
117
|
+
if (!isProxyConfigured() || proxyStateCode() === "cf_reconnect_required") {
|
|
118
|
+
connectError =
|
|
119
|
+
"Cloudflare connected, but the assistant backend still reports the connection as unusable. " +
|
|
120
|
+
"Try reconnecting, or use your own API key below.";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
69
124
|
async function connect() {
|
|
70
125
|
if (busy) {
|
|
71
126
|
return;
|
|
@@ -74,13 +129,7 @@ export function createManagedConnect(opts: ManagedConnectOptions): ManagedConnec
|
|
|
74
129
|
connectError = "";
|
|
75
130
|
opts.requestRender();
|
|
76
131
|
try {
|
|
77
|
-
|
|
78
|
-
if (connection) {
|
|
79
|
-
// Re-probe: /models flips to configured once the connection lands, opening the gate.
|
|
80
|
-
await fetchAvailableModels({ force: true });
|
|
81
|
-
} else {
|
|
82
|
-
connectError = "Cloudflare connection was not completed.";
|
|
83
|
-
}
|
|
132
|
+
await settle((await getPlatform().cfConnect?.()) ?? null);
|
|
84
133
|
} catch (error) {
|
|
85
134
|
connectError = error instanceof Error ? error.message : String(error);
|
|
86
135
|
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
aiConnection,
|
|
27
27
|
cachedModels,
|
|
28
28
|
fetchAvailableModels,
|
|
29
|
+
modelToolSupport,
|
|
29
30
|
preferredModel,
|
|
30
31
|
} from "../services/ai-models";
|
|
31
32
|
import { setModel } from "../services/ai-settings";
|
|
@@ -39,6 +40,21 @@ export const RETRY_MODELS = "__retry_models__";
|
|
|
39
40
|
/** The `sp-menu-item` value of the disabled placeholder shown during the first fetch. */
|
|
40
41
|
export const LOADING_MODELS = "__loading__";
|
|
41
42
|
|
|
43
|
+
/** What a listed model's row says when the backend reported it cannot call tools. */
|
|
44
|
+
export const NO_TOOLS_SUFFIX = " — no tools";
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A model's row label: its name, plus a note when it is known to be chat-only.
|
|
48
|
+
*
|
|
49
|
+
* Labelled, never filtered or disabled. Choosing a chat-only model to ask questions of is a
|
|
50
|
+
* legitimate thing to do, and a picker that hides half a managed catalogue would be reporting a
|
|
51
|
+
* capability gap as an outage. Only `toolSupport === false` is labelled — `undefined` is a BYOK
|
|
52
|
+
* provider saying nothing, and a suffix there would be an invention.
|
|
53
|
+
*/
|
|
54
|
+
function itemLabel(model: { id: string; name: string }): string {
|
|
55
|
+
return modelToolSupport(model.id) === false ? `${model.name}${NO_TOOLS_SUFFIX}` : model.name;
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
export interface ModelPickerOptions {
|
|
43
59
|
/** Host re-render scheduler — called whenever a fetch settles. */
|
|
44
60
|
requestRender: () => void;
|
|
@@ -66,6 +82,12 @@ export interface ModelPicker {
|
|
|
66
82
|
isLoading: () => boolean;
|
|
67
83
|
/** The last fetch's failure, or `""`. */
|
|
68
84
|
error: () => string;
|
|
85
|
+
/**
|
|
86
|
+
* Whether the selected model is known NOT to support tools — for a host that wants to say so.
|
|
87
|
+
*
|
|
88
|
+
* `false` while the backend has no opinion, so a host reads it as "warn" rather than as "block".
|
|
89
|
+
*/
|
|
90
|
+
selectedLacksTools: () => boolean;
|
|
69
91
|
}
|
|
70
92
|
|
|
71
93
|
/**
|
|
@@ -143,7 +165,7 @@ export function createModelPicker(opts: ModelPickerOptions): ModelPicker {
|
|
|
143
165
|
.value=${live(current)}
|
|
144
166
|
@change=${onPickerChange}
|
|
145
167
|
>
|
|
146
|
-
${items.map((m) => html`<sp-menu-item value=${m.id}>${m
|
|
168
|
+
${items.map((m) => html`<sp-menu-item value=${m.id}>${itemLabel(m)}</sp-menu-item>`)}
|
|
147
169
|
${
|
|
148
170
|
loading
|
|
149
171
|
? html`<sp-menu-item disabled value=${LOADING_MODELS}>Loading models…</sp-menu-item>`
|
|
@@ -158,5 +180,10 @@ export function createModelPicker(opts: ModelPickerOptions): ModelPicker {
|
|
|
158
180
|
`;
|
|
159
181
|
}
|
|
160
182
|
|
|
161
|
-
return {
|
|
183
|
+
return {
|
|
184
|
+
error: () => failure,
|
|
185
|
+
isLoading: () => loading,
|
|
186
|
+
render,
|
|
187
|
+
selectedLacksTools: () => modelToolSupport(getModel()) === false,
|
|
188
|
+
};
|
|
162
189
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Cf-account-picker.ts — "which Cloudflare account?", the question nothing in Studio asked.
|
|
4
|
+
*
|
|
5
|
+
* A Cloudflare grant can reach several accounts, and the broker stores exactly one id per
|
|
6
|
+
* connection. Until one is chosen every Cloudflare-backed call answers `cf_account_required`, so a
|
|
7
|
+
* multi-account user finished the hosted OAuth flow and landed in a state that reads as connected
|
|
8
|
+
* and behaves as broken: the assistant 401s, publishing 401s, and no surface says why. The broker
|
|
9
|
+
* has had `/cf/accounts` and `/cf/select-account` the whole time; this is the first caller.
|
|
10
|
+
*
|
|
11
|
+
* ONE picker, invoked wherever a connect resolves account-less — the assistant's managed-connect
|
|
12
|
+
* CTA, the publish panel, and a button on the Preferences row. A second picker per surface is how
|
|
13
|
+
* three surfaces end up disagreeing about what "connected" means.
|
|
14
|
+
*
|
|
15
|
+
* Cloud-only by construction: the PAL members it drives are the brokered ones, so a platform
|
|
16
|
+
* holding a pasted API token (desktop, dev server) keeps its own local account field and this
|
|
17
|
+
* resolves null without opening anything.
|
|
18
|
+
*
|
|
19
|
+
* @docs studio/ai
|
|
20
|
+
* @license MIT
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { html, render as litRender, nothing } from "lit-html";
|
|
24
|
+
import { ref } from "lit-html/directives/ref.js";
|
|
25
|
+
import type { TemplateResult } from "lit-html";
|
|
26
|
+
import { getPlatform, hasPlatform } from "../platform";
|
|
27
|
+
import { notifyCredentialsChanged } from "../settings/preferences-accounts";
|
|
28
|
+
import { showDialog } from "./layers";
|
|
29
|
+
import { overlayRegion } from "./regions";
|
|
30
|
+
import type { CfAccountSummary } from "../types";
|
|
31
|
+
|
|
32
|
+
/** Whether this platform brokers the connection, and so can be asked which accounts it reaches. */
|
|
33
|
+
function canPick(): boolean {
|
|
34
|
+
if (!hasPlatform()) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const platform = getPlatform();
|
|
38
|
+
return Boolean(platform.cfAccounts && platform.cfSelectAccount);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Ask which Cloudflare account this connection should use, and store the answer.
|
|
43
|
+
*
|
|
44
|
+
* Resolves the chosen account, or null — dismissed, or a platform that does not broker connections
|
|
45
|
+
* at all. A caller treats null as "still unusable" rather than as a failure: nothing was changed,
|
|
46
|
+
* and the surface that opened this is the one that knows what to say about it.
|
|
47
|
+
*
|
|
48
|
+
* @returns {Promise<CfAccountSummary | null>}
|
|
49
|
+
*/
|
|
50
|
+
export function openCfAccountPicker(): Promise<CfAccountSummary | null> {
|
|
51
|
+
if (!canPick()) {
|
|
52
|
+
return Promise.resolve(null);
|
|
53
|
+
}
|
|
54
|
+
return showDialog<CfAccountSummary | null>(
|
|
55
|
+
(done) => {
|
|
56
|
+
let accounts: CfAccountSummary[] = [];
|
|
57
|
+
let loading = true;
|
|
58
|
+
/** The last refusal — a listing that failed, or an account the broker would not accept. */
|
|
59
|
+
let failure = "";
|
|
60
|
+
/** The id being committed, so its own row can say so rather than the whole list going grey. */
|
|
61
|
+
let choosing = "";
|
|
62
|
+
let settled = false;
|
|
63
|
+
let wrapperEl: HTMLElement | null = null;
|
|
64
|
+
|
|
65
|
+
function finish(account: CfAccountSummary | null): void {
|
|
66
|
+
settled = true;
|
|
67
|
+
done(account);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function repaint(): void {
|
|
71
|
+
/* Resolved lazily, and only while the dialog is still up: `done` releases the slot, so a
|
|
72
|
+
late `load()` landing after a dismissal would otherwise paint into a detached host. */
|
|
73
|
+
const host = settled ? null : wrapperEl?.parentElement;
|
|
74
|
+
if (host) {
|
|
75
|
+
litRender(build(), host);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function load(): Promise<void> {
|
|
80
|
+
loading = true;
|
|
81
|
+
failure = "";
|
|
82
|
+
repaint();
|
|
83
|
+
try {
|
|
84
|
+
accounts = (await getPlatform().cfAccounts?.()) ?? [];
|
|
85
|
+
} catch (error) {
|
|
86
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
87
|
+
}
|
|
88
|
+
loading = false;
|
|
89
|
+
repaint();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function choose(account: CfAccountSummary): Promise<void> {
|
|
93
|
+
choosing = account.id;
|
|
94
|
+
failure = "";
|
|
95
|
+
repaint();
|
|
96
|
+
try {
|
|
97
|
+
await getPlatform().cfSelectAccount?.({ id: account.id, name: account.name });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
100
|
+
choosing = "";
|
|
101
|
+
repaint();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
/* The stored account is a credential like any other: the assistant's gate, the publish
|
|
105
|
+
panel and the Preferences row all re-read from the same announcement. */
|
|
106
|
+
notifyCredentialsChanged();
|
|
107
|
+
finish(account);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function listTpl(): TemplateResult | typeof nothing {
|
|
111
|
+
if (loading) {
|
|
112
|
+
return html`<p class="cf-account-picker-empty">Reading your Cloudflare accounts…</p>`;
|
|
113
|
+
}
|
|
114
|
+
/* Nothing to show and a reason why. The retry is offered only here: a commit that was
|
|
115
|
+
refused (below) keeps the list, because listing again is not what would fix it. */
|
|
116
|
+
if (accounts.length === 0) {
|
|
117
|
+
return html`
|
|
118
|
+
<p class="cf-account-picker-empty">
|
|
119
|
+
${
|
|
120
|
+
failure
|
|
121
|
+
? `Cloudflare could not be reached: ${failure}`
|
|
122
|
+
: "This Cloudflare login reaches no accounts. Create one at dash.cloudflare.com, then try again."
|
|
123
|
+
}
|
|
124
|
+
</p>
|
|
125
|
+
${
|
|
126
|
+
failure
|
|
127
|
+
? html`<sp-button
|
|
128
|
+
size="s"
|
|
129
|
+
treatment="outline"
|
|
130
|
+
@click=${() => {
|
|
131
|
+
void load();
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
Try again
|
|
135
|
+
</sp-button>`
|
|
136
|
+
: nothing
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
/* The row shape is Preferences › Accounts', deliberately reused: this IS an account list,
|
|
141
|
+
and a second stylesheet entry for the same shape is how two lists drift apart. */
|
|
142
|
+
return html`
|
|
143
|
+
${failure ? html`<p class="cf-account-picker-error">${failure}</p>` : nothing}
|
|
144
|
+
<div class="prefs-accounts">
|
|
145
|
+
${accounts.map(
|
|
146
|
+
(account) => html`
|
|
147
|
+
<div class="prefs-account" data-account=${account.id}>
|
|
148
|
+
<div class="prefs-account-text">
|
|
149
|
+
<span class="prefs-account-label">${account.name}</span>
|
|
150
|
+
<span class="prefs-account-detail">${account.id}</span>
|
|
151
|
+
</div>
|
|
152
|
+
<sp-button
|
|
153
|
+
size="s"
|
|
154
|
+
variant="accent"
|
|
155
|
+
?disabled=${choosing !== ""}
|
|
156
|
+
@click=${() => {
|
|
157
|
+
void choose(account);
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
${choosing === account.id ? "Selecting…" : "Use this account"}
|
|
161
|
+
</sp-button>
|
|
162
|
+
</div>
|
|
163
|
+
`,
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function build(): TemplateResult {
|
|
170
|
+
return html`
|
|
171
|
+
<sp-dialog-wrapper
|
|
172
|
+
open
|
|
173
|
+
underlay
|
|
174
|
+
headline="Choose a Cloudflare account"
|
|
175
|
+
cancel-label="Cancel"
|
|
176
|
+
@cancel=${() => finish(null)}
|
|
177
|
+
@close=${() => finish(null)}
|
|
178
|
+
${ref((el?: Element) => {
|
|
179
|
+
if (el) {
|
|
180
|
+
wrapperEl = el as HTMLElement;
|
|
181
|
+
}
|
|
182
|
+
})}
|
|
183
|
+
>
|
|
184
|
+
<div class="cf-account-picker">
|
|
185
|
+
<p class="cf-account-picker-lede">
|
|
186
|
+
Your Cloudflare login reaches more than one account. Publishing and the assistant
|
|
187
|
+
both run against the one you pick; you can change it in Preferences › Accounts.
|
|
188
|
+
</p>
|
|
189
|
+
${listTpl()}
|
|
190
|
+
</div>
|
|
191
|
+
</sp-dialog-wrapper>
|
|
192
|
+
`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
void load();
|
|
196
|
+
return build();
|
|
197
|
+
},
|
|
198
|
+
{ region: overlayRegion("dialog", "cf-accounts") },
|
|
199
|
+
);
|
|
200
|
+
}
|
package/styles/shell.css
CHANGED
|
@@ -800,6 +800,13 @@
|
|
|
800
800
|
max-width: 140px;
|
|
801
801
|
min-width: 0;
|
|
802
802
|
}
|
|
803
|
+
/* The chat-only-model note, under the picker that caused it. Advisory weight on purpose: the
|
|
804
|
+
assistant still answers, it just cannot edit. */
|
|
805
|
+
.ai-composer-note {
|
|
806
|
+
color: var(--fg-dim);
|
|
807
|
+
font-size: var(--spectrum-font-size-50, 11px);
|
|
808
|
+
line-height: 1.3;
|
|
809
|
+
}
|
|
803
810
|
/* Sessions list */
|
|
804
811
|
.ai-sessions {
|
|
805
812
|
flex: 1 1 0;
|
|
@@ -1375,6 +1382,35 @@
|
|
|
1375
1382
|
font-size: var(--spectrum-font-size-50, 11px);
|
|
1376
1383
|
color: var(--fg-dim);
|
|
1377
1384
|
}
|
|
1385
|
+
/* The account picker borrows `.prefs-account` for its rows on purpose — it is the same object in a
|
|
1386
|
+
different frame, and a second row style would drift from this one. Only its own chrome is here. */
|
|
1387
|
+
.cf-account-picker {
|
|
1388
|
+
display: flex;
|
|
1389
|
+
flex-direction: column;
|
|
1390
|
+
gap: 12px;
|
|
1391
|
+
min-width: 320px;
|
|
1392
|
+
}
|
|
1393
|
+
.cf-account-picker-lede {
|
|
1394
|
+
margin: 0;
|
|
1395
|
+
color: var(--fg-dim);
|
|
1396
|
+
font-size: var(--spectrum-font-size-75, 12px);
|
|
1397
|
+
}
|
|
1398
|
+
.cf-account-picker-empty {
|
|
1399
|
+
margin: 0;
|
|
1400
|
+
color: var(--fg-dim);
|
|
1401
|
+
font-size: var(--spectrum-font-size-75, 12px);
|
|
1402
|
+
}
|
|
1403
|
+
.cf-account-picker-error {
|
|
1404
|
+
margin: 0;
|
|
1405
|
+
color: var(--danger);
|
|
1406
|
+
font-size: var(--spectrum-font-size-75, 12px);
|
|
1407
|
+
}
|
|
1408
|
+
.prefs-account-actions {
|
|
1409
|
+
display: flex;
|
|
1410
|
+
align-items: center;
|
|
1411
|
+
gap: 8px;
|
|
1412
|
+
flex-shrink: 0;
|
|
1413
|
+
}
|
|
1378
1414
|
.prefs-keys {
|
|
1379
1415
|
display: flex;
|
|
1380
1416
|
flex-direction: column;
|