dsh-plugin-subscription-oauth 0.8.13 → 0.8.15
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/lib/auth/provider-controller.js +21 -17
- package/lib/auth/tokens.js +1 -1
- package/lib/client/FastModeButton.d.ts +50 -0
- package/lib/client/FastModeButton.js +94 -0
- package/lib/client/UsageChip.js +2 -1
- package/lib/client/index.d.ts +14 -1
- package/lib/client/index.js +46 -2
- package/lib/client/styles.d.ts +8 -0
- package/lib/client/styles.js +61 -0
- package/lib/client.js +205 -3
- package/lib/client.js.map +1 -1
- package/lib/index.js +2 -0
- package/lib/protocol.d.ts +13 -1
- package/lib/providers/antigravity-catalog.d.ts +29 -9
- package/lib/providers/antigravity-catalog.js +212 -56
- package/lib/providers/antigravity.d.ts +2 -0
- package/lib/providers/antigravity.js +25 -3
- package/lib/providers/ark-provider.js +4 -0
- package/lib/providers/ark.d.ts +4 -0
- package/lib/providers/ark.js +16 -2
- package/lib/providers/glm-provider.js +4 -0
- package/lib/providers/glm.d.ts +4 -0
- package/lib/providers/glm.js +16 -2
- package/lib/providers/grok.d.ts +109 -3
- package/lib/providers/grok.js +258 -34
- package/lib/providers/kimi-provider.js +9 -0
- package/lib/providers/kimi.d.ts +28 -0
- package/lib/providers/kimi.js +89 -9
- package/lib/translate/antigravity.js +3 -3
- package/package.json +1 -1
|
@@ -86,16 +86,17 @@ export class ProviderController {
|
|
|
86
86
|
}
|
|
87
87
|
/** Current state of the route. */
|
|
88
88
|
async status() {
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// and a failure must never make `status` itself fail.
|
|
93
|
-
await this.refreshIfLapsing();
|
|
89
|
+
// A status read must never trigger a network request: the settings page polls it,
|
|
90
|
+
// and a refresh here would mean every poll is a token call. The token is refreshed
|
|
91
|
+
// lazily when a request actually needs it (or by discover/usage below).
|
|
94
92
|
const accounts = await this.options.store.list();
|
|
95
93
|
const defaultAccount = await this.options.store.defaultAccount();
|
|
96
94
|
const pending = this.options.flow.pending();
|
|
97
95
|
const keys = Object.keys(accounts);
|
|
98
96
|
const spec = this.options.spec;
|
|
97
|
+
// A stored token within its life is reported as fresh; a lapsed one reads as expired
|
|
98
|
+
// until a real request refreshes it. This is honest (the stored expiry is what we
|
|
99
|
+
// know) and avoids a network call on every status poll.
|
|
99
100
|
return {
|
|
100
101
|
provider: spec.id,
|
|
101
102
|
displayName: spec.displayName,
|
|
@@ -147,7 +148,7 @@ export class ProviderController {
|
|
|
147
148
|
}
|
|
148
149
|
catch (error) {
|
|
149
150
|
this.lastRefreshError = error;
|
|
150
|
-
if (isReauthRequired(error)) {
|
|
151
|
+
if (isReauthRequired(error) || isDeadGrant(messageOf(error))) {
|
|
151
152
|
this.lastError = `${this.displayName} sign-in has lapsed; connect again`;
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -226,6 +227,8 @@ export class ProviderController {
|
|
|
226
227
|
await this.options.store.save(account ?? accountKeyOf(settled), settled);
|
|
227
228
|
this.lastError = undefined;
|
|
228
229
|
// A fresh login can reveal models this build has not seen yet.
|
|
230
|
+
// Discovery uses the stored token directly (no resolve), so it never opens
|
|
231
|
+
// the browser from here.
|
|
229
232
|
await this.discover(true);
|
|
230
233
|
}
|
|
231
234
|
catch (error) {
|
|
@@ -297,21 +300,22 @@ export class ProviderController {
|
|
|
297
300
|
// every startup would be noise.
|
|
298
301
|
if (!await this.options.store.hasAny())
|
|
299
302
|
return this.status();
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
303
|
+
// Discovery resolves tokens to read the model list, but a resolve must never
|
|
304
|
+
// open the browser: a transient network failure, or a token the endpoint refuses
|
|
305
|
+
// while still being within its stored expiry, is not a signal to start a login.
|
|
306
|
+
// Only an explicit user action (the connect button) opens the consent page.
|
|
304
307
|
const discovered = [];
|
|
305
308
|
for (const account of Object.keys(await this.options.store.list())) {
|
|
306
|
-
let
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
309
|
+
let session = await this.options.store.get(account);
|
|
310
|
+
if (session === undefined)
|
|
311
|
+
continue;
|
|
312
|
+
// A token that is still within its stored expiry is used as-is; a lapsed one
|
|
313
|
+
// is a silent skip (the route works, the catalog is just not refreshed).
|
|
314
|
+
if (session.expiresAt <= Date.now()) {
|
|
315
|
+
this.options.onWarn?.(`${spec.id} model discovery for ${account} skipped: token expired`);
|
|
312
316
|
continue;
|
|
313
317
|
}
|
|
314
|
-
const ids = await spec.discover(accessToken);
|
|
318
|
+
const ids = await spec.discover(session.accessToken);
|
|
315
319
|
if (ids !== undefined) {
|
|
316
320
|
for (const id of ids)
|
|
317
321
|
if (!discovered.includes(id))
|
package/lib/auth/tokens.js
CHANGED
|
@@ -55,7 +55,7 @@ export class TokenManager {
|
|
|
55
55
|
: `${this.options.displayName} account ${account} is not signed in`, REAUTH_REQUIRED);
|
|
56
56
|
}
|
|
57
57
|
const { account: key, session } = found;
|
|
58
|
-
if (!force && Date.now()
|
|
58
|
+
if (!force && Date.now() < session.expiresAt) {
|
|
59
59
|
return { account: key, accessToken: session.accessToken, expiresAt: session.expiresAt };
|
|
60
60
|
}
|
|
61
61
|
const refreshed = await this.refresh(key, session);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The composer's quick-mode button.
|
|
3
|
+
*
|
|
4
|
+
* The route's own fast/lightspeed variant is not a row in the model picker: it shares its
|
|
5
|
+
* base model's visibility and is reached from here. The composer renders
|
|
6
|
+
* `conversation.input.right` immediately before its model seat, so this button sits at
|
|
7
|
+
* the model selector's left — the control a reader looks for when they want the same
|
|
8
|
+
* model, faster.
|
|
9
|
+
*
|
|
10
|
+
* Off, the selection is the base model and pressing the button selects the fast variant.
|
|
11
|
+
* On, the selection *is* the fast variant, so the button reports active and pressing it
|
|
12
|
+
* returns to the base — which is why `model-variants` answers from both sides of the pair
|
|
13
|
+
* rather than only from the base id.
|
|
14
|
+
*
|
|
15
|
+
* The button renders nothing when the model has no fast variant, when the route is not
|
|
16
|
+
* one this plugin serves, or when the host exposes no way to change the selection: an
|
|
17
|
+
* absent control is honest, a disabled one is noise.
|
|
18
|
+
*
|
|
19
|
+
* @module dsh-plugin-subscription-oauth/client/FastModeButton
|
|
20
|
+
*/
|
|
21
|
+
import type { ReactNode } from 'react';
|
|
22
|
+
import type { SessionStandardProps } from '@deepseek-ai/dsh-client-ui-slots';
|
|
23
|
+
import type { SubscriptionsCall } from './subscriptions-rpc.js';
|
|
24
|
+
/** Injected props the button is registered with. */
|
|
25
|
+
export interface FastModeButtonInjected {
|
|
26
|
+
/** The plugin's own RPC caller, for the one `model-variants` read. */
|
|
27
|
+
call: SubscriptionsCall;
|
|
28
|
+
/** Whether the provider is one this plugin serves. */
|
|
29
|
+
isOwnProvider: (provider: string) => boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The session's model directory, when the host exposes one.
|
|
32
|
+
*
|
|
33
|
+
* Written when the button switches between a model and its fast variant. Absent on a
|
|
34
|
+
* host without it, in which case the button renders nothing.
|
|
35
|
+
*/
|
|
36
|
+
modelDirectory?: {
|
|
37
|
+
/** Select one model; resolves when the picker has accepted it. */
|
|
38
|
+
select?: (selection: {
|
|
39
|
+
provider: string;
|
|
40
|
+
model: string;
|
|
41
|
+
}) => Promise<unknown>;
|
|
42
|
+
} | undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The quick-mode control, for the composer's row of standard controls.
|
|
46
|
+
*
|
|
47
|
+
* @param props - injected RPC caller, directory, and the session standard kit.
|
|
48
|
+
* @returns the button, or null when this model has no fast variant.
|
|
49
|
+
*/
|
|
50
|
+
export declare function FastModeButton({ call, isOwnProvider, modelDirectory, useProjection }: FastModeButtonInjected & SessionStandardProps): ReactNode;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The composer's quick-mode button.
|
|
4
|
+
*
|
|
5
|
+
* The route's own fast/lightspeed variant is not a row in the model picker: it shares its
|
|
6
|
+
* base model's visibility and is reached from here. The composer renders
|
|
7
|
+
* `conversation.input.right` immediately before its model seat, so this button sits at
|
|
8
|
+
* the model selector's left — the control a reader looks for when they want the same
|
|
9
|
+
* model, faster.
|
|
10
|
+
*
|
|
11
|
+
* Off, the selection is the base model and pressing the button selects the fast variant.
|
|
12
|
+
* On, the selection *is* the fast variant, so the button reports active and pressing it
|
|
13
|
+
* returns to the base — which is why `model-variants` answers from both sides of the pair
|
|
14
|
+
* rather than only from the base id.
|
|
15
|
+
*
|
|
16
|
+
* The button renders nothing when the model has no fast variant, when the route is not
|
|
17
|
+
* one this plugin serves, or when the host exposes no way to change the selection: an
|
|
18
|
+
* absent control is honest, a disabled one is noise.
|
|
19
|
+
*
|
|
20
|
+
* @module dsh-plugin-subscription-oauth/client/FastModeButton
|
|
21
|
+
*/
|
|
22
|
+
import { useEffect, useState } from 'react';
|
|
23
|
+
import { providerOf } from './UsageChip.js';
|
|
24
|
+
import { fastModeCss } from './styles.js';
|
|
25
|
+
/**
|
|
26
|
+
* The quick-mode control, for the composer's row of standard controls.
|
|
27
|
+
*
|
|
28
|
+
* @param props - injected RPC caller, directory, and the session standard kit.
|
|
29
|
+
* @returns the button, or null when this model has no fast variant.
|
|
30
|
+
*/
|
|
31
|
+
export function FastModeButton({ call, isOwnProvider, modelDirectory, useProjection }) {
|
|
32
|
+
const [pair, setPair] = useState(undefined);
|
|
33
|
+
// A switch that the host refuses must not look like a dead button: the reason is kept
|
|
34
|
+
// and shown, because the only alternative is a control that silently does nothing.
|
|
35
|
+
const [failure, setFailure] = useState(undefined);
|
|
36
|
+
const [pending, setPending] = useState(false);
|
|
37
|
+
const selection = useProjection('modelSelection', (value) => value);
|
|
38
|
+
const provider = providerOf(selection);
|
|
39
|
+
const model = selection?.next?.model ?? selection?.lastUsed?.model;
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (provider === undefined || model === undefined || !isOwnProvider(provider)) {
|
|
42
|
+
setPair(undefined);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let live = true;
|
|
46
|
+
void call('model-variants', { provider, id: model })
|
|
47
|
+
.then((result) => {
|
|
48
|
+
if (!live)
|
|
49
|
+
return;
|
|
50
|
+
setPair(result?.fast === undefined
|
|
51
|
+
? undefined
|
|
52
|
+
: { wireId: result.fast, ...result.fastBase === undefined ? {} : { baseId: result.fastBase } });
|
|
53
|
+
})
|
|
54
|
+
.catch(() => { if (live)
|
|
55
|
+
setPair(undefined); });
|
|
56
|
+
return () => { live = false; };
|
|
57
|
+
}, [provider, model, isOwnProvider, call]);
|
|
58
|
+
// A switch that landed clears the reason, so the marker follows the truth on screen.
|
|
59
|
+
useEffect(() => { setFailure(undefined); }, [model]);
|
|
60
|
+
if (pair === undefined || provider === undefined || modelDirectory?.select === undefined)
|
|
61
|
+
return null;
|
|
62
|
+
const active = pair.wireId === model;
|
|
63
|
+
const target = active ? pair.baseId : pair.wireId;
|
|
64
|
+
// Active without a base to return to would be a control that cannot switch anything off.
|
|
65
|
+
if (target === undefined || target === model)
|
|
66
|
+
return null;
|
|
67
|
+
const base = active
|
|
68
|
+
? 'Fast mode is on; press to go back to the standard model'
|
|
69
|
+
: 'Fast mode: switch to the fast variant of this model';
|
|
70
|
+
const title = failure === undefined
|
|
71
|
+
? base
|
|
72
|
+
: `${base}\nThe switch was refused — ${`${failure.code ?? ''} ${failure.message}`.trim()}`;
|
|
73
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { children: fastModeCss }), _jsxs("button", { type: "button", className: "dsh-subscription-fast-mode", "data-active": active, "data-pending": pending, "data-failed": failure !== undefined, "aria-pressed": active, "aria-busy": pending, title: title, "aria-label": title, onClick: (event) => {
|
|
74
|
+
event.stopPropagation();
|
|
75
|
+
setPending(true);
|
|
76
|
+
setFailure(undefined);
|
|
77
|
+
void Promise.resolve(modelDirectory.select?.({ provider, model: target }))
|
|
78
|
+
.then((result) => {
|
|
79
|
+
// The directory reports its own outcome: a refused selection carries why.
|
|
80
|
+
const outcome = result;
|
|
81
|
+
if (outcome?.ok !== false)
|
|
82
|
+
return;
|
|
83
|
+
const code = outcome.error?.code;
|
|
84
|
+
const message = outcome.error?.message ?? code ?? 'the host refused the selection';
|
|
85
|
+
setFailure({ ...code === undefined ? {} : { code }, message });
|
|
86
|
+
console.warn(`[subscription-oauth] fast mode ${provider}/${target} refused:`, outcome.error);
|
|
87
|
+
})
|
|
88
|
+
.catch((error) => {
|
|
89
|
+
setFailure({ message: error instanceof Error ? error.message : String(error) });
|
|
90
|
+
console.warn(`[subscription-oauth] fast mode ${provider}/${target} failed:`, error);
|
|
91
|
+
})
|
|
92
|
+
.finally(() => { setPending(false); });
|
|
93
|
+
}, children: [_jsx("svg", { className: "dsh-subscription-fast-mode-bolt", viewBox: "0 0 24 24", width: "16", height: "16", "aria-hidden": "true", fill: "none", children: _jsx("path", { d: "M13 2 3.6 13.2a1.2 1.2 0 0 0 .92 1.98H11l-.86 6.42a1.2 1.2 0 0 0 2.2.7L21.4 10.6a1.2 1.2 0 0 0-.92-1.98H13l.86-6.42A1.2 1.2 0 0 0 13 2z", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }) }), failure !== undefined && (_jsx("span", { className: "dsh-subscription-fast-mode-reason", children: failure.code ?? failure.message }))] })] }));
|
|
94
|
+
}
|
package/lib/client/UsageChip.js
CHANGED
|
@@ -299,7 +299,8 @@ export function UsageChip({ call, t, isOwnProvider, accountBalance, ccSwitchBala
|
|
|
299
299
|
const lastOwn = useRef(undefined);
|
|
300
300
|
// A projection read, so the chip follows the picker: changing the model re-renders it
|
|
301
301
|
// with the new provider instead of waiting for the next unrelated render.
|
|
302
|
-
const
|
|
302
|
+
const selection = useProjection('modelSelection', (value) => value);
|
|
303
|
+
const selected = providerOf(selection);
|
|
303
304
|
// A non-subscription provider has no usage windows to meter, so the chip shows its
|
|
304
305
|
// balance instead, read by cc-switch's query. The host's account (deepseek-account)
|
|
305
306
|
// has its own balance source and takes that over cc-switch; a subscription this plugin
|
package/lib/client/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
12
12
|
import type { SubscriptionsKey } from './locales.js';
|
|
13
13
|
export type { SubscriptionsSectionInjected, SubscriptionsSectionProps } from './SubscriptionsSection.js';
|
|
14
|
+
export type { FastModeButtonInjected } from './FastModeButton.js';
|
|
14
15
|
export type { UsageChipInjected } from './UsageChip.js';
|
|
15
16
|
export type { SubscriptionsKey } from './locales.js';
|
|
16
17
|
export { createSubscriptionsCall, SubscriptionsRpcError } from './subscriptions-rpc.js';
|
|
@@ -33,6 +34,16 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
33
34
|
kind: 'list';
|
|
34
35
|
scope: 'session';
|
|
35
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* The composer's row of compact controls, rendered immediately before its model seat.
|
|
39
|
+
*
|
|
40
|
+
* The shell declares it as a session-scoped list; this plugin's quick-mode button is
|
|
41
|
+
* one entry, which is what puts it at the model selector's left.
|
|
42
|
+
*/
|
|
43
|
+
'conversation.input.right': {
|
|
44
|
+
kind: 'list';
|
|
45
|
+
scope: 'session';
|
|
46
|
+
};
|
|
36
47
|
}
|
|
37
48
|
/**
|
|
38
49
|
* The session standard kit, of which this plugin reads one member.
|
|
@@ -49,7 +60,9 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
49
60
|
}
|
|
50
61
|
/**
|
|
51
62
|
* Required services: `slots` carries the registration seat, `connection` the
|
|
52
|
-
* RPC caller,
|
|
63
|
+
* RPC caller, `locale` the copy dictionaries, and `modelDirectories` the picker's
|
|
64
|
+
* per-session model directory (written when the quick-mode button routes to a fast
|
|
65
|
+
* variant).
|
|
53
66
|
*/
|
|
54
67
|
export declare const inject: string[];
|
|
55
68
|
/**
|
package/lib/client/index.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @module dsh-plugin-subscription-oauth/client
|
|
10
10
|
*/
|
|
11
11
|
import { SubscriptionsSection } from './SubscriptionsSection.js';
|
|
12
|
+
import { FastModeButton } from './FastModeButton.js';
|
|
12
13
|
import { UsageChip } from './UsageChip.js';
|
|
13
14
|
import { createSubscriptionsCall, createAccountBalanceCall, createCcSwitchBalanceCall } from './subscriptions-rpc.js';
|
|
14
15
|
import { en, zh } from './locales.js';
|
|
@@ -31,11 +32,18 @@ const SECTION_ORDER = 92;
|
|
|
31
32
|
* chip follows them rather than displacing them.
|
|
32
33
|
*/
|
|
33
34
|
const CHIP_ORDER = 10;
|
|
35
|
+
/**
|
|
36
|
+
* Composer-control position: ahead of the shell's own entries in that row, if it ever
|
|
37
|
+
* adds any, so the quick-mode button stays adjacent to the model selector it belongs to.
|
|
38
|
+
*/
|
|
39
|
+
const FAST_MODE_ORDER = 10;
|
|
34
40
|
/**
|
|
35
41
|
* Required services: `slots` carries the registration seat, `connection` the
|
|
36
|
-
* RPC caller,
|
|
42
|
+
* RPC caller, `locale` the copy dictionaries, and `modelDirectories` the picker's
|
|
43
|
+
* per-session model directory (written when the quick-mode button routes to a fast
|
|
44
|
+
* variant).
|
|
37
45
|
*/
|
|
38
|
-
export const inject = ['slots', 'connection', 'locale'];
|
|
46
|
+
export const inject = ['slots', 'connection', 'locale', 'modelDirectories'];
|
|
39
47
|
/**
|
|
40
48
|
* Register the Subscriptions settings section.
|
|
41
49
|
*
|
|
@@ -51,6 +59,9 @@ export function apply(ctx) {
|
|
|
51
59
|
const connection = ctx.get('connection');
|
|
52
60
|
const t = ctx.locale.bind(NS);
|
|
53
61
|
const call = createSubscriptionsCall(connection.rpc);
|
|
62
|
+
// Read once: the resolver is a service, and the per-session directory is resolved at
|
|
63
|
+
// registration time (see `modelDirectoryFor`).
|
|
64
|
+
const modelDirectories = ctx.get('modelDirectories');
|
|
54
65
|
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
55
66
|
name: 'settings.section',
|
|
56
67
|
id: 'subscriptions',
|
|
@@ -75,4 +86,37 @@ export function apply(ctx) {
|
|
|
75
86
|
ccSwitchBalance: createCcSwitchBalanceCall(connection.rpc),
|
|
76
87
|
}),
|
|
77
88
|
}, UsageChip));
|
|
89
|
+
// The composer renders this seat immediately before its model selector, so the
|
|
90
|
+
// quick-mode button lands at the picker's left. It renders nothing unless the current
|
|
91
|
+
// model really has a fast variant, so a route without one shows no control at all.
|
|
92
|
+
ctx.slots.inject('conversation.input.right', () => ctx.slots.register({
|
|
93
|
+
name: 'conversation.input.right',
|
|
94
|
+
id: 'subscription-fast-mode',
|
|
95
|
+
order: FAST_MODE_ORDER,
|
|
96
|
+
inject: (sessionId) => ({
|
|
97
|
+
call,
|
|
98
|
+
isOwnProvider: (provider) => OWN_PROVIDER_IDS.has(provider),
|
|
99
|
+
modelDirectory: modelDirectoryFor(modelDirectories, sessionId),
|
|
100
|
+
}),
|
|
101
|
+
}, FastModeButton));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The session's model directory, in the shape the quick-mode button writes through.
|
|
105
|
+
*
|
|
106
|
+
* Absent when the host exposes no directory for the session, or exposes one that cannot
|
|
107
|
+
* select — in which case the button renders nothing rather than a dead control.
|
|
108
|
+
*
|
|
109
|
+
* @param modelDirectories - the host's directory resolver, when it has one.
|
|
110
|
+
* @param sessionId - the session whose directory is wanted.
|
|
111
|
+
* @returns the reader/writer pair, or undefined.
|
|
112
|
+
*/
|
|
113
|
+
function modelDirectoryFor(modelDirectories, sessionId) {
|
|
114
|
+
if (sessionId === undefined || modelDirectories?.directoryFor === undefined)
|
|
115
|
+
return undefined;
|
|
116
|
+
const directory = modelDirectories.directoryFor(sessionId);
|
|
117
|
+
if (directory?.select === undefined)
|
|
118
|
+
return undefined;
|
|
119
|
+
return {
|
|
120
|
+
select: (selection) => directory.select?.(selection) ?? Promise.resolve(),
|
|
121
|
+
};
|
|
78
122
|
}
|
package/lib/client/styles.d.ts
CHANGED
|
@@ -30,3 +30,11 @@ export declare const subscriptionsCss = "\n.dsh-subscriptions-settings,\n.dsh-su
|
|
|
30
30
|
* define it.
|
|
31
31
|
*/
|
|
32
32
|
export declare const usageChipCss = "\n.dsh-subscription-usage-chip {\n position: relative;\n display: inline-flex;\n align-items: center;\n /* The bar is a centred flex row whose children are this slot's entries followed by the\n shell's context meter, and a list slot renders its entries as bare children (no\n wrapper). So the chip is itself a flex item: a higher order places it after the\n context meter, and no auto margin is set, so it sits immediately beside it rather\n than being pushed to the edge of the bar. */\n order: 1;\n min-width: 0;\n font-family: inherit;\n font-size: 12px;\n line-height: 1;\n}\n.dsh-subscription-usage-chip-trigger {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin: 0;\n padding: 2px 6px;\n border: 0;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font: inherit;\n cursor: pointer;\n}\n.dsh-subscription-usage-chip-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n}\n.dsh-subscription-usage-chip-trigger:focus-visible {\n outline: 2px solid var(--dsw-alias-border-focus, #4d6bfe);\n outline-offset: 1px;\n}\n.dsh-subscription-usage-chip-label {\n overflow: hidden;\n max-width: 6ch;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh-subscription-usage-chip-cell {\n display: inline-flex;\n align-items: baseline;\n gap: 2px;\n}\n.dsh-subscription-usage-chip-window {\n opacity: 0.75;\n}\n.dsh-subscription-usage-chip-value {\n color: var(--dsw-alias-label-primary, #e8e8e8);\n font-variant-numeric: tabular-nums;\n}\n.dsh-subscription-usage-chip-account {\n display: flex;\n flex-direction: column;\n gap: 6px;\n /* A hairline between accounts, so two sign-ins read as two groups not one run. */\n padding-top: 6px;\n border-top: 1px solid var(--dsw-alias-border-secondary, #ffffff14);\n}\n.dsh-subscription-usage-chip-account:first-of-type {\n padding-top: 0;\n border-top: 0;\n}\n.dsh-subscription-usage-chip-panel {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n z-index: 20;\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 180px;\n padding: 12px 14px;\n border: 1px solid var(--dsw-alias-border-secondary, #ffffff1f);\n border-radius: var(--dsw-radius-md, 10px);\n background: var(--dsw-alias-bg-layer-2, #26262a);\n box-shadow: var(--dsw-elevation-prominent, 0 12px 32px #0007);\n color: var(--dsw-alias-label-primary, #e8e8e8);\n white-space: nowrap;\n}\n.dsh-subscription-usage-chip-full {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n}\n.dsh-subscription-usage-chip-meter {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.dsh-subscription-usage-chip-meterHead {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n.dsh-subscription-usage-chip-detail {\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font-variant-numeric: tabular-nums;\n}\n.dsh-subscription-usage-chip-track {\n display: block;\n width: 100%;\n height: 5px;\n border-radius: 999px;\n /* As on the settings meter: an unspent window has to show its track, so the neutral and\n its ring are explicit rather than a layer token that may match the panel. */\n background: #8a8f9826;\n box-shadow: inset 0 0 0 1px #8a8f9833;\n overflow: hidden;\n}\n.dsh-subscription-usage-chip-fill {\n display: block;\n height: 100%;\n background: var(--dsw-alias-state-success-primary, #067647);\n}\n";
|
|
33
|
+
/**
|
|
34
|
+
* The composer's quick-mode button stylesheet.
|
|
35
|
+
*
|
|
36
|
+
* It renders in the composer's own control row — the same row as the model selector —
|
|
37
|
+
* so it borrows that row's geometry: a compact, borderless control that only colours on
|
|
38
|
+
* hover and when the mode is on. Tokens carry fallbacks for hosts that define none.
|
|
39
|
+
*/
|
|
40
|
+
export declare const fastModeCss = "\n.dsh-subscription-fast-mode {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin: 0;\n padding: 3px;\n border: 0;\n border-radius: var(--dsw-radius-sm, 6px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #8a8f98);\n font: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n}\n.dsh-subscription-fast-mode:hover {\n background: var(--dsw-alias-interactive-bg-hover, #ffffff14);\n}\n/* A click has to look like one, even before the selection lands. */\n.dsh-subscription-fast-mode:active {\n background: var(--dsw-alias-interactive-bg-pressed, #ffffff2e);\n}\n.dsh-subscription-fast-mode[data-pending=\"true\"] {\n opacity: 0.7;\n cursor: progress;\n}\n/* A switch the host refused is marked rather than left looking merely off, so a failure\n is visible where it happened; the reason rides the title and the console. */\n.dsh-subscription-fast-mode[data-failed=\"true\"] {\n color: var(--dsw-alias-state-error-primary, #b42318);\n}\n.dsh-subscription-fast-mode:focus-visible {\n outline: 2px solid var(--dsw-alias-border-focus, #4d6bfe);\n outline-offset: 1px;\n}\n/* Off: the bolt is dim, so an available mode reads as an invitation, not a state. */\n.dsh-subscription-fast-mode[data-active=\"false\"] .dsh-subscription-fast-mode-bolt {\n opacity: 0.55;\n}\n/* On: the bolt takes the accent and glows, which is the only thing distinguishing the\n fast variant from its base \u2014 both answer with the same model name. */\n.dsh-subscription-fast-mode[data-active=\"true\"] .dsh-subscription-fast-mode-bolt {\n color: var(--dsw-alias-state-warning-primary, #f5a524);\n filter: drop-shadow(0 0 6px var(--dsw-alias-state-warning-primary, #f5a52455));\n}\n.dsh-subscription-fast-mode-reason {\n max-width: 14ch;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 11px;\n}\n";
|
package/lib/client/styles.js
CHANGED
|
@@ -394,3 +394,64 @@ export const usageChipCss = `
|
|
|
394
394
|
background: var(--dsw-alias-state-success-primary, #067647);
|
|
395
395
|
}
|
|
396
396
|
`;
|
|
397
|
+
/**
|
|
398
|
+
* The composer's quick-mode button stylesheet.
|
|
399
|
+
*
|
|
400
|
+
* It renders in the composer's own control row — the same row as the model selector —
|
|
401
|
+
* so it borrows that row's geometry: a compact, borderless control that only colours on
|
|
402
|
+
* hover and when the mode is on. Tokens carry fallbacks for hosts that define none.
|
|
403
|
+
*/
|
|
404
|
+
export const fastModeCss = `
|
|
405
|
+
.dsh-subscription-fast-mode {
|
|
406
|
+
display: inline-flex;
|
|
407
|
+
align-items: center;
|
|
408
|
+
gap: 4px;
|
|
409
|
+
margin: 0;
|
|
410
|
+
padding: 3px;
|
|
411
|
+
border: 0;
|
|
412
|
+
border-radius: var(--dsw-radius-sm, 6px);
|
|
413
|
+
background: transparent;
|
|
414
|
+
color: var(--dsw-alias-label-secondary, #8a8f98);
|
|
415
|
+
font: inherit;
|
|
416
|
+
font-size: 12px;
|
|
417
|
+
line-height: 1;
|
|
418
|
+
cursor: pointer;
|
|
419
|
+
}
|
|
420
|
+
.dsh-subscription-fast-mode:hover {
|
|
421
|
+
background: var(--dsw-alias-interactive-bg-hover, #ffffff14);
|
|
422
|
+
}
|
|
423
|
+
/* A click has to look like one, even before the selection lands. */
|
|
424
|
+
.dsh-subscription-fast-mode:active {
|
|
425
|
+
background: var(--dsw-alias-interactive-bg-pressed, #ffffff2e);
|
|
426
|
+
}
|
|
427
|
+
.dsh-subscription-fast-mode[data-pending="true"] {
|
|
428
|
+
opacity: 0.7;
|
|
429
|
+
cursor: progress;
|
|
430
|
+
}
|
|
431
|
+
/* A switch the host refused is marked rather than left looking merely off, so a failure
|
|
432
|
+
is visible where it happened; the reason rides the title and the console. */
|
|
433
|
+
.dsh-subscription-fast-mode[data-failed="true"] {
|
|
434
|
+
color: var(--dsw-alias-state-error-primary, #b42318);
|
|
435
|
+
}
|
|
436
|
+
.dsh-subscription-fast-mode:focus-visible {
|
|
437
|
+
outline: 2px solid var(--dsw-alias-border-focus, #4d6bfe);
|
|
438
|
+
outline-offset: 1px;
|
|
439
|
+
}
|
|
440
|
+
/* Off: the bolt is dim, so an available mode reads as an invitation, not a state. */
|
|
441
|
+
.dsh-subscription-fast-mode[data-active="false"] .dsh-subscription-fast-mode-bolt {
|
|
442
|
+
opacity: 0.55;
|
|
443
|
+
}
|
|
444
|
+
/* On: the bolt takes the accent and glows, which is the only thing distinguishing the
|
|
445
|
+
fast variant from its base — both answer with the same model name. */
|
|
446
|
+
.dsh-subscription-fast-mode[data-active="true"] .dsh-subscription-fast-mode-bolt {
|
|
447
|
+
color: var(--dsw-alias-state-warning-primary, #f5a524);
|
|
448
|
+
filter: drop-shadow(0 0 6px var(--dsw-alias-state-warning-primary, #f5a52455));
|
|
449
|
+
}
|
|
450
|
+
.dsh-subscription-fast-mode-reason {
|
|
451
|
+
max-width: 14ch;
|
|
452
|
+
overflow: hidden;
|
|
453
|
+
text-overflow: ellipsis;
|
|
454
|
+
white-space: nowrap;
|
|
455
|
+
font-size: 11px;
|
|
456
|
+
}
|
|
457
|
+
`;
|