dsh-subscriptions 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -180
- package/lib/auth/claude-code-creds.js +31 -9
- package/lib/auth/device-flow.js +16 -9
- package/lib/auth/oauth-flow.js +13 -10
- package/lib/auth/rpc.js +74 -23
- package/lib/auth/store.js +80 -31
- package/lib/client/CursorCard.d.ts +1 -1
- package/lib/client/CursorCard.js +50 -16
- package/lib/client/ExternalUsageCards.d.ts +1 -1
- package/lib/client/ExternalUsageCards.js +37 -16
- package/lib/client/ImageGallery.d.ts +2 -2
- package/lib/client/ImageGallery.js +92 -33
- package/lib/client/ImageGenerateToolview.js +50 -14
- package/lib/client/OllamaCard.d.ts +1 -1
- package/lib/client/OllamaCard.js +26 -8
- package/lib/client/OllamaSettingsManager.js +113 -30
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +107 -26
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +257 -46
- package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
- package/lib/client/SubscriptionUsageBadge.js +300 -112
- package/lib/client/SubscriptionsSection.js +258 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +62 -24
- package/lib/client/VideoGenerateToolview.js +69 -17
- package/lib/client/account-preferences.d.ts +8 -0
- package/lib/client/account-preferences.js +20 -5
- package/lib/client/host-contract.d.ts +11 -2
- package/lib/client/host-contract.js +20 -3
- package/lib/client/index.d.ts +5 -5
- package/lib/client/index.js +34 -12
- package/lib/client/locales.d.ts +10 -0
- package/lib/client/locales.js +61 -51
- package/lib/client/ollama-draft.js +21 -12
- package/lib/client/reset-action.js +6 -4
- package/lib/client/reset-credits-view.d.ts +1 -1
- package/lib/client/reset-credits-view.js +66 -17
- package/lib/client/subscription-card-styles.js +94 -24
- package/lib/client/usage-color-preferences.js +4 -2
- package/lib/client/usage-pace.js +30 -12
- package/lib/client.js +287 -47
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +118 -64
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +12 -0
- package/lib/provider-settings.js +58 -16
- package/lib/providers/account-preferences.js +51 -22
- package/lib/providers/accounts.js +15 -9
- package/lib/providers/antigravity-oauth-client.js +2 -2
- package/lib/providers/antigravity.js +141 -68
- package/lib/providers/catalog-store.js +120 -41
- package/lib/providers/claude-cli-version.js +3 -3
- package/lib/providers/claude.d.ts +9 -2
- package/lib/providers/claude.js +153 -90
- package/lib/providers/codex-client-version.js +3 -3
- package/lib/providers/codex-search.js +40 -13
- package/lib/providers/codex.js +196 -112
- package/lib/providers/common.js +71 -39
- package/lib/providers/copilot.js +143 -82
- package/lib/providers/cursor-adapter.js +42 -18
- package/lib/providers/cursor-auth.d.ts +1 -1
- package/lib/providers/cursor-auth.js +69 -24
- package/lib/providers/cursor-model-settings.js +14 -5
- package/lib/providers/cursor-usage.js +27 -13
- package/lib/providers/external-usage-controller.js +14 -5
- package/lib/providers/external-usage.js +59 -23
- package/lib/providers/grok.js +152 -89
- package/lib/providers/image-pool.js +29 -11
- package/lib/providers/minimax-usage.js +31 -11
- package/lib/providers/npm-cli-version.js +10 -3
- package/lib/providers/ollama-settings.js +24 -8
- package/lib/providers/ollama-web.js +29 -11
- package/lib/providers/ollama.js +129 -66
- package/lib/providers/pool-family.js +11 -3
- package/lib/providers/pool-health.js +28 -8
- package/lib/providers/pool-usage.js +16 -9
- package/lib/providers/pool.d.ts +8 -0
- package/lib/providers/pool.js +81 -42
- package/lib/providers/rate-limit.js +8 -2
- package/lib/providers/replay.js +40 -17
- package/lib/providers/reset-redemption.js +18 -6
- package/lib/tools/image-generate.js +123 -65
- package/lib/tools/registration.js +5 -2
- package/lib/tools/video-generate.js +51 -31
- package/lib/tools/x-search.js +50 -22
- package/lib/translate/anthropic.js +85 -37
- package/lib/translate/antigravity-schema.js +71 -28
- package/lib/translate/antigravity-thinking.js +22 -9
- package/lib/translate/antigravity.js +141 -50
- package/lib/translate/chat-completions.js +67 -23
- package/lib/translate/resolved.js +55 -21
- package/lib/translate/responses.js +69 -29
- package/lib/translate/sse.js +4 -1
- package/package.json +26 -4
|
@@ -26,13 +26,17 @@ function errorText(error) {
|
|
|
26
26
|
export function externalUsageWindowLabel(t, window) {
|
|
27
27
|
const named = window.scope !== undefined && window.scope !== '';
|
|
28
28
|
if (window.kind === 'other') {
|
|
29
|
-
return window.scope === 'Monthly'
|
|
29
|
+
return window.scope === 'Monthly'
|
|
30
|
+
? t('usageMonthly')
|
|
31
|
+
: named
|
|
32
|
+
? window.scope
|
|
33
|
+
: t('usageWindow');
|
|
30
34
|
}
|
|
31
35
|
const base = window.kind === 'session' ? t('usageSession') : t('usageWeekly');
|
|
32
36
|
return named ? `${base} · ${window.scope}` : base;
|
|
33
37
|
}
|
|
34
38
|
/** Quota cards for API-key providers already available through DSH itself. */
|
|
35
|
-
export function ExternalUsageCards({ rpc, t }) {
|
|
39
|
+
export function ExternalUsageCards({ rpc, t, }) {
|
|
36
40
|
const [status, setStatus] = useState();
|
|
37
41
|
const [statusError, setStatusError] = useState();
|
|
38
42
|
const [usage, setUsage] = useState({});
|
|
@@ -40,20 +44,23 @@ export function ExternalUsageCards({ rpc, t }) {
|
|
|
40
44
|
const [errors, setErrors] = useState({});
|
|
41
45
|
const [loading, setLoading] = useState({});
|
|
42
46
|
const refresh = useCallback(async (source) => {
|
|
43
|
-
setLoading(prev => ({ ...prev, [source]: true }));
|
|
47
|
+
setLoading((prev) => ({ ...prev, [source]: true }));
|
|
44
48
|
try {
|
|
45
49
|
const value = await callSubscriptionsAuth(rpc, 'externalUsage', { source });
|
|
46
|
-
setUsage(prev => ({ ...prev, [source]: value }));
|
|
50
|
+
setUsage((prev) => ({ ...prev, [source]: value }));
|
|
47
51
|
// Prefer the server's observation time; a cached reading must not look fresh.
|
|
48
|
-
setObservedAt(prev => ({
|
|
49
|
-
|
|
52
|
+
setObservedAt((prev) => ({
|
|
53
|
+
...prev,
|
|
54
|
+
[source]: value.observedAt ?? Date.now(),
|
|
55
|
+
}));
|
|
56
|
+
setErrors((prev) => ({ ...prev, [source]: undefined }));
|
|
50
57
|
window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
|
|
51
58
|
}
|
|
52
59
|
catch (error) {
|
|
53
|
-
setErrors(prev => ({ ...prev, [source]: errorText(error) }));
|
|
60
|
+
setErrors((prev) => ({ ...prev, [source]: errorText(error) }));
|
|
54
61
|
}
|
|
55
62
|
finally {
|
|
56
|
-
setLoading(prev => ({ ...prev, [source]: false }));
|
|
63
|
+
setLoading((prev) => ({ ...prev, [source]: false }));
|
|
57
64
|
}
|
|
58
65
|
}, [rpc]);
|
|
59
66
|
const refreshStatus = useCallback(async () => {
|
|
@@ -72,16 +79,30 @@ export function ExternalUsageCards({ rpc, t }) {
|
|
|
72
79
|
useEffect(() => {
|
|
73
80
|
void refreshStatus();
|
|
74
81
|
}, [refreshStatus]);
|
|
75
|
-
return _jsx(_Fragment, { children: SOURCES.map(({ id, name, ref }) => {
|
|
82
|
+
return (_jsx(_Fragment, { children: SOURCES.map(({ id, name, ref }) => {
|
|
76
83
|
const configured = status?.[id]?.configured === true;
|
|
77
84
|
const snapshot = usage[id];
|
|
78
|
-
return _jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: {
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: {
|
|
86
|
+
...styles.dot,
|
|
87
|
+
background: configured
|
|
88
|
+
? 'var(--dsw-alias-state-success-primary)'
|
|
89
|
+
: 'var(--dsw-alias-label-dimmed)',
|
|
90
|
+
} }), _jsx("span", { style: styles.name, children: name })] }), !(status === undefined && statusError !== undefined) && (_jsx("p", { style: styles.status, children: status === undefined
|
|
91
|
+
? t('checking')
|
|
92
|
+
: configured
|
|
93
|
+
? t('externalUsageConnected')
|
|
94
|
+
: t('externalUsageNotConfigured') })), statusError !== undefined && (_jsx("p", { style: styles.error, children: t('externalUsageUnavailable', { message: statusError }) })), status !== undefined && !configured && (_jsx("p", { style: styles.status, children: t('externalUsageConfigure', { ref }) })), configured && (_jsxs("div", { style: styles.account, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("span", { style: styles.defaultStar, title: t('defaultBadge'), children: "\u2605" }), _jsx("span", { style: styles.accountName, children: t('externalUsageApiKey') })] }), _jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), snapshot?.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: t('usagePlan', { plan: snapshot.plan }) })), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: loading[id] === true, onClick: () => {
|
|
95
|
+
void refresh(id);
|
|
96
|
+
}, children: t('usageRefresh') })] }), loading[id] === true && snapshot === undefined && (_jsx("p", { style: styles.status, children: t('usageLoading') })), errors[id] !== undefined && (_jsx("p", { style: styles.error, children: t('usageError', { message: errors[id] }) })), snapshot !== undefined && snapshot.supported === false && (_jsx("p", { style: styles.status, children: t('usageUnsupported') })), snapshot?.windows?.length === 0 && (_jsx("p", { style: styles.status, children: t('usageEmpty') })), snapshot?.windows?.map((window, index) => {
|
|
81
97
|
const percent = displayUsedPercent(window.usedPercent);
|
|
82
98
|
const label = externalUsageWindowLabel(t, window);
|
|
83
|
-
return _jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined
|
|
100
|
+
? t('usageMeterInvalid')
|
|
101
|
+
: `${percent}%`, window.resetsAt === undefined
|
|
102
|
+
? ''
|
|
103
|
+
: ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt[id], stale: errors[id] !== undefined || snapshot?.stale === true })] }, index));
|
|
104
|
+
})] })] })), !configured && (_jsx("div", { style: styles.actions, children: _jsx("button", { type: "button", style: styles.button, onClick: () => {
|
|
105
|
+
void refreshStatus();
|
|
106
|
+
}, children: t('usageRefresh') }) }))] }, id));
|
|
107
|
+
}) }));
|
|
87
108
|
}
|
|
@@ -37,7 +37,7 @@ export interface MessageImageLabels {
|
|
|
37
37
|
* preview. A lone image renders at its `singleFit` size; an image among
|
|
38
38
|
* several renders as a fixed 64px square tile.
|
|
39
39
|
*/
|
|
40
|
-
export declare function MessageImage({ attachment, load, variant, labels }: {
|
|
40
|
+
export declare function MessageImage({ attachment, load, variant, labels, }: {
|
|
41
41
|
attachment: ImageAttachmentRef;
|
|
42
42
|
load: ImageLoader;
|
|
43
43
|
variant: 'single' | 'tile';
|
|
@@ -45,7 +45,7 @@ export declare function MessageImage({ attachment, load, variant, labels }: {
|
|
|
45
45
|
}): import("react").JSX.Element;
|
|
46
46
|
/** Wrapping image group: a lone image renders large, several render as 64px
|
|
47
47
|
* square tiles (same rule as the platform gallery). */
|
|
48
|
-
export declare function ImageGallery({ images, load, labels }: {
|
|
48
|
+
export declare function ImageGallery({ images, load, labels, }: {
|
|
49
49
|
images: readonly {
|
|
50
50
|
attachment: ImageAttachmentRef;
|
|
51
51
|
}[];
|
|
@@ -20,7 +20,9 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
20
20
|
function singleFit(attachment) {
|
|
21
21
|
const natural = attachment.width / attachment.height;
|
|
22
22
|
const ratio = Math.min(4, Math.max(0.25, natural));
|
|
23
|
-
const box = ratio >= 1
|
|
23
|
+
const box = ratio >= 1
|
|
24
|
+
? { width: 240, height: 240 / ratio }
|
|
25
|
+
: { width: 240 * ratio, height: 240 };
|
|
24
26
|
const scale = Math.min(1, attachment.width / box.width, attachment.height / box.height);
|
|
25
27
|
return {
|
|
26
28
|
width: Math.max(1, Math.round(box.width * scale)),
|
|
@@ -29,29 +31,68 @@ function singleFit(attachment) {
|
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
const styles = {
|
|
32
|
-
gallery: {
|
|
34
|
+
gallery: {
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexWrap: 'wrap',
|
|
37
|
+
gap: 8,
|
|
38
|
+
justifyContent: 'flex-start',
|
|
39
|
+
},
|
|
33
40
|
frame: {
|
|
34
|
-
display: 'grid',
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
display: 'grid',
|
|
42
|
+
placeItems: 'center',
|
|
43
|
+
overflow: 'hidden',
|
|
44
|
+
padding: 0,
|
|
45
|
+
border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)',
|
|
46
|
+
borderRadius: 8,
|
|
47
|
+
background: 'var(--dsw-alias-interactive-bg-hover-solid)',
|
|
48
|
+
cursor: 'zoom-in',
|
|
37
49
|
},
|
|
38
50
|
tile: { width: 64, height: 64 },
|
|
39
51
|
img: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
|
|
40
|
-
loading: {
|
|
52
|
+
loading: {
|
|
53
|
+
fontSize: 12,
|
|
54
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
55
|
+
padding: '0 8px',
|
|
56
|
+
},
|
|
41
57
|
error: {
|
|
42
|
-
fontSize: 12,
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
fontSize: 12,
|
|
59
|
+
color: 'var(--dsw-alias-state-error-primary)',
|
|
60
|
+
cursor: 'pointer',
|
|
61
|
+
border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)',
|
|
62
|
+
borderRadius: 8,
|
|
63
|
+
background: 'transparent',
|
|
64
|
+
padding: '6px 10px',
|
|
45
65
|
},
|
|
46
66
|
overlay: {
|
|
47
|
-
position: 'fixed',
|
|
48
|
-
|
|
67
|
+
position: 'fixed',
|
|
68
|
+
inset: 0,
|
|
69
|
+
zIndex: 1000,
|
|
70
|
+
display: 'grid',
|
|
71
|
+
placeItems: 'center',
|
|
72
|
+
background: 'rgba(0, 0, 0, 0.72)',
|
|
73
|
+
padding: 24,
|
|
74
|
+
},
|
|
75
|
+
overlayImg: {
|
|
76
|
+
maxWidth: '92vw',
|
|
77
|
+
maxHeight: '92vh',
|
|
78
|
+
objectFit: 'contain',
|
|
79
|
+
borderRadius: 4,
|
|
49
80
|
},
|
|
50
|
-
overlayImg: { maxWidth: '92vw', maxHeight: '92vh', objectFit: 'contain', borderRadius: 4 },
|
|
51
81
|
close: {
|
|
52
|
-
position: 'absolute',
|
|
53
|
-
|
|
54
|
-
|
|
82
|
+
position: 'absolute',
|
|
83
|
+
top: 12,
|
|
84
|
+
right: 12,
|
|
85
|
+
width: 32,
|
|
86
|
+
height: 32,
|
|
87
|
+
display: 'grid',
|
|
88
|
+
placeItems: 'center',
|
|
89
|
+
border: 'none',
|
|
90
|
+
borderRadius: '50%',
|
|
91
|
+
cursor: 'pointer',
|
|
92
|
+
background: 'rgba(255, 255, 255, 0.16)',
|
|
93
|
+
color: '#fff',
|
|
94
|
+
fontSize: 16,
|
|
95
|
+
lineHeight: 1,
|
|
55
96
|
},
|
|
56
97
|
};
|
|
57
98
|
/**
|
|
@@ -59,52 +100,70 @@ const styles = {
|
|
|
59
100
|
* Escape all dismiss; the image itself is inert so a click on it does not
|
|
60
101
|
* fall through to the backdrop dismissal.
|
|
61
102
|
*/
|
|
62
|
-
function ImageLightbox({ src, alt, labels, onClose }) {
|
|
103
|
+
function ImageLightbox({ src, alt, labels, onClose, }) {
|
|
63
104
|
useEffect(() => {
|
|
64
|
-
const onKey = (event) => {
|
|
65
|
-
|
|
105
|
+
const onKey = (event) => {
|
|
106
|
+
if (event.key === 'Escape')
|
|
107
|
+
onClose();
|
|
108
|
+
};
|
|
66
109
|
window.addEventListener('keydown', onKey);
|
|
67
|
-
return () => {
|
|
110
|
+
return () => {
|
|
111
|
+
window.removeEventListener('keydown', onKey);
|
|
112
|
+
};
|
|
68
113
|
}, [onClose]);
|
|
69
|
-
return (_jsxs("div", { role: "dialog", "aria-label": labels.dialog, style: styles.overlay, onClick: onClose, children: [_jsx("img", { src: src, alt: alt, style: styles.overlayImg, onClick: event => {
|
|
114
|
+
return (_jsxs("div", { role: "dialog", "aria-label": labels.dialog, style: styles.overlay, onClick: onClose, children: [_jsx("img", { src: src, alt: alt, style: styles.overlayImg, onClick: (event) => {
|
|
115
|
+
event.stopPropagation();
|
|
116
|
+
} }), _jsx("button", { type: "button", "aria-label": labels.close, style: styles.close, onClick: onClose, children: "\u00D7" })] }));
|
|
70
117
|
}
|
|
71
118
|
/**
|
|
72
119
|
* Compact history renderer with retryable loading and click-to-open original
|
|
73
120
|
* preview. A lone image renders at its `singleFit` size; an image among
|
|
74
121
|
* several renders as a fixed 64px square tile.
|
|
75
122
|
*/
|
|
76
|
-
export function MessageImage({ attachment, load, variant, labels }) {
|
|
123
|
+
export function MessageImage({ attachment, load, variant, labels, }) {
|
|
77
124
|
const [src, setSrc] = useState(null);
|
|
78
125
|
const [error, setError] = useState(false);
|
|
79
126
|
const [open, setOpen] = useState(false);
|
|
80
127
|
// Retry re-arms the one load effect below, so every attempt — first load or
|
|
81
128
|
// retry — runs under the same liveness guard and the same reset.
|
|
82
129
|
const [attempt, setAttempt] = useState(0);
|
|
83
|
-
const retry = useCallback(() => {
|
|
84
|
-
|
|
130
|
+
const retry = useCallback(() => {
|
|
131
|
+
setAttempt((a) => a + 1);
|
|
132
|
+
}, []);
|
|
133
|
+
const close = useCallback(() => {
|
|
134
|
+
setOpen(false);
|
|
135
|
+
}, []);
|
|
85
136
|
const fit = useMemo(() => (variant === 'single' ? singleFit(attachment) : undefined), [attachment, variant]);
|
|
86
137
|
useEffect(() => {
|
|
87
138
|
let live = true;
|
|
88
139
|
setError(false);
|
|
89
140
|
setSrc(null);
|
|
90
|
-
void load(attachment)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
141
|
+
void load(attachment)
|
|
142
|
+
.then((url) => {
|
|
143
|
+
if (live)
|
|
144
|
+
setSrc(url);
|
|
145
|
+
})
|
|
146
|
+
.catch(() => {
|
|
147
|
+
if (live)
|
|
148
|
+
setError(true);
|
|
149
|
+
});
|
|
150
|
+
return () => {
|
|
151
|
+
live = false;
|
|
152
|
+
};
|
|
94
153
|
}, [attachment, load, attempt]);
|
|
95
154
|
const label = attachment.name ?? labels.image;
|
|
96
155
|
if (error) {
|
|
97
|
-
return _jsx("button", { type: "button", style: styles.error, onClick: retry, children: labels.loadFailed });
|
|
156
|
+
return (_jsx("button", { type: "button", style: styles.error, onClick: retry, children: labels.loadFailed }));
|
|
98
157
|
}
|
|
99
158
|
const box = fit === undefined ? styles.tile : { width: fit.width, height: fit.height };
|
|
100
|
-
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", style: { ...styles.frame, ...box }, title: labels.open, "aria-label": labels.openNamed(label), onClick: () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
159
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", style: { ...styles.frame, ...box }, title: labels.open, "aria-label": labels.openNamed(label), onClick: () => {
|
|
160
|
+
if (src !== null)
|
|
161
|
+
setOpen(true);
|
|
162
|
+
}, children: src === null ? (_jsx("span", { style: styles.loading, children: labels.loading })) : (_jsx("img", { src: src, alt: label, style: { ...styles.img, objectPosition: fit?.objectPosition } })) }), open && src !== null && (_jsx(ImageLightbox, { src: src, alt: label, labels: labels.lightbox, onClose: close }))] }));
|
|
104
163
|
}
|
|
105
164
|
/** Wrapping image group: a lone image renders large, several render as 64px
|
|
106
165
|
* square tiles (same rule as the platform gallery). */
|
|
107
|
-
export function ImageGallery({ images, load, labels }) {
|
|
166
|
+
export function ImageGallery({ images, load, labels, }) {
|
|
108
167
|
if (images.length === 0)
|
|
109
168
|
return null;
|
|
110
169
|
const variant = images.length === 1 ? 'single' : 'tile';
|
|
@@ -16,8 +16,9 @@ const PROMPT_MAX_LENGTH = 60;
|
|
|
16
16
|
export function createImageLoader(rpc) {
|
|
17
17
|
// The host validates a full ImageAttachmentRef payload (readImage takes the
|
|
18
18
|
// whole ref), so forward the attachment verbatim.
|
|
19
|
-
return attachment => callSubscriptionsAuth(rpc, 'image', {
|
|
20
|
-
|
|
19
|
+
return (attachment) => callSubscriptionsAuth(rpc, 'image', {
|
|
20
|
+
...attachment,
|
|
21
|
+
}).then((result) => `data:${result.mediaType};base64,${result.dataBase64}`);
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* English-dictionary fallback for a missing locale seat (standalone renders);
|
|
@@ -58,7 +59,9 @@ function derivePrompt(argsRaw) {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
const line = (prompt ?? argsRaw).split('\n', 1)[0] ?? '';
|
|
61
|
-
return line.length > PROMPT_MAX_LENGTH
|
|
62
|
+
return line.length > PROMPT_MAX_LENGTH
|
|
63
|
+
? `${line.slice(0, PROMPT_MAX_LENGTH)}…`
|
|
64
|
+
: line;
|
|
62
65
|
}
|
|
63
66
|
/** Flatten a settled result's text blocks (the degraded text-only route and the error line). */
|
|
64
67
|
function resultText(block) {
|
|
@@ -85,19 +88,46 @@ function resultImages(block) {
|
|
|
85
88
|
return images;
|
|
86
89
|
}
|
|
87
90
|
const styles = {
|
|
88
|
-
container: {
|
|
91
|
+
container: {
|
|
92
|
+
display: 'flex',
|
|
93
|
+
flexDirection: 'column',
|
|
94
|
+
gap: 6,
|
|
95
|
+
padding: '4px 0',
|
|
96
|
+
},
|
|
89
97
|
row: { display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 },
|
|
90
|
-
icon: {
|
|
98
|
+
icon: {
|
|
99
|
+
display: 'inline-flex',
|
|
100
|
+
flexShrink: 0,
|
|
101
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
102
|
+
},
|
|
91
103
|
title: {
|
|
92
|
-
fontSize: 13,
|
|
93
|
-
|
|
104
|
+
fontSize: 13,
|
|
105
|
+
lineHeight: '20px',
|
|
106
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
107
|
+
overflow: 'hidden',
|
|
108
|
+
textOverflow: 'ellipsis',
|
|
109
|
+
whiteSpace: 'nowrap',
|
|
110
|
+
},
|
|
111
|
+
subtle: {
|
|
112
|
+
margin: 0,
|
|
113
|
+
fontSize: 12,
|
|
114
|
+
lineHeight: '18px',
|
|
115
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
94
116
|
},
|
|
95
|
-
subtle: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },
|
|
96
117
|
output: {
|
|
97
|
-
margin: 0,
|
|
98
|
-
|
|
118
|
+
margin: 0,
|
|
119
|
+
fontSize: 12,
|
|
120
|
+
lineHeight: '18px',
|
|
121
|
+
color: 'var(--dsw-alias-label-secondary)',
|
|
122
|
+
whiteSpace: 'pre-wrap',
|
|
123
|
+
overflowWrap: 'anywhere',
|
|
124
|
+
},
|
|
125
|
+
error: {
|
|
126
|
+
margin: 0,
|
|
127
|
+
fontSize: 12,
|
|
128
|
+
lineHeight: '18px',
|
|
129
|
+
color: 'var(--dsw-alias-state-error-primary)',
|
|
99
130
|
},
|
|
100
|
-
error: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },
|
|
101
131
|
};
|
|
102
132
|
/**
|
|
103
133
|
* The `image_generate` keyed toolview component.
|
|
@@ -110,21 +140,27 @@ export function ImageGenerateToolview(props) {
|
|
|
110
140
|
if (block === undefined)
|
|
111
141
|
return null;
|
|
112
142
|
const settled = 'kind' in block;
|
|
113
|
-
const argsRaw = (settled
|
|
143
|
+
const argsRaw = (settled
|
|
144
|
+
? block.call?.argsRaw
|
|
145
|
+
: block.phase === 'start'
|
|
146
|
+
? block.argsRaw
|
|
147
|
+
: undefined) ?? '';
|
|
114
148
|
let references = 0;
|
|
115
149
|
try {
|
|
116
150
|
const args = JSON.parse(argsRaw);
|
|
117
151
|
if (Array.isArray(args?.referenceImages))
|
|
118
152
|
references = args.referenceImages.length;
|
|
119
153
|
}
|
|
120
|
-
catch {
|
|
154
|
+
catch {
|
|
155
|
+
/* Arguments may still be streaming. */
|
|
156
|
+
}
|
|
121
157
|
const title = `image_generate${references > 0 ? ` (${references} ref)` : ''}: ${derivePrompt(argsRaw)}`;
|
|
122
158
|
const images = resultImages(block);
|
|
123
159
|
const text = settled ? resultText(block) : '';
|
|
124
160
|
const labels = {
|
|
125
161
|
image: t('image'),
|
|
126
162
|
open: t('viewImage'),
|
|
127
|
-
openNamed: name => t('viewImageNamed', { name }),
|
|
163
|
+
openNamed: (name) => t('viewImageNamed', { name }),
|
|
128
164
|
loading: t('imageLoading'),
|
|
129
165
|
loadFailed: t('imageLoadFailed'),
|
|
130
166
|
lightbox: { dialog: t('imagePreview'), close: t('imageClose') },
|
|
@@ -6,7 +6,7 @@ type Translate = SubscriptionsSectionInjected['t'];
|
|
|
6
6
|
* capability toggle, and the API-key usage row. The settings dialog owns
|
|
7
7
|
* every write; the card only reads, so a save is reflected by re-reading.
|
|
8
8
|
*/
|
|
9
|
-
export declare function OllamaCard({ rpc, t }: {
|
|
9
|
+
export declare function OllamaCard({ rpc, t, }: {
|
|
10
10
|
rpc: ConnectionHandle['rpc'];
|
|
11
11
|
t: Translate;
|
|
12
12
|
}): import("react").JSX.Element;
|
package/lib/client/OllamaCard.js
CHANGED
|
@@ -15,7 +15,7 @@ function message(error) {
|
|
|
15
15
|
* capability toggle, and the API-key usage row. The settings dialog owns
|
|
16
16
|
* every write; the card only reads, so a save is reflected by re-reading.
|
|
17
17
|
*/
|
|
18
|
-
export function OllamaCard({ rpc, t }) {
|
|
18
|
+
export function OllamaCard({ rpc, t, }) {
|
|
19
19
|
const [view, setView] = useState();
|
|
20
20
|
const [configured, setConfigured] = useState();
|
|
21
21
|
const [usage, setUsage] = useState();
|
|
@@ -64,13 +64,31 @@ export function OllamaCard({ rpc, t }) {
|
|
|
64
64
|
setStatusError(message(failure));
|
|
65
65
|
}
|
|
66
66
|
}, [rpc, refreshUsage]);
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
void refreshStatus();
|
|
69
|
+
}, [refreshStatus]);
|
|
70
|
+
return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: {
|
|
71
|
+
...styles.dot,
|
|
72
|
+
background: configured === true
|
|
73
|
+
? 'var(--dsw-alias-state-success-primary)'
|
|
74
|
+
: 'var(--dsw-alias-label-dimmed)',
|
|
75
|
+
} }), _jsx("span", { style: styles.name, children: t('ollamaTitle') })] }), _jsx("p", { style: styles.status, children: view === undefined
|
|
76
|
+
? t('checking')
|
|
77
|
+
: view.models.length > 0
|
|
78
|
+
? t('ollamaStatus', { count: view.models.length })
|
|
79
|
+
: t('ollamaStatusNone') }), view !== undefined && (_jsx("p", { style: styles.status, children: view.effective.web ? t('ollamaWebOn') : t('ollamaWebOff') })), statusError !== undefined && (_jsx("p", { style: styles.error, children: t('ollamaModelsLoadFailed', { message: statusError }) })), view !== undefined && configured === false && (_jsx("p", { style: styles.status, children: t('ollamaConfigureHint', { ref: view.effective.apiKeyRef }) })), configured === true && (_jsxs("div", { style: styles.account, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("span", { style: styles.defaultStar, title: t('defaultBadge'), children: "\u2605" }), _jsx("span", { style: styles.accountName, children: t('externalUsageApiKey') })] }), _jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), usage?.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: t('usagePlan', { plan: usage.plan }) })), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: loading, onClick: () => {
|
|
80
|
+
void refreshUsage();
|
|
81
|
+
}, children: t('usageRefresh') })] }), loading && usage === undefined && (_jsx("p", { style: styles.status, children: t('usageLoading') })), usageError !== undefined && (_jsx("p", { style: styles.error, children: t('usageError', { message: usageError }) })), usage !== undefined && usage.supported === false && (_jsx("p", { style: styles.status, children: t('usageUnsupported') })), usage?.windows?.length === 0 && (_jsx("p", { style: styles.status, children: t('usageEmpty') })), usage?.windows?.map((window, index) => {
|
|
71
82
|
const percent = displayUsedPercent(window.usedPercent);
|
|
72
83
|
const label = externalUsageWindowLabel(t, window);
|
|
73
|
-
return _jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [percent === undefined
|
|
85
|
+
? t('usageMeterInvalid')
|
|
86
|
+
: `${percent}%`, window.resetsAt === undefined
|
|
87
|
+
? ''
|
|
88
|
+
: ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt, stale: usageStale })] }, index));
|
|
89
|
+
})] })] })), view?.configOverridesModels === true && (_jsx("p", { style: styles.status, children: t('ollamaCatalogOverride') })), _jsxs("div", { style: styles.actions, children: [_jsx("button", { type: "button", style: styles.button, "aria-haspopup": "dialog", onClick: () => setManage(true), children: t('accountsManage') }), configured !== true && (_jsx("button", { type: "button", style: styles.button, onClick: () => {
|
|
90
|
+
void refreshStatus();
|
|
91
|
+
}, children: t('usageRefresh') }))] }), manage && (_jsx(OllamaSettingsManager, { rpc: rpc, t: t, onClose: () => setManage(false), onSaved: () => {
|
|
92
|
+
void refreshStatus();
|
|
93
|
+
} }))] }));
|
|
76
94
|
}
|