Pushing initial work for done in Query plan feature to main (#17986)
* Adding initial boilerplate for qp2 * Adding feature flag in query plan 2 * Clearing show plan 2 after every run * Adding sub tree cost * removing unused method. * WIP 2 * Adding properties view and relative cost to query plan * WIP * Add icons to ads * Assing relative costs and prop windows * Enabling older query plan again * Making some PR fixes * Some more PR related fixes * Use MS org azdataGraph module * Moving new properties to azdata proposed. * Moving new class properties to proposed * added missing doc component. * Changing how azdatagraph package is referenced * Removing empty lines, fixing localization keys * Removing empty line, localizing some string * making css classes more specific * making some logic concise * localizing some more strings * Making more css classes specific * Removing important tag from css props * Checking if sum is greater than 0 to prevent divide by zero exceptions * Fixed loader error in bootstrap * Fixing query index * -fixing image paths -making css class more class specific by using nested selectors Co-authored-by: kburtram <karlb@microsoft.com>
@@ -761,7 +761,8 @@
|
|||||||
"vscode-textmate",
|
"vscode-textmate",
|
||||||
"vscode-oniguruma",
|
"vscode-oniguruma",
|
||||||
"iconv-lite-umd",
|
"iconv-lite-umd",
|
||||||
"jschardet"
|
"jschardet",
|
||||||
|
"azdataGraph"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -120,7 +120,8 @@
|
|||||||
"xterm-headless": "4.14.0-beta.11",
|
"xterm-headless": "4.14.0-beta.11",
|
||||||
"yauzl": "^2.9.2",
|
"yauzl": "^2.9.2",
|
||||||
"yazl": "^2.4.3",
|
"yazl": "^2.4.3",
|
||||||
"zone.js": "^0.8.4"
|
"zone.js": "^0.8.4",
|
||||||
|
"azdataGraph": "github:Microsoft/azdataGraph#0.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"7zip": "0.0.6",
|
"7zip": "0.0.6",
|
||||||
|
|||||||
5
src/bootstrap-window.js
vendored
@@ -141,7 +141,8 @@
|
|||||||
'jschardet': `${baseNodeModulesPath}/jschardet/dist/jschardet.min.js`,
|
'jschardet': `${baseNodeModulesPath}/jschardet/dist/jschardet.min.js`,
|
||||||
'@vscode/vscode-languagedetection': `${baseNodeModulesPath}/@vscode/vscode-languagedetection/dist/lib/index.js`,
|
'@vscode/vscode-languagedetection': `${baseNodeModulesPath}/@vscode/vscode-languagedetection/dist/lib/index.js`,
|
||||||
'tas-client-umd': `${baseNodeModulesPath}/tas-client-umd/lib/tas-client-umd.js`,
|
'tas-client-umd': `${baseNodeModulesPath}/tas-client-umd/lib/tas-client-umd.js`,
|
||||||
'ansi_up': `${baseNodeModulesPath}/ansi_up/ansi_up.js`
|
'ansi_up': `${baseNodeModulesPath}/ansi_up/ansi_up.js`,
|
||||||
|
'azdataGraph': `${baseNodeModulesPath}/azdataGraph/dist/build.js`
|
||||||
};
|
};
|
||||||
|
|
||||||
// For priviledged renderers, allow to load built-in and other node.js
|
// For priviledged renderers, allow to load built-in and other node.js
|
||||||
@@ -153,7 +154,7 @@
|
|||||||
// the expected method and so nothing needs to be done - but if it's AMD then the VS Code loader will throw an error
|
// the expected method and so nothing needs to be done - but if it's AMD then the VS Code loader will throw an error
|
||||||
// (Can only have one anonymous define call per script file). In order to make packages that do this load correctly
|
// (Can only have one anonymous define call per script file). In order to make packages that do this load correctly
|
||||||
// we need to add them to the list below to tell the loader that these should be loaded using AMD as well
|
// we need to add them to the list below to tell the loader that these should be loaded using AMD as well
|
||||||
loaderConfig.amdModulesPattern = /(vs|sql)\/|(^vscode-textmate$)|(^vscode-oniguruma$)|(^xterm$)|(^xterm-addon-search$)|(^xterm-addon-unicode11$)|(^xterm-addon-webgl$)|(^iconv-lite-umd$)|(^jschardet$)|(^@vscode\/vscode-languagedetection$)|(^tas-client-umd$)|(^ansi_up$)/; // {{SQL CARBON EDIT}} include sql and ansi_up in regex
|
loaderConfig.amdModulesPattern = /(vs|sql)\/|(^vscode-textmate$)|(^vscode-oniguruma$)|(^xterm$)|(^xterm-addon-search$)|(^xterm-addon-unicode11$)|(^xterm-addon-webgl$)|(^iconv-lite-umd$)|(^jschardet$)|(^@vscode\/vscode-languagedetection$)|(^tas-client-umd$)|(^ansi_up$)|(^azdataGraph$)/; // {{SQL CARBON EDIT}} include sql and ansi_up in regex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal before require.config()
|
// Signal before require.config()
|
||||||
|
|||||||
107
src/sql/azdata.proposed.d.ts
vendored
@@ -913,11 +913,22 @@ declare module 'azdata' {
|
|||||||
action: ActionOnCellCheckboxCheck;
|
action: ActionOnCellCheckboxCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QueryExecuteResultSetNotificationParams {
|
||||||
|
/**
|
||||||
|
* Contains query plans returned by the database in ResultSets.
|
||||||
|
*/
|
||||||
|
executionPlans: QueryPlanGraph[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ResultSetSummary {
|
export interface ResultSetSummary {
|
||||||
/**
|
/**
|
||||||
* The visualization options for the result set.
|
* The visualization options for the result set.
|
||||||
*/
|
*/
|
||||||
visualization?: VisualizationOptions;
|
visualization?: VisualizationOptions;
|
||||||
|
/**
|
||||||
|
* Generic query plan graph to be displayed in the results view.
|
||||||
|
*/
|
||||||
|
showplangraph?: QueryPlanGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1392,4 +1403,100 @@ declare module 'azdata' {
|
|||||||
errors?: { message: string, property?: DesignerEditPath }[];
|
errors?: { message: string, property?: DesignerEditPath }[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface QueryPlanGraph {
|
||||||
|
/**
|
||||||
|
* Root of the query plan tree
|
||||||
|
*/
|
||||||
|
root: QueryPlanGraphNode;
|
||||||
|
/**
|
||||||
|
* Underlying query for the query plan graph.
|
||||||
|
*/
|
||||||
|
query: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryPlanGraphNode {
|
||||||
|
/**
|
||||||
|
* Type of the node. This property determines the icon that is displayed for it
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
/**
|
||||||
|
* Cost associated with the node
|
||||||
|
*/
|
||||||
|
cost: number;
|
||||||
|
/**
|
||||||
|
* Cost of the node subtree
|
||||||
|
*/
|
||||||
|
subTreeCost: number;
|
||||||
|
/**
|
||||||
|
* Relative cost of the node compared to its siblings.
|
||||||
|
*/
|
||||||
|
relativeCost: number;
|
||||||
|
/**
|
||||||
|
* Time take by the node operation in milliseconds
|
||||||
|
*/
|
||||||
|
elapsedTimeInMs: number;
|
||||||
|
/**
|
||||||
|
* Node properties to be shown in the tooltip
|
||||||
|
*/
|
||||||
|
properties: QueryPlanGraphElementProperty[];
|
||||||
|
/**
|
||||||
|
* Display name for the node
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Description associated with the node.
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
/**
|
||||||
|
* Subtext displayed under the node name
|
||||||
|
*/
|
||||||
|
subtext: string[];
|
||||||
|
/**
|
||||||
|
* Direct children of the nodes.
|
||||||
|
*/
|
||||||
|
children: QueryPlanGraphNode[];
|
||||||
|
/**
|
||||||
|
* Edges corresponding to the children.
|
||||||
|
*/
|
||||||
|
edges: QueryGraphEdge[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryGraphEdge {
|
||||||
|
/**
|
||||||
|
* Count of the rows returned by the subtree of the edge.
|
||||||
|
*/
|
||||||
|
rowCount: number;
|
||||||
|
/**
|
||||||
|
* Size of the rows returned by the subtree of the edge.
|
||||||
|
*/
|
||||||
|
rowSize: number;
|
||||||
|
/**
|
||||||
|
* Edge properties to be shown in the tooltip.
|
||||||
|
*/
|
||||||
|
properties: QueryPlanGraphElementProperty[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryPlanGraphElementProperty {
|
||||||
|
/**
|
||||||
|
* Name of the property
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Formatted value for the property
|
||||||
|
*/
|
||||||
|
formattedValue: string;
|
||||||
|
/**
|
||||||
|
* Flag to show/hide props in tooltip
|
||||||
|
*/
|
||||||
|
showInToolTip: boolean;
|
||||||
|
/**
|
||||||
|
* Display order of property
|
||||||
|
*/
|
||||||
|
displayOrder: number;
|
||||||
|
/**
|
||||||
|
* Flag to indicate if the property has a longer value so that it will be shown at the bottom of the tooltip
|
||||||
|
*/
|
||||||
|
isLongString: boolean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { URI } from 'vs/base/common/uri';
|
|||||||
import { attachTabbedPanelStyler } from 'sql/workbench/common/styler';
|
import { attachTabbedPanelStyler } from 'sql/workbench/common/styler';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
import { QueryPlan2Tab } from 'sql/workbench/contrib/queryplan2/browser/queryPlan';
|
||||||
|
|
||||||
class MessagesView extends Disposable implements IPanelView {
|
class MessagesView extends Disposable implements IPanelView {
|
||||||
private messagePanel: MessagePanel;
|
private messagePanel: MessagePanel;
|
||||||
@@ -163,6 +164,7 @@ export class QueryResultsView extends Disposable {
|
|||||||
private messagesTab: MessagesTab;
|
private messagesTab: MessagesTab;
|
||||||
private chartTab: ChartTab;
|
private chartTab: ChartTab;
|
||||||
private qpTab: QueryPlanTab;
|
private qpTab: QueryPlanTab;
|
||||||
|
private qp2Tab: QueryPlan2Tab;
|
||||||
private topOperationsTab: TopOperationsTab;
|
private topOperationsTab: TopOperationsTab;
|
||||||
private dynamicModelViewTabs: QueryModelViewTab[] = [];
|
private dynamicModelViewTabs: QueryModelViewTab[] = [];
|
||||||
|
|
||||||
@@ -183,6 +185,7 @@ export class QueryResultsView extends Disposable {
|
|||||||
this._panelView = this._register(new TabbedPanel(container, { showHeaderWhenSingleView: true }));
|
this._panelView = this._register(new TabbedPanel(container, { showHeaderWhenSingleView: true }));
|
||||||
this._register(attachTabbedPanelStyler(this._panelView, themeService));
|
this._register(attachTabbedPanelStyler(this._panelView, themeService));
|
||||||
this.qpTab = this._register(new QueryPlanTab());
|
this.qpTab = this._register(new QueryPlanTab());
|
||||||
|
this.qp2Tab = this._register(new QueryPlan2Tab());
|
||||||
this.topOperationsTab = this._register(new TopOperationsTab(instantiationService));
|
this.topOperationsTab = this._register(new TopOperationsTab(instantiationService));
|
||||||
|
|
||||||
this._panelView.pushTab(this.resultsTab);
|
this._panelView.pushTab(this.resultsTab);
|
||||||
@@ -223,6 +226,7 @@ export class QueryResultsView extends Disposable {
|
|||||||
this.hideResults();
|
this.hideResults();
|
||||||
this.hideChart();
|
this.hideChart();
|
||||||
this.hidePlan();
|
this.hidePlan();
|
||||||
|
this.hidePlan2();
|
||||||
this.hideDynamicViewModelTabs();
|
this.hideDynamicViewModelTabs();
|
||||||
this.input?.state.visibleTabs.clear();
|
this.input?.state.visibleTabs.clear();
|
||||||
if (this.input) {
|
if (this.input) {
|
||||||
@@ -245,6 +249,15 @@ export class QueryResultsView extends Disposable {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this.runnerDisposables.add(runner.onQueryPlan2Available(e => {
|
||||||
|
if (this.qp2Tab) {
|
||||||
|
if (!this.input.state.visibleTabs.has(this.qp2Tab.identifier)) {
|
||||||
|
this.showPlan2();
|
||||||
|
}
|
||||||
|
this.qp2Tab.view.addGraphs(e.planGraphs);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
if (this.input?.state.visibleTabs.has(this.chartTab.identifier) && !this._panelView.contains(this.chartTab)) {
|
if (this.input?.state.visibleTabs.has(this.chartTab.identifier) && !this._panelView.contains(this.chartTab)) {
|
||||||
this._panelView.pushTab(this.chartTab);
|
this._panelView.pushTab(this.chartTab);
|
||||||
} else if (!this.input?.state.visibleTabs.has(this.chartTab.identifier) && this._panelView.contains(this.chartTab)) {
|
} else if (!this.input?.state.visibleTabs.has(this.chartTab.identifier) && this._panelView.contains(this.chartTab)) {
|
||||||
@@ -257,6 +270,12 @@ export class QueryResultsView extends Disposable {
|
|||||||
this._panelView.removeTab(this.qpTab.identifier);
|
this._panelView.removeTab(this.qpTab.identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.input?.state.visibleTabs.has(this.qp2Tab.identifier) && !this._panelView.contains(this.qp2Tab)) {
|
||||||
|
this._panelView.pushTab(this.qp2Tab);
|
||||||
|
} else if (!this.input?.state.visibleTabs.has(this.qp2Tab.identifier) && this._panelView.contains(this.qp2Tab)) {
|
||||||
|
this._panelView.removeTab(this.qp2Tab.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && !this._panelView.contains(this.topOperationsTab)) {
|
if (this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && !this._panelView.contains(this.topOperationsTab)) {
|
||||||
this._panelView.pushTab(this.topOperationsTab);
|
this._panelView.pushTab(this.topOperationsTab);
|
||||||
} else if (!this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && this._panelView.contains(this.topOperationsTab)) {
|
} else if (!this.input?.state.visibleTabs.has(this.topOperationsTab.identifier) && this._panelView.contains(this.topOperationsTab)) {
|
||||||
@@ -309,7 +328,7 @@ export class QueryResultsView extends Disposable {
|
|||||||
this._input = input;
|
this._input = input;
|
||||||
this.runnerDisposables.clear();
|
this.runnerDisposables.clear();
|
||||||
|
|
||||||
[this.resultsTab, this.messagesTab, this.qpTab, this.topOperationsTab, this.chartTab].forEach(t => t.clear());
|
[this.resultsTab, this.messagesTab, this.qpTab, this.qp2Tab, this.topOperationsTab, this.chartTab].forEach(t => t.clear());
|
||||||
this.dynamicModelViewTabs.forEach(t => t.clear());
|
this.dynamicModelViewTabs.forEach(t => t.clear());
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
@@ -412,6 +431,16 @@ export class QueryResultsView extends Disposable {
|
|||||||
this.topOperationsTab.view.showPlan(xml);
|
this.topOperationsTab.view.showPlan(xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public showPlan2() {
|
||||||
|
if (!this._panelView.contains(this.qp2Tab)) {
|
||||||
|
this.input?.state.visibleTabs.add(this.qp2Tab.identifier);
|
||||||
|
if (!this._panelView.contains(this.qp2Tab)) {
|
||||||
|
this._panelView.pushTab(this.qp2Tab);
|
||||||
|
}
|
||||||
|
this._panelView.showTab(this.qp2Tab.identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public hidePlan() {
|
public hidePlan() {
|
||||||
if (this._panelView.contains(this.qpTab)) {
|
if (this._panelView.contains(this.qpTab)) {
|
||||||
this._panelView.removeTab(this.qpTab.identifier);
|
this._panelView.removeTab(this.qpTab.identifier);
|
||||||
@@ -422,6 +451,13 @@ export class QueryResultsView extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hidePlan2() {
|
||||||
|
if (this._panelView.contains(this.qp2Tab)) {
|
||||||
|
this.qp2Tab.clear();
|
||||||
|
this._panelView.removeTab(this.qp2Tab.identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public hideDynamicViewModelTabs() {
|
public hideDynamicViewModelTabs() {
|
||||||
this.dynamicModelViewTabs.forEach(tab => {
|
this.dynamicModelViewTabs.forEach(tab => {
|
||||||
if (this._panelView.contains(tab)) {
|
if (this._panelView.contains(tab)) {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { QueryPlan2 } from 'sql/workbench/contrib/queryplan2/browser/queryPlan';
|
||||||
|
import { Action } from 'vs/base/common/actions';
|
||||||
|
import { Codicon } from 'vs/base/common/codicons';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
|
||||||
|
|
||||||
|
export class PropertiesAction extends Action {
|
||||||
|
public static ID = 'qp.propertiesAction';
|
||||||
|
public static LABEL = localize('queryPlanPropertiesActionLabel', "Properties");
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(PropertiesAction.ID, PropertiesAction.LABEL, Codicon.listUnordered.classNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async run(context: QueryPlan2): Promise<void> {
|
||||||
|
context.propContainer.style.visibility = context.propContainer.style.visibility === 'visible' ? 'hidden' : 'visible';
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/sql/workbench/contrib/queryplan2/browser/images/collapse.gif
Normal file
|
After Width: | Height: | Size: 846 B |
BIN
src/sql/workbench/contrib/queryplan2/browser/images/expand.gif
Normal file
|
After Width: | Height: | Size: 851 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 867 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 908 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 980 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/sql/workbench/contrib/queryplan2/browser/images/icons/if.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 966 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 843 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 783 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 908 B |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 897 B |
|
After Width: | Height: | Size: 995 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |