Merge changes from topic "winscope-ui-tweak"

* changes:
  Added a Favicon for better tab identification
  Make the timeline size relative to the parent
  Cleanup the UI for better visibility
  Move the Video to the top next to the timeline
  Migrate to new MaterialVue
This commit is contained in:
Pablo Gamito
2020-06-04 08:17:43 +00:00
committed by Android (Google) Code Review
19 changed files with 1167 additions and 788 deletions

View File

@@ -2,3 +2,4 @@ node_modules/
adb_proxy/venv/
.vscode/
dist/
yarn-error.log

View File

@@ -10,13 +10,13 @@
},
"dependencies": {
"vue": "^2.3.3",
"vue-material": "0.8.1"
"vue-material": "^1.0.0-beta-11"
},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-loader": "^6.0.0",
"babel-preset-env": "^1.5.1",
"cross-env": "^3.0.0",
"cross-env": "^7.0.2",
"css-loader": "^0.25.0",
"file-loader": "^0.9.0",
"html-webpack-inline-source-plugin": "^0.0.9",

View File

@@ -14,29 +14,59 @@
-->
<template>
<div id="app">
<md-whiteframe md-tag="md-toolbar">
<h1 class="md-title" style="flex: 1">{{title}}</h1>
<a class="md-button md-accent md-raised md-theme-default" @click="clear()" v-if="dataLoaded">Clear</a>
</md-whiteframe>
<div class="main-content">
<md-layout v-if="!dataLoaded" class="m-2">
<dataadb ref="adb" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/>
<datainput ref="input" :store="store" @dataReady="onDataReady" @statusChange="setStatus"/>
</md-layout>
<md-card v-if="dataLoaded">
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<h2 class="md-title">Timeline</h2>
<datafilter v-for="file in files" :key="file.filename" :store="store" :file="file" />
</md-whiteframe>
<md-list>
<md-list-item v-for="(file, idx) in files" :key="file.filename">
<md-icon>{{file.type.icon}}</md-icon>
<timeline :items="file.timeline" :selected-index="file.selectedIndex" :scale="scale" @item-selected="onTimelineItemSelected($event, idx)" class="timeline" />
</md-list-item>
</md-list>
</md-card>
<dataview v-for="file in files" :key="file.filename" :ref="file.filename" :store="store" :file="file" @focus="onDataViewFocus(file.filename)" />
</div>
<md-app>
<md-app-toolbar md-tag="md-toolbar">
<h1 class="md-title" style="flex: 1">{{title}}</h1>
<md-button
class="md-accent md-raised md-theme-default"
@click="clear()"
v-if="dataLoaded"
>Clear</md-button>
</md-app-toolbar>
<md-app-content class="main-content">
<div class="md-layout m-2" v-if="!dataLoaded">
<dataadb ref="adb" :store="store" @dataReady="onDataReady" @statusChange="setStatus" />
<datainput ref="input" :store="store" @dataReady="onDataReady" @statusChange="setStatus" />
</div>
<div class="md-layout md-alignment-left-center " v-if="dataLoaded">
<div class="md-layout-item video" v-if="video">
<videoview :file="video" :ref="video.filename" />
</div>
<div class="md-layout-item">
<md-toolbar
md-elevation="0"
class="md-transparent">
<h2 class="md-title">Timeline </h2>
<span> {{seekTime}} </span>
<datafilter v-for="file in files" :key="file.filename" :store="store" :file="file" />
</md-toolbar>
<md-list>
<md-list-item v-for="(file, idx) in files" :key="file.filename">
<md-icon>
{{file.type.icon}}
<md-tooltip md-direction="right">{{file.type.name}}</md-tooltip>
</md-icon>
<timeline
:items="file.timeline"
:selected-index="file.selectedIndex"
:scale="scale"
@item-selected="onTimelineItemSelected($event, idx)"
class="timeline"
/>
</md-list-item>
</md-list>
</div>
</div>
<dataview
v-for="file in files"
:key="file.filename"
:ref="file.filename"
:store="store"
:file="file"
@focus="onDataViewFocus(file.filename)"
/>
</md-app-content>
</md-app>
</div>
</template>
<script>
@@ -44,10 +74,13 @@ import TreeView from './TreeView.vue'
import Timeline from './Timeline.vue'
import Rects from './Rects.vue'
import DataView from './DataView.vue'
import VideoView from './VideoView.vue'
import DataInput from './DataInput.vue'
import LocalStore from './localstore.js'
import DataAdb from './DataAdb.vue'
import DataFilter from './DataFilter.vue'
import FileType from './FileType.js'
import { nanos_to_string } from './transform.js'
const APP_NAME = "Winscope"
@@ -68,9 +101,11 @@ function findLastMatchingSorted(array, predicate) {
export default {
name: 'app',
mixins: [FileType],
data() {
return {
files: [],
video: null,
title: APP_NAME,
currentTimestamp: 0,
activeDataView: null,
@@ -143,7 +178,13 @@ export default {
return true;
},
onDataReady(files) {
this.files = files;
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (this.isVideo(file)) {
this.video = file;
}
this.files.push(file);
}
},
setStatus(status) {
if (status) {
@@ -151,7 +192,7 @@ export default {
} else {
this.title = APP_NAME;
}
}
},
},
computed: {
prettyDump: function() { return JSON.stringify(this.dump, null, 2); },
@@ -166,6 +207,9 @@ export default {
this.activeDataView = this.files[0].filename;
}
return this.activeDataView;
},
seekTime: function() {
return nanos_to_string(this.currentTimestamp);
}
},
watch: {
@@ -174,14 +218,14 @@ export default {
}
},
components: {
'timeline': Timeline,
'dataview': DataView,
'datainput': DataInput,
'dataadb': DataAdb,
'datafilter': DataFilter,
},
}
timeline: Timeline,
dataview: DataView,
videoview: VideoView,
datainput: DataInput,
dataadb: DataAdb,
datafilter: DataFilter
}
};
</script>
<style>
.main-content>* {
@@ -201,14 +245,14 @@ export default {
flex-wrap: wrap;
}
.md-layout > .md-card {
margin: 0.5em;
}
.md-button {
margin-top: 1em
}
.video {
flex-grow: 0;
}
h1,
h2 {
font-weight: normal;
@@ -227,5 +271,4 @@ li {
a {
color: #42b983;
}
</style>

View File

@@ -18,7 +18,7 @@
<div class="md-title">ADB Connect</div>
</md-card-header>
<md-card-content v-if="status === STATES.CONNECTING">
<md-spinner md-indeterminate></md-spinner>
<md-progress-spinner md-indeterminate></md-progress-spinner>
</md-card-content>
<md-card-content v-if="status === STATES.NO_PROXY">
<md-icon class="md-accent">error</md-icon>
@@ -52,10 +52,10 @@
<md-card-content v-if="status === STATES.UNAUTH">
<md-icon class="md-accent">lock</md-icon>
<span class="md-subheading">Proxy authorisation required</span>
<md-input-container>
<md-field>
<label>Enter Winscope proxy token</label>
<md-input v-model="adbStore.proxyKey"></md-input>
</md-input-container>
</md-field>
<div class="md-body-2">The proxy token is printed to console on proxy launch, copy and paste it above.</div>
<div class="md-layout md-gutter">
<md-button class="md-accent md-raised" @click="restart">Connect</md-button>
@@ -65,15 +65,17 @@
<div class="md-subheading">{{ Object.keys(devices).length > 0 ? "Connected devices:" : "No devices detected" }}</div>
<md-list>
<md-list-item v-for="(device, id) in devices" :key="id" @click="selectDevice(id)" :disabled="!device.authorised">
<md-icon>{{ device.authorised ? "smartphone" : "screen_lock_portrait" }}</md-icon><span>{{ device.authorised ? device.model : "unauthorised" }} ({{ id }})</span>
<md-icon>{{ device.authorised ? "smartphone" : "screen_lock_portrait" }}</md-icon>
<span class="md-list-item-text">{{ device.authorised ? device.model : "unauthorised" }} ({{ id }})</span>
</md-list-item>
</md-list>
<md-spinner :md-size="30" md-indeterminate></md-spinner>
<md-progress-spinner :md-size="30" md-indeterminate></md-progress-spinner>
</md-card-content>
<md-card-content v-if="status === STATES.START_TRACE">
<md-list>
<md-list-item>
<md-icon>smartphone</md-icon><span>{{ devices[selectedDevice].model }} ({{ selectedDevice }})</span>
<md-icon>smartphone</md-icon>
<span class="md-list-item-text">{{ devices[selectedDevice].model }} ({{ selectedDevice }})</span>
</md-list-item>
</md-list>
<div>
@@ -84,7 +86,7 @@
<p>Dump targets:</p>
<md-checkbox v-for="file in DUMP_FILES" :key="file" v-model="adbStore[file]">{{FILE_TYPES[file].name}}</md-checkbox>
</div>
<div class="md-layout md-gutter">
<div class="md-layout">
<md-button class="md-accent md-raised" @click="startTrace">Start trace</md-button>
<md-button class="md-accent md-raised" @click="dumpState">Dump state</md-button>
<md-button class="md-raised" @click="resetLastDevice">Device list</md-button>
@@ -100,14 +102,14 @@
</md-card-content>
<md-card-content v-if="status === STATES.END_TRACE">
<span class="md-subheading">Tracing...</span>
<md-progress md-indeterminate></md-progress>
<div class="md-layout md-gutter">
<md-progress-bar md-mode="indeterminate"></md-progress-bar>
<div class="md-layout">
<md-button class="md-accent md-raised" @click="endTrace">End trace</md-button>
</div>
</md-card-content>
<md-card-content v-if="status === STATES.LOAD_DATA">
<span class="md-subheading">Loading data...</span>
<md-progress :md-progress="loadProgress"></md-progress>
<md-progress-bar md-mode="determinate" :md-value="loadProgress"></md-progress-bar>
</md-card-content>
</md-card>
</template>

View File

@@ -14,15 +14,17 @@
-->
<template>
<div class="bounds" v-if="visible">
<md-select v-model="visibleTransactions" name="visibleTransactions" id="visibleTransactions"
placeholder="Everything Turned Off" md-dense multiple @input="updateFilter()" >
<md-option value="displayCreation, displayDeletion">Display</md-option>
<md-option value="powerModeUpdate">Power Mode</md-option>
<md-option value="surfaceCreation, surfaceDeletion">Surface</md-option>
<md-option value="transaction">Transaction</md-option>
<md-option value="vsyncEvent">vsync</md-option>
<md-option value="bufferUpdate">Buffer</md-option>
</md-select>
<md-field>
<md-select v-model="visibleTransactions" name="visibleTransactions" id="visibleTransactions"
placeholder="Everything Turned Off" md-dense multiple @input="updateFilter()" >
<md-option value="displayCreation, displayDeletion">Display</md-option>
<md-option value="powerModeUpdate">Power Mode</md-option>
<md-option value="surfaceCreation, surfaceDeletion">Surface</md-option>
<md-option value="transaction">Transaction</md-option>
<md-option value="vsyncEvent">vsync</md-option>
<md-option value="bufferUpdate">Buffer</md-option>
</md-select>
</md-field>
</div>
</template>
<script>

View File

@@ -37,15 +37,17 @@
</div>
<div class="md-layout">
<div class="md-layout-item md-small-size-100">
<md-field>
<md-select v-model="fileType" id="file-type" placeholder="File type">
<md-option value="auto">Detect type</md-option>
<md-option :value="k" v-for="(v,k) in FILE_TYPES" v-bind:key="v.name">{{v.name}}</md-option>
</md-select>
</md-field>
</div>
</div>
<div class="md-layout md-gutter">
<input type="file" @change="onLoadFile" id="upload-file" v-show="false" />
<label class="md-button md-accent md-raised md-theme-default" for="upload-file">Add File</label>
<div class="md-layout">
<input type="file" @change="onLoadFile" ref="fileUpload" v-show="false" />
<md-button class="md-accent md-raised md-theme-default" @click="$refs.fileUpload.click()">Add File</md-button>
<md-button v-if="dataReady" @click="onSubmit" class="md-button md-primary md-raised md-theme-default">Submit</md-button>
</div>
</md-card-content>

View File

@@ -13,33 +13,32 @@
limitations under the License.
-->
<template>
<md-card v-if="file">
<md-card v-if="(isLog(file) || isTrace(file))">
<md-card-header>
<md-card-header-text>
<div class="md-title">
<md-icon>{{file.type.icon}}</md-icon> {{file.filename}}
<md-icon>{{file.type.icon}}</md-icon>
{{file.filename}}
</div>
</md-card-header-text>
<md-button :href="file.blobUrl" :download="file.filename" class="md-icon-button">
<md-icon>save_alt</md-icon>
</md-button>
</md-card-header>
<traceview v-if="isTrace" :store="store" :file="file" ref="view" />
<videoview v-if="isVideo" :file="file" ref="view" />
<logview v-if="isLog" :file="file" ref="view" />
<div v-if="!(isTrace || isVideo || isLog)">
<traceview v-if="isTrace(file)" :store="store" :file="file" ref="view" />
<logview v-if="isLog(file)" :file="file" ref="view" />
<div v-if="!(isTrace(file) || isVideo(file) || isLog(file))">
<h1 class="bad">Unrecognized DataType</h1>
</div>
</md-card>
</template>
<script>
import TraceView from './TraceView.vue'
import VideoView from './VideoView.vue'
import LogView from './LogView.vue'
import { DATA_TYPES } from './decode.js'
import TraceView from "./TraceView.vue";
import LogView from "./LogView.vue";
import FileType from "./FileType.js";
export default {
name: 'dataview',
name: "dataview",
data() {
return {}
},
@@ -49,32 +48,15 @@ export default {
},
arrowDown() {
return this.$refs.view.arrowDown();
},
},
props: ['store', 'file'],
computed: {
isTrace() {
return this.file.type == DATA_TYPES.WINDOW_MANAGER ||
this.file.type == DATA_TYPES.SURFACE_FLINGER ||
this.file.type == DATA_TYPES.TRANSACTION ||
this.file.type == DATA_TYPES.WAYLAND ||
this.file.type == DATA_TYPES.SYSTEM_UI ||
this.file.type == DATA_TYPES.LAUNCHER
},
isVideo() {
return this.file.type == DATA_TYPES.SCREEN_RECORDING;
},
isLog() {
return this.file.type == DATA_TYPES.PROTO_LOG
}
},
props: ["store", "file"],
mixins: [FileType],
components: {
'traceview': TraceView,
'videoview': VideoView,
'logview': LogView,
traceview: TraceView,
logview: LogView
}
}
};
</script>
<style>
.bad {

View File

@@ -0,0 +1,21 @@
import { DATA_TYPES } from './decode.js'
export default {
name: 'FileType',
methods: {
isTrace: function (file) {
return file.type == DATA_TYPES.WINDOW_MANAGER ||
file.type == DATA_TYPES.SURFACE_FLINGER ||
file.type == DATA_TYPES.TRANSACTION ||
file.type == DATA_TYPES.WAYLAND ||
file.type == DATA_TYPES.SYSTEM_UI ||
file.type == DATA_TYPES.LAUNCHER
},
isVideo(file) {
return file.type == DATA_TYPES.SCREEN_RECORDING;
},
isLog(file) {
return file.type == DATA_TYPES.PROTO_LOG
}
}
}

View File

@@ -15,20 +15,20 @@
<template>
<md-card-content class="container">
<md-table class="log-table">
<md-table-header>
<md-table-row>
<md-table-head class="time-column-header">Time</md-table-head>
<md-table-head class="tag-column-header">Tag</md-table-head>
<md-table-head class="at-column-header">At</md-table-head>
<md-table-head>Message</md-table-head>
</md-table-header>
<md-table-body>
</md-table-row>
<md-table-row v-for="line in data" :key="line.timestamp">
<md-table-cell class="time-column">{{line.time}}</md-table-cell>
<md-table-cell class="tag-column">{{line.tag}}</md-table-cell>
<md-table-cell class="at-column">{{line.at}}</md-table-cell>
<md-table-cell>{{line.text}}</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</md-card-content>
</template>

View File

@@ -31,6 +31,7 @@ export default {
props: ['bounds', 'rects', 'highlight'],
data () {
return {
desiredHeight: 800,
desiredWidth: 400,
};
},
@@ -50,7 +51,13 @@ export default {
},
methods: {
s(sourceCoordinate) { // translate source into target coordinates
return sourceCoordinate / this.boundsC.width * this.desiredWidth;
var scale;
if(this.boundsC.width < this.boundsC.height) {
scale = this.desiredHeight / this.boundsC.height;
} else {
scale = this.desiredWidth / this.boundsC.width;
}
return sourceCoordinate * scale;
},
rectToStyle(r) {
var x = this.s(r.left);
@@ -82,10 +89,10 @@ export default {
}
.rect {
border: 1px solid black;
background-color: rgba(100, 100, 100, 0.8);
background-color: rgba(110, 114, 116, 0.8);
}
.highlight {
border: 2px solid red;
border: 2px solid rgb(235, 52, 52);
pointer-events: none;
}
.label {

View File

@@ -13,17 +13,38 @@
limitations under the License.
-->
<template>
<svg width="2000" height="20" viewBox="-5,0,2010,20">
<circle :cx="position(item)" cy="10" r="5" v-for="(item, idx) in items" @click="onItemClick(idx)" />
<circle v-if="items.length" :cx="position(selected)" cy="10" r="5" class="selected" />
<svg width="100%" height="20">
<rect
:x="position(item)"
y="0"
:width="pointWidth"
:height="pointHeight"
:rx="corner"
v-for="(item, idx) in items"
:key="item"
@click="onItemClick(idx)"
/>
<rect
v-if="items.length"
:x="position(selected)"
y="0"
:width="pointWidth"
:height="pointHeight"
:rx="corner"
class="selected"
/>
</svg>
</template>
<script>
export default {
name: 'timeline',
props: ['items', 'selectedIndex', 'scale'],
name: "timeline",
props: ["items", "selectedIndex", "scale"],
data() {
return {};
return {
pointWidth: "1%",
pointHeight: 15,
corner: 2
};
},
methods: {
position(item) {
@@ -34,11 +55,11 @@ export default {
if (scale[0] >= scale[1]) {
return cx;
}
return (cx - scale[0]) / (scale[1] - scale[0]) * 2000;
return (((cx - scale[0]) / (scale[1] - scale[0])) * 100) + "%";
},
onItemClick(index) {
this.$emit('item-selected', index);
},
this.$emit("item-selected", index);
}
},
computed: {
timestamps() {
@@ -50,13 +71,11 @@ export default {
selected() {
return this.items[this.selectedIndex];
}
},
}
}
};
</script>
<style scoped>
.selected {
fill: red;
fill: rgb(240, 59, 59);
}
</style>

View File

@@ -15,29 +15,29 @@
<template>
<md-card-content class="container">
<md-card class="rects" v-if="hasScreenView">
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<h2 class="md-title">Screen</h2>
</md-whiteframe>
<md-whiteframe md-elevation="8">
</md-content>
<md-content class="md-elevation-8">
<rects :bounds="bounds" :rects="rects" :highlight="highlight" @rect-click="onRectClick" />
</md-whiteframe>
</md-content>
</md-card>
<md-card class="hierarchy">
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<h2 class="md-title" style="flex: 1;">Hierarchy</h2>
<md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
<md-checkbox v-model="store.flattened">Flat</md-checkbox>
<input id="filter" type="search" placeholder="Filter..." v-model="hierarchyPropertyFilterString" />
</md-whiteframe>
</md-content>
<tree-view class="data-card" :item="tree" @item-selected="itemSelected" :selected="hierarchySelected" :filter="hierarchyFilter" :flattened="store.flattened" ref="hierarchy" />
</md-card>
<md-card class="properties">
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<md-content md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<h2 class="md-title" style="flex: 1">Properties</h2>
<div class="filter">
<input id="filter" type="search" placeholder="Filter..." v-model="propertyFilterString" />
</div>
</md-whiteframe>
</md-content>
<tree-view class="pre-line-data-card" :item="selectedTree" :filter="propertyFilter" />
</md-card>
</md-card-content>

View File

@@ -14,28 +14,61 @@
-->
<template>
<div class="tree-view">
<div @click="clicked" :class="computedClass">
<span class="kind">{{item.kind}}</span><span v-if="item.kind && item.name"> - </span><span>{{item.name}}</span>
<div v-for="c in item.chips" :title="c.long" :class="chipClassForChip(c)">
{{c.short}}
</div>
<div @click="clicked" :class="computedClass" class="node">
<span class="kind">{{item.kind}}</span>
<span v-if="item.kind && item.name">-</span>
<span>{{item.name}}</span>
<div
v-for="c in item.chips"
v-bind:key="c.long"
:title="c.long"
:class="chipClassForChip(c)"
>{{c.short}}</div>
</div>
<div class="children" v-if="children">
<tree-view v-for="(c,i) in children" :item="c" @item-selected="childItemSelected" :selected="selected" :key="i" :chip-class='chipClass' :filter="childFilter(c)" :flattened="flattened" :force-flattened="applyingFlattened" v-show="filterMatches(c)" ref='children' />
<tree-view
v-for="(c,i) in children"
:item="c"
@item-selected="childItemSelected"
:selected="selected"
:key="i"
:chip-class="chipClass"
:filter="childFilter(c)"
:flattened="flattened"
:force-flattened="applyingFlattened"
v-show="filterMatches(c)"
ref="children"
/>
</div>
</div>
</template>
<script>
import jsonProtoDefs from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto'
import protobuf from 'protobufjs'
import jsonProtoDefs from "frameworks/base/core/proto/android/server/windowmanagertrace.proto";
import protobuf from "protobufjs";
var protoDefs = protobuf.Root.fromJSON(jsonProtoDefs);
var TraceMessage = protoDefs.lookupType(
"com.android.server.wm.WindowManagerTraceFileProto"
);
var ServiceMessage = protoDefs.lookupType(
"com.android.server.wm.WindowManagerServiceDumpProto"
);
export default {
name: 'tree-view',
props: ['item', 'selected', 'chipClass', 'filter', 'flattened', 'force-flattened'],
data() {
return {};
name: "tree-view",
props: [
"item",
"selected",
"chipClass",
"filter",
"flattened",
"force-flattened"
],
data: function() {
return {
isChildSelected: false
};
},
methods: {
selectNext(found, parent) {
@@ -43,7 +76,7 @@ export default {
this.clicked();
return false;
}
if (this.selected === this.item) {
if (this.isCurrentSelected()) {
found = true;
}
if (this.$refs.children) {
@@ -63,20 +96,23 @@ export default {
this.clicked();
return false;
}
if (this.selected === this.item) {
if (this.isCurrentSelected()) {
found = true;
}
return found;
},
childItemSelected(item) {
this.$emit('item-selected', item);
this.isChildSelected = true;
this.$emit("item-selected", item);
},
clicked() {
this.$emit('item-selected', this.item);
this.$emit("item-selected", this.item);
},
chipClassForChip(c) {
return ['tree-view-internal-chip', this.chipClassOrDefault,
this.chipClassOrDefault + '-' + (c.class || 'default')
return [
"tree-view-internal-chip",
this.chipClassOrDefault,
this.chipClassOrDefault + "-" + (c.class || "default")
];
},
filterMatches(c) {
@@ -99,40 +135,72 @@ export default {
}
return this.filter;
},
isCurrentSelected() {
return this.selected === this.item;
},
},
computed: {
computedClass() {
return (this.item == this.selected) ? 'selected' : ''
},
chipClassOrDefault() {
return this.chipClass || 'tree-view-chip';
return this.chipClass || "tree-view-chip";
},
applyingFlattened() {
return this.flattened && this.item.flattened || this.forceFlattened;
return (this.flattened && this.item.flattened) || this.forceFlattened;
},
children() {
return this.applyingFlattened ? this.item.flattened : this.item.children;
},
}
}
};
</script>
<style>
.data-card > .tree-view {
border: none;
}
.tree-view {
display: block;
border-left: 1px solid rgb(238, 238, 238);
}
.tree-view.tree-view {
margin: 0;
padding: 1px 8px;
}
.tree-view .node {
display: inline-block;
padding: 2px;
cursor: pointer;
}
.tree-view .node:hover:not(.selected) {
background: #f1f1f1;
}
.children {
margin-left: 24px;
margin-left: 8px;
margin-top: 0px;
}
.kind {
color: #333;
font-weight: bold;
}
.selected {
background-color: #3f51b5;
background-color: #365179;
color: white;
}
.childSelected {
border-left: 1px solid rgb(233, 22, 22)
}
.selected .kind {
color: #ccc;
color: #e9e9e9;
}
.tree-view-internal-chip {

View File

@@ -13,16 +13,7 @@
limitations under the License.
-->
<template>
<md-card-content class="container">
<md-card class="rects">
<md-whiteframe md-tag="md-toolbar" md-elevation="0" class="card-toolbar md-transparent md-dense">
<h2 class="md-title">Screen</h2>
</md-whiteframe>
<md-whiteframe md-elevation="8">
<video :id="file.filename" class="screen" :src="file.data" />
</md-whiteframe>
</md-card>
</md-card-content>
<video class="md-elevation-2 screen" :id="file.filename" :src="file.data" />
</template>
<script>
const EPSILON = 0.00001
@@ -70,6 +61,6 @@ export default {
<style>
.screen {
max-height: 50em;
max-width: 50em;
}
</style>

View File

@@ -13,10 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<html lang="en">
<html lang="en" class="md-scrollbar">
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="icon" type="image/svg" href="static/favicon.svg"/>
<meta charset="utf-8">
<title>winscope</title>

View File

@@ -18,6 +18,8 @@ import Vue from 'vue'
import App from './App.vue'
import 'style-loader!css-loader!vue-material/dist/vue-material.css'
import 'style-loader!css-loader!vue-material/dist/theme/default.css'
import VueMaterial from 'vue-material'
Vue.use(VueMaterial);

View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="512"
height="512"
viewBox="0 0 135.46666 135.46667"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="winscope.svg">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient144">
<stop
style="stop-color:#3ddb85;stop-opacity:1;"
offset="0"
id="stop140" />
<stop
style="stop-color:#3ddb85;stop-opacity:0;"
offset="1"
id="stop142" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient144"
id="linearGradient146"
x1="334.86557"
y1="288.25122"
x2="362.64478"
y2="405.42892"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient144"
id="linearGradient152"
gradientUnits="userSpaceOnUse"
x1="334.86557"
y1="288.25122"
x2="362.64478"
y2="405.42892" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="369.78115"
inkscape:cy="43.694507"
inkscape:document-units="mm"
inkscape:current-layer="layer2"
showgrid="false"
units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="2560"
inkscape:window-height="1399"
inkscape:window-x="1440"
inkscape:window-y="20"
inkscape:window-maximized="0" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
style="display:inline"
transform="translate(0,-161.53331)">
<g
id="g60"
transform="matrix(1.4161793,0,0,1.4161793,-266.89731,76.345061)">
<path
id="path46"
d="m 254.98635,139.13116 c -4.3276,0 -7.84784,-3.71395 -7.84784,-8.27594 0,-4.56195 4.14824,-8.27278 7.84784,-8.27285 3.6996,-9e-5 7.84855,3.71014 7.84855,8.27285 0,4.56276 -3.52025,8.27594 -7.84855,8.27594 m -36.93749,-2.38905 c -3.0836,0 -5.59192,-2.64634 -5.59192,-5.89694 0,-3.25059 2.50784,-5.89476 5.59192,-5.89476 3.08358,0 5.5919,2.64364 5.5919,5.89476 0,3.25114 -2.50781,5.89694 -5.5919,5.89694 m 52.09761,-36.05821 11.17666,-20.405632 c 0.64058,-1.172551 0.25981,-2.670449 -0.85112,-3.348238 -1.11236,-0.674958 -2.53307,-0.273633 -3.17621,0.897219 l -11.31638,20.663525 c -8.6545,-4.162927 -18.37356,-6.482308 -28.74463,-6.482308 -10.37108,0 -20.09013,2.319381 -28.74411,6.482308 L 197.17379,77.827249 c -0.64314,-1.170852 -2.06385,-1.572177 -3.17621,-0.897219 -1.11167,0.677199 -1.49261,2.175808 -0.85113,3.348238 L 204.3231,100.6839 c -19.19192,11.00346 -32.318,31.48516 -34.23828,55.68305 h 134.29993 c -1.9218,-24.19736 -15.04789,-44.67959 -34.23828,-55.68357"
inkscape:connector-curvature="0"
style="fill:#3ddb85;stroke-width:0.52547503"
sodipodi:nodetypes="csssccsssccccccscccccccc" />
<path
id="path48"
d="M -0.75490308,-4.3032616 H 466.2911 V 245.86074 H -0.75490308 Z"
inkscape:connector-curvature="0"
style="fill:none" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2"
transform="translate(0,-161.53331)"
style="display:inline">
<g
id="g150"
transform="matrix(1.2390981,0,0,1.2745122,-22.278191,-60.145091)">
<path
inkscape:connector-curvature="0"
style="font-weight:400;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;display:inline;overflow:visible;opacity:0.92156863;isolation:auto;mix-blend-mode:normal;fill:#000000;stroke-width:3.05656147"
d="m 93.327473,227.20793 c -11.817096,0 -21.462353,9.58571 -21.462353,21.32967 0,11.74397 9.645257,21.32967 21.462353,21.32967 10.773667,0 19.717027,-7.97589 21.216847,-18.28256 h 2.56476 c 1.69335,1e-5 3.06608,-1.36423 3.06607,-3.04711 -1e-5,-1.68286 -1.37273,-3.04709 -3.06607,-3.04708 h -2.56476 c -1.49999,-10.3067 -10.44318,-18.28259 -21.216847,-18.28259 z m 0,6.09419 c 8.503037,0 15.330367,6.78508 15.330367,15.23548 0,8.45041 -6.82733,15.2355 -15.330367,15.2355 -8.503052,0 -15.330212,-6.78509 -15.330212,-15.2355 0,-8.45041 6.82716,-15.23548 15.330212,-15.23548 z m 23.781607,21.32968 c -1.69335,-2e-5 -3.06609,1.36422 -3.06609,3.0471 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,1e-5 3.06608,-1.36423 3.06607,-3.04711 0,-1.68287 -1.37273,-3.04711 -3.06607,-3.0471 z m 0,9.1413 c -1.69334,-2e-5 -3.06607,1.3642 -3.06609,3.04706 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,10e-6 3.06608,-1.36423 3.06607,-3.04711 -2e-5,-1.68286 -1.37274,-3.04707 -3.06607,-3.04706 z m 0,9.14127 c -1.69335,-2e-5 -3.06609,1.36421 -3.06609,3.04709 0,1.68288 1.37274,3.04712 3.06609,3.0471 1.69334,1e-5 3.06607,-1.36423 3.06607,-3.0471 0,-1.68287 -1.37273,-3.0471 -3.06607,-3.04709 z m 0,9.14129 c -1.69334,-2e-5 -3.06608,1.36421 -3.06609,3.04708 -1e-5,1.68288 1.37274,3.04713 3.06609,3.04711 1.69335,10e-6 3.06608,-1.36423 3.06607,-3.04711 -1e-5,-1.68286 -1.37273,-3.04709 -3.06607,-3.04708 z"
font-weight="400"
overflow="visible"
id="path62"
sodipodi:nodetypes="sssccsccsssssscccccccccccscsccscsc" />
<path
transform="matrix(0.26458333,0,0,0.26458333,0,161.53331)"
sodipodi:nodetypes="sssssscssscs"
inkscape:connector-curvature="0"
id="path130"
d="m 346.52914,385.88304 c -9.74153,-1.15668 -19.18712,-4.7389 -27.36682,-10.3788 -4.29704,-2.96281 -11.19439,-9.96762 -14.26053,-14.48272 -3.09123,-4.55204 -6.34453,-11.66167 -7.89403,-17.2513 -1.63959,-5.91462 -2.25943,-17.8322 -1.2539,-24.10849 3.61484,-22.56303 20.01002,-40.31938 42.90934,-46.47177 4.28814,-1.1521 5.83599,-1.30305 13.67432,-1.33348 9.26661,-0.036 11.69398,0.2988 18.66447,2.5742 8.55206,2.79168 15.83504,7.31293 22.56577,14.00876 20.25076,20.14579 22.48959,52.06917 5.25566,74.94032 -8.89686,11.80702 -22.47121,19.85596 -37.24125,22.08226 -4.528,0.6825 -11.2641,0.87091 -15.05303,0.42102 z"
style="opacity:0.49673205;fill:url(#linearGradient152);fill-opacity:1;stroke:none;stroke-width:0.50507629" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1,5 @@
module.exports = {
configureWebpack: {
devtool: 'source-map'
}
}

File diff suppressed because it is too large Load Diff