dsh-subscriptions 0.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/LICENSE +21 -0
- package/README.md +129 -0
- package/cordis.patch.yml +12 -0
- package/lib/auth/claude-code-creds.d.ts +23 -0
- package/lib/auth/claude-code-creds.js +172 -0
- package/lib/auth/device-flow.d.ts +55 -0
- package/lib/auth/device-flow.js +177 -0
- package/lib/auth/jwt.d.ts +10 -0
- package/lib/auth/jwt.js +25 -0
- package/lib/auth/oauth-flow.d.ts +91 -0
- package/lib/auth/oauth-flow.js +227 -0
- package/lib/auth/pkce.d.ts +31 -0
- package/lib/auth/pkce.js +35 -0
- package/lib/auth/rpc.d.ts +209 -0
- package/lib/auth/rpc.js +386 -0
- package/lib/auth/store.d.ts +196 -0
- package/lib/auth/store.js +468 -0
- package/lib/client/CursorCard.d.ts +9 -0
- package/lib/client/CursorCard.js +113 -0
- package/lib/client/ExternalUsageCards.d.ts +9 -0
- package/lib/client/ExternalUsageCards.js +72 -0
- package/lib/client/ImageGallery.d.ts +54 -0
- package/lib/client/ImageGallery.js +112 -0
- package/lib/client/ImageGenerateToolview.d.ts +48 -0
- package/lib/client/ImageGenerateToolview.js +133 -0
- package/lib/client/ProviderAccountManager.d.ts +13 -0
- package/lib/client/ProviderAccountManager.js +147 -0
- package/lib/client/ProviderModelEditor.d.ts +39 -0
- package/lib/client/ProviderModelEditor.js +107 -0
- package/lib/client/SpeedSelect.d.ts +71 -0
- package/lib/client/SpeedSelect.js +177 -0
- package/lib/client/SubscriptionUsageBadge.d.ts +116 -0
- package/lib/client/SubscriptionUsageBadge.js +543 -0
- package/lib/client/SubscriptionsSection.d.ts +140 -0
- package/lib/client/SubscriptionsSection.js +544 -0
- package/lib/client/UsageBadgeDisplaySetting.d.ts +7 -0
- package/lib/client/UsageBadgeDisplaySetting.js +36 -0
- package/lib/client/VideoGenerateToolview.d.ts +42 -0
- package/lib/client/VideoGenerateToolview.js +175 -0
- package/lib/client/account-preferences.d.ts +24 -0
- package/lib/client/account-preferences.js +21 -0
- package/lib/client/fast-command.d.ts +2 -0
- package/lib/client/fast-command.js +4 -0
- package/lib/client/host-contract.d.ts +90 -0
- package/lib/client/host-contract.js +101 -0
- package/lib/client/host-icons.d.ts +24 -0
- package/lib/client/host-icons.js +21 -0
- package/lib/client/index.d.ts +27 -0
- package/lib/client/index.js +136 -0
- package/lib/client/locales.d.ts +297 -0
- package/lib/client/locales.js +295 -0
- package/lib/client/subscription-card-styles.d.ts +3 -0
- package/lib/client/subscription-card-styles.js +53 -0
- package/lib/client/subscriptions-rpc.d.ts +14 -0
- package/lib/client/subscriptions-rpc.js +34 -0
- package/lib/client/usage-badge-preferences.d.ts +11 -0
- package/lib/client/usage-badge-preferences.js +41 -0
- package/lib/client.js +4472 -0
- package/lib/client.js.map +1 -0
- package/lib/compat.d.ts +36 -0
- package/lib/compat.js +20 -0
- package/lib/http.d.ts +6 -0
- package/lib/http.js +15 -0
- package/lib/index.d.ts +80 -0
- package/lib/index.js +14018 -0
- package/lib/model-defaults.d.ts +23 -0
- package/lib/model-defaults.js +237 -0
- package/lib/provider-settings.d.ts +39 -0
- package/lib/provider-settings.js +151 -0
- package/lib/providers/account-preferences.d.ts +41 -0
- package/lib/providers/account-preferences.js +153 -0
- package/lib/providers/accounts.d.ts +112 -0
- package/lib/providers/accounts.js +142 -0
- package/lib/providers/antigravity-oauth-client.d.ts +7 -0
- package/lib/providers/antigravity-oauth-client.js +7 -0
- package/lib/providers/antigravity.d.ts +110 -0
- package/lib/providers/antigravity.js +516 -0
- package/lib/providers/catalog-store.d.ts +48 -0
- package/lib/providers/catalog-store.js +243 -0
- package/lib/providers/claude-cli-version.d.ts +15 -0
- package/lib/providers/claude-cli-version.js +24 -0
- package/lib/providers/claude.d.ts +181 -0
- package/lib/providers/claude.js +680 -0
- package/lib/providers/codex-client-version.d.ts +8 -0
- package/lib/providers/codex-client-version.js +17 -0
- package/lib/providers/codex-search.d.ts +29 -0
- package/lib/providers/codex-search.js +198 -0
- package/lib/providers/codex.d.ts +229 -0
- package/lib/providers/codex.js +821 -0
- package/lib/providers/common.d.ts +389 -0
- package/lib/providers/common.js +656 -0
- package/lib/providers/copilot.d.ts +344 -0
- package/lib/providers/copilot.js +888 -0
- package/lib/providers/cursor-adapter.d.ts +15 -0
- package/lib/providers/cursor-adapter.js +39 -0
- package/lib/providers/cursor-auth.d.ts +43 -0
- package/lib/providers/cursor-auth.js +169 -0
- package/lib/providers/cursor-model-settings.d.ts +15 -0
- package/lib/providers/cursor-model-settings.js +51 -0
- package/lib/providers/cursor-usage.d.ts +9 -0
- package/lib/providers/cursor-usage.js +112 -0
- package/lib/providers/external-usage-controller.d.ts +19 -0
- package/lib/providers/external-usage-controller.js +32 -0
- package/lib/providers/external-usage.d.ts +8 -0
- package/lib/providers/external-usage.js +82 -0
- package/lib/providers/grok.d.ts +205 -0
- package/lib/providers/grok.js +713 -0
- package/lib/providers/image-pool.d.ts +30 -0
- package/lib/providers/image-pool.js +89 -0
- package/lib/providers/npm-cli-version.d.ts +56 -0
- package/lib/providers/npm-cli-version.js +98 -0
- package/lib/providers/pool-family.d.ts +56 -0
- package/lib/providers/pool-family.js +45 -0
- package/lib/providers/pool-health.d.ts +74 -0
- package/lib/providers/pool-health.js +150 -0
- package/lib/providers/pool-usage.d.ts +86 -0
- package/lib/providers/pool-usage.js +223 -0
- package/lib/providers/pool.d.ts +108 -0
- package/lib/providers/pool.js +368 -0
- package/lib/providers/rate-limit.d.ts +192 -0
- package/lib/providers/rate-limit.js +338 -0
- package/lib/tools/image-generate.d.ts +128 -0
- package/lib/tools/image-generate.js +438 -0
- package/lib/tools/registration.d.ts +15 -0
- package/lib/tools/registration.js +24 -0
- package/lib/tools/video-generate.d.ts +89 -0
- package/lib/tools/video-generate.js +257 -0
- package/lib/tools/x-search.d.ts +58 -0
- package/lib/tools/x-search.js +197 -0
- package/lib/translate/anthropic.d.ts +161 -0
- package/lib/translate/anthropic.js +515 -0
- package/lib/translate/antigravity-schema.d.ts +4 -0
- package/lib/translate/antigravity-schema.js +145 -0
- package/lib/translate/antigravity-thinking.d.ts +5 -0
- package/lib/translate/antigravity-thinking.js +44 -0
- package/lib/translate/antigravity.d.ts +114 -0
- package/lib/translate/antigravity.js +366 -0
- package/lib/translate/chat-completions.d.ts +120 -0
- package/lib/translate/chat-completions.js +382 -0
- package/lib/translate/resolved.d.ts +80 -0
- package/lib/translate/resolved.js +118 -0
- package/lib/translate/responses.d.ts +179 -0
- package/lib/translate/responses.js +408 -0
- package/lib/translate/sse.d.ts +21 -0
- package/lib/translate/sse.js +56 -0
- package/package.json +114 -0
- package/vendor/cursor/LICENSE +21 -0
- package/vendor/cursor/index.d.ts +18 -0
- package/vendor/cursor/index.js +3574 -0
- package/vendor/cursor/proto.js +254 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Keyed toolview for the `video_generate` tool: renders the generated video
|
|
4
|
+
* inline in the conversation. The row shows the call's prompt while running
|
|
5
|
+
* and after settling; a settled success loads the MP4 bytes through the node
|
|
6
|
+
* half's `/subscriptions-auth` `video` endpoint (by bare file name), builds a
|
|
7
|
+
* Blob URL, and plays it in a native `<video controls>` element. The file
|
|
8
|
+
* name comes from the result's presentation meta when the dispatch was
|
|
9
|
+
* top-level, and is recovered from the "Saved video to …" text line for
|
|
10
|
+
* nested (Code Mode) dispatches, where the harness computes no meta. A result
|
|
11
|
+
* with neither renders its text; an error result renders the first error line.
|
|
12
|
+
*
|
|
13
|
+
* The 'tool.call.toolview' SlotMap entry is declared by
|
|
14
|
+
* ImageGenerateToolview.tsx in this same package (one declaration per
|
|
15
|
+
* augmentation), so this file only mirrors the owner-props shape.
|
|
16
|
+
*/
|
|
17
|
+
import { useEffect, useState } from 'react';
|
|
18
|
+
import * as primitives from '@deepseek-ai/dsh-client-ui-primitives';
|
|
19
|
+
import { hostIcon } from './host-icons.js';
|
|
20
|
+
import { callSubscriptionsAuth } from './subscriptions-rpc.js';
|
|
21
|
+
import { en } from './locales.js';
|
|
22
|
+
/** DSH 0.1.7 renamed IconSparkle16; resolving it by name keeps older hosts working too. */
|
|
23
|
+
const SparkleIcon = hostIcon(primitives, 'Sparkle');
|
|
24
|
+
/** Title prompt truncation budget (characters). */
|
|
25
|
+
const PROMPT_MAX_LENGTH = 60;
|
|
26
|
+
/**
|
|
27
|
+
* Build the video loader over the `/subscriptions-auth` `video` endpoint.
|
|
28
|
+
* @param rpc - Connection RPC caller.
|
|
29
|
+
* @returns loader resolving a bare file name to the decoded bytes.
|
|
30
|
+
*/
|
|
31
|
+
export function createVideoLoader(rpc) {
|
|
32
|
+
return name => callSubscriptionsAuth(rpc, 'video', { name });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* English-dictionary fallback for a missing locale seat (standalone renders);
|
|
36
|
+
* the framework always supplies the namespace-bound one.
|
|
37
|
+
*/
|
|
38
|
+
function fallbackTranslate(key, params) {
|
|
39
|
+
let text = en[key];
|
|
40
|
+
for (const [name, value] of Object.entries(params ?? {})) {
|
|
41
|
+
text = text.replaceAll(`{${name}}`, String(value));
|
|
42
|
+
}
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
/** Extract the prompt from the call's raw args JSON; falls back to the first string value, then the raw line. */
|
|
46
|
+
function derivePrompt(argsRaw) {
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(argsRaw);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Non-JSON args (mid-stream truncation): fall back to the raw string below.
|
|
53
|
+
parsed = undefined;
|
|
54
|
+
}
|
|
55
|
+
let prompt;
|
|
56
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
57
|
+
const args = parsed;
|
|
58
|
+
if (typeof args.prompt === 'string' && args.prompt !== '')
|
|
59
|
+
prompt = args.prompt;
|
|
60
|
+
else {
|
|
61
|
+
for (const value of Object.values(args)) {
|
|
62
|
+
if (typeof value === 'string' && value !== '') {
|
|
63
|
+
prompt = value;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const line = (prompt ?? argsRaw).split('\n', 1)[0] ?? '';
|
|
70
|
+
return line.length > PROMPT_MAX_LENGTH ? `${line.slice(0, PROMPT_MAX_LENGTH)}…` : line;
|
|
71
|
+
}
|
|
72
|
+
/** Flatten a settled result's text blocks (the fallback body and the error line). */
|
|
73
|
+
function resultText(block) {
|
|
74
|
+
if (!('kind' in block))
|
|
75
|
+
return '';
|
|
76
|
+
const parts = [];
|
|
77
|
+
for (const part of block.content) {
|
|
78
|
+
if (part.type === 'text')
|
|
79
|
+
parts.push(part.text);
|
|
80
|
+
}
|
|
81
|
+
if (parts.length === 0 && block.error !== undefined)
|
|
82
|
+
parts.push(`${block.error.name}: ${block.error.code}`);
|
|
83
|
+
return parts.join('\n');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The generated video's bare file name: presentation meta first (top-level
|
|
87
|
+
* dispatches), then the render text's "Saved video to …" line (nested
|
|
88
|
+
* dispatches compute no meta).
|
|
89
|
+
*/
|
|
90
|
+
function resolveFileName(block) {
|
|
91
|
+
if (!('kind' in block))
|
|
92
|
+
return undefined;
|
|
93
|
+
const meta = block.meta;
|
|
94
|
+
if (typeof meta === 'object' && meta !== null) {
|
|
95
|
+
const fileName = meta.fileName;
|
|
96
|
+
if (typeof fileName === 'string' && fileName.length > 0)
|
|
97
|
+
return fileName;
|
|
98
|
+
}
|
|
99
|
+
const match = /^Saved video to (.+\.mp4)/m.exec(resultText(block));
|
|
100
|
+
if (match === null)
|
|
101
|
+
return undefined;
|
|
102
|
+
const path = match[1];
|
|
103
|
+
return path.slice(path.lastIndexOf('/') + 1);
|
|
104
|
+
}
|
|
105
|
+
/** Decode a base64 payload into bytes (browser-side; no Buffer). */
|
|
106
|
+
function base64Bytes(dataBase64) {
|
|
107
|
+
const binary = atob(dataBase64);
|
|
108
|
+
const bytes = new Uint8Array(binary.length);
|
|
109
|
+
for (let index = 0; index < binary.length; index++)
|
|
110
|
+
bytes[index] = binary.charCodeAt(index);
|
|
111
|
+
return bytes;
|
|
112
|
+
}
|
|
113
|
+
const styles = {
|
|
114
|
+
container: { display: 'flex', flexDirection: 'column', gap: 6, padding: '4px 0' },
|
|
115
|
+
row: { display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 },
|
|
116
|
+
icon: { display: 'inline-flex', flexShrink: 0, color: 'var(--dsw-alias-label-tertiary)' },
|
|
117
|
+
title: {
|
|
118
|
+
fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)',
|
|
119
|
+
overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
|
120
|
+
},
|
|
121
|
+
subtle: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },
|
|
122
|
+
output: {
|
|
123
|
+
margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-secondary)',
|
|
124
|
+
whiteSpace: 'pre-wrap', overflowWrap: 'anywhere',
|
|
125
|
+
},
|
|
126
|
+
error: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },
|
|
127
|
+
video: {
|
|
128
|
+
display: 'block', maxWidth: 480, width: '100%', borderRadius: 8,
|
|
129
|
+
backgroundColor: 'var(--dsw-alias-bg-skeleton)',
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* The `video_generate` keyed toolview component.
|
|
134
|
+
* @param props - owner share, inject face, and locale seat (spread flat).
|
|
135
|
+
* @returns the call row plus, once settled, the player / text / error body.
|
|
136
|
+
*/
|
|
137
|
+
export function VideoGenerateToolview(props) {
|
|
138
|
+
const { block, loadVideo } = props;
|
|
139
|
+
const t = props.t ?? fallbackTranslate;
|
|
140
|
+
const settled = block !== undefined && 'kind' in block;
|
|
141
|
+
const isError = settled && block.isError;
|
|
142
|
+
const fileName = block !== undefined && settled && !isError ? resolveFileName(block) : undefined;
|
|
143
|
+
const [load, setLoad] = useState({ phase: 'loading' });
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (fileName === undefined || loadVideo === undefined)
|
|
146
|
+
return;
|
|
147
|
+
let cancelled = false;
|
|
148
|
+
let objectUrl;
|
|
149
|
+
setLoad({ phase: 'loading' });
|
|
150
|
+
loadVideo(fileName).then((video) => {
|
|
151
|
+
if (cancelled)
|
|
152
|
+
return;
|
|
153
|
+
// Uint8Array#slice detaches a plain ArrayBuffer view for BlobPart.
|
|
154
|
+
objectUrl = URL.createObjectURL(new Blob([base64Bytes(video.dataBase64).slice()], { type: video.mediaType }));
|
|
155
|
+
setLoad({ phase: 'ready', url: objectUrl });
|
|
156
|
+
}, (error) => {
|
|
157
|
+
if (cancelled)
|
|
158
|
+
return;
|
|
159
|
+
setLoad({ phase: 'failed', message: error instanceof Error ? error.message : String(error) });
|
|
160
|
+
});
|
|
161
|
+
return () => {
|
|
162
|
+
cancelled = true;
|
|
163
|
+
if (objectUrl !== undefined)
|
|
164
|
+
URL.revokeObjectURL(objectUrl);
|
|
165
|
+
};
|
|
166
|
+
}, [fileName, loadVideo]);
|
|
167
|
+
if (block === undefined)
|
|
168
|
+
return null;
|
|
169
|
+
const argsRaw = (settled ? block.call?.argsRaw : block.phase === 'start' ? block.argsRaw : undefined) ?? '';
|
|
170
|
+
const title = `video_generate: ${derivePrompt(argsRaw)}`;
|
|
171
|
+
const text = settled ? resultText(block) : '';
|
|
172
|
+
return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.row, children: [_jsx("span", { style: styles.icon, children: _jsx(SparkleIcon, { size: 14 }) }), _jsx("span", { style: styles.title, children: title })] }), !settled && _jsx("p", { style: styles.subtle, children: t('generatingVideo') }), settled && isError && text !== '' && (_jsx("p", { style: styles.error, children: text.split('\n', 1)[0] })), settled && !isError && fileName !== undefined && load.phase === 'loading' && (_jsx("p", { style: styles.subtle, children: t('videoLoading') })), settled && !isError && fileName !== undefined && load.phase === 'failed' && (_jsx("p", { style: styles.error, children: t('videoLoadFailed', { message: load.message }) })), settled && !isError && fileName !== undefined && load.phase === 'ready' && (
|
|
173
|
+
// eslint-disable-next-line jsx-a11y/media-has-caption -- generated clip; no caption track exists.
|
|
174
|
+
_jsx("video", { style: styles.video, src: load.url, controls: true, preload: "metadata" })), settled && !isError && fileName === undefined && text !== '' && (_jsx("p", { style: styles.output, children: text }))] }));
|
|
175
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AccountPreferences, ProviderPreferences } from '../provider-settings.js';
|
|
2
|
+
export interface AccountCatalogRow {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
models: {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}[];
|
|
9
|
+
unavailable?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Missing allowlist follows discovery; explicit [] must remain empty. */
|
|
12
|
+
export declare function accountPoolSelection(account: AccountPreferences, models: readonly {
|
|
13
|
+
id: string;
|
|
14
|
+
}[]): Set<string>;
|
|
15
|
+
/** Retain unavailable saved IDs so a temporary catalog failure cannot erase them. */
|
|
16
|
+
export declare function accountModelRows(account: AccountCatalogRow, preferences: AccountPreferences): {
|
|
17
|
+
unavailable: boolean;
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
}[];
|
|
21
|
+
/** The model editor owns everything except the separately managed accounts. */
|
|
22
|
+
export declare function mergeLatestAccounts(draft: ProviderPreferences, latest: ProviderPreferences): ProviderPreferences;
|
|
23
|
+
/** Only touched accounts replace their latest preferences; other settings survive. */
|
|
24
|
+
export declare function mergeAccountChanges(latest: ProviderPreferences, changes: Record<string, AccountPreferences>): ProviderPreferences;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Missing allowlist follows discovery; explicit [] must remain empty. */
|
|
2
|
+
export function accountPoolSelection(account, models) {
|
|
3
|
+
return new Set(account.poolModels ?? models.map(model => model.id));
|
|
4
|
+
}
|
|
5
|
+
/** Retain unavailable saved IDs so a temporary catalog failure cannot erase them. */
|
|
6
|
+
export function accountModelRows(account, preferences) {
|
|
7
|
+
const known = new Set(account.models.map(model => model.id));
|
|
8
|
+
return [
|
|
9
|
+
...account.models.map(model => ({ ...model, unavailable: false })),
|
|
10
|
+
...(preferences.poolModels ?? []).filter(id => !known.has(id)).map(id => ({ id, name: id, unavailable: true })),
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
/** The model editor owns everything except the separately managed accounts. */
|
|
14
|
+
export function mergeLatestAccounts(draft, latest) {
|
|
15
|
+
const { accounts: _stale, ...settings } = draft;
|
|
16
|
+
return { ...settings, ...(latest.accounts === undefined ? {} : { accounts: latest.accounts }) };
|
|
17
|
+
}
|
|
18
|
+
/** Only touched accounts replace their latest preferences; other settings survive. */
|
|
19
|
+
export function mergeAccountChanges(latest, changes) {
|
|
20
|
+
return { ...latest, accounts: { ...latest.accounts, ...changes } };
|
|
21
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything the client half assumes about the DSH host that the compiler
|
|
3
|
+
* cannot check.
|
|
4
|
+
*
|
|
5
|
+
* The client bundle reads host modules at runtime, not at build time. A
|
|
6
|
+
* renamed icon export compiles against the old type declarations and then
|
|
7
|
+
* renders `undefined`. A slot outlet the host stops rendering never mounts our
|
|
8
|
+
* entry, so the page looks normal and the feature is just gone. A removed DOM
|
|
9
|
+
* marker only breaks layout. None of these fail `tsc`, and a unit test with
|
|
10
|
+
* a hand-made host cannot see them either.
|
|
11
|
+
*
|
|
12
|
+
* So each such assumption is listed here once, next to the host package that
|
|
13
|
+
* owns it. `scripts/check-host-contract.mjs` downloads every supported DSH
|
|
14
|
+
* version and checks that each entry appears in that version's published
|
|
15
|
+
* code. The host E2E reads the same diagnostics entries to catch slot
|
|
16
|
+
* crashes. The spec test/host-contract.spec.ts scans src/client and fails
|
|
17
|
+
* when code starts relying on a literal host name that is missing here.
|
|
18
|
+
*
|
|
19
|
+
* This module has no imports, so plain Node scripts can import it directly.
|
|
20
|
+
*/
|
|
21
|
+
/** Prefix of the console warning the client logs when a host lacks a listed name. */
|
|
22
|
+
export declare const HOST_CONTRACT_MISS = "dsh-subscriptions: host contract miss";
|
|
23
|
+
export declare const HOST_CONTRACT: {
|
|
24
|
+
/**
|
|
25
|
+
* Host modules the client bundle loads by specifier. The web shell serves
|
|
26
|
+
* only the modules in its static module table (`staticModules` in
|
|
27
|
+
* dsh-web-frontend), so each specifier the built lib/client.js loads must
|
|
28
|
+
* be a key of that table.
|
|
29
|
+
*/
|
|
30
|
+
readonly moduleTable: "@deepseek-ai/dsh-web-frontend";
|
|
31
|
+
/** Named runtime exports the client reads from a host module. */
|
|
32
|
+
readonly exports: {
|
|
33
|
+
readonly '@deepseek-ai/dsh-client-ui-primitives': readonly ["useAnchoredPosition", "useDismissOnOutsidePointer"];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* 16px glyphs read through hostIcon(). The host must export
|
|
37
|
+
* `Icon<Name>Regular` (DSH 0.1.7 and later) or `Icon<Name>16` (earlier).
|
|
38
|
+
*/
|
|
39
|
+
readonly icons: {
|
|
40
|
+
readonly package: "@deepseek-ai/dsh-client-ui-primitives";
|
|
41
|
+
readonly names: readonly ["Sparkle", "DataOutline"];
|
|
42
|
+
};
|
|
43
|
+
/** Slots the client registers into, each with the package that renders its outlet. */
|
|
44
|
+
readonly slots: {
|
|
45
|
+
readonly 'settings.section': "@deepseek-ai/dsh-client-ui-settings-general";
|
|
46
|
+
readonly 'tool.call.toolview': "@deepseek-ai/dsh-client-ui-tool";
|
|
47
|
+
readonly 'conversation.input.right': "@deepseek-ai/dsh-client-ui-conversation";
|
|
48
|
+
readonly 'conversation.composer.dock': "@deepseek-ai/dsh-client-ui-conversation";
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Context services the client injects or reads, each with the package that
|
|
52
|
+
* provides it (`ctx.provide("<name>")` or a cordis Service named `<name>`).
|
|
53
|
+
* A service nobody provides leaves the plugin's apply() waiting forever,
|
|
54
|
+
* with nothing in the console.
|
|
55
|
+
*/
|
|
56
|
+
readonly services: {
|
|
57
|
+
readonly commandUi: "@deepseek-ai/dsh-client-ui-commands";
|
|
58
|
+
readonly connection: "@deepseek-ai/dsh-client-connection";
|
|
59
|
+
readonly locale: "@deepseek-ai/dsh-client-locale";
|
|
60
|
+
readonly modelDirectories: "@deepseek-ai/dsh-client-ui-model-selection";
|
|
61
|
+
readonly slots: "@deepseek-ai/dsh-client-ui-renderer";
|
|
62
|
+
};
|
|
63
|
+
/** DOM attributes the client looks up in host-rendered markup. */
|
|
64
|
+
readonly markers: {
|
|
65
|
+
/** The shipped StatsPills row; the usage badge renders into it as a third pill. */
|
|
66
|
+
readonly composerStats: {
|
|
67
|
+
readonly attribute: "data-composer-stats";
|
|
68
|
+
readonly package: "@deepseek-ai/dsh-client-ui-chat";
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* What the slot renderer emits when an entry throws. It catches the error,
|
|
73
|
+
* logs `crashLog`, and renders an element carrying `errorAttribute` in the
|
|
74
|
+
* entry's place, so a crash never reaches the page's error handlers. The
|
|
75
|
+
* host E2E watches for both.
|
|
76
|
+
*/
|
|
77
|
+
readonly diagnostics: {
|
|
78
|
+
readonly package: "@deepseek-ai/dsh-client-ui-renderer";
|
|
79
|
+
readonly crashLog: "slot entry crashed in";
|
|
80
|
+
readonly errorAttribute: "data-slot-error";
|
|
81
|
+
readonly slotAttribute: "data-slot";
|
|
82
|
+
};
|
|
83
|
+
/** Design tokens the client styles with; the host theme must define each one. */
|
|
84
|
+
readonly tokens: {
|
|
85
|
+
readonly package: "@deepseek-ai/dsh-client-ui-theme";
|
|
86
|
+
readonly names: readonly ["--dsw-alias-bg-layer-1", "--dsw-alias-bg-skeleton", "--dsw-alias-border-l2", "--dsw-alias-border-l2-darkmode-thin", "--dsw-alias-interactive-bg-hover", "--dsw-alias-interactive-bg-hover-solid", "--dsw-alias-label-dimmed", "--dsw-alias-label-primary", "--dsw-alias-label-secondary", "--dsw-alias-label-tertiary", "--dsw-alias-state-error-primary", "--dsw-alias-state-success-primary", "--dsw-alias-state-warn-label", "--dsw-elevation-prominent", "--dsw-specific-menu"];
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/** A glyph name listed in {@link HOST_CONTRACT}; hostIcon() accepts no other. */
|
|
90
|
+
export type HostIconName = (typeof HOST_CONTRACT.icons.names)[number];
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything the client half assumes about the DSH host that the compiler
|
|
3
|
+
* cannot check.
|
|
4
|
+
*
|
|
5
|
+
* The client bundle reads host modules at runtime, not at build time. A
|
|
6
|
+
* renamed icon export compiles against the old type declarations and then
|
|
7
|
+
* renders `undefined`. A slot outlet the host stops rendering never mounts our
|
|
8
|
+
* entry, so the page looks normal and the feature is just gone. A removed DOM
|
|
9
|
+
* marker only breaks layout. None of these fail `tsc`, and a unit test with
|
|
10
|
+
* a hand-made host cannot see them either.
|
|
11
|
+
*
|
|
12
|
+
* So each such assumption is listed here once, next to the host package that
|
|
13
|
+
* owns it. `scripts/check-host-contract.mjs` downloads every supported DSH
|
|
14
|
+
* version and checks that each entry appears in that version's published
|
|
15
|
+
* code. The host E2E reads the same diagnostics entries to catch slot
|
|
16
|
+
* crashes. The spec test/host-contract.spec.ts scans src/client and fails
|
|
17
|
+
* when code starts relying on a literal host name that is missing here.
|
|
18
|
+
*
|
|
19
|
+
* This module has no imports, so plain Node scripts can import it directly.
|
|
20
|
+
*/
|
|
21
|
+
/** Prefix of the console warning the client logs when a host lacks a listed name. */
|
|
22
|
+
export const HOST_CONTRACT_MISS = 'dsh-subscriptions: host contract miss';
|
|
23
|
+
export const HOST_CONTRACT = {
|
|
24
|
+
/**
|
|
25
|
+
* Host modules the client bundle loads by specifier. The web shell serves
|
|
26
|
+
* only the modules in its static module table (`staticModules` in
|
|
27
|
+
* dsh-web-frontend), so each specifier the built lib/client.js loads must
|
|
28
|
+
* be a key of that table.
|
|
29
|
+
*/
|
|
30
|
+
moduleTable: '@deepseek-ai/dsh-web-frontend',
|
|
31
|
+
/** Named runtime exports the client reads from a host module. */
|
|
32
|
+
exports: {
|
|
33
|
+
'@deepseek-ai/dsh-client-ui-primitives': ['useAnchoredPosition', 'useDismissOnOutsidePointer'],
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* 16px glyphs read through hostIcon(). The host must export
|
|
37
|
+
* `Icon<Name>Regular` (DSH 0.1.7 and later) or `Icon<Name>16` (earlier).
|
|
38
|
+
*/
|
|
39
|
+
icons: {
|
|
40
|
+
package: '@deepseek-ai/dsh-client-ui-primitives',
|
|
41
|
+
names: ['Sparkle', 'DataOutline'],
|
|
42
|
+
},
|
|
43
|
+
/** Slots the client registers into, each with the package that renders its outlet. */
|
|
44
|
+
slots: {
|
|
45
|
+
'settings.section': '@deepseek-ai/dsh-client-ui-settings-general',
|
|
46
|
+
'tool.call.toolview': '@deepseek-ai/dsh-client-ui-tool',
|
|
47
|
+
'conversation.input.right': '@deepseek-ai/dsh-client-ui-conversation',
|
|
48
|
+
'conversation.composer.dock': '@deepseek-ai/dsh-client-ui-conversation',
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* Context services the client injects or reads, each with the package that
|
|
52
|
+
* provides it (`ctx.provide("<name>")` or a cordis Service named `<name>`).
|
|
53
|
+
* A service nobody provides leaves the plugin's apply() waiting forever,
|
|
54
|
+
* with nothing in the console.
|
|
55
|
+
*/
|
|
56
|
+
services: {
|
|
57
|
+
commandUi: '@deepseek-ai/dsh-client-ui-commands',
|
|
58
|
+
connection: '@deepseek-ai/dsh-client-connection',
|
|
59
|
+
locale: '@deepseek-ai/dsh-client-locale',
|
|
60
|
+
modelDirectories: '@deepseek-ai/dsh-client-ui-model-selection',
|
|
61
|
+
slots: '@deepseek-ai/dsh-client-ui-renderer',
|
|
62
|
+
},
|
|
63
|
+
/** DOM attributes the client looks up in host-rendered markup. */
|
|
64
|
+
markers: {
|
|
65
|
+
/** The shipped StatsPills row; the usage badge renders into it as a third pill. */
|
|
66
|
+
composerStats: { attribute: 'data-composer-stats', package: '@deepseek-ai/dsh-client-ui-chat' },
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* What the slot renderer emits when an entry throws. It catches the error,
|
|
70
|
+
* logs `crashLog`, and renders an element carrying `errorAttribute` in the
|
|
71
|
+
* entry's place, so a crash never reaches the page's error handlers. The
|
|
72
|
+
* host E2E watches for both.
|
|
73
|
+
*/
|
|
74
|
+
diagnostics: {
|
|
75
|
+
package: '@deepseek-ai/dsh-client-ui-renderer',
|
|
76
|
+
crashLog: 'slot entry crashed in',
|
|
77
|
+
errorAttribute: 'data-slot-error',
|
|
78
|
+
slotAttribute: 'data-slot',
|
|
79
|
+
},
|
|
80
|
+
/** Design tokens the client styles with; the host theme must define each one. */
|
|
81
|
+
tokens: {
|
|
82
|
+
package: '@deepseek-ai/dsh-client-ui-theme',
|
|
83
|
+
names: [
|
|
84
|
+
'--dsw-alias-bg-layer-1',
|
|
85
|
+
'--dsw-alias-bg-skeleton',
|
|
86
|
+
'--dsw-alias-border-l2',
|
|
87
|
+
'--dsw-alias-border-l2-darkmode-thin',
|
|
88
|
+
'--dsw-alias-interactive-bg-hover',
|
|
89
|
+
'--dsw-alias-interactive-bg-hover-solid',
|
|
90
|
+
'--dsw-alias-label-dimmed',
|
|
91
|
+
'--dsw-alias-label-primary',
|
|
92
|
+
'--dsw-alias-label-secondary',
|
|
93
|
+
'--dsw-alias-label-tertiary',
|
|
94
|
+
'--dsw-alias-state-error-primary',
|
|
95
|
+
'--dsw-alias-state-success-primary',
|
|
96
|
+
'--dsw-alias-state-warn-label',
|
|
97
|
+
'--dsw-elevation-prominent',
|
|
98
|
+
'--dsw-specific-menu',
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glyphs from the host's icon set that survive its renames. DSH 0.1.7
|
|
3
|
+
* renamed the 16px glyphs from `Icon<Name>16` to `Icon<Name>Regular`. The
|
|
4
|
+
* client reads host modules at runtime, so a named import of a glyph the
|
|
5
|
+
* host no longer exports is `undefined`, and rendering it crashes the whole
|
|
6
|
+
* slot (React error #130).
|
|
7
|
+
*
|
|
8
|
+
* A glyph name must be listed in HOST_CONTRACT.icons, which
|
|
9
|
+
* scripts/check-host-contract.mjs checks against each supported DSH version.
|
|
10
|
+
*/
|
|
11
|
+
import type { ComponentType } from 'react';
|
|
12
|
+
import type { IconProps } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
13
|
+
import type { HostIconName } from './host-contract.js';
|
|
14
|
+
/**
|
|
15
|
+
* Resolve one 16px glyph under either host naming.
|
|
16
|
+
*
|
|
17
|
+
* A glyph the host lacks renders nothing, and the lookup logs a
|
|
18
|
+
* {@link HOST_CONTRACT_MISS} warning. The host E2E fails on that warning, so a
|
|
19
|
+
* missing icon is reported instead of shipping as a blank space.
|
|
20
|
+
* @param icons - the host primitives module (`import * as`), read by name.
|
|
21
|
+
* @param name - the glyph name without prefix or size, e.g. `Sparkle`.
|
|
22
|
+
* @returns the host's component, or one rendering nothing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function hostIcon(icons: object, name: HostIconName): ComponentType<IconProps>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HOST_CONTRACT, HOST_CONTRACT_MISS } from './host-contract.js';
|
|
2
|
+
/** A decorative glyph the host lacks renders nothing rather than crashing. */
|
|
3
|
+
const NoIcon = () => null;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve one 16px glyph under either host naming.
|
|
6
|
+
*
|
|
7
|
+
* A glyph the host lacks renders nothing, and the lookup logs a
|
|
8
|
+
* {@link HOST_CONTRACT_MISS} warning. The host E2E fails on that warning, so a
|
|
9
|
+
* missing icon is reported instead of shipping as a blank space.
|
|
10
|
+
* @param icons - the host primitives module (`import * as`), read by name.
|
|
11
|
+
* @param name - the glyph name without prefix or size, e.g. `Sparkle`.
|
|
12
|
+
* @returns the host's component, or one rendering nothing.
|
|
13
|
+
*/
|
|
14
|
+
export function hostIcon(icons, name) {
|
|
15
|
+
const exports = icons;
|
|
16
|
+
const icon = exports[`Icon${name}Regular`] ?? exports[`Icon${name}16`];
|
|
17
|
+
if (icon !== undefined)
|
|
18
|
+
return icon;
|
|
19
|
+
console.warn(`${HOST_CONTRACT_MISS}: ${HOST_CONTRACT.icons.package} exports neither Icon${name}Regular nor Icon${name}16`);
|
|
20
|
+
return NoIcon;
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { SubscriptionsKey } from './locales.js';
|
|
3
|
+
export type { SubscriptionsSectionInjected, SubscriptionsSectionProps } from './SubscriptionsSection.js';
|
|
4
|
+
export type { ImageGenerateToolviewInjected, ImageGenerateToolviewProps } from './ImageGenerateToolview.js';
|
|
5
|
+
export type { VideoGenerateToolviewInjected, VideoGenerateToolviewProps } from './VideoGenerateToolview.js';
|
|
6
|
+
export type { SpeedSelectInjected, SpeedSelectProps, SpeedState, SpeedTier } from './SpeedSelect.js';
|
|
7
|
+
export type { SubscriptionUsageBadgeInjected, SubscriptionUsageBadgeProps } from './SubscriptionUsageBadge.js';
|
|
8
|
+
export type { SubscriptionsKey } from './locales.js';
|
|
9
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
10
|
+
interface LocaleNamespaceMap {
|
|
11
|
+
/** The Subscriptions settings page copy. */
|
|
12
|
+
'settings.subscriptions': SubscriptionsKey;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Required services (cordis fiber inject): `slots` carries the registration
|
|
17
|
+
* seat, `connection` the `/subscriptions-auth` RPC caller, and `locale` the copy
|
|
18
|
+
* dictionaries.
|
|
19
|
+
*/
|
|
20
|
+
export declare const inject: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Register the Subscriptions section once the `settings.section` declaration
|
|
23
|
+
* is on the ledger (the shell's apply order relative to this one is NOT
|
|
24
|
+
* constrained; registration depends on the slot through `slots.inject()`).
|
|
25
|
+
* @param ctx - client root context.
|
|
26
|
+
*/
|
|
27
|
+
export declare function apply(ctx: ClientContext): void;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// `.js` extension: this package's tsconfig lacks the reference repo's
|
|
2
|
+
// allowImportingTsExtensions/rewriteRelativeImportExtensions pair; under
|
|
3
|
+
// nodenext the .js specifier resolves to the .tsx source (see README note).
|
|
4
|
+
import { SubscriptionsSection } from './SubscriptionsSection.js';
|
|
5
|
+
import { ImageGenerateToolview, createImageLoader } from './ImageGenerateToolview.js';
|
|
6
|
+
import { VideoGenerateToolview, createVideoLoader } from './VideoGenerateToolview.js';
|
|
7
|
+
import { SpeedSelect, createSpeedLoader, createSpeedSetter } from './SpeedSelect.js';
|
|
8
|
+
import { SubscriptionUsageBadge, createCurrentModelReader } from './SubscriptionUsageBadge.js';
|
|
9
|
+
import { en, zh } from './locales.js';
|
|
10
|
+
import { fastCommandDescription } from './fast-command.js';
|
|
11
|
+
/** Dictionary namespace owned by this plugin. */
|
|
12
|
+
const NS = 'settings.subscriptions';
|
|
13
|
+
/**
|
|
14
|
+
* Required services (cordis fiber inject): `slots` carries the registration
|
|
15
|
+
* seat, `connection` the `/subscriptions-auth` RPC caller, and `locale` the copy
|
|
16
|
+
* dictionaries.
|
|
17
|
+
*/
|
|
18
|
+
export const inject = ['slots', 'connection', 'locale'];
|
|
19
|
+
/**
|
|
20
|
+
* Register the Subscriptions section once the `settings.section` declaration
|
|
21
|
+
* is on the ledger (the shell's apply order relative to this one is NOT
|
|
22
|
+
* constrained; registration depends on the slot through `slots.inject()`).
|
|
23
|
+
* @param ctx - client root context.
|
|
24
|
+
*/
|
|
25
|
+
export function apply(ctx) {
|
|
26
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-subscriptions: copy dictionaries');
|
|
27
|
+
// Settings-shell nudge: the panel (nav title + header row + section body)
|
|
28
|
+
// sits flush against the panel's top edge; push it down a little to leave
|
|
29
|
+
// breathing room. Scoped by the settings panel's own dialog role + nav child
|
|
30
|
+
// so other aria-modal dialogs (e.g. the attachment lightbox) are untouched.
|
|
31
|
+
ctx.effect(() => {
|
|
32
|
+
const style = document.createElement('style');
|
|
33
|
+
style.setAttribute('data-plugin', 'dsh-subscriptions');
|
|
34
|
+
style.textContent = 'div[role="dialog"][aria-modal="true"]:has(> nav) { padding-top: 14px; }';
|
|
35
|
+
document.head.appendChild(style);
|
|
36
|
+
return () => style.remove();
|
|
37
|
+
}, 'dsh-subscriptions: settings panel breathing room');
|
|
38
|
+
// The shell's Context merge types `connection` as the host handle; in the
|
|
39
|
+
// browser shell the same key holds the full client ConnectionHandle.
|
|
40
|
+
const connection = ctx.get('connection');
|
|
41
|
+
const t = ctx.locale.bind(NS);
|
|
42
|
+
const injected = () => ({ rpc: connection.rpc, t });
|
|
43
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
44
|
+
name: 'settings.section',
|
|
45
|
+
id: 'subscriptions',
|
|
46
|
+
order: 90,
|
|
47
|
+
// A thunk re-evaluated per read, so the nav label follows the active locale.
|
|
48
|
+
label: () => t('nav'),
|
|
49
|
+
inject: injected,
|
|
50
|
+
}, SubscriptionsSection));
|
|
51
|
+
// The image_generate keyed toolview owns how image calls render inline; its
|
|
52
|
+
// gallery bytes ride the same channel through the injected loader. The
|
|
53
|
+
// framework synthesizes the toolview's own `t` seat from `locale: NS`.
|
|
54
|
+
const toolviewInjected = () => ({ load: createImageLoader(connection.rpc) });
|
|
55
|
+
ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({
|
|
56
|
+
name: 'tool.call.toolview',
|
|
57
|
+
key: 'image_generate',
|
|
58
|
+
locale: NS,
|
|
59
|
+
inject: toolviewInjected,
|
|
60
|
+
}, ImageGenerateToolview));
|
|
61
|
+
// The video_generate keyed toolview plays the saved MP4 inline; its bytes
|
|
62
|
+
// ride the same channel's `video` endpoint through the injected loader.
|
|
63
|
+
const videoToolviewInjected = () => ({ loadVideo: createVideoLoader(connection.rpc) });
|
|
64
|
+
ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({
|
|
65
|
+
name: 'tool.call.toolview',
|
|
66
|
+
key: 'video_generate',
|
|
67
|
+
locale: NS,
|
|
68
|
+
inject: videoToolviewInjected,
|
|
69
|
+
}, VideoGenerateToolview));
|
|
70
|
+
// The composer Speed toggle (codex fast tier) sits in the right tool row,
|
|
71
|
+
// just left of the model selector; the framework synthesizes its `t` seat
|
|
72
|
+
// from `locale: NS`, and the inject face binds each session's RPC calls.
|
|
73
|
+
// The current-model read rides ui-model-selection's `modelDirectories`
|
|
74
|
+
// service, resolved lazily so registration order never matters.
|
|
75
|
+
const models = () => ctx.get('modelDirectories');
|
|
76
|
+
ctx.slots.inject('conversation.input.right', () => ctx.slots.register({
|
|
77
|
+
name: 'conversation.input.right',
|
|
78
|
+
id: 'codex-speed',
|
|
79
|
+
order: 0,
|
|
80
|
+
locale: NS,
|
|
81
|
+
inject: (sessionId) => ({
|
|
82
|
+
loadSpeed: createSpeedLoader(connection, models, sessionId),
|
|
83
|
+
setSpeed: createSpeedSetter(connection, sessionId),
|
|
84
|
+
}),
|
|
85
|
+
}, SpeedSelect));
|
|
86
|
+
// The subscription usage badge renders a stats pill in the composer's dock
|
|
87
|
+
// (conversation.composer.dock) — collapsed, the current model's provider
|
|
88
|
+
// (e.g. "Codex 6d1h 25%"); clicking opens every provider's windows. A fresh
|
|
89
|
+
// id means it appears beside the shipped StatsPills, never replacing them;
|
|
90
|
+
// the current-model read shares the Speed toggle's `modelDirectories` path.
|
|
91
|
+
ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register({
|
|
92
|
+
name: 'conversation.composer.dock',
|
|
93
|
+
id: 'subscription-usage',
|
|
94
|
+
order: 10,
|
|
95
|
+
locale: NS,
|
|
96
|
+
inject: (sessionId) => ({
|
|
97
|
+
rpc: connection.rpc,
|
|
98
|
+
currentModel: createCurrentModelReader(models, sessionId),
|
|
99
|
+
}),
|
|
100
|
+
}, SubscriptionUsageBadge));
|
|
101
|
+
// The /fast slash command offers the same Standard/Fast choice as a popup.
|
|
102
|
+
// `available` is synchronous and sees only the session id, so the command
|
|
103
|
+
// stays listed everywhere; `options` throws the friendly gate when the
|
|
104
|
+
// session's current model is not a fast-capable codex model (the same
|
|
105
|
+
// in-popup error posture the /model contribution uses for its guards).
|
|
106
|
+
ctx.inject(['commandUi'], (scope) => {
|
|
107
|
+
const command = scope.get('commandUi');
|
|
108
|
+
scope.effect(() => command.register({
|
|
109
|
+
name: 'fast',
|
|
110
|
+
// dsh 0.1.5-alpha made `description` a locale resolver evaluated per
|
|
111
|
+
// candidate pass (commit 5d9603b76, "feat(web): localize slash command
|
|
112
|
+
// descriptions"); earlier lines read the value as a plain string. The
|
|
113
|
+
// bare string threw `contribution.description is not a function` inside
|
|
114
|
+
// the registry's candidate pass on 0.1.5, aborting the whole `/` source
|
|
115
|
+
// and hiding every host command — /plan, /model, /goal, ... — not just
|
|
116
|
+
// /fast. Older lines render a function child as empty copy, no crash.
|
|
117
|
+
description: fastCommandDescription(() => t('commandFast')),
|
|
118
|
+
available: () => true,
|
|
119
|
+
ui: {
|
|
120
|
+
kind: 'popupSelect',
|
|
121
|
+
options: async (session) => {
|
|
122
|
+
const state = await createSpeedLoader(connection, models, session.sessionId)();
|
|
123
|
+
if (!state.visible)
|
|
124
|
+
throw new Error(t('commandFastUnavailable'));
|
|
125
|
+
return [
|
|
126
|
+
{ id: 'standard', label: t('speedStandard'), detail: t('speedStandardDescription') },
|
|
127
|
+
{ id: 'fast', label: t('speedFast'), detail: t('speedFastDescription') },
|
|
128
|
+
].map(option => ({ ...option, active: option.id === state.tier }));
|
|
129
|
+
},
|
|
130
|
+
onSelect: async (option, session) => {
|
|
131
|
+
await createSpeedSetter(connection, session.sessionId)(option.id);
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
}), 'dsh-subscriptions: /fast contribution');
|
|
135
|
+
});
|
|
136
|
+
}
|