dsh-vision-router 2.2.8 → 2.3.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/README.md +7 -7
- package/README.zh.md +7 -7
- package/docs/architecture/2.3.0-compat-retirement.md +147 -0
- package/docs/architecture/2.3.0-execution-plan.md +410 -0
- package/docs/architecture/2.3.0-final-audit.md +279 -0
- package/docs/architecture/2.3.0-reopened-plan.md +720 -0
- package/docs/architecture/compat-inventory.md +26 -34
- package/docs/architecture/dsh-compatibility-matrix.md +65 -54
- package/docs/architecture/dsh-support-window.md +27 -21
- package/docs/architecture/final-closure-audit.md +2 -0
- package/docs/releases/v2.3.0.md +53 -0
- package/entry.js +8 -56
- package/index.js +161 -691
- package/lib/catalog-corrections.js +1 -2
- package/lib/client-maintenance-actions.js +145 -0
- package/lib/client-presentation-boundary-main.js +75 -17
- package/lib/client-presentation-boundary.js +53 -28
- package/lib/client.js +246 -144
- package/lib/core-apply-lifecycle.js +47 -0
- package/lib/core-primitives.js +88 -135
- package/lib/desktop-screenshot-tool.js +262 -0
- package/lib/dsh-contract-compat.js +11 -26
- package/lib/dsh-support-window.js +3 -3
- package/lib/http-compat.js +1 -2
- package/lib/local-remote-settings-permission.js +33 -7
- package/lib/maintenance-routes.js +136 -0
- package/lib/public-config.js +57 -0
- package/lib/public-entry.js +2 -7
- package/lib/remote-settings-bridge.js +11 -0
- package/lib/remote-settings-risk-client-boundary.js +155 -0
- package/lib/remote-settings-risk-confirmation.js +17 -105
- package/lib/runtime-composition.js +105 -30
- package/lib/runtime-i18n-boundary.js +10 -11
- package/lib/session-message-source-compat.js +7 -3
- package/lib/session-vision-index.js +154 -9
- package/lib/settings-client-loader-lifecycle.js +129 -0
- package/lib/sharp-runtime.js +0 -7
- package/lib/structured-flow-hardening.js +3 -2
- package/lib/vision-backend-runtime-policy.js +3 -2
- package/lib/vision-capability-benchmark-service.js +1 -0
- package/lib/vision-diagnostics-routes.js +216 -0
- package/lib/vision-evidence-guidance.js +3 -1
- package/lib/vision-provider-transport.js +0 -29
- package/lib/vision-toggle-root-hardening.js +22 -5
- package/lib/vision-tool-runtime-boundary.js +5 -1
- package/lib/web/remote-settings-client.js +2 -2
- package/package.json +23 -13
- package/lib/android-attachment-compat.js +0 -244
- package/lib/settings-client-rc8-lifecycle.js +0 -397
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
readResponseTextBounded,
|
|
16
16
|
} from './http-body-limit.js'
|
|
17
17
|
import { stripTrailingSlashes } from './string-normalization.js'
|
|
18
|
-
import { currentVisionProviderTransport } from './vision-provider-transport.js'
|
|
19
18
|
import { directSessionAffinityHeaders } from './session-affinity.js'
|
|
20
19
|
import { isOffloadedImageBlock, offloadedImagePlaceholder } from './image-offload-compat.js'
|
|
21
20
|
|
|
@@ -262,7 +261,7 @@ export async function callAnthropicCompatible(provider, messages, options = {})
|
|
|
262
261
|
const url = `${stripTrailingSlashes(provider.baseURL)}/v1/messages`
|
|
263
262
|
let response
|
|
264
263
|
try {
|
|
265
|
-
const transport =
|
|
264
|
+
const transport = options.providerTransport
|
|
266
265
|
const requestFetch = transport
|
|
267
266
|
? (input, init) => transport.fetch(input, init, { providerName: provider.name, allowProxy: true })
|
|
268
267
|
: fetch
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side product maintenance actions, independent of React rendering.
|
|
3
|
+
*
|
|
4
|
+
* State access is supplied through getters/setters so the generated client
|
|
5
|
+
* bundle can keep one small action owner without giving it Settings, routing or
|
|
6
|
+
* Host-context authority.
|
|
7
|
+
*/
|
|
8
|
+
export function createClientMaintenanceActions({
|
|
9
|
+
fetchImpl,
|
|
10
|
+
t,
|
|
11
|
+
getTestState,
|
|
12
|
+
setTestState,
|
|
13
|
+
getUpdateState,
|
|
14
|
+
setUpdateState,
|
|
15
|
+
getSelfUpdateState,
|
|
16
|
+
setSelfUpdateState,
|
|
17
|
+
confirmImpl,
|
|
18
|
+
alertImpl,
|
|
19
|
+
} = {}) {
|
|
20
|
+
if (typeof fetchImpl !== 'function') throw new TypeError('client maintenance actions require fetch')
|
|
21
|
+
const translate = typeof t === 'function' ? t : (key) => String(key)
|
|
22
|
+
|
|
23
|
+
const diagnosticError = (value, fallback) => {
|
|
24
|
+
if (typeof value === 'string' && value.trim() !== '') return value.trim()
|
|
25
|
+
if (value && typeof value.message === 'string' && value.message.trim() !== '') {
|
|
26
|
+
return value.message.trim()
|
|
27
|
+
}
|
|
28
|
+
return fallback
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const runTestConnection = async () => {
|
|
32
|
+
if (getTestState?.()?.status === 'running') return
|
|
33
|
+
setTestState?.({ status: 'running' })
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetchImpl('/_dsh/vision-router/test-connection')
|
|
36
|
+
const result = await response.json().catch(() => undefined)
|
|
37
|
+
setTestState?.({ status: 'done', result })
|
|
38
|
+
} catch (error) {
|
|
39
|
+
setTestState?.({
|
|
40
|
+
status: 'done',
|
|
41
|
+
result: { ok: false, error: error && error.message ? error.message : String(error) },
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const requestDesktopScreenshotPermission = () => {
|
|
47
|
+
// Fire-and-forget: the native permission dialog must never hold Settings
|
|
48
|
+
// save completion open. The actual screenshot tool still reports denial.
|
|
49
|
+
try {
|
|
50
|
+
void Promise.resolve(fetchImpl('/_dsh/vision-router/request-screenshot-permission', {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
cache: 'no-store',
|
|
53
|
+
})).catch(() => {})
|
|
54
|
+
} catch {
|
|
55
|
+
// Permission prompting is best-effort after an already-landed setting.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const runUpdateCheck = async (force = false) => {
|
|
60
|
+
const current = getUpdateState?.() ?? {}
|
|
61
|
+
if (current.status === 'running') return
|
|
62
|
+
setUpdateState?.({ status: 'running', result: current.result })
|
|
63
|
+
try {
|
|
64
|
+
const response = await fetchImpl(
|
|
65
|
+
'/_dsh/vision-router/update-check' + (force ? '?force=1' : ''),
|
|
66
|
+
{ cache: 'no-store' },
|
|
67
|
+
)
|
|
68
|
+
const result = await response.json().catch(() => undefined)
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new Error(diagnosticError(result && result.error, `HTTP ${response.status}`))
|
|
71
|
+
}
|
|
72
|
+
if (!result || typeof result !== 'object' || typeof result.ok !== 'boolean') {
|
|
73
|
+
throw new Error(translate('updateInvalidResponse'))
|
|
74
|
+
}
|
|
75
|
+
setUpdateState?.({ status: 'done', result })
|
|
76
|
+
} catch (error) {
|
|
77
|
+
setUpdateState?.({
|
|
78
|
+
status: 'done',
|
|
79
|
+
result: {
|
|
80
|
+
ok: false,
|
|
81
|
+
error: error && error.message ? error.message : String(error),
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const runSelfUpdate = async () => {
|
|
88
|
+
if (getSelfUpdateState?.()?.status === 'running') return
|
|
89
|
+
const checked = getUpdateState?.()?.result
|
|
90
|
+
const auto = checked && checked.autoUpdate
|
|
91
|
+
if (!checked || checked.ok !== true || checked.updateAvailable !== true || !auto || !auto.token) return
|
|
92
|
+
if (typeof confirmImpl === 'function' && confirmImpl(translate('updateConfirm')) !== true) return
|
|
93
|
+
setSelfUpdateState?.({ status: 'running', result: undefined })
|
|
94
|
+
try {
|
|
95
|
+
const response = await fetchImpl('/_dsh/vision-router/self-update', {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
cache: 'no-store',
|
|
98
|
+
headers: { 'x-dsh-vision-router-update-token': auto.token },
|
|
99
|
+
})
|
|
100
|
+
const result = await response.json().catch(() => undefined)
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(diagnosticError(result && result.error, `HTTP ${response.status}`))
|
|
103
|
+
}
|
|
104
|
+
if (!result || typeof result !== 'object' || typeof result.ok !== 'boolean') {
|
|
105
|
+
throw new Error(translate('updateInvalidResponse'))
|
|
106
|
+
}
|
|
107
|
+
setSelfUpdateState?.({ status: 'done', result })
|
|
108
|
+
} catch (error) {
|
|
109
|
+
setSelfUpdateState?.({
|
|
110
|
+
status: 'error',
|
|
111
|
+
result: { ok: false, error: error && error.message ? error.message : String(error) },
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const openLogFolder = async () => {
|
|
117
|
+
try {
|
|
118
|
+
const response = await fetchImpl('/_dsh/vision-router/logs', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
cache: 'no-store',
|
|
121
|
+
})
|
|
122
|
+
const result = await response.json().catch(() => undefined)
|
|
123
|
+
if (!response.ok || !result || result.ok !== true) {
|
|
124
|
+
throw new Error(result && result.error ? result.error : `HTTP ${response.status}`)
|
|
125
|
+
}
|
|
126
|
+
return result
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (typeof alertImpl === 'function') {
|
|
129
|
+
alertImpl(
|
|
130
|
+
translate('openLogFolderFailed') + ':' +
|
|
131
|
+
(error && error.message ? error.message : String(error)),
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
runTestConnection,
|
|
140
|
+
requestDesktopScreenshotPermission,
|
|
141
|
+
runUpdateCheck,
|
|
142
|
+
runSelfUpdate,
|
|
143
|
+
openLogFolder,
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -1058,21 +1058,52 @@ export const CLIENT_PRESENTATION_PRELUDE = String.raw`(function(){
|
|
|
1058
1058
|
var visionConfig = settingsState && settingsState.value && typeof settingsState.value === 'object'
|
|
1059
1059
|
? settingsState.value
|
|
1060
1060
|
: {};
|
|
1061
|
-
|
|
1061
|
+
// A Vision click may briefly drive this same Host directory through
|
|
1062
|
+
// selecting -> idle/loading while its projection is recomputed. Smooth
|
|
1063
|
+
// only that owned transaction: unrelated Host reloads (connection,
|
|
1064
|
+
// adapters, settings, credentials) must keep their loading authority.
|
|
1065
|
+
var usable = !!(state.current &&
|
|
1066
|
+
typeof state.current.provider === 'string' && state.current.provider !== '') &&
|
|
1067
|
+
Array.isArray(state.groups) && state.groups.length > 0;
|
|
1068
|
+
var settling = state.status === 'idle' || state.status === 'loading';
|
|
1069
|
+
var livePair = resolveVisionModePair(state.groups, state.current, visionConfig);
|
|
1070
|
+
var complete = usable && livePair.mode !== 'unavailable';
|
|
1071
|
+
var settledMode = React.useRef(null);
|
|
1072
|
+
var toggleTransition = React.useRef(null);
|
|
1073
|
+
if (settledMode.current && settledMode.current.directory !== directory) settledMode.current = null;
|
|
1074
|
+
if (toggleTransition.current && toggleTransition.current.directory !== directory) toggleTransition.current = null;
|
|
1075
|
+
if (complete) settledMode.current = { directory: directory, mode: livePair.mode };
|
|
1076
|
+
var transition = toggleTransition.current;
|
|
1077
|
+
if (transition && state.status === 'selecting') {
|
|
1078
|
+
var pending = state.pending;
|
|
1079
|
+
var matchesPending = !!(pending &&
|
|
1080
|
+
pending.provider === transition.target.provider &&
|
|
1081
|
+
pending.model === transition.target.model &&
|
|
1082
|
+
pending.reasoningEffort === transition.target.reasoningEffort);
|
|
1083
|
+
if (matchesPending) transition.seenSelecting = true;
|
|
1084
|
+
else toggleTransition.current = null;
|
|
1085
|
+
}
|
|
1086
|
+
transition = toggleTransition.current;
|
|
1087
|
+
var ownSettling = !!(transition && transition.seenSelecting && settling && settledMode.current);
|
|
1088
|
+
if (!ownSettling && transition && state.status !== 'selecting' &&
|
|
1089
|
+
(state.status === 'error' || state.status === 'ready')) {
|
|
1090
|
+
toggleTransition.current = null;
|
|
1091
|
+
}
|
|
1092
|
+
var pair = ownSettling ? { mode: settledMode.current.mode } : livePair;
|
|
1062
1093
|
var active = pair.mode === 'on';
|
|
1063
1094
|
var toastState = React.useState(null);
|
|
1064
1095
|
var toast = toastState[0];
|
|
1065
1096
|
var setToast = toastState[1];
|
|
1066
1097
|
var busy = state.status === 'selecting';
|
|
1067
|
-
var loading =
|
|
1098
|
+
var loading = settling;
|
|
1068
1099
|
var removed = props.session && props.session.removed === true;
|
|
1069
|
-
var disabled = props.available !== true || removed || busy || loading ||
|
|
1100
|
+
var disabled = props.available !== true || removed || busy || loading || livePair.mode === 'unavailable';
|
|
1070
1101
|
var t = typeof props.t === 'function' ? props.t : fallbackTranslate(active);
|
|
1071
|
-
var title = busy
|
|
1102
|
+
var title = busy || ownSettling
|
|
1072
1103
|
? t('switching')
|
|
1073
1104
|
: loading
|
|
1074
1105
|
? t('loading')
|
|
1075
|
-
:
|
|
1106
|
+
: livePair.mode === 'unavailable'
|
|
1076
1107
|
? t('unavailable')
|
|
1077
1108
|
: active ? t('disable') : t('enable');
|
|
1078
1109
|
var style = {
|
|
@@ -1094,7 +1125,7 @@ export const CLIENT_PRESENTATION_PRELUDE = String.raw`(function(){
|
|
|
1094
1125
|
lineHeight: 1.4,
|
|
1095
1126
|
fontWeight: active ? 650 : 500,
|
|
1096
1127
|
cursor: disabled ? 'default' : 'pointer',
|
|
1097
|
-
opacity: disabled && (
|
|
1128
|
+
opacity: disabled && !ownSettling && (livePair.mode === 'unavailable' || loading) ? 0.45 : 1,
|
|
1098
1129
|
whiteSpace: 'nowrap'
|
|
1099
1130
|
};
|
|
1100
1131
|
|
|
@@ -1121,25 +1152,52 @@ export const CLIENT_PRESENTATION_PRELUDE = String.raw`(function(){
|
|
|
1121
1152
|
'data-vision-router-mode-toggle': 'true',
|
|
1122
1153
|
'aria-pressed': active,
|
|
1123
1154
|
'aria-label': title,
|
|
1155
|
+
'aria-busy': busy || loading,
|
|
1124
1156
|
title: title,
|
|
1125
1157
|
disabled: disabled,
|
|
1126
1158
|
style: style,
|
|
1127
1159
|
onClick: function() {
|
|
1128
1160
|
if (disabled || !pair.target || typeof props.select !== 'function') return;
|
|
1161
|
+
var target = pair.target;
|
|
1162
|
+
toggleTransition.current = {
|
|
1163
|
+
directory: directory,
|
|
1164
|
+
target: target,
|
|
1165
|
+
seenSelecting: false
|
|
1166
|
+
};
|
|
1129
1167
|
setToast(null);
|
|
1130
|
-
void props.select(
|
|
1131
|
-
if (!accepted)
|
|
1168
|
+
void props.select(target).then(function(accepted){
|
|
1169
|
+
if (!accepted) {
|
|
1170
|
+
toggleTransition.current = null;
|
|
1171
|
+
announceRejectedSelection();
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
var latest;
|
|
1175
|
+
try { latest = store && typeof store.getSnapshot === 'function' ? store.getSnapshot() : undefined; } catch (_) {}
|
|
1176
|
+
if (!latest || (latest.status !== 'selecting' && latest.status !== 'idle' && latest.status !== 'loading')) {
|
|
1177
|
+
toggleTransition.current = null;
|
|
1178
|
+
}
|
|
1132
1179
|
});
|
|
1133
1180
|
}
|
|
1134
1181
|
},
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
active
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
:
|
|
1182
|
+
// One fixed 14px leading slot carries the state icon: the eye when the
|
|
1183
|
+
// route is ordinary, the check when Vision is on. Appending the check
|
|
1184
|
+
// only while active changed the chip width by ~18px and reflowed the
|
|
1185
|
+
// composer row; reserving trailing space kept the width but left the
|
|
1186
|
+
// inactive chip visually off-centre.
|
|
1187
|
+
React.createElement('span', {
|
|
1188
|
+
'aria-hidden': 'true',
|
|
1189
|
+
style: {
|
|
1190
|
+
display: 'inline-flex',
|
|
1191
|
+
alignItems: 'center',
|
|
1192
|
+
justifyContent: 'center',
|
|
1193
|
+
flex: '0 0 auto',
|
|
1194
|
+
width: 14,
|
|
1195
|
+
height: 14,
|
|
1196
|
+
fontSize: 13,
|
|
1197
|
+
lineHeight: 1
|
|
1198
|
+
}
|
|
1199
|
+
}, active ? '✓' : '👁'),
|
|
1200
|
+
React.createElement('span', null, t('label'))
|
|
1143
1201
|
);
|
|
1144
1202
|
|
|
1145
1203
|
var toastNode = toast
|
|
@@ -1184,7 +1242,7 @@ export const CLIENT_PRESENTATION_PRELUDE = String.raw`(function(){
|
|
|
1184
1242
|
}
|
|
1185
1243
|
try {
|
|
1186
1244
|
return Promise.resolve(directory.select(selection)).then(
|
|
1187
|
-
function(){ return
|
|
1245
|
+
function(result){ return !result || result.ok !== false; },
|
|
1188
1246
|
function(){ return false; }
|
|
1189
1247
|
);
|
|
1190
1248
|
} catch (_) {
|
|
@@ -10,39 +10,50 @@ import { installVisionModelVisibilityBoundary } from './vision-model-visibility-
|
|
|
10
10
|
|
|
11
11
|
const CLIENT_PRESENTATION_MARK = 'data-vision-router-presentation-boundary'
|
|
12
12
|
|
|
13
|
-
const VISION_TOGGLE_TEXT_GLYPH = String.raw`
|
|
14
|
-
|
|
15
|
-
active
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
const VISION_TOGGLE_TEXT_GLYPH = String.raw` // One fixed 14px leading slot carries the state icon: the eye when the
|
|
14
|
+
// route is ordinary, the check when Vision is on. Appending the check
|
|
15
|
+
// only while active changed the chip width by ~18px and reflowed the
|
|
16
|
+
// composer row; reserving trailing space kept the width but left the
|
|
17
|
+
// inactive chip visually off-centre.
|
|
18
|
+
React.createElement('span', {
|
|
19
|
+
'aria-hidden': 'true',
|
|
20
|
+
style: {
|
|
21
|
+
display: 'inline-flex',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
justifyContent: 'center',
|
|
24
|
+
flex: '0 0 auto',
|
|
25
|
+
width: 14,
|
|
26
|
+
height: 14,
|
|
27
|
+
fontSize: 13,
|
|
28
|
+
lineHeight: 1
|
|
29
|
+
}
|
|
30
|
+
}, active ? '✓' : '👁'),
|
|
31
|
+
React.createElement('span', null, t('label'))`
|
|
32
|
+
|
|
33
|
+
const VISION_TOGGLE_FIXED_SVG = String.raw` // One fixed 14px leading slot carries the state icon: the eye when the
|
|
34
|
+
// route is ordinary, the check when Vision is on. Appending the check
|
|
35
|
+
// only while active changed the chip width by ~18px and reflowed the
|
|
36
|
+
// composer row; reserving trailing space kept the width but left the
|
|
37
|
+
// inactive chip visually off-centre.
|
|
38
|
+
React.createElement('span', {
|
|
27
39
|
'aria-hidden': 'true',
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
active
|
|
40
|
+
style: {
|
|
41
|
+
display: 'inline-flex',
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
flex: '0 0 auto',
|
|
45
|
+
width: 14,
|
|
46
|
+
height: 14
|
|
47
|
+
}
|
|
48
|
+
}, active
|
|
38
49
|
? React.createElement('svg', {
|
|
39
|
-
width:
|
|
40
|
-
height:
|
|
50
|
+
width: 14,
|
|
51
|
+
height: 14,
|
|
41
52
|
viewBox: '0 0 14 14',
|
|
42
53
|
fill: 'none',
|
|
43
54
|
'aria-hidden': 'true',
|
|
44
55
|
focusable: 'false',
|
|
45
|
-
style: { display: 'block', flex: '0 0 auto'
|
|
56
|
+
style: { display: 'block', flex: '0 0 auto' }
|
|
46
57
|
}, React.createElement('path', {
|
|
47
58
|
d: 'M2.75 7.15 5.6 10 11.25 4.35',
|
|
48
59
|
stroke: 'currentColor',
|
|
@@ -50,7 +61,21 @@ const VISION_TOGGLE_FIXED_SVG = String.raw` React.createElement('svg',
|
|
|
50
61
|
strokeLinecap: 'round',
|
|
51
62
|
strokeLinejoin: 'round'
|
|
52
63
|
}))
|
|
53
|
-
:
|
|
64
|
+
: React.createElement('svg', {
|
|
65
|
+
width: 14,
|
|
66
|
+
height: 14,
|
|
67
|
+
viewBox: '0 0 14 14',
|
|
68
|
+
fill: 'none',
|
|
69
|
+
'aria-hidden': 'true',
|
|
70
|
+
focusable: 'false',
|
|
71
|
+
style: { display: 'block', flex: '0 0 auto' }
|
|
72
|
+
}, React.createElement('path', {
|
|
73
|
+
fillRule: 'evenodd',
|
|
74
|
+
clipRule: 'evenodd',
|
|
75
|
+
d: 'M7 2.25c-2.84 0-5.04 1.69-6.25 4.25a1.15 1.15 0 0 0 0 1C1.96 10.06 4.16 11.75 7 11.75s5.04-1.69 6.25-4.25a1.15 1.15 0 0 0 0-1C12.04 3.94 9.84 2.25 7 2.25Zm0 1.25c2.16 0 3.96 1.21 5.05 3.5C10.96 9.29 9.16 10.5 7 10.5S3.04 9.29 1.95 7C3.04 4.71 4.84 3.5 7 3.5Zm0 1.25A2.25 2.25 0 1 0 7 9.25a2.25 2.25 0 0 0 0-4.5Zm0 1.25a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z',
|
|
76
|
+
fill: 'currentColor'
|
|
77
|
+
}))),
|
|
78
|
+
React.createElement('span', null, t('label'))`
|
|
54
79
|
|
|
55
80
|
const BITMAP_LIMITS_BASE = String.raw` var maxBitmapSourceBytes = 64 * 1024 * 1024;
|
|
56
81
|
var maxBitmapDimension = 10000;
|