ydb-embedded-ui 5.1.0 → 5.2.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.
@@ -9,9 +9,13 @@ export declare function useComponent<T extends Parameters<ComponentsRegistry['ge
9
9
  StaffCard: typeof import("../User/StaffCard").StaffCard;
10
10
  } & {
11
11
  AsideNavigation: typeof import("../../containers/AsideNavigation/AsideNavigation").AsideNavigation;
12
+ } & {
13
+ ErrorBoundary: typeof import("../ErrorBoundary/ErrorBoundary").ErrorBoundaryInner;
12
14
  })[T] extends React.ComponentType<any> ? React.ComponentType<React.PropsWithoutRef<React.ComponentProps<({
13
15
  StaffCard: typeof import("../User/StaffCard").StaffCard;
14
16
  } & {
15
17
  AsideNavigation: typeof import("../../containers/AsideNavigation/AsideNavigation").AsideNavigation;
18
+ } & {
19
+ ErrorBoundary: typeof import("../ErrorBoundary/ErrorBoundary").ErrorBoundaryInner;
16
20
  })[T]>>> : never;
17
21
  export {};
@@ -1,10 +1,13 @@
1
1
  import { StaffCard } from '../User/StaffCard';
2
2
  import { AsideNavigation } from '../../containers/AsideNavigation/AsideNavigation';
3
3
  import { ComponentsRegistryTemplate, Registry } from './registry';
4
+ import { ErrorBoundaryInner } from '../ErrorBoundary/ErrorBoundary';
4
5
  declare const componentsRegistryInner: Registry<{
5
6
  StaffCard: typeof StaffCard;
6
7
  } & {
7
8
  AsideNavigation: typeof AsideNavigation;
9
+ } & {
10
+ ErrorBoundary: typeof ErrorBoundaryInner;
8
11
  }>;
9
12
  export declare type ComponentsRegistry = ComponentsRegistryTemplate<typeof componentsRegistryInner>;
10
13
  export declare const componentsRegistry: ComponentsRegistry;
@@ -1,7 +1,9 @@
1
1
  import { StaffCard } from '../User/StaffCard';
2
2
  import { AsideNavigation } from '../../containers/AsideNavigation/AsideNavigation';
3
3
  import { Registry } from './registry';
4
+ import { ErrorBoundaryInner } from '../ErrorBoundary/ErrorBoundary';
4
5
  const componentsRegistryInner = new Registry()
5
6
  .register('StaffCard', StaffCard)
6
- .register('AsideNavigation', AsideNavigation);
7
+ .register('AsideNavigation', AsideNavigation)
8
+ .register('ErrorBoundary', ErrorBoundaryInner);
7
9
  export const componentsRegistry = componentsRegistryInner;
@@ -1,9 +1,19 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import './ErrorBoundary.scss';
3
+ export declare function ErrorBoundary({ children }: {
4
+ children?: ReactNode;
5
+ }): JSX.Element;
3
6
  interface ErrorBoundaryProps {
4
7
  children?: ReactNode;
5
8
  useRetry?: boolean;
6
9
  onReportProblem?: (error?: Error) => void;
7
10
  }
8
- export declare const ErrorBoundary: ({ children, useRetry, onReportProblem }: ErrorBoundaryProps) => JSX.Element;
11
+ export declare function ErrorBoundaryInner({ children, useRetry, onReportProblem, }: ErrorBoundaryProps): JSX.Element;
12
+ interface ErrorBoundaryFallbackProps {
13
+ error: Error;
14
+ useRetry?: boolean;
15
+ resetErrorBoundary: () => void;
16
+ onReportProblem?: (error?: Error) => void;
17
+ }
18
+ export declare function ErrorBoundaryFallback({ error, resetErrorBoundary, useRetry, onReportProblem, }: ErrorBoundaryFallbackProps): JSX.Element;
9
19
  export {};
@@ -4,13 +4,21 @@ import cn from 'bem-cn-lite';
4
4
  import { Button, Disclosure } from '@gravity-ui/uikit';
5
5
  import { registerError } from '../../utils/registerError';
6
6
  import { Illustration } from '../Illustration';
7
+ import { useComponent } from '../ComponentsProvider/ComponentsProvider';
7
8
  import i18n from './i18n';
8
9
  import './ErrorBoundary.scss';
9
10
  const b = cn('ydb-error-boundary');
10
- export const ErrorBoundary = ({ children, useRetry = true, onReportProblem }) => {
11
+ export function ErrorBoundary({ children }) {
12
+ const ErrorBoundaryComponent = useComponent('ErrorBoundary');
13
+ return _jsx(ErrorBoundaryComponent, { children: children });
14
+ }
15
+ export function ErrorBoundaryInner({ children, useRetry = true, onReportProblem, }) {
11
16
  return (_jsx(ErrorBoundaryBase, Object.assign({ onError: (error, info) => {
12
17
  registerError(error, info.componentStack, 'error-boundary');
13
18
  }, fallbackRender: ({ error, resetErrorBoundary }) => {
14
- return (_jsxs("div", Object.assign({ className: b(null) }, { children: [_jsx(Illustration, { name: "error", className: b('illustration') }), _jsxs("div", Object.assign({ className: b('content') }, { children: [_jsx("h2", Object.assign({ className: b('error-title') }, { children: i18n('error-title') })), _jsx("div", Object.assign({ className: b('error-description') }, { children: i18n('error-description') })), _jsx(Disclosure, Object.assign({ summary: i18n('show-details'), className: b('show-details'), size: "m" }, { children: _jsx("pre", Object.assign({ className: b('error-details') }, { children: error.stack })) })), _jsxs("div", Object.assign({ className: b('actions') }, { children: [useRetry && (_jsx(Button, Object.assign({ view: "outlined", onClick: resetErrorBoundary }, { children: i18n('button-reset') }))), onReportProblem && (_jsx(Button, Object.assign({ view: "outlined", onClick: () => onReportProblem(error) }, { children: i18n('report-problem') })))] }))] }))] })));
19
+ return (_jsx(ErrorBoundaryFallback, { error: error, useRetry: useRetry, resetErrorBoundary: resetErrorBoundary, onReportProblem: onReportProblem }));
15
20
  } }, { children: children })));
16
- };
21
+ }
22
+ export function ErrorBoundaryFallback({ error, resetErrorBoundary, useRetry, onReportProblem, }) {
23
+ return (_jsxs("div", Object.assign({ className: b() }, { children: [_jsx(Illustration, { name: "error", className: b('illustration') }), _jsxs("div", Object.assign({ className: b('content') }, { children: [_jsx("h2", Object.assign({ className: b('error-title') }, { children: i18n('error-title') })), _jsx("div", Object.assign({ className: b('error-description') }, { children: i18n('error-description') })), _jsx(Disclosure, Object.assign({ summary: i18n('show-details'), className: b('show-details'), size: "m" }, { children: _jsx("pre", Object.assign({ className: b('error-details') }, { children: error.stack })) })), _jsxs("div", Object.assign({ className: b('actions') }, { children: [useRetry && (_jsx(Button, Object.assign({ view: "outlined", onClick: resetErrorBoundary }, { children: i18n('button-reset') }))), onReportProblem && (_jsx(Button, Object.assign({ view: "outlined", onClick: () => onReportProblem(error) }, { children: i18n('report-problem') })))] }))] }))] })));
24
+ }
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react';
2
+ export interface ClusterModeGuardProps {
3
+ children: ReactNode;
4
+ mode: 'single' | 'multi';
5
+ }
6
+ export declare function ClusterModeGuard({ children, mode }: ClusterModeGuardProps): JSX.Element | null;
@@ -0,0 +1,6 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTypedSelector } from '../../lib';
3
+ export function ClusterModeGuard({ children, mode }) {
4
+ const shouldRender = useTypedSelector((state) => mode === 'single' ? state.singleClusterMode : !state.singleClusterMode);
5
+ return shouldRender ? _jsx(_Fragment, { children: children }) : null;
6
+ }
@@ -0,0 +1 @@
1
+ export * from './ClusterModeGuard';
@@ -0,0 +1 @@
1
+ export * from './ClusterModeGuard';
@@ -3,6 +3,7 @@ export declare type SettingsElementType = 'switch' | 'radio';
3
3
  export interface SettingProps {
4
4
  type?: SettingsElementType;
5
5
  title: string;
6
+ description?: ReactNode;
6
7
  settingKey: string;
7
8
  helpPopoverContent?: ReactNode;
8
9
  options?: {
@@ -12,4 +13,4 @@ export interface SettingProps {
12
13
  defaultValue?: unknown;
13
14
  onValueUpdate?: VoidFunction;
14
15
  }
15
- export declare const Setting: ({ type, settingKey, title, helpPopoverContent, options, defaultValue, onValueUpdate, }: SettingProps) => JSX.Element;
16
+ export declare const Setting: ({ type, settingKey, title, description, helpPopoverContent, options, defaultValue, onValueUpdate, }: SettingProps) => JSX.Element;
@@ -4,7 +4,7 @@ import { Settings } from '@gravity-ui/navigation';
4
4
  import { LabelWithPopover } from '../../components/LabelWithPopover/LabelWithPopover';
5
5
  import { useSetting } from '../../utils/hooks';
6
6
  import { b } from './UserSettings';
7
- export const Setting = ({ type = 'switch', settingKey, title, helpPopoverContent, options, defaultValue, onValueUpdate, }) => {
7
+ export const Setting = ({ type = 'switch', settingKey, title, description, helpPopoverContent, options, defaultValue, onValueUpdate, }) => {
8
8
  const [settingValue, setValue] = useSetting(settingKey, defaultValue);
9
9
  const onUpdate = (value) => {
10
10
  setValue(value);
@@ -33,5 +33,5 @@ export const Setting = ({ type = 'switch', settingKey, title, helpPopoverContent
33
33
  return null;
34
34
  }
35
35
  };
36
- return (_jsx(Settings.Item, Object.assign({ title: title, highlightedTitle: title, renderTitleComponent: renderTitleComponent }, { children: getSettingsElement(type) })));
36
+ return (_jsx(Settings.Item, Object.assign({ title: title, highlightedTitle: title, description: description, renderTitleComponent: renderTitleComponent }, { children: getSettingsElement(type) })));
37
37
  };
@@ -10,6 +10,8 @@
10
10
  "settings.language.title": "Interface language",
11
11
  "settings.language.option-russian": "Russian",
12
12
  "settings.language.option-english": "English",
13
+ "settings.binaryDataInPlainTextDisplay.title": "Display binary data in plain text",
14
+ "settings.binaryDataInPlainTextDisplay.description": "Available starting from version 24.1",
13
15
  "settings.invertedDisks.title": "Inverted disks space indicators",
14
16
  "settings.useNodesEndpoint.title": "Break the Nodes tab in Diagnostics",
15
17
  "settings.useNodesEndpoint.popover": "Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It could return incorrect data on some versions",
@@ -1,2 +1,2 @@
1
- declare const _default: (key: string, params?: import("@gravity-ui/i18n").Params | undefined) => string;
1
+ declare const _default: (key: "page.general" | "section.appearance" | "page.experiments" | "section.experiments" | "settings.theme.title" | "settings.theme.option-dark" | "settings.theme.option-light" | "settings.theme.option-system" | "settings.language.title" | "settings.language.option-russian" | "settings.language.option-english" | "settings.binaryDataInPlainTextDisplay.title" | "settings.binaryDataInPlainTextDisplay.description" | "settings.invertedDisks.title" | "settings.useNodesEndpoint.title" | "settings.useNodesEndpoint.popover" | "settings.useVirtualTables.title" | "settings.useVirtualTables.popover" | "settings.queryUseMultiSchema.title" | "settings.queryUseMultiSchema.popover", params?: import("@gravity-ui/i18n").Params | undefined) => string;
2
2
  export default _default;
@@ -1,7 +1,3 @@
1
- import { i18n, Lang } from '../../../utils/i18n';
1
+ import { registerKeysets } from '../../../utils/i18n';
2
2
  import en from './en.json';
3
- import ru from './ru.json';
4
- const COMPONENT = 'ydb-user-settings';
5
- i18n.registerKeyset(Lang.En, COMPONENT, en);
6
- i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
7
- export default i18n.keyset(COMPONENT);
3
+ export default registerKeysets('ydb-user-settings', { en });
@@ -14,6 +14,7 @@ export interface SettingsPage {
14
14
  export declare type YDBEmbeddedUISettings = SettingsPage[];
15
15
  export declare const themeSetting: SettingProps;
16
16
  export declare const languageSetting: SettingProps;
17
+ export declare const binaryDataInPlainTextDisplay: SettingProps;
17
18
  export declare const invertedDisksSetting: SettingProps;
18
19
  export declare const useNodesEndpointSetting: SettingProps;
19
20
  export declare const useVirtualTables: SettingProps;
@@ -1,8 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  import favoriteFilledIcon from '../../assets/icons/star.svg';
2
3
  import flaskIcon from '../../assets/icons/flask.svg';
3
- import { INVERTED_DISKS_KEY, LANGUAGE_KEY, THEME_KEY, USE_BACKEND_PARAMS_FOR_TABLES_KEY, USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY, QUERY_USE_MULTI_SCHEMA_KEY, } from '../../utils/constants';
4
+ import { INVERTED_DISKS_KEY, LANGUAGE_KEY, THEME_KEY, USE_BACKEND_PARAMS_FOR_TABLES_KEY, USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY, QUERY_USE_MULTI_SCHEMA_KEY, BINARY_DATA_IN_PLAIN_TEXT_DISPLAY, } from '../../utils/constants';
4
5
  import { Lang, defaultLang } from '../../utils/i18n';
5
6
  import i18n from './i18n';
7
+ import { ClusterModeGuard } from '../ClusterModeGuard';
6
8
  const themeOptions = [
7
9
  {
8
10
  value: 'system',
@@ -43,6 +45,11 @@ export const languageSetting = {
43
45
  window.location.reload();
44
46
  },
45
47
  };
48
+ export const binaryDataInPlainTextDisplay = {
49
+ settingKey: BINARY_DATA_IN_PLAIN_TEXT_DISPLAY,
50
+ title: i18n('settings.binaryDataInPlainTextDisplay.title'),
51
+ description: (_jsx(ClusterModeGuard, Object.assign({ mode: "multi" }, { children: i18n('settings.binaryDataInPlainTextDisplay.description') }))),
52
+ };
46
53
  export const invertedDisksSetting = {
47
54
  settingKey: INVERTED_DISKS_KEY,
48
55
  title: i18n('settings.invertedDisks.title'),
@@ -65,7 +72,7 @@ export const queryUseMultiSchemaSetting = {
65
72
  export const appearanceSection = {
66
73
  id: 'appearanceSection',
67
74
  title: i18n('section.appearance'),
68
- settings: [themeSetting, invertedDisksSetting],
75
+ settings: [themeSetting, invertedDisksSetting, binaryDataInPlainTextDisplay],
69
76
  };
70
77
  export const experimentsSection = {
71
78
  id: 'experimentsSection',
package/dist/lib.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { App as SingleClusterApp, AppSlots } from './containers/App';
2
2
  export { AppWithClusters as MultiClusterApp } from './containers/AppWithClusters/AppWithClusters';
3
- export { ErrorBoundary } from './components/ErrorBoundary/ErrorBoundary';
3
+ export { ErrorBoundaryInner as ErrorBoundary, ErrorBoundaryFallback, } from './components/ErrorBoundary/ErrorBoundary';
4
4
  export { configureStore, rootReducer } from './store';
5
+ export { default as appRoutes } from './routes';
5
6
  export { createApi, YdbEmbeddedAPI, YdbWebVersionAPI } from './services/api';
6
7
  export { settingsManager } from './services/settings';
7
8
  export { settings as userSettings } from './containers/UserSettings/settings';
package/dist/lib.js CHANGED
@@ -1,7 +1,8 @@
1
1
  export { App as SingleClusterApp, AppSlots } from './containers/App';
2
2
  export { AppWithClusters as MultiClusterApp } from './containers/AppWithClusters/AppWithClusters';
3
- export { ErrorBoundary } from './components/ErrorBoundary/ErrorBoundary';
3
+ export { ErrorBoundaryInner as ErrorBoundary, ErrorBoundaryFallback, } from './components/ErrorBoundary/ErrorBoundary';
4
4
  export { configureStore, rootReducer } from './store';
5
+ export { default as appRoutes } from './routes';
5
6
  export { createApi, YdbEmbeddedAPI, YdbWebVersionAPI } from './services/api';
6
7
  export { settingsManager } from './services/settings';
7
8
  export { settings as userSettings } from './containers/UserSettings/settings';
@@ -12,8 +12,10 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import AxiosWrapper from '@gravity-ui/axios-wrapper';
13
13
  import { backend as BACKEND, metaBackend as META_BACKEND } from '../store';
14
14
  import { prepareSortValue } from '../utils/filters';
15
+ import { BINARY_DATA_IN_PLAIN_TEXT_DISPLAY } from '../utils/constants';
15
16
  import { parseMetaCluster } from './parsers/parseMetaCluster';
16
17
  import { parseMetaTenants } from './parsers/parseMetaTenants';
18
+ import { settingsManager } from './settings';
17
19
  export class YdbEmbeddedAPI extends AxiosWrapper {
18
20
  getPath(path) {
19
21
  return `${BACKEND !== null && BACKEND !== void 0 ? BACKEND : ''}${path}`;
@@ -179,7 +181,12 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
179
181
  // Time difference to ensure that timeout from ui will be shown rather than backend error
180
182
  const uiTimeout = 9 * 60 * 1000;
181
183
  const backendTimeout = 10 * 60 * 1000;
182
- return this.post(this.getPath(`/viewer/json/query?timeout=${backendTimeout}${schema ? `&schema=${schema}` : ''}`), params, {}, {
184
+ /**
185
+ * Return strings using base64 encoding.
186
+ * @link https://github.com/ydb-platform/ydb/pull/647
187
+ */
188
+ const base64 = !settingsManager.readUserSettingsValue(BINARY_DATA_IN_PLAIN_TEXT_DISPLAY, true);
189
+ return this.post(this.getPath(`/viewer/json/query?timeout=${backendTimeout}&base64=${base64}${schema ? `&schema=${schema}` : ''}`), params, {}, {
183
190
  concurrentId,
184
191
  timeout: uiTimeout,
185
192
  });
@@ -1,5 +1,5 @@
1
1
  import { TENANT_PAGES_IDS } from '../store/reducers/tenant/constants';
2
- import { ASIDE_HEADER_COMPACT_KEY, INVERTED_DISKS_KEY, LANGUAGE_KEY, LAST_USED_QUERY_ACTION_KEY, PARTITIONS_HIDDEN_COLUMNS_KEY, QUERY_INITIAL_MODE_KEY, QUERY_USE_MULTI_SCHEMA_KEY, SAVED_QUERIES_KEY, TENANT_INITIAL_PAGE_KEY, THEME_KEY, USE_BACKEND_PARAMS_FOR_TABLES_KEY, USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY, USE_CLUSTER_BALANCER_AS_BACKEND_KEY, } from '../utils/constants';
2
+ import { ASIDE_HEADER_COMPACT_KEY, INVERTED_DISKS_KEY, LANGUAGE_KEY, LAST_USED_QUERY_ACTION_KEY, PARTITIONS_HIDDEN_COLUMNS_KEY, QUERY_INITIAL_MODE_KEY, QUERY_USE_MULTI_SCHEMA_KEY, SAVED_QUERIES_KEY, TENANT_INITIAL_PAGE_KEY, THEME_KEY, USE_BACKEND_PARAMS_FOR_TABLES_KEY, USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY, USE_CLUSTER_BALANCER_AS_BACKEND_KEY, BINARY_DATA_IN_PLAIN_TEXT_DISPLAY, } from '../utils/constants';
3
3
  import { QUERY_ACTIONS, QUERY_MODES } from '../utils/query';
4
4
  import { parseJson } from '../utils/utils';
5
5
  /** User settings keys and their default values */
@@ -9,6 +9,7 @@ export const DEFAULT_USER_SETTINGS = {
9
9
  [INVERTED_DISKS_KEY]: false,
10
10
  [USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY]: false,
11
11
  [QUERY_USE_MULTI_SCHEMA_KEY]: false,
12
+ [BINARY_DATA_IN_PLAIN_TEXT_DISPLAY]: true,
12
13
  [SAVED_QUERIES_KEY]: [],
13
14
  [TENANT_INITIAL_PAGE_KEY]: TENANT_PAGES_IDS.query,
14
15
  [QUERY_INITIAL_MODE_KEY]: QUERY_MODES.script,
@@ -56,6 +56,7 @@ export declare const SAVED_QUERIES_KEY = "saved_queries";
56
56
  export declare const ASIDE_HEADER_COMPACT_KEY = "asideHeaderCompact";
57
57
  export declare const QUERIES_HISTORY_KEY = "queries_history";
58
58
  export declare const DATA_QA_TUNE_COLUMNS_POPUP = "tune-columns-popup";
59
+ export declare const BINARY_DATA_IN_PLAIN_TEXT_DISPLAY = "binaryDataInPlainTextDisplay";
59
60
  export declare const DEFAULT_SIZE_RESULT_PANE_KEY = "default-size-result-pane";
60
61
  export declare const DEFAULT_SIZE_TENANT_SUMMARY_KEY = "default-size-tenant-summary-pane";
61
62
  export declare const DEFAULT_SIZE_TENANT_KEY = "default-size-tenant-pane";
@@ -72,6 +72,7 @@ export const SAVED_QUERIES_KEY = 'saved_queries';
72
72
  export const ASIDE_HEADER_COMPACT_KEY = 'asideHeaderCompact';
73
73
  export const QUERIES_HISTORY_KEY = 'queries_history';
74
74
  export const DATA_QA_TUNE_COLUMNS_POPUP = 'tune-columns-popup';
75
+ export const BINARY_DATA_IN_PLAIN_TEXT_DISPLAY = 'binaryDataInPlainTextDisplay';
75
76
  export const DEFAULT_SIZE_RESULT_PANE_KEY = 'default-size-result-pane';
76
77
  export const DEFAULT_SIZE_TENANT_SUMMARY_KEY = 'default-size-tenant-summary-pane';
77
78
  export const DEFAULT_SIZE_TENANT_KEY = 'default-size-tenant-pane';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "5.1.0",
3
+ "version": "5.2.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -1,20 +0,0 @@
1
- {
2
- "page.general": "Общие",
3
- "section.appearance": "Внешний вид",
4
- "page.experiments": "Эксперименты",
5
- "section.experiments": "Эксперименты",
6
- "settings.theme.title": "Тема",
7
- "settings.theme.option-dark": "Тёмная",
8
- "settings.theme.option-light": "Светлая",
9
- "settings.theme.option-system": "Системная",
10
- "settings.language.title": "Язык интерфейса",
11
- "settings.language.option-russian": "Русский",
12
- "settings.language.option-english": "English",
13
- "settings.invertedDisks.title": "Инвертированные индикаторы места на дисках",
14
- "settings.useNodesEndpoint.title": "Сломать вкладку Nodes в диагностике",
15
- "settings.useNodesEndpoint.popover": "Использовать эндпоинт /viewer/json/nodes для вкладки Nodes в диагностике. Может возвращать некорректные данные на некоторых версиях",
16
- "settings.useVirtualTables.title": "Использовать таблицу с загрузкой данных по скроллу для вкладок Nodes и Storage",
17
- "settings.useVirtualTables.popover": "Это улучшит производительность, но может работать нестабильно",
18
- "settings.queryUseMultiSchema.title": "Разрешить запросы с несколькими результатами",
19
- "settings.queryUseMultiSchema.popover": "Использовать для запросов схему 'multi', которая позволяет выполнять запросы с несколькими результатами. На версиях 23-3 и старше результат не возвращается вообще"
20
- }