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.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/cordis.patch.yml +12 -0
  4. package/lib/auth/claude-code-creds.d.ts +23 -0
  5. package/lib/auth/claude-code-creds.js +172 -0
  6. package/lib/auth/device-flow.d.ts +55 -0
  7. package/lib/auth/device-flow.js +177 -0
  8. package/lib/auth/jwt.d.ts +10 -0
  9. package/lib/auth/jwt.js +25 -0
  10. package/lib/auth/oauth-flow.d.ts +91 -0
  11. package/lib/auth/oauth-flow.js +227 -0
  12. package/lib/auth/pkce.d.ts +31 -0
  13. package/lib/auth/pkce.js +35 -0
  14. package/lib/auth/rpc.d.ts +209 -0
  15. package/lib/auth/rpc.js +386 -0
  16. package/lib/auth/store.d.ts +196 -0
  17. package/lib/auth/store.js +468 -0
  18. package/lib/client/CursorCard.d.ts +9 -0
  19. package/lib/client/CursorCard.js +113 -0
  20. package/lib/client/ExternalUsageCards.d.ts +9 -0
  21. package/lib/client/ExternalUsageCards.js +72 -0
  22. package/lib/client/ImageGallery.d.ts +54 -0
  23. package/lib/client/ImageGallery.js +112 -0
  24. package/lib/client/ImageGenerateToolview.d.ts +48 -0
  25. package/lib/client/ImageGenerateToolview.js +133 -0
  26. package/lib/client/ProviderAccountManager.d.ts +13 -0
  27. package/lib/client/ProviderAccountManager.js +147 -0
  28. package/lib/client/ProviderModelEditor.d.ts +39 -0
  29. package/lib/client/ProviderModelEditor.js +107 -0
  30. package/lib/client/SpeedSelect.d.ts +71 -0
  31. package/lib/client/SpeedSelect.js +177 -0
  32. package/lib/client/SubscriptionUsageBadge.d.ts +116 -0
  33. package/lib/client/SubscriptionUsageBadge.js +543 -0
  34. package/lib/client/SubscriptionsSection.d.ts +140 -0
  35. package/lib/client/SubscriptionsSection.js +544 -0
  36. package/lib/client/UsageBadgeDisplaySetting.d.ts +7 -0
  37. package/lib/client/UsageBadgeDisplaySetting.js +36 -0
  38. package/lib/client/VideoGenerateToolview.d.ts +42 -0
  39. package/lib/client/VideoGenerateToolview.js +175 -0
  40. package/lib/client/account-preferences.d.ts +24 -0
  41. package/lib/client/account-preferences.js +21 -0
  42. package/lib/client/fast-command.d.ts +2 -0
  43. package/lib/client/fast-command.js +4 -0
  44. package/lib/client/host-contract.d.ts +90 -0
  45. package/lib/client/host-contract.js +101 -0
  46. package/lib/client/host-icons.d.ts +24 -0
  47. package/lib/client/host-icons.js +21 -0
  48. package/lib/client/index.d.ts +27 -0
  49. package/lib/client/index.js +136 -0
  50. package/lib/client/locales.d.ts +297 -0
  51. package/lib/client/locales.js +295 -0
  52. package/lib/client/subscription-card-styles.d.ts +3 -0
  53. package/lib/client/subscription-card-styles.js +53 -0
  54. package/lib/client/subscriptions-rpc.d.ts +14 -0
  55. package/lib/client/subscriptions-rpc.js +34 -0
  56. package/lib/client/usage-badge-preferences.d.ts +11 -0
  57. package/lib/client/usage-badge-preferences.js +41 -0
  58. package/lib/client.js +4472 -0
  59. package/lib/client.js.map +1 -0
  60. package/lib/compat.d.ts +36 -0
  61. package/lib/compat.js +20 -0
  62. package/lib/http.d.ts +6 -0
  63. package/lib/http.js +15 -0
  64. package/lib/index.d.ts +80 -0
  65. package/lib/index.js +14018 -0
  66. package/lib/model-defaults.d.ts +23 -0
  67. package/lib/model-defaults.js +237 -0
  68. package/lib/provider-settings.d.ts +39 -0
  69. package/lib/provider-settings.js +151 -0
  70. package/lib/providers/account-preferences.d.ts +41 -0
  71. package/lib/providers/account-preferences.js +153 -0
  72. package/lib/providers/accounts.d.ts +112 -0
  73. package/lib/providers/accounts.js +142 -0
  74. package/lib/providers/antigravity-oauth-client.d.ts +7 -0
  75. package/lib/providers/antigravity-oauth-client.js +7 -0
  76. package/lib/providers/antigravity.d.ts +110 -0
  77. package/lib/providers/antigravity.js +516 -0
  78. package/lib/providers/catalog-store.d.ts +48 -0
  79. package/lib/providers/catalog-store.js +243 -0
  80. package/lib/providers/claude-cli-version.d.ts +15 -0
  81. package/lib/providers/claude-cli-version.js +24 -0
  82. package/lib/providers/claude.d.ts +181 -0
  83. package/lib/providers/claude.js +680 -0
  84. package/lib/providers/codex-client-version.d.ts +8 -0
  85. package/lib/providers/codex-client-version.js +17 -0
  86. package/lib/providers/codex-search.d.ts +29 -0
  87. package/lib/providers/codex-search.js +198 -0
  88. package/lib/providers/codex.d.ts +229 -0
  89. package/lib/providers/codex.js +821 -0
  90. package/lib/providers/common.d.ts +389 -0
  91. package/lib/providers/common.js +656 -0
  92. package/lib/providers/copilot.d.ts +344 -0
  93. package/lib/providers/copilot.js +888 -0
  94. package/lib/providers/cursor-adapter.d.ts +15 -0
  95. package/lib/providers/cursor-adapter.js +39 -0
  96. package/lib/providers/cursor-auth.d.ts +43 -0
  97. package/lib/providers/cursor-auth.js +169 -0
  98. package/lib/providers/cursor-model-settings.d.ts +15 -0
  99. package/lib/providers/cursor-model-settings.js +51 -0
  100. package/lib/providers/cursor-usage.d.ts +9 -0
  101. package/lib/providers/cursor-usage.js +112 -0
  102. package/lib/providers/external-usage-controller.d.ts +19 -0
  103. package/lib/providers/external-usage-controller.js +32 -0
  104. package/lib/providers/external-usage.d.ts +8 -0
  105. package/lib/providers/external-usage.js +82 -0
  106. package/lib/providers/grok.d.ts +205 -0
  107. package/lib/providers/grok.js +713 -0
  108. package/lib/providers/image-pool.d.ts +30 -0
  109. package/lib/providers/image-pool.js +89 -0
  110. package/lib/providers/npm-cli-version.d.ts +56 -0
  111. package/lib/providers/npm-cli-version.js +98 -0
  112. package/lib/providers/pool-family.d.ts +56 -0
  113. package/lib/providers/pool-family.js +45 -0
  114. package/lib/providers/pool-health.d.ts +74 -0
  115. package/lib/providers/pool-health.js +150 -0
  116. package/lib/providers/pool-usage.d.ts +86 -0
  117. package/lib/providers/pool-usage.js +223 -0
  118. package/lib/providers/pool.d.ts +108 -0
  119. package/lib/providers/pool.js +368 -0
  120. package/lib/providers/rate-limit.d.ts +192 -0
  121. package/lib/providers/rate-limit.js +338 -0
  122. package/lib/tools/image-generate.d.ts +128 -0
  123. package/lib/tools/image-generate.js +438 -0
  124. package/lib/tools/registration.d.ts +15 -0
  125. package/lib/tools/registration.js +24 -0
  126. package/lib/tools/video-generate.d.ts +89 -0
  127. package/lib/tools/video-generate.js +257 -0
  128. package/lib/tools/x-search.d.ts +58 -0
  129. package/lib/tools/x-search.js +197 -0
  130. package/lib/translate/anthropic.d.ts +161 -0
  131. package/lib/translate/anthropic.js +515 -0
  132. package/lib/translate/antigravity-schema.d.ts +4 -0
  133. package/lib/translate/antigravity-schema.js +145 -0
  134. package/lib/translate/antigravity-thinking.d.ts +5 -0
  135. package/lib/translate/antigravity-thinking.js +44 -0
  136. package/lib/translate/antigravity.d.ts +114 -0
  137. package/lib/translate/antigravity.js +366 -0
  138. package/lib/translate/chat-completions.d.ts +120 -0
  139. package/lib/translate/chat-completions.js +382 -0
  140. package/lib/translate/resolved.d.ts +80 -0
  141. package/lib/translate/resolved.js +118 -0
  142. package/lib/translate/responses.d.ts +179 -0
  143. package/lib/translate/responses.js +408 -0
  144. package/lib/translate/sse.d.ts +21 -0
  145. package/lib/translate/sse.js +56 -0
  146. package/package.json +114 -0
  147. package/vendor/cursor/LICENSE +21 -0
  148. package/vendor/cursor/index.d.ts +18 -0
  149. package/vendor/cursor/index.js +3574 -0
  150. package/vendor/cursor/proto.js +254 -0
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { callSubscriptionsAuth } from './subscriptions-rpc.js';
4
+ import { subscriptionCardStyles as styles } from './subscription-card-styles.js';
5
+ import { USAGE_BADGE_REFRESH_EVENT } from './usage-badge-preferences.js';
6
+ const SOURCES = [
7
+ { id: 'opencode-go', name: 'OpenCode Go', ref: 'OPENCODE_GO_API_KEY' },
8
+ { id: 'kimi-code', name: 'Kimi Code', ref: 'KIMI_CODING_API_KEY' },
9
+ ];
10
+ function errorText(error) {
11
+ return error instanceof Error ? error.message : String(error);
12
+ }
13
+ function barColor(percent) {
14
+ if (percent >= 95)
15
+ return 'var(--dsw-alias-state-error-primary)';
16
+ if (percent >= 80)
17
+ return 'var(--dsw-alias-state-warn-label)';
18
+ return 'var(--dsw-alias-state-success-primary)';
19
+ }
20
+ /** Quota cards for API-key providers already available through DSH itself. */
21
+ export function ExternalUsageCards({ rpc, t }) {
22
+ const [status, setStatus] = useState();
23
+ const [statusError, setStatusError] = useState();
24
+ const [usage, setUsage] = useState({});
25
+ const [errors, setErrors] = useState({});
26
+ const [loading, setLoading] = useState({});
27
+ const refresh = useCallback(async (source) => {
28
+ setLoading(prev => ({ ...prev, [source]: true }));
29
+ setErrors(prev => ({ ...prev, [source]: undefined }));
30
+ try {
31
+ const value = await callSubscriptionsAuth(rpc, 'externalUsage', { source });
32
+ setUsage(prev => ({ ...prev, [source]: value }));
33
+ window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
34
+ }
35
+ catch (error) {
36
+ setErrors(prev => ({ ...prev, [source]: errorText(error) }));
37
+ }
38
+ finally {
39
+ setLoading(prev => ({ ...prev, [source]: false }));
40
+ }
41
+ }, [rpc]);
42
+ const refreshStatus = useCallback(async () => {
43
+ try {
44
+ const value = await callSubscriptionsAuth(rpc, 'externalStatus', {});
45
+ setStatus(value);
46
+ setStatusError(undefined);
47
+ for (const { id } of SOURCES)
48
+ if (value[id]?.configured)
49
+ void refresh(id);
50
+ }
51
+ catch (error) {
52
+ setStatusError(errorText(error));
53
+ }
54
+ }, [rpc, refresh]);
55
+ useEffect(() => {
56
+ void refreshStatus();
57
+ }, [refreshStatus]);
58
+ return _jsx(_Fragment, { children: SOURCES.map(({ id, name, ref }) => {
59
+ const configured = status?.[id]?.configured === true;
60
+ const snapshot = usage[id];
61
+ return _jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: { ...styles.dot, background: configured
62
+ ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-label-dimmed)' } }), _jsx("span", { style: styles.name, children: name })] }), !(status === undefined && statusError !== undefined) && _jsx("p", { style: styles.status, children: status === undefined ? t('checking')
63
+ : configured ? t('externalUsageConnected') : 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: () => { void refresh(id); }, 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?.windows?.length === 0 && _jsx("p", { style: styles.status, children: t('usageEmpty') }), snapshot?.windows?.map((window, index) => {
64
+ const percent = Math.min(100, Math.max(0, window.usedPercent));
65
+ const label = window.kind === 'session' ? t('usageSession')
66
+ : window.kind === 'weekly' ? t('usageWeekly')
67
+ : window.scope === 'Monthly' ? t('usageMonthly') : window.scope ?? t('usageWindow');
68
+ return _jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: label }), _jsxs("span", { children: [Math.round(percent), "%", window.resetsAt === undefined ? ''
69
+ : ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx("div", { style: styles.usageTrack, children: _jsx("div", { style: { ...styles.usageFill, width: `${percent}%`, background: barColor(percent) } }) })] }, index);
70
+ })] })] }), !configured && _jsx("div", { style: styles.actions, children: _jsx("button", { type: "button", style: styles.button, onClick: () => { void refreshStatus(); }, children: t('usageRefresh') }) })] }, id);
71
+ }) });
72
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Mirror of ImageAttachmentRef (packages/attachment/attachment/src/types.ts);
3
+ * the brand on attachmentId is compile-time only, so string suffices here.
4
+ */
5
+ export interface ImageAttachmentRef {
6
+ attachmentId: string;
7
+ mediaType: string;
8
+ bytes: number;
9
+ width: number;
10
+ height: number;
11
+ name?: string;
12
+ }
13
+ /** Loads a session-authorized durable image URL (resolves to a data/blob URL). */
14
+ export type ImageLoader = (attachment: ImageAttachmentRef) => Promise<string>;
15
+ /** Lightbox strings forwarded to the opened preview. */
16
+ export interface ImageLightboxLabels {
17
+ dialog: string;
18
+ close: string;
19
+ }
20
+ /** Message-image strings the owner resolves from its own locale namespace. */
21
+ export interface MessageImageLabels {
22
+ /** Fallback display name for an unnamed image. */
23
+ image: string;
24
+ /** Thumbnail tooltip inviting the original-image preview. */
25
+ open: string;
26
+ /** Accessible thumbnail label; receives the image's display name. */
27
+ openNamed: (label: string) => string;
28
+ /** Loading placeholder shown until bytes resolve. */
29
+ loading: string;
30
+ /** Retry-control label shown when the load fails. */
31
+ loadFailed: string;
32
+ /** Lightbox strings forwarded to the opened preview. */
33
+ lightbox: ImageLightboxLabels;
34
+ }
35
+ /**
36
+ * Compact history renderer with retryable loading and click-to-open original
37
+ * preview. A lone image renders at its `singleFit` size; an image among
38
+ * several renders as a fixed 64px square tile.
39
+ */
40
+ export declare function MessageImage({ attachment, load, variant, labels }: {
41
+ attachment: ImageAttachmentRef;
42
+ load: ImageLoader;
43
+ variant: 'single' | 'tile';
44
+ labels: MessageImageLabels;
45
+ }): import("react").JSX.Element;
46
+ /** Wrapping image group: a lone image renders large, several render as 64px
47
+ * square tiles (same rule as the platform gallery). */
48
+ export declare function ImageGallery({ images, load, labels }: {
49
+ images: readonly {
50
+ attachment: ImageAttachmentRef;
51
+ }[];
52
+ load: ImageLoader;
53
+ labels: MessageImageLabels;
54
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,112 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * Plugin-local image gallery for generated-image toolviews.
4
+ *
5
+ * Since web-app rc.8, `@deepseek-ai/dsh-client-ui-attachment`'s browser module
6
+ * exports only its cordis plugin surface (`apply`/`inject`) — the React
7
+ * components are no longer package values, so importing `ImageGallery` from it
8
+ * yields `undefined` at runtime and crashes the toolview inside the slot error
9
+ * boundary (the whole call row disappears). This module re-implements the
10
+ * gallery contract the toolview needs (loader-driven thumbnails, retry on
11
+ * failure, click-to-open lightbox) with the same sizing rules as the platform
12
+ * component, keeping this plugin independent of harness component exports.
13
+ */
14
+ import { useCallback, useEffect, useMemo, useState } from 'react';
15
+ /** Display box for a lone image (platform rule): long edge 240px with the
16
+ * rendered aspect ratio clamped to [0.25, 4] — the overflow is cropped by
17
+ * `object-fit: cover` — and never upscaled past the image's natural size. The
18
+ * crop anchor keeps the top of very tall images and the left of very wide
19
+ * ones, where the informative content usually starts. */
20
+ function singleFit(attachment) {
21
+ const natural = attachment.width / attachment.height;
22
+ const ratio = Math.min(4, Math.max(0.25, natural));
23
+ const box = ratio >= 1 ? { width: 240, height: 240 / ratio } : { width: 240 * ratio, height: 240 };
24
+ const scale = Math.min(1, attachment.width / box.width, attachment.height / box.height);
25
+ return {
26
+ width: Math.max(1, Math.round(box.width * scale)),
27
+ height: Math.max(1, Math.round(box.height * scale)),
28
+ objectPosition: natural < 0.25 ? 'center top' : natural > 4 ? 'left center' : 'center',
29
+ };
30
+ }
31
+ const styles = {
32
+ gallery: { display: 'flex', flexWrap: 'wrap', gap: 8, justifyContent: 'flex-start' },
33
+ frame: {
34
+ display: 'grid', placeItems: 'center', overflow: 'hidden', padding: 0,
35
+ border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)', borderRadius: 8,
36
+ background: 'var(--dsw-alias-interactive-bg-hover-solid)', cursor: 'zoom-in',
37
+ },
38
+ tile: { width: 64, height: 64 },
39
+ img: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' },
40
+ loading: { fontSize: 12, color: 'var(--dsw-alias-label-tertiary)', padding: '0 8px' },
41
+ error: {
42
+ fontSize: 12, color: 'var(--dsw-alias-state-error-primary)', cursor: 'pointer',
43
+ border: '1px solid var(--dsw-alias-border-l2-darkmode-thin)', borderRadius: 8,
44
+ background: 'transparent', padding: '6px 10px',
45
+ },
46
+ overlay: {
47
+ position: 'fixed', inset: 0, zIndex: 1000, display: 'grid', placeItems: 'center',
48
+ background: 'rgba(0, 0, 0, 0.72)', padding: 24,
49
+ },
50
+ overlayImg: { maxWidth: '92vw', maxHeight: '92vh', objectFit: 'contain', borderRadius: 4 },
51
+ close: {
52
+ position: 'absolute', top: 12, right: 12, width: 32, height: 32, display: 'grid',
53
+ placeItems: 'center', border: 'none', borderRadius: '50%', cursor: 'pointer',
54
+ background: 'rgba(255, 255, 255, 0.16)', color: '#fff', fontSize: 16, lineHeight: 1,
55
+ },
56
+ };
57
+ /**
58
+ * Full-viewport original-image preview: backdrop or close-control click and
59
+ * Escape all dismiss; the image itself is inert so a click on it does not
60
+ * fall through to the backdrop dismissal.
61
+ */
62
+ function ImageLightbox({ src, alt, labels, onClose }) {
63
+ useEffect(() => {
64
+ const onKey = (event) => { if (event.key === 'Escape')
65
+ onClose(); };
66
+ window.addEventListener('keydown', onKey);
67
+ return () => { window.removeEventListener('keydown', onKey); };
68
+ }, [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 => { event.stopPropagation(); } }), _jsx("button", { type: "button", "aria-label": labels.close, style: styles.close, onClick: onClose, children: "\u00D7" })] }));
70
+ }
71
+ /**
72
+ * Compact history renderer with retryable loading and click-to-open original
73
+ * preview. A lone image renders at its `singleFit` size; an image among
74
+ * several renders as a fixed 64px square tile.
75
+ */
76
+ export function MessageImage({ attachment, load, variant, labels }) {
77
+ const [src, setSrc] = useState(null);
78
+ const [error, setError] = useState(false);
79
+ const [open, setOpen] = useState(false);
80
+ // Retry re-arms the one load effect below, so every attempt — first load or
81
+ // retry — runs under the same liveness guard and the same reset.
82
+ const [attempt, setAttempt] = useState(0);
83
+ const retry = useCallback(() => { setAttempt(a => a + 1); }, []);
84
+ const close = useCallback(() => { setOpen(false); }, []);
85
+ const fit = useMemo(() => (variant === 'single' ? singleFit(attachment) : undefined), [attachment, variant]);
86
+ useEffect(() => {
87
+ let live = true;
88
+ setError(false);
89
+ setSrc(null);
90
+ void load(attachment).then((url) => { if (live)
91
+ setSrc(url); }).catch(() => { if (live)
92
+ setError(true); });
93
+ return () => { live = false; };
94
+ }, [attachment, load, attempt]);
95
+ const label = attachment.name ?? labels.image;
96
+ if (error) {
97
+ return _jsx("button", { type: "button", style: styles.error, onClick: retry, children: labels.loadFailed });
98
+ }
99
+ 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: () => { if (src !== null)
101
+ setOpen(true); }, children: src === null
102
+ ? _jsx("span", { style: styles.loading, children: labels.loading })
103
+ : _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
+ }
105
+ /** Wrapping image group: a lone image renders large, several render as 64px
106
+ * square tiles (same rule as the platform gallery). */
107
+ export function ImageGallery({ images, load, labels }) {
108
+ if (images.length === 0)
109
+ return null;
110
+ const variant = images.length === 1 ? 'single' : 'tile';
111
+ return (_jsx("div", { style: styles.gallery, children: images.map((image, index) => (_jsx(MessageImage, { attachment: image.attachment, load: load, variant: variant, labels: labels }, `${image.attachment.attachmentId}:${index}`))) }));
112
+ }
@@ -0,0 +1,48 @@
1
+ import type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client';
2
+ import type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client';
3
+ import type { ImageLoader } from './ImageGallery.js';
4
+ import type { SubscriptionsKey } from './locales.js';
5
+ /** Mirror of ui-tool's ToolCallOwnerProps (see the module header). */
6
+ interface ToolCallOwnerProps {
7
+ callId: string;
8
+ toolName: string;
9
+ block: ToolCallBlock;
10
+ cwd?: string | undefined;
11
+ openFile: (path: string) => void;
12
+ inspect?: (() => void) | undefined;
13
+ }
14
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
15
+ interface SlotMap {
16
+ /** Mirror of ui-tool's keyed atomic Tool view declaration (see the module header). */
17
+ 'tool.call.toolview': {
18
+ kind: 'keyed';
19
+ scope: 'session';
20
+ owner: ToolCallOwnerProps;
21
+ };
22
+ }
23
+ }
24
+ /** Injected dependencies of {@link ImageGenerateToolview} (slot `inject`). */
25
+ export interface ImageGenerateToolviewInjected {
26
+ /** Session-authorized image URL loader riding the `/subscriptions-auth` channel. */
27
+ load: ImageLoader;
28
+ }
29
+ /**
30
+ * Props delivered by the toolview outlet: the owner share plus the inject
31
+ * face and the framework locale seat, spread flat.
32
+ */
33
+ export type ImageGenerateToolviewProps = Partial<ToolCallOwnerProps> & Partial<ImageGenerateToolviewInjected> & {
34
+ t?: ((key: SubscriptionsKey, params?: Record<string, unknown>) => string) | undefined;
35
+ };
36
+ /**
37
+ * Build the ImageGallery loader over the `image` endpoint.
38
+ * @param rpc - Connection RPC caller.
39
+ * @returns loader resolving an attachment ref to a data URL.
40
+ */
41
+ export declare function createImageLoader(rpc: ConnectionHandle['rpc']): ImageLoader;
42
+ /**
43
+ * The `image_generate` keyed toolview component.
44
+ * @param props - owner share, inject face, and locale seat (spread flat).
45
+ * @returns the call row plus, once settled, the gallery / text / error body.
46
+ */
47
+ export declare function ImageGenerateToolview(props: ImageGenerateToolviewProps): import("react").JSX.Element | null;
48
+ export {};
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as primitives from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { ImageGallery } from './ImageGallery.js';
4
+ import { hostIcon } from './host-icons.js';
5
+ import { callSubscriptionsAuth } from './subscriptions-rpc.js';
6
+ import { en } from './locales.js';
7
+ /** DSH 0.1.7 renamed IconSparkle16; resolving it by name keeps older hosts working too. */
8
+ const SparkleIcon = hostIcon(primitives, 'Sparkle');
9
+ /** Title prompt truncation budget (characters). */
10
+ const PROMPT_MAX_LENGTH = 60;
11
+ /**
12
+ * Build the ImageGallery loader over the `image` endpoint.
13
+ * @param rpc - Connection RPC caller.
14
+ * @returns loader resolving an attachment ref to a data URL.
15
+ */
16
+ export function createImageLoader(rpc) {
17
+ // The host validates a full ImageAttachmentRef payload (readImage takes the
18
+ // whole ref), so forward the attachment verbatim.
19
+ return attachment => callSubscriptionsAuth(rpc, 'image', { ...attachment })
20
+ .then(result => `data:${result.mediaType};base64,${result.dataBase64}`);
21
+ }
22
+ /**
23
+ * English-dictionary fallback for a missing locale seat (standalone renders);
24
+ * the framework always supplies the namespace-bound one.
25
+ * @param key - dictionary key.
26
+ * @param params - `{name}` template params.
27
+ * @returns the template with params substituted.
28
+ */
29
+ function fallbackTranslate(key, params) {
30
+ let text = en[key];
31
+ for (const [name, value] of Object.entries(params ?? {})) {
32
+ text = text.replaceAll(`{${name}}`, String(value));
33
+ }
34
+ return text;
35
+ }
36
+ /** Extract the prompt from the call's raw args JSON; falls back to the first string value, then the raw line. */
37
+ function derivePrompt(argsRaw) {
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(argsRaw);
41
+ }
42
+ catch {
43
+ // Non-JSON args (mid-stream truncation): fall back to the raw string below.
44
+ parsed = undefined;
45
+ }
46
+ let prompt;
47
+ if (typeof parsed === 'object' && parsed !== null) {
48
+ const args = parsed;
49
+ if (typeof args.prompt === 'string' && args.prompt !== '')
50
+ prompt = args.prompt;
51
+ else {
52
+ for (const value of Object.values(args)) {
53
+ if (typeof value === 'string' && value !== '') {
54
+ prompt = value;
55
+ break;
56
+ }
57
+ }
58
+ }
59
+ }
60
+ const line = (prompt ?? argsRaw).split('\n', 1)[0] ?? '';
61
+ return line.length > PROMPT_MAX_LENGTH ? `${line.slice(0, PROMPT_MAX_LENGTH)}…` : line;
62
+ }
63
+ /** Flatten a settled result's text blocks (the degraded text-only route and the error line). */
64
+ function resultText(block) {
65
+ if (!('kind' in block))
66
+ return '';
67
+ const parts = [];
68
+ for (const part of block.content) {
69
+ if (part.type === 'text')
70
+ parts.push(part.text);
71
+ }
72
+ if (parts.length === 0 && block.error !== undefined)
73
+ parts.push(`${block.error.name}: ${block.error.code}`);
74
+ return parts.join('\n');
75
+ }
76
+ /** Image attachments of a settled result; empty while running or on the text-only route. */
77
+ function resultImages(block) {
78
+ if (!('kind' in block))
79
+ return [];
80
+ const images = [];
81
+ for (const part of block.content) {
82
+ if (part.type === 'image')
83
+ images.push({ attachment: part.attachment });
84
+ }
85
+ return images;
86
+ }
87
+ const styles = {
88
+ container: { display: 'flex', flexDirection: 'column', gap: 6, padding: '4px 0' },
89
+ row: { display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 },
90
+ icon: { display: 'inline-flex', flexShrink: 0, color: 'var(--dsw-alias-label-tertiary)' },
91
+ title: {
92
+ fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)',
93
+ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
94
+ },
95
+ subtle: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)' },
96
+ output: {
97
+ margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-secondary)',
98
+ whiteSpace: 'pre-wrap', overflowWrap: 'anywhere',
99
+ },
100
+ error: { margin: 0, fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-state-error-primary)' },
101
+ };
102
+ /**
103
+ * The `image_generate` keyed toolview component.
104
+ * @param props - owner share, inject face, and locale seat (spread flat).
105
+ * @returns the call row plus, once settled, the gallery / text / error body.
106
+ */
107
+ export function ImageGenerateToolview(props) {
108
+ const { block, load } = props;
109
+ const t = props.t ?? fallbackTranslate;
110
+ if (block === undefined)
111
+ return null;
112
+ const settled = 'kind' in block;
113
+ const argsRaw = (settled ? block.call?.argsRaw : block.phase === 'start' ? block.argsRaw : undefined) ?? '';
114
+ let references = 0;
115
+ try {
116
+ const args = JSON.parse(argsRaw);
117
+ if (Array.isArray(args?.referenceImages))
118
+ references = args.referenceImages.length;
119
+ }
120
+ catch { /* Arguments may still be streaming. */ }
121
+ const title = `image_generate${references > 0 ? ` (${references} ref)` : ''}: ${derivePrompt(argsRaw)}`;
122
+ const images = resultImages(block);
123
+ const text = settled ? resultText(block) : '';
124
+ const labels = {
125
+ image: t('image'),
126
+ open: t('viewImage'),
127
+ openNamed: name => t('viewImageNamed', { name }),
128
+ loading: t('imageLoading'),
129
+ loadFailed: t('imageLoadFailed'),
130
+ lightbox: { dialog: t('imagePreview'), close: t('imageClose') },
131
+ };
132
+ 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('generating') }), settled && block.isError && text !== '' && (_jsx("p", { style: styles.error, children: text.split('\n', 1)[0] })), settled && !block.isError && images.length > 0 && load !== undefined && (_jsx(ImageGallery, { images: images, load: load, labels: labels })), settled && !block.isError && images.length === 0 && text !== '' && (_jsx("p", { style: styles.output, children: text }))] }));
133
+ }
@@ -0,0 +1,13 @@
1
+ import type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client';
2
+ import type { SubscriptionProvider } from './SubscriptionsSection.js';
3
+ import type { SubscriptionsKey } from './locales.js';
4
+ interface Props {
5
+ provider: SubscriptionProvider | 'cursor-subscription';
6
+ name: string;
7
+ rpc: ConnectionHandle['rpc'];
8
+ t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string;
9
+ onClose: () => void;
10
+ }
11
+ /** Native top-layer dialog provides keyboard containment, Escape and focus restoration. */
12
+ export declare function ProviderAccountManager({ provider, name, rpc, t, onClose }: Props): import("react").JSX.Element;
13
+ export {};
@@ -0,0 +1,147 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useRef, useState } from 'react';
3
+ import { callSubscriptionsAuth } from './subscriptions-rpc.js';
4
+ import { accountModelRows, accountPoolSelection, mergeAccountChanges, mergeLatestAccounts } from './account-preferences.js';
5
+ import { ProviderModelEditor } from './ProviderModelEditor.js';
6
+ const border = '1px solid var(--dsw-alias-border-l2)';
7
+ const control = {
8
+ font: 'inherit', color: 'inherit', background: 'var(--dsw-alias-bg-layer-1)',
9
+ border, borderRadius: 8, padding: '7px 12px', minWidth: 0,
10
+ };
11
+ const button = { ...control, cursor: 'pointer' };
12
+ const hint = { margin: 0, fontSize: 12, lineHeight: 1.6, color: 'var(--dsw-alias-label-tertiary)' };
13
+ const stack = { display: 'grid', gap: 12, minWidth: 0 };
14
+ const actions = { display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' };
15
+ /** Native top-layer dialog provides keyboard containment, Escape and focus restoration. */
16
+ export function ProviderAccountManager({ provider, name, rpc, t, onClose }) {
17
+ const dialog = useRef(null);
18
+ const title = useId();
19
+ const description = useId();
20
+ const [catalog, setCatalog] = useState();
21
+ const [changes, setChanges] = useState({});
22
+ const [loading, setLoading] = useState(true);
23
+ const [saving, setSaving] = useState(false);
24
+ const [error, setError] = useState('');
25
+ const [saveError, setSaveError] = useState('');
26
+ const [attempt, setAttempt] = useState(0);
27
+ const [modelsDirty, setModelsDirty] = useState(false);
28
+ const alive = useRef(true);
29
+ const saveLock = useRef(false);
30
+ const editor = useRef(null);
31
+ useEffect(() => {
32
+ alive.current = true;
33
+ const element = dialog.current;
34
+ const previous = document.activeElement;
35
+ element.showModal();
36
+ return () => {
37
+ alive.current = false;
38
+ element.close();
39
+ if (previous instanceof HTMLElement && previous.isConnected)
40
+ previous.focus();
41
+ };
42
+ }, []);
43
+ useEffect(() => {
44
+ let current = true;
45
+ setLoading(true);
46
+ setError('');
47
+ void callSubscriptionsAuth(rpc, provider === 'cursor-subscription' ? 'cursorSettings' : 'providerSettings', { provider }).then(data => {
48
+ if (current)
49
+ setCatalog(data);
50
+ }).catch(error => {
51
+ if (current)
52
+ setError(t('accountsLoadFailed', { message: error instanceof Error ? error.message : String(error) }));
53
+ }).finally(() => { if (current)
54
+ setLoading(false); });
55
+ return () => { current = false; };
56
+ }, [provider, rpc, attempt]);
57
+ function edit(key, next) {
58
+ setChanges(current => ({ ...current, [key]: next }));
59
+ }
60
+ /**
61
+ * One submit for both drafts: the model editor's (visibility, effort,
62
+ * context, tools) and this dialog's account edits. A failure keeps the
63
+ * dialog open with every edit intact.
64
+ */
65
+ async function save() {
66
+ if (saveLock.current)
67
+ return;
68
+ // An invalid model draft (e.g. a malformed context window) blocks the
69
+ // whole submit; the editor shows the reason next to the field.
70
+ const models = editor.current === null ? { efforts: [] } : editor.current.collect();
71
+ if (models === undefined)
72
+ return;
73
+ saveLock.current = true;
74
+ setSaving(true);
75
+ setSaveError('');
76
+ let savedEfforts = 0;
77
+ try {
78
+ if (provider !== 'cursor-subscription') {
79
+ for (const { model, effort } of models.efforts) {
80
+ await callSubscriptionsAuth(rpc, 'setModelDefault', { provider, model, ...(effort ? { effort } : {}) });
81
+ savedEfforts++;
82
+ if (!alive.current)
83
+ return;
84
+ }
85
+ }
86
+ // Re-read before writing: another client may have saved since this
87
+ // dialog loaded, and a stale snapshot must never be written back.
88
+ const latest = await callSubscriptionsAuth(rpc, provider === 'cursor-subscription' ? 'cursorSettings' : 'providerSettings', { provider });
89
+ if (!alive.current)
90
+ return;
91
+ if (provider === 'cursor-subscription') {
92
+ await callSubscriptionsAuth(rpc, 'cursorSetSettings', { settings: models.settings ?? latest.settings });
93
+ }
94
+ else {
95
+ const base = models.settings === undefined ? latest.settings : mergeLatestAccounts(models.settings, latest.settings);
96
+ await callSubscriptionsAuth(rpc, 'setProviderSettings', { provider, settings: mergeAccountChanges(base, changes) });
97
+ }
98
+ if (alive.current)
99
+ onClose();
100
+ }
101
+ catch (error) {
102
+ if (alive.current) {
103
+ const message = error instanceof Error ? error.message : String(error);
104
+ setSaveError((savedEfforts ? t('modelsPartialSave') + ' ' : '') + t('accountsSaveFailed', { message }));
105
+ }
106
+ }
107
+ finally {
108
+ saveLock.current = false;
109
+ if (alive.current)
110
+ setSaving(false);
111
+ }
112
+ }
113
+ return _jsx("dialog", { ref: dialog, "aria-labelledby": title, "aria-describedby": description, onCancel: event => { if (saveLock.current)
114
+ event.preventDefault(); }, onClose: onClose, style: { width: 620, maxWidth: 'calc(100vw - 32px)', maxHeight: 'calc(100dvh - 32px)', boxSizing: 'border-box',
115
+ padding: 0, border, borderRadius: 16, color: 'var(--dsw-alias-label-primary)',
116
+ background: 'var(--dsw-alias-bg-layer-1)', boxShadow: '0 20px 70px #0004', overflow: 'auto' }, children: _jsxs("div", { style: { ...stack, padding: 20 }, children: [_jsxs("header", { style: { ...actions, justifyContent: 'space-between' }, children: [_jsx("h2", { id: title, style: { margin: 0, fontSize: 18 }, children: t('accountsTitle', { provider: name }) }), _jsx("button", { type: "button", autoFocus: true, style: button, disabled: saving, onClick: onClose, children: t('imageClose') })] }), _jsx("p", { id: description, style: hint, children: t(provider === 'cursor-subscription' ? 'cursorAccountsHint' : 'accountsHint') }), error && _jsx("p", { role: "alert", style: { ...hint, color: 'var(--dsw-alias-state-error-primary)' }, children: error }), loading && _jsx("p", { role: "status", style: hint, children: t('accountsLoading') }), !loading && !catalog && _jsx("button", { type: "button", style: button, onClick: () => setAttempt(value => value + 1), children: t('modelDefaultsRetry') }), catalog && _jsxs("fieldset", { disabled: saving || loading, style: { ...stack, border: 0, margin: 0, padding: 0 }, children: [catalog.accounts.length === 0 && _jsx("p", { style: hint, children: t(provider === 'cursor-subscription' ? 'cursorManageSignIn' : 'accountsEmpty') }), provider === 'cursor-subscription' && catalog.accounts.length > 0 &&
117
+ _jsxs("fieldset", { style: { border, borderRadius: 12, padding: 14 }, children: [_jsx("legend", { style: { padding: '0 6px', fontWeight: 600, fontSize: 14 }, children: name }), _jsx("p", { style: hint, children: t('cursorConnected') })] }), provider !== 'cursor-subscription' && catalog.accounts.map(account => {
118
+ const preferences = changes[account.key] ?? catalog.settings.accounts?.[account.key] ?? {};
119
+ const selected = accountPoolSelection(preferences, account.models);
120
+ const models = accountModelRows(account, preferences);
121
+ return _jsxs("fieldset", { style: { ...stack, border, borderRadius: 12, padding: 14, margin: 0 }, children: [_jsx("legend", { style: { padding: '0 6px', fontWeight: 600, fontSize: 14, overflowWrap: 'anywhere', maxWidth: '100%' }, children: account.label }), account.unavailable && _jsx("p", { style: hint, children: t('accountsCatalogUnavailable') }), _jsxs("label", { style: { ...stack, gap: 6 }, children: [_jsx("span", { style: { fontSize: 12 }, children: t('accountsAlias') }), _jsx("input", { style: control, value: preferences.alias ?? '', placeholder: account.label, onChange: event => {
122
+ const next = { ...preferences };
123
+ if (event.target.value.trim())
124
+ next.alias = event.target.value;
125
+ else
126
+ delete next.alias;
127
+ edit(account.key, next);
128
+ } })] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolEnabled !== false, onChange: event => edit(account.key, { ...preferences, poolEnabled: event.target.checked }) }), " ", t('accountsPoolEnabled')] }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.independentEntry === true, onChange: event => edit(account.key, { ...preferences, independentEntry: event.target.checked }) }), " ", t('accountsIndependent')] }), _jsx("p", { style: hint, children: t('accountsIndependentHint') }), _jsxs("details", { children: [_jsxs("summary", { style: { cursor: 'pointer', fontSize: 13 }, children: [t('accountsModels'), " \u00B7 ", preferences.poolModels === undefined
129
+ ? t('accountsModelsAll') : t('accountsModelsCount', { count: preferences.poolModels.length })] }), _jsxs("div", { style: { ...stack, marginTop: 12 }, children: [_jsx("p", { style: hint, children: t('accountsModelsHint') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", checked: preferences.poolModels === undefined, onChange: event => {
130
+ const next = { ...preferences };
131
+ if (event.target.checked)
132
+ delete next.poolModels;
133
+ else
134
+ next.poolModels = account.models.map(model => model.id);
135
+ edit(account.key, next);
136
+ } }), " ", t('accountsModelsAutomatic')] }), _jsxs("div", { style: actions, children: [_jsx("button", { type: "button", style: button, onClick: () => edit(account.key, { ...preferences, poolModels: models.map(model => model.id) }), children: t('modelsSelectAll') }), _jsx("button", { type: "button", style: button, onClick: () => edit(account.key, { ...preferences, poolModels: [] }), children: t('modelsSelectNone') })] }), _jsxs("div", { style: { ...stack, gap: 8, maxHeight: 240, overflowY: 'auto' }, children: [models.map(model => _jsxs("label", { style: { fontSize: 13, overflowWrap: 'anywhere' }, children: [_jsx("input", { type: "checkbox", checked: selected.has(model.id), onChange: event => {
137
+ const next = new Set(selected);
138
+ if (event.target.checked)
139
+ next.add(model.id);
140
+ else
141
+ next.delete(model.id);
142
+ edit(account.key, { ...preferences, poolModels: [...next] });
143
+ } }), " ", model.name, model.unavailable && ` (${t('modelsUnavailable')})`] }, model.id)), models.length === 0 && _jsx("p", { style: hint, children: t('accountsNoModels') })] })] })] })] }, account.key);
144
+ })] }), (provider !== 'cursor-subscription' || (catalog?.accounts.length ?? 0) > 0) &&
145
+ _jsx(ProviderModelEditor, { ref: editor, provider: provider, rpc: rpc, t: t, disabled: saving, onDirtyChange: setModelsDirty }), _jsxs("footer", { style: { ...stack, gap: 8, borderTop: border, padding: '14px 0 0',
146
+ position: 'sticky', bottom: 0, background: 'var(--dsw-alias-bg-layer-1)' }, children: [saveError && _jsx("p", { role: "alert", style: { ...hint, color: 'var(--dsw-alias-state-error-primary)' }, children: saveError }), _jsxs("div", { style: { ...actions, justifyContent: 'flex-end' }, children: [_jsx("button", { type: "button", style: button, disabled: saving, onClick: onClose, children: t('cancel') }), _jsx("button", { type: "button", style: { ...button, fontWeight: 600 }, disabled: loading || saving || (!Object.keys(changes).length && !modelsDirty), onClick: () => { void save(); }, children: saving ? t('modelDefaultsSaving') : t('modelsSave') })] })] })] }) });
147
+ }
@@ -0,0 +1,39 @@
1
+ import type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client';
2
+ import type { ProviderPreferences } from '../provider-settings.js';
3
+ import type { SubscriptionProvider } from './SubscriptionsSection.js';
4
+ import type { SubscriptionsKey } from './locales.js';
5
+ interface Props {
6
+ provider: SubscriptionProvider | 'cursor-subscription';
7
+ rpc: ConnectionHandle['rpc'];
8
+ t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string;
9
+ /** Freeze the editor while the owning dialog submits. */
10
+ disabled?: boolean;
11
+ /** Reports whether the draft differs from the loaded catalog. */
12
+ onDirtyChange?: (dirty: boolean) => void;
13
+ }
14
+ /** The editor's unsaved edits, collected by the owning dialog's single Save. */
15
+ export interface ModelDraft {
16
+ /** Provider settings minus `accounts`; absent when the draft has no edits. */
17
+ settings?: ProviderPreferences;
18
+ /** Reasoning-effort overrides that differ from the catalog; '' clears one. */
19
+ efforts: {
20
+ model: string;
21
+ effort: string;
22
+ }[];
23
+ }
24
+ export interface ProviderModelEditorHandle {
25
+ /**
26
+ * Validate and collect the draft without mutating it, so a failed submit
27
+ * keeps every edit. Returns undefined when a value is invalid; the editor
28
+ * shows the reason itself.
29
+ */
30
+ collect(): ModelDraft | undefined;
31
+ }
32
+ /**
33
+ * Embedded section of the account manager dialog. It owns the model draft
34
+ * (visibility, effort, context, tools) but not its submission: the dialog
35
+ * collects the draft through the handle and saves it with the account edits
36
+ * in one go, so there is a single Save/Cancel pair.
37
+ */
38
+ export declare const ProviderModelEditor: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<ProviderModelEditorHandle>>;
39
+ export {};