remove flecs-dash

master
Dominik Madarász 2023-06-30 15:04:20 +02:00
parent 00f0a672f8
commit 200d0a1e6d
53 changed files with 0 additions and 3775 deletions

View File

@ -1,97 +0,0 @@
Vue.component('app-browser', {
data: function() {
return {
scope: "",
entity: "",
entity_validated: "",
entity_components: {},
entity_request: undefined,
error: false
}
},
methods: {
e_select_entity(event) {
this.entity = event.entity;
this.$refs.editor.select_entity(event.entity);
},
e_select_scope(event) {
this.scope = event.scope;
this.$refs.data.select_scope(event.scope);
},
e_select_component(event) {
this.$refs.component_search.activate(event);
}
},
template: `
<div class="browser">
<link rel="stylesheet" href="apps/browser/style.css">
<div clas="entity-tree-container">
<entity-tree
v-on:select-entity="e_select_entity"
v-on:select-scope="e_select_scope"
:show_nav="true"
:auto_update="true">
</entity-tree>
</div>
<div class="entity-data-container">
<entity-data
:scope="scope"
ref="data">
</entity-data>
</div>
<entity-editor
:entity="entity"
:components="entity_components"
v-on:select-component="e_select_component"
ref="editor">
</entity-editor>
<component-search ref="component_search">
</component-search>
</div>
`
});
// Signal app has loaded & pass on dependencies
app.app_loaded("browser", [{
name: "entity-tree",
url: "apps/browser/entity_tree.js"
}, {
name: "entity-tree-item",
url: "apps/browser/entity_tree_item.js"
}, {
name: "entity-search",
url: "apps/browser/entity_search.js"
}, {
name: "entity-kind",
url: "apps/browser/entity_kind.js"
}, {
name: "entity-editor",
url: "apps/browser/entity_editor.js"
}, {
name: "entity-component",
url: "apps/browser/entity_component.js"
}, {
name: "component-properties",
url: "apps/browser/component_properties.js"
}, {
name: "property-value",
url: "apps/browser/property_value.js"
}, {
name: "entity-tag",
url: "apps/browser/entity_tag.js"
}, {
name: "component-search",
url: "apps/browser/component_search.js"
}, {
name: "entity-data",
url: "apps/browser/entity_data.js"
}, {
name: "entity-table",
url: "apps/browser/entity_table.js"
}]
);

View File

@ -1,13 +0,0 @@
Vue.component('component-properties', {
props: [ "name", "value" ],
methods: {
},
template: `
<div>
<div class="component-key">{{ name }}</div>
<property-value :value="value"></property-value>
</div>
`
});

View File

@ -1,48 +0,0 @@
Vue.component('component-search', {
data: function() {
return {
entity: "",
visible: false,
callback: undefined
}
},
methods: {
activate(event) {
this.entity = "";
this.$refs.tree.reset();
this.visible = true;
this.callback = event.callback;
},
deactivate() {
this.visible = false;
this.callback = undefined;
},
e_set_entity(event) {
this.callback(event.entity);
this.deactivate();
}
},
computed: {
visibility: function() {
if (this.visible) {
return "visible";
} else {
return "hidden";
}
}
},
template: `
<div class="component-search-darken" :style="'visibility: ' + visibility" v-on:click="deactivate">
<div class="component-search" v-on:click.stop="">
<entity-tree
v-on:select-entity="e_set_entity"
:filter="['Component', 'Module']"
:auto_update="false"
search_text="Find a component"
ref="tree">
</entity-tree>
</div>
</div>
`
});

View File

@ -1,20 +0,0 @@
Vue.component('entity-component', {
props: [ "name", "value" ],
methods: {
remove_component() {
this.$emit('remove_component', {component: this.name});
}
},
template: `
<div class="entity-component">
<div class="entity-component-name">{{ name }}</div>
<img src="images/delete.png" class="entity-remove-icon" v-on:click="remove_component">
<component-properties v-for="(v, name) in value" class="component" :key="name"
:name="name"
:value="v">
</component-properties>
</div>
`
});

View File

@ -1,64 +0,0 @@
Vue.component('entity-data', {
props: [ "filter" ],
mounted: function() {
this.startRequesting("");
},
beforeDestroy: function() {
this.stopRequesting();
},
data: function() {
return {
scope: "",
scope_pending: "",
data: [],
data_request: undefined,
error: false
}
},
methods: {
request_data(scope) {
var url = "scope/" + scope + "?include=Name";
if (this.filter) {
url += "," + this.filter.join(",");
}
app.get(url, (msg) => {
this.data = msg;
this.error = false;
this.scope = this.scope_pending;
}, (Http) => {
if (Http.status == 404) {
this.error = true;
}
});
},
// Stop periodically requesting the scope
stopRequesting() {
this.data = [];
clearInterval(this.data_request);
},
// Start periodically requesting a scope
startRequesting(scope) {
this.stopRequesting();
// Initial request
var scope_url = scope.replace(/\./g, "/");
this.request_data(scope_url);
// Start periodic request
this.data_request = window.setInterval(function() {
this.request_data(scope_url);
}.bind(this), 1000);
},
select_scope(scope) {
this.scope_pending = scope;
this.startRequesting(scope);
}
},
template: `
<div>
<entity-table v-for="table in data" :data="table" :key="table.type.join(',')"></entity-table>
</div>
`
});

View File

@ -1,128 +0,0 @@
Vue.component('entity-editor', {
data: function() {
return {
entity_pending: "",
entity: "",
components: {},
}
},
methods: {
entity_url(entity) {
return entity.replace(/\./g, "/");
},
request_entity(entity) {
if (!entity || !entity.length) {
this.entity_pending = "";
this.entity = "";
this.components = {};
return;
}
this.entity_pending = entity;
var entity_url = this.entity_url(entity);
app.get("entity/" + entity_url, (msg) => {
this.entity = this.entity_pending;
this.components = msg;
this.error = false;
}, (Http) => {
if (Http.status == 404) {
// Entity can no longer be found on URL
this.entity_pending = "";
this.entity = "";
this.components = {};
}
});
},
select_entity(entity) {
if (entity != this.entity) {
this.entity_pending = entity;
this.request_entity(entity);
}
},
entity_hidden() {
if (this.entity) {
return 'visible;';
} else {
return 'hidden;';
}
},
components_hidden() {
if (this.components) {
return 'visible;';
} else {
return 'hidden;';
}
},
e_add(event) {
this.$emit('select-component', {
callback: function(component) {
app.put("entity/" + this.entity_url(this.entity) + "?select=" + component, undefined, (msg) => {
this.request_entity(this.entity);
});
}.bind(this)
});
},
e_sync(event) {
this.request_entity(this.entity);
},
e_remove_component(event) {
app.delete("entity/" + this.entity_url(this.entity) + "?select=" + event.component, undefined, (msg) => {
this.request_entity(this.entity);
});
},
e_delete(event) {
app.delete("entity/" + this.entity_url(this.entity), undefined, (msg) => {
this.request_entity(undefined);
});
}
},
computed: {
tags: function() {
var type = this.components.type;
if (!type) {
return [];
}
var data = this.components.data;
var tags = [];
for (var i = 0; i < type.length; i ++) {
var tag = type[i];
if (!data.hasOwnProperty(tag)) {
if (Array.isArray(tag)) {
tags.push(type[i].join(" | "));
} else {
tags.push(tag);
}
}
}
return tags;
}
},
template: `
<div>
<div class="entity-editor" :style="'visibility: ' + entity_hidden()">
<div class="entity-name">
{{ components.path }}
<img src="images/sync.png" class="entity-icon" v-on:click="e_sync">
<img src="images/add.png" class="entity-icon" v-on:click="e_add">
<img src="images/delete.png" class="entity-remove-icon" v-on:click="e_delete">
</div>
<div v-for="(value, key) in components.data" :style="'visibility: ' + components_hidden()">
<entity-component :name="key" :value="value" v-on:remove_component="e_remove_component">
</entity-component>
</div>
<div class="entity-component-name" v-if="tags.length">Tags</div>
<div class="entity-tags">
<div v-for="tag in tags" :style="'visibility: ' + components_hidden()">
<entity-tag :name="tag" v-on:remove_component="e_remove_component"></entity-tag>
</div>
</div>
</div>
</div>
`
});

View File

@ -1,36 +0,0 @@
Vue.component('entity-kind', {
props: [ "kind" ],
methods: {
css() {
var result = "entity-kind";
if (this.kind && this.kind.length) {
result += " entity-kind-" + this.kind;
}
return result;
},
},
computed: {
kind_string: function() {
if (this.kind == "Module") {
return "m";
} else if (this.kind == "Component") {
return "c";
} else if (this.kind == "System") {
return "s";
} else if (this.kind == "Type") {
return "t";
} else if (this.kind == "Prefab") {
return "p";
} else {
return "e";
}
}
},
template: `
<span :class="css()">
{{ kind_string }}
</span>
`
});

View File

@ -1,45 +0,0 @@
Vue.component('entity-search', {
props: [ "error", "search_text", "found" ],
data: function() {
return {
search_scope: ""
}
},
methods: {
css() {
var result = "entity-search";
if (this.valid) {
result += " entity-search-found";
}
return result;
},
add_entity: function(e) {
this.$emit('add-entity', {entity: this.search_scope});
},
changed: function(e) {
this.$emit('changed', {entity: e.target.value});
},
set_value(scope) {
this.search_scope = scope;
}
},
computed: {
default_text: function() {
if (this.search_text) {
return this.search_text;
} else {
return "Find an entity";
}
},
valid: function() {
return this.found && this.search_scope.length;
}
},
template: `
<div :class="css()">
<input v-on:keyup="changed" v-model="search_scope" :placeholder="default_text"></input>
<img src="images/add.png" class="entity-remove-icon" v-if="!found && search_scope.length" v-on:click="add_entity">
</div>
`
});

View File

@ -1,85 +0,0 @@
Vue.component('entity-table', {
props: [ "data" ],
methods: {
component_headers: function(name) {
const value = this.data.data[name][0];
let headers = [];
for (key in value) {
headers.push(key);
}
return headers;
},
component_member: function(value) {
if (Array.isArray(value)) {
return "[...]";
} else if (typeof value === 'object') {
return "{...}";
} else if (typeof value === 'number') {
return value.toFixed(1);
} else {
return value;
}
},
type: function(value) {
if (Array.isArray(value)) {
return 'array';
} else if (typeof value === 'object') {
return 'object';
} else if (typeof value === 'number') {
return 'number';
} else if (typeof value === 'boolean') {
return 'boolean';
} else if (typeof value === 'string') {
return 'string';
} else {
return '';
}
},
value_css: function(value) {
return "property-value-" + this.type(value);
}
},
template: `
<div class="entity-table">
<div class="entity-table-body">
<table>
<thead>
<tr>
<th class="table-component">Id</th>
<th class="table-separator"></th>
<th class="table-component">Name</th>
<template v-for="(component,key) in data.data" v-if="key != 'Name'">
<th class="table-separator"></th>
<th class="table-component" :colspan="component_headers(key).length"> {{ key }} </th>
</template>
</tr>
<tr>
<th class="table-member"></th>
<th class="table-separator"></th>
<th class="table-member"></th>
<template v-for="(component, name) in data.data" v-if="name != 'Name'">
<th class="table-separator"></th>
<th class="table-member" v-for="member in component_headers(name)">{{ member }}</th>
</template>
</tr>
</thead>
<tbody>
<tr v-for="(entity, i) in data.entities">
<td class="table-value">{{ entity }}</td>
<td class="table-separator"></td>
<td class="table-value">{{ data.data.Name[i].value }}</td>
<template v-for="(component,key) in data.data" v-if="key != 'Name'">
<td class="table-separator"></td>
<td class="table-value" v-for="member in component_headers(key)" :class="value_css(component[i][member])">
{{ component_member(component[i][member]) }}
</td>
</template>
</tr>
</tbody>
</table>
</div>
</div>
`
});

View File

@ -1,16 +0,0 @@
Vue.component('entity-tag', {
props: [ "name" ],
methods: {
remove_component() {
this.$emit('remove_component', {component: this.name});
}
},
template: `
<span class="entity-tag">
{{ name }}
<img src="images/close.png" class="entity-remove-icon" v-on:click="remove_component">
</span>
`
});

View File

@ -1,207 +0,0 @@
// Component that implements a traversable entity tree
Vue.component('entity-tree', {
props: {
filter: Array,
search_text: String,
auto_update: Boolean
},
mounted: function() {
this.startRequesting("");
},
beforeDestroy: function() {
this.stopRequesting();
},
data: function() {
return {
scope_pending: "",
scope: "",
scope_info: {},
scope_request: undefined,
entities: [],
entity: "",
error: false
}
},
methods: {
// Send an HTTP request for the current scope
request_scope(scope) {
var url = "browse/" + scope + "?include=Name";
if (this.filter) {
url += "," + this.filter.join(",");
}
app.get(url, (msg) => {
if (this.scope != this.scope_pending) {
this.$emit("select-scope", {scope: this.scope_pending});
}
this.scope = this.scope_pending;
this.entities = msg;
this.error = false;
}, (Http) => {
if (Http.status == 404) {
this.error = true;
}
});
},
// Send an HTTP request for the current scope
request_scope_info(scope) {
var filter = ""
if (this.filter) {
filter = "?include=" + this.filter.join(",");
}
app.get("info/" + scope + filter, (msg) => {
this.scope_info = msg;
}, (Http) => {
if (Http.status == 404) {
this.error = true;
}
});
},
// Send an HTTP request for the current scope
request_add_entity(entity) {
app.post("entity/" + entity, (msg) => {
}, (Http) => {
if (Http.status == 404) {
this.error = true;
}
});
},
// Stop periodically requesting the scope
stopRequesting() {
this.scope = undefined;
this.scope_validated = undefined;
clearInterval(this.scope_request);
},
// Start periodically requesting a scope
startRequesting(scope) {
this.stopRequesting();
// Initial request
var scope_url = scope.replace(/\./g, "/");
this.request_scope(scope_url);
this.request_scope_info(scope_url);
// Start periodic request
if (this.auto_update) {
this.scope_request = window.setInterval(function() {
this.request_scope(scope_url);
}.bind(this), 1000);
}
},
// Reset to initial state
reset() {
this.entity = "";
this.entities = [];
this.scope = "";
this.scope_pending = "";
this.$refs.search.set_value("");
if (!this.auto_update) {
this.startRequesting(this.scope);
}
},
// Change current scope to fully qualified path
set_scope(scope) {
this.scope_pending = scope;
this.$refs.search.set_value(scope);
this.startRequesting(scope);
if (this.entity != "") {
this.select_entity("");
}
},
// Event that contains an full path
e_set_scope: function(event) {
this.set_scope(event.entity);
},
// Event that contains a relative entity id
e_set_scope_relative: function(event) {
var scope = "";
if (this.scope && this.scope != "") {
scope = this.scope + "." + event.entity;
} else {
scope = event.entity;
}
this.set_scope(scope);
},
// Select an entity from the tree
select_entity(entity) {
if (entity == this.entity) {
this.entity = "";
} else {
this.entity = entity;
}
this.$emit("select-entity", {entity: this.entity});
},
// Select the current scope
e_select_scope: function() {
this.select_entity(this.scope);
},
e_add_entity: function(e) {
this.request_add_entity(e.entity);
this.scope = this.scope_pending;
var parent = this.parent;
this.scope_pending = parent;
this.startRequesting(parent);
this.$refs.search.set_value(parent);
},
// Select a relative entity identifier
e_select_entity_relative: function(event) {
var entity;
if (this.scope) {
entity = this.scope + "." + event.entity;
} else {
entity = event.entity;
}
this.select_entity(entity);
},
entity_tree_css() {
var result = "entity-tree-list";
if (this.show_nav) {
result += " entity-tree-list-container";
}
return result;
}
},
computed: {
parent: function() {
if (this.scope && this.scope.indexOf(".")) {
return this.scope.substr(0, this.scope.lastIndexOf("."));
} else {
return "";
}
}
},
template: `
<div class="entity-tree">
<entity-search
:found="scope === scope_pending"
:search_text="search_text"
v-on:changed="e_set_scope"
v-on:add-entity="e_add_entity"
ref="search">
</entity-search>
<div :class="entity_tree_css()">
<entity-tree-item :entity="scope_info" :text="'.'" :current="entity" v-if="scope && scope.length"
v-on:select-entity="e_select_scope">
</entity-tree-item>
<div class="entity-tree-separator" v-if="scope && scope.length">
</div>
<entity-tree-item :entity="{'name':parent, 'type':[]}" :text="'..'" nav_only="true" v-if="scope && scope.length"
v-on:nav-scope="e_set_scope">
</entity-tree-item>
<entity-tree-item v-for="(e, i) in entities" :entity="e" :current="entity" :key="e.name"
v-on:nav-scope="e_set_scope_relative"
v-on:select-entity="e_select_entity_relative">
</entity-tree-item>
</div>
</div>
`
});

View File

@ -1,121 +0,0 @@
Vue.component('entity-tree-item', {
props: [ "entity", "current", "text", "nav_only", "" ],
data: function() {
return {
clicks: 0,
timer: null
}
},
methods: {
nav_scope: function() {
this.$emit("nav-scope", {entity: this.entity.name});
},
single_click_nav_scope: function() {
if (this.nav_only) {
this.nav_scope();
} else {
this.$emit("select-entity", {entity: this.entity.name});
}
},
is_disabled() {
for (var i = 0; i < this.entity.type.length; i ++) {
var el = this.entity.type[i];
if (el == "Disabled") {
return true;
}
}
return false;
},
css() {
var result = "entity-tree-item";
if (this.is_disabled()) {
result += " entity-tree-item-disabled";
}
if (!this.current) {
return result;
}
var entity = this.entity.name;
var current;
if (this.current.indexOf(".") != -1) {
var pos = this.current.lastIndexOf(".") + 1;
current = this.current.substr(pos, this.current.length - pos);
} else {
current = this.current;
}
if (entity === current) {
result += " entity-tree-item-active";
}
return result;
},
kind() {
var component = "";
for (var i = 0; i < this.entity.type.length; i ++) {
var el = this.entity.type[i];
if (el == "Module") {
component = el;
break;
} else if (el == "Component") {
component = el;
// Don't break, entity can also be module
} else if (el == "Prefab") {
component = el;
break;
} else if (el == "flecs.system.System") {
component = "System";
break;
} else if (el == "Type") {
component = el;
break;
}
}
return component;
}
},
computed: {
entity_id: function() {
var text = this.text ? this.text : "";
var entity = "";
if (this.entity) {
entity = this.entity.name ? this.entity.name : "";
}
// A bit of a convoluted way to makes sure that we show text instead of an
// entity name when provided, except when the text is "." in which case we
// show the entity name when there is one.
if (text.length && (text != "." || !entity.length)) {
return text;
} else {
return entity;
}
},
show_nav() {
return !this.nav_only && this.text != ".";
}
},
template: `
<div :class="css()" v-on:click.stop="single_click_nav_scope">
<div>
<img src="images/nav.png" class="entity-remove-icon" v-on:click.stop="nav_scope" v-if="show_nav && entity.child_count">
<entity-kind :kind="kind()" v-if="entity_id.length"></entity-kind>
<span class="entity-item-name">{{entity_id}}</span>
</div>
</div>
`
});

View File

@ -1,43 +0,0 @@
Vue.component('property-value', {
props: [ "value" ],
methods: {
css() {
var result = "property-value";
if (this.type.length) {
result += " property-value-" + this.type;
}
return result;
}
},
computed: {
type: function() {
if (Array.isArray(this.value)) {
return 'array';
} else if (typeof this.value === 'object') {
return 'object';
} else if (typeof this.value === 'number') {
return 'number';
} else if (typeof this.value === 'boolean') {
return 'boolean';
} else if (typeof this.value === 'string') {
return 'string';
} else {
return '';
}
}
},
template: `
<div :class="css()">
<div v-if="type === 'object'">
</div>
<div v-else-if="type === 'array'">
</div>
<div v-else>
<input :value="value" spellcheck="false"></input>
</div>
</div>
`
});

View File

@ -1,420 +0,0 @@
div.browser {
width: 100%;
height: 100%;
color: #eee;
}
div.entity-tree-list-container {
position: absolute;
top: 60px;
height: calc(100% - 200px);
}
div.entity-data-container {
position: absolute;
top: 0px;
left: 270px;
max-width: calc(100% - 275px);
height: calc(100% - 10px);
overflow-y: auto;
}
div.entity-table {
margin: 10px;
}
div.entity-table-header {
background-color: #1E2022;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-style: solid;
border-color: #1E2022;
padding: 5px;
}
div.entity-table-body {
padding-left: 20px;
padding-right: 20px;
padding-bottom: 20px;
overflow-x: auto;
}
div.entity-table-body table {
color: #eee;
border-collapse: collapse;
background-color: #303136;
border-radius: 5px;
box-shadow: 3px 5px 10px rgba(0, 0, 0, 0.5);
}
th.table-component {
background-color: #1E2022;
padding-top: 10px;
padding-left: 16px;
padding-right: 16px;
text-transform: uppercase;
font-size: 14px;
font-weight: bold;
}
th.table-component:first-child {
border-top-left-radius: 5px;
}
th.table-component:last-child {
border-top-right-radius: 5px;
}
.table-separator {
background-color: #37393E !important;
padding: 0.5px !important;
}
td.table-separator {
border-top-color: #37393E;
border-top-style: solid;
border-top-width: 1px;
}
th.table-member {
background-color: #1E2022 !important;
color: #aaa !important;
text-align: center;
padding-left: 8px;
padding-right: 8px;
padding-bottom: 5px;
font-weight: normal;
}
td.table-value {
font-weight: 300;
padding: 8px;
padding-left: 10px;
padding-right: 10px;
text-align: center;
white-space: nowrap;
border-top-color: #37393E;
border-top-style: solid;
border-top-width: 1px;
}
div.entity-tree {
position: relative;
top: 0px;
width: 250px;
min-width: 200px;
height: calc(100% - 20px);
padding: 10px;
background-color: #303136;
margin: 0px;
box-shadow: 5px 0px 8px rgba(0, 0, 0, 0.4);
}
div.entity-tree-list {
position: absolute;
top: 60px;
width: 250px;
min-width: 250px;
height: calc(100% - 60px);
overflow-y: auto;
margin: 0px;
}
div.entity-tree-item {
position: relative;
font-size: 16px;
padding-left: 10px;
padding-right: 20px;
padding-top: 10px;
padding-bottom: 10px;
min-height: 20px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
div.entity-tree-separator {
border-top-style: solid;
border-top-color: #37393E;
border-top-width: 1px;
margin-top: 10px;
margin-bottom: 10px;
}
div.entity-tree-item:hover {
background-color: #37393E;
}
div.entity-tree-item-disabled {
color: #aaa;
}
div.entity-tree-item-active {
background-color: #37393E;
color: #eee;
}
span.entity-item-name {
padding-left: 30px;
}
span.entity-kind {
position: absolute;
border-style: solid;
border-width: 1px;
border-color: #eee;
border-radius: 4px;
top: 11px;
left: 15px;
padding: 2px;
min-width: 10px;
min-height: 10px;
font-size: 10px;
text-align: center;
text-transform: uppercase;
}
span.entity-kind-Module {
color: #C7CA6C;
border-color: #C7CA6C;
}
span.entity-kind-Component {
color: #47B576;
border-color: #47B576;
}
span.entity-kind-System {
color: #4981B5;
border-color: #4981B5;
}
span.entity-kind-Type {
color: #4981B5;
border-color: #4981B5;
}
span.entity-kind-Prefab {
color: #B5488F;
border-color: #B5488F;
}
div.entity-search {
position: absolute;
}
div.entity-search input {
background-color: #1E2022;
border-radius: 5px;
border-style: solid;
border-width: 1px;
border-color: #1E2022;
margin: 2px;
width: 246px;
font-size: 16px;
padding: 8px;
color: #eee;
transition: border-color 200ms;
}
div.entity-search-found input {
border-color: #47B576;
}
p.list-header {
position: absolute;
text-transform: uppercase;
font-size: 14px;
font-weight: bold;
padding: 10px;
}
#scope-parent {
position: absolute;
top: 55px;
width: 220px;
}
#scope-entity {
position: absolute;
top: 95px;
width: 220px;
}
#header-children {
top: 130px;
}
div.entity-editor {
background-color: #303136;
position: absolute;
top: 0px;
right: 0px;
width: 350px;
min-width: 350px;
height: calc(100%);
margin: 0px;
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.5);
}
div.entity-name {
background-color: #1E2022;
font-size: 14px;
font-weight: bold;
padding: 10px;
padding-top: 5px;
}
div.entity-component {
position: relative;
}
div.entity-component-buttons {
position: relative;
padding: 10px;
}
button {
background-color: #eee;
color: #1E2022;
padding: 6px;
margin-top: 5px;
margin-left: 5px;
border: none;
font-size: 14px;
cursor: pointer;
outline: none;
}
div.entity-component-name {
font-weight: bold;
padding: 10px;
margin-top: 20px;
border-top-style: solid;
border-top-width: 1px;
border-top-color: #37393E;
text-transform: uppercase;
font-size: 14px;
}
div.entity-component-value {
padding: 5px;
margin-top: 5px;
margin-bottom: 10px;
}
span.entity-tag {
position: relative;
background-color: #1E2022;
padding: 8px;
padding-right: 30px;
margin: 2px;
float: left;
cursor: pointer;
}
div.entity-tags {
margin-left: 20px;
}
img.entity-icon {
position: relative;
top: 3px;
left: 5px;
width: 18px;
height: 18px;
cursor: pointer;
}
img.entity-remove-icon {
position: absolute;
top: 8px;
right: 8px;
width: 18px;
height: 18px;
cursor: pointer;
}
div.component {
position: relative;
padding: 10px;
padding-left: 20px;
margin-top: 5px;
height: 5px;
}
div.component-key {
position: absolute;
top: 0px;
font-size: 14px;
color: #aaa;
}
div.property-value {
position: absolute;
top: -5px;
left: 100px;
width: calc(100% - 110px);
}
div.property-value input {
background-color: #1E2022;
border-radius: 2px;
border-style: solid;
border-width: 1px;
border-color: #1E2022;
font-size: 14px;
margin-bottom: 5px;
width: 100%;
}
div.property-value-string input {
color: #B56D48;
}
div.property-value-number input {
color: #67B588;
}
div.property-value-boolean input {
color: #4981B5;
}
.property-value-string {
color: #B56D48;
}
.property-value-number {
color: #67B588;
}
.property-value-boolean {
color: #4981B5;
}
div.component-search-darken {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
div.component-search {
background-color: #303136;
position: absolute;
padding-bottom: 10px;
top: 35px;
right: 40px;
height: 400px;
max-width: 270px;
box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.5);
}

View File

@ -1,122 +0,0 @@
const delta_time_chart = {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'avg',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#5BE595',
],
borderWidth: 2,
pointRadius: 0
},
{
label: 'min',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#40805B',
],
borderWidth: 1,
pointRadius: 0
},
{
label: 'max',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#40805B',
],
borderWidth: 1,
pointRadius: 0
}
]
},
options: {
title: {
display: false
},
responsive: true,
maintainAspectRatio: false,
lineTension: 1,
animation: {
duration: 0
},
scales: {
yAxes: [{
id: 'time',
position: 'right',
ticks: {
beginAtZero: false,
padding: 10,
callback: function(value, index, values) {
return (1000 * value).toFixed(2) + "ms";
}
}
}],
xAxes: [{
ticks: {
minRotation: 0,
maxRotation: 0,
maxTicksLimit: 20
}
}]
}
}
}
Vue.component('delta-time-graph', {
props: ['tick', 'data'],
updated() {
this.updateChart();
},
data: function() {
return {
chart: undefined
}
},
methods: {
setValues() {
if (!this.data.world) {
return;
}
if (!this.chart) {
this.createChart();
}
var labels = [];
var length = this.data.world.history_1m.frame_time_total.avg.length;
for (var i = 0; i < length; i ++) {
labels.push((length - i) + "s");
}
delta_time_chart.data.labels = labels;
delta_time_chart.data.datasets[0].data = this.data.world.history_1m.delta_time.avg;
delta_time_chart.data.datasets[1].data = this.data.world.history_1m.delta_time.min;
delta_time_chart.data.datasets[2].data = this.data.world.history_1m.delta_time.max;
},
createChart() {
const ctx = document.getElementById('delta-time-graph');
this.chart = new Chart(ctx, {
type: delta_time_chart.type,
data: delta_time_chart.data,
options: delta_time_chart.options
});
},
updateChart() {
if (!this.data.world) {
return;
}
this.setValues();
this.chart.update();
}
},
template: `
<div class="app-graph">
<canvas id="delta-time-graph" :data-fps="tick"></canvas>
</div>`
});

View File

@ -1,116 +0,0 @@
const entity_chart = {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'Total',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#5BE595',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Matched',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#46D9E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
}
]
},
options: {
title: {
display: false
},
responsive: true,
maintainAspectRatio: false,
lineTension: 1,
animation: {
duration: 0
},
legend: {
display: true
},
scales: {
yAxes: [{
id: 'count',
ticks: {
beginAtZero: true,
padding: 10,
callback: function(value, index, values) {
return value;
}
}
}],
xAxes: [{
ticks: {
minRotation: 0,
maxRotation: 0,
maxTicksLimit: 20
}
}]
}
}
}
Vue.component('entity-graph', {
props: ['tick', 'data'],
updated() {
this.updateChart();
},
data: function() {
return {
chart: undefined
}
},
methods: {
setValues() {
if (!this.data.world) {
return;
}
if (!this.chart) {
this.createChart();
}
var labels = [];
var length = this.data.world.history_1m.frame_time_total.avg.length;
for (var i = 0; i < length; i ++) {
labels.push((length - i) + "s");
}
entity_chart.data.labels = labels;
entity_chart.data.datasets[0].data = this.data.world.history_1m.entity_count.avg;
entity_chart.data.datasets[1].data = this.data.world.history_1m.matched_entity_count.avg;
},
createChart() {
const ctx = document.getElementById('entity-graph');
this.chart = new Chart(ctx, {
type: entity_chart.type,
data: entity_chart.data,
options: entity_chart.options
});
},
updateChart() {
if (!this.data.world) {
return;
}
this.setValues();
this.chart.update();
}
},
template: `
<div class="app-graph">
<canvas id="entity-graph" :data-fps="tick"></canvas>
</div>`
});

View File

@ -1,149 +0,0 @@
const operation_chart = {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'New',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#5BE595',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Delete',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#46D9E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Set',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#2D5BE6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Add',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#6146E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Remove',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#E550E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
}
]
},
options: {
title: {
display: false
},
responsive: true,
maintainAspectRatio: false,
lineTension: 1,
animation: {
duration: 0
},
scales: {
yAxes: [{
id: 'count',
ticks: {
beginAtZero: true,
padding: 10,
callback: function(value, index, values) {
return value + "/s";
}
}
}],
xAxes: [{
ticks: {
minRotation: 0,
maxRotation: 0,
maxTicksLimit: 20
}
}]
}
}
}
Vue.component('operation-graph', {
props: ['tick', 'data'],
updated() {
this.updateChart();
},
data: function() {
return {
chart: undefined
}
},
methods: {
setValues() {
if (!this.data.world) {
return;
}
if (!this.chart) {
this.createChart();
}
var labels = [];
var length = this.data.world.history_1m.frame_time_total.avg.length;
for (var i = 0; i < length; i ++) {
labels.push((length - i) + "s");
}
operation_chart.data.labels = labels;
operation_chart.data.datasets[0].data = this.data.world.history_1m.new_count.avg;
operation_chart.data.datasets[1].data = this.data.world.history_1m.delete_count.avg;
operation_chart.data.datasets[2].data = this.data.world.history_1m.set_count.avg;
operation_chart.data.datasets[3].data = this.data.world.history_1m.add_count.avg;
operation_chart.data.datasets[4].data = this.data.world.history_1m.remove_count.avg;
},
createChart() {
const ctx = document.getElementById('operation-graph');
this.chart = new Chart(ctx, {
type: operation_chart.type,
data: operation_chart.data,
options: operation_chart.options
});
},
updateChart() {
if (!this.data.world) {
return;
}
this.setValues();
this.chart.update();
}
},
template: `
<div class="app-graph">
<canvas id="operation-graph" :data-fps="tick"></canvas>
</div>`
});

View File

@ -1,190 +0,0 @@
Vue.component('app-overview', {
mounted: function() {
this.startRequesting("");
},
beforeDestroy: function() {
this.stopRequesting();
},
data: function() {
return {
data: {},
data_request: undefined,
error: false,
tick: 0
}
},
methods: {
request_data() {
var url = "metrics?world=yes";
this.tick ++;
app.get(url, (msg) => {
this.data = msg;
this.error = false;
}, (Http) => {
if (Http.status == 404) {
this.error = true;
}
});
},
// Stop periodically requesting the scope
stopRequesting() {
this.data = [];
clearInterval(this.data_request);
},
// Start periodically requesting a scope
startRequesting(scope) {
this.stopRequesting();
// Initial request
this.request_data();
// Start periodic request
this.data_request = window.setInterval(function() {
this.request_data();
}.bind(this), 1000);
},
entity_count() {
if (!this.data.world) {
return 0;
}
return this.data.world.current.entity_count;
},
operation_count() {
if (!this.data.world) {
return 0;
}
return (this.data.world.current.add_count +
this.data.world.current.remove_count +
this.data.world.current.clear_count +
this.data.world.current.delete_count +
this.data.world.current.set_count +
this.data.world.current.bulk_new_count).toFixed(0);
},
system_count() {
if (!this.data.world) {
return 0;
}
return (this.data.world.current.systems_ran).toFixed(0);
},
component_count() {
if (!this.data.world) {
return 0;
}
return this.data.world.current.component_count;
},
table_count() {
if (!this.data.world) {
return 0;
}
return this.data.world.current.table_count;
},
fragmentation() {
if (!this.data.world) {
return 0;
}
/* Compute global fragmentation as total matched tables divided by total
* matched entities. Subtract one from the tables, so that if there is a
* single entity matching a single table, fragmentation is 0% */
return (100 * (this.data.world.current.matched_table_count - 1) /
this.data.world.current.matched_entity_count).toFixed(0);
}
},
template: `
<div class="overview">
<div class="metric-row">
<div class="metric metric-column">
<div class="metric-header">entities</div>
<div class="metric-content">
{{entity_count()}}
</div>
</div>
<div class="metric metric-column">
<div class="metric-header">components</div>
<div class="metric-content">
{{component_count()}}
</div>
</div>
<div class="metric metric-column">
<div class="metric-header">systems</div>
<div class="metric-content">
{{system_count()}}
</div>
</div>
<div class="metric metric-column">
<div class="metric-header">tables</div>
<div class="metric-content">
{{table_count()}}
</div>
</div>
<div class="metric metric-column">
<div class="metric-header">fragmentation</div>
<div class="metric-content">
{{fragmentation()}}%
</div>
</div>
</div>
<div class="metric">
<div class="metric-header">performance</div>
<div class="metric-content">
<performance-graph :data="data" :tick="tick"></performance-graph>
</div>
</div>
<div class="metric">
<div class="metric-header">delta time</div>
<div class="metric-content">
<delta-time-graph :data="data" :tick="tick"></delta-time-graph>
</div>
</div>
<div class="metric">
<div class="metric-header">operations ({{operation_count()}})</div>
<div class="metric-content">
<operation-graph :data="data" :tick="tick"></operation-graph>
</div>
</div>
<div class="metric">
<div class="metric-header">entities ({{entity_count()}})</div>
<div class="metric-content">
<entity-graph :data="data" :tick="tick"></entity-graph>
</div>
</div>
<div class="metric">
<div class="metric-header">tables ({{table_count()}})</div>
<div class="metric-content">
<table-graph :data="data" :tick="tick"></table-graph>
</div>
</div>
</div>
`
});
app.app_loaded("overview", [{
name: "entity-graph",
url: "apps/overview/entity_graph.js"
}, {
name: "performance-graph",
url: "apps/overview/performance_graph.js"
}, {
name: "operation-graph",
url: "apps/overview/operation_graph.js"
}, {
name: "table-graph",
url: "apps/overview/table_graph.js"
}, {
name: "delta-time-graph",
url: "apps/overview/delta_time_graph.js"
}]);

View File

@ -1,149 +0,0 @@
const performance_chart = {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'FPS',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#5BE595',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "fps"
},
{
label: 'Frame',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#6146E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "time"
},
{
label: 'Systems',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#46D9E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "time"
},
{
label: 'Merging',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#E550E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "time"
}
]
},
options: {
title: {
display: false
},
responsive: true,
maintainAspectRatio: false,
lineTension: 1,
animation: {
duration: 0
},
scales: {
yAxes: [{
id: 'fps',
ticks: {
beginAtZero: true,
padding: 10,
callback: function(value, index, values) {
return value + "Hz";
}
}
}, {
id: 'time',
position: 'right',
ticks: {
beginAtZero: true,
padding: 10,
callback: function(value, index, values) {
return (1000 * value).toFixed(2) + "ms";
}
}
}],
xAxes: [{
ticks: {
minRotation: 0,
maxRotation: 0,
maxTicksLimit: 20
}
}]
}
}
}
Vue.component('performance-graph', {
props: ['tick', 'data'],
updated() {
this.updateChart();
},
data: function() {
return {
chart: undefined
}
},
methods: {
setValues() {
if (!this.data.world) {
return;
}
if (!this.chart) {
this.createChart();
}
var labels = [];
var length = this.data.world.history_1m.frame_time_total.avg.length;
for (var i = 0; i < length; i ++) {
labels.push((length - i) + "s");
}
performance_chart.data.labels = labels;
performance_chart.data.datasets[0].data = this.data.world.history_1m.fps.avg;
performance_chart.data.datasets[1].data = this.data.world.history_1m.frame_time_total.avg;
performance_chart.data.datasets[2].data = this.data.world.history_1m.system_time_total.avg;
performance_chart.data.datasets[3].data = this.data.world.history_1m.merge_time_total.avg;
},
createChart() {
const ctx = document.getElementById('fps-graph');
performance_chart.options.scales.yAxes[1].ticks.suggestedMax = 1.0 / this.data.target_fps;
this.chart = new Chart(ctx, {
type: performance_chart.type,
data: performance_chart.data,
options: performance_chart.options
});
},
updateChart() {
if (!this.data.world) {
return;
}
this.setValues();
this.chart.update();
}
},
template: `
<div class="app-graph">
<canvas id="fps-graph" :data-fps="tick"></canvas>
</div>`
});

View File

@ -1,140 +0,0 @@
const table_chart = {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'Total',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#5BE595',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Matched',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#46D9E6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Singleton',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#4596E5',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
},
{
label: 'Empty',
data: [],
backgroundColor: [ 'rgba(0,0,0,0)' ],
borderColor: [
'#2D5BE6',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "count"
}
]
},
options: {
title: {
display: false
},
responsive: true,
maintainAspectRatio: false,
lineTension: 1,
animation: {
duration: 0
},
legend: {
display: true
},
scales: {
yAxes: [{
id: 'count',
ticks: {
beginAtZero: true,
padding: 10,
callback: function(value, index, values) {
return value;
}
}
}],
xAxes: [{
ticks: {
minRotation: 0,
maxRotation: 0,
maxTicksLimit: 20
}
}]
}
}
}
Vue.component('table-graph', {
props: ['tick', 'data'],
updated() {
this.updateChart();
},
data: function() {
return {
chart: undefined
}
},
methods: {
setValues() {
if (!this.data.world) {
return;
}
if (!this.chart) {
this.createChart();
}
var labels = [];
var length = this.data.world.history_1m.table_count.avg.length;
for (var i = 0; i < length; i ++) {
labels.push((length - i) + "s");
}
table_chart.data.labels = labels;
table_chart.data.datasets[0].data = this.data.world.history_1m.table_count.avg;
table_chart.data.datasets[1].data = this.data.world.history_1m.matched_table_count.avg;
table_chart.data.datasets[2].data = this.data.world.history_1m.singleton_table_count.avg;
table_chart.data.datasets[3].data = this.data.world.history_1m.empty_table_count.avg;
},
createChart() {
const ctx = document.getElementById('table-graph');
this.chart = new Chart(ctx, {
type: table_chart.type,
data: table_chart.data,
options: table_chart.options
});
},
updateChart() {
if (!this.data.world) {
return;
}
this.setValues();
this.chart.update();
}
},
template: `
<div class="app-graph">
<canvas id="table-graph" :data-fps="tick"></canvas>
</div>`
});

View File

@ -1,187 +0,0 @@
div.system-header {
padding-bottom: 10px;
}
div.systems {
position: relative;
}
div.system {
position: relative;
background-color: #1E2022;
color: #eee;
font-size: 16px;
padding: 10px;
padding-top: 10px;
padding-bottom: 5px;
margin-left: 10px;
margin-right: 10px;
margin-top: 5px;
height: 20px;
border-left-style: solid;
border-left-width: 5px;
border-left-color: #181a1b;
transition: border-left-color 0.3s;
}
div.system-query {
position: relative;
background-color: #26292b;
color: #eee;
font-size: 16px;
padding: 10px;
padding-top: 5px;
margin-left: 10px;
margin-right: 10px;
margin-top: 0px;
height: 20px;
border-left-style: solid;
border-left-width: 5px;
border-left-color: #181a1b;
transition: border-left-color 0.3s;
}
span.system-module {
color: #88949b;
font-size: 14px;
font-weight: normal;
padding-left: 5px;
}
span.system-metrics {
position: absolute;
top: 6px;
right: 380px;
background-color: #26292b;
color: #88949b;
font-size: 14px;
font-weight: normal;
border-radius: 2px;
margin-left: 5px;
padding: 3px;
padding-left: 5px;
padding-right: 5px;
}
div.merge {
position: relative;
background-color: #1E2022;
color: #eee;
font-size: 16px;
padding: 10px;
margin-left: 10px;
margin-right: 10px;
margin-top: 5px;
border-left-color: #181818;
border-left-style: solid;
border-left-width: 5px;
}
div.system-entity-graph {
position: absolute;
top: 7px;
right: 35px;
height: 28px;
width: 300px;
background-color: #26292b;
}
div.system-time-graph {
position: absolute;
top: 7px;
right: 35px;
height: 28px;
width: 300px
}
div.system-graph-label {
position: absolute;
top: -5px;
left: -65px;
text-align: right;
padding-top: 8px;
padding-bottom: 8px;
padding-right: 5px;
width: 60px;
font-size: 13px;
color: #888;
}
div.system-graph-label-filled {
background-color: #26292b;
}
div.system-graph-label-secondary {
position: absolute;
top: 0px;
left: 295px;
font-size: 13px;
}
span.system-component {
position: relative;
top: 5px;
background-color: #1E2022;
padding: 4px;
color: #4fa7ff;
margin-right: 5px;
font-size: 14px;
}
span.system-component-annotation {
position: relative;
left: -5px;
background-color: #181a1b;
padding: 4px;
padding-left: 3px;
padding-right: 0px;
color: #47B576;
margin-right: 0px;
font-size: 14px;
}
span.system-component-not {
color: #B5494B;
}
span.system-component-tag {
color: #9546E5;
}
div.pct-lowest {
color: #888;
}
div.pct-low {
color: #5BE595;
}
div.pct-medium {
color: #C6CA50;
}
div.pct-high {
color: #B56D48;
}
div.pct-highest {
color: #B5494B;
}
div.system-low {
border-left-color: #5BE595;
}
div.system-medium {
border-left-color: #C6CA50;
}
div.system-high {
border-left-color: #B56D48;
}
div.system-highest {
border-left-color: #B5494B;
}

View File

@ -1,48 +0,0 @@
Vue.component('system', {
props: ['tick', 'system', 'system_time'],
methods: {
entity_count() {
return this.system.current.matched_entity_count;
},
table_count() {
return this.system.current.matched_table_count;
},
fragmentation() {
return (((this.table_count() - 1) / this.entity_count()) * 100).toFixed(1) + "%";
},
percentage() {
return (this.system.current.time_spent / this.system_time) * 100;
},
percentage_class() {
let pct = this.percentage();
if (pct < 1.0) {
return "system-lowest";
} else if (pct < 5.0) {
return "system-low";
} else if (pct < 10.0) {
return "system-medium";
} else if (pct < 20.0) {
return "system-high";
} else {
return "system-highest";
}
}
},
template: `
<div>
<div :class="'system ' + percentage_class()">
<div class="system-header">
{{system.name}}
<span class="system-module" v-if="system.module">{{system.module}}</span>
<span class="system-metrics" v-if="entity_count()">T:{{table_count()}} - F:{{fragmentation()}}</span>
<system-time-graph :system="system" :system_time="system_time" :percentage="percentage()" :tick="tick"></system-time-graph>
</div>
</div>
<div :class="'system-query ' + percentage_class()">
<system-component v-for="(component, i) in system.signature" :component="component" :key="i"></system-component>
<system-entity-graph :system="system" :tick="tick"></system-entity-graph>
</div>
</div>`
});

View File

@ -1,31 +0,0 @@
Vue.component('system-component', {
props: ['component'],
methods: {
css() {
if (this.component.exclude) {
return "system-component-not";
}
return "";
}
},
template: `
<span :class="'system-component ' + css()">
<span class="system-component-annotation" v-if="component.const">
in
</span>
<span class="system-component-annotation" v-if="component.singleton">
$
</span>
<span class="system-component-annotation" v-else-if="component.parent">
parent
</span>
<span class="system-component-annotation" v-else-if="component.ref">
ref
</span>
<span class="system-component-annotation" v-else-if="component.empty">
0
</span>
{{component.name}}
</span>`
});

View File

@ -1,125 +0,0 @@
const system_entity_chart = {
type: 'line',
data: {
labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 12, 26, 27, 28, 29, 30,
31, 32, 33, 34, 13, 36, 37, 38, 39, 40,
41, 42, 43, 44, 14, 46, 47, 48, 49, 50,
51, 52, 53, 54, 15, 56, 57, 58, 59, 60],
datasets: [
{
label: 'Matched entities',
data: [],
backgroundColor: [ '#26352c' ],
borderColor: [ '#5BE595', ],
borderWidth: 2,
pointRadius: 0,
fill: true,
yAxisID: "entities"
}
]
},
options: {
title: {
display: false
},
responsive: true,
maintainAspectRatio: false,
lineTension: 1,
animation: {
duration: 0
},
hover: {
animationDuration: 0 // duration of animations when hovering an item
},
responsiveAnimationDuration: 0,
legend: {
display: false
},
elements: {
line: {
tension: 0 // disables bezier curves
}
},
scales: {
yAxes: [{
id: 'entities',
position: 'right',
gridLines: {
display:false
},
ticks: {
display: false,
beginAtZero: true,
maxTicksLimit: 2
}
}],
xAxes: [{
gridLines: {
display:false
},
ticks: {
display: false
}
}]
}
}
}
Vue.component('system-entity-graph', {
props: ['tick', 'system'],
updated() {
this.updateChart();
},
data: function() {
return {
chart: undefined
}
},
methods: {
setValues() {
if (!this.system.history_1m) {
return;
}
if (!this.chart) {
this.createChart();
}
system_entity_chart.data.datasets[0].data = this.system.history_1m.matched_entity_count.avg;
},
createChart() {
const ctx = document.getElementById('system-entity-' + this.system.name + '-graph');
this.chart = new Chart(ctx, {
type: system_entity_chart.type,
data: system_entity_chart.data,
options: system_entity_chart.options
});
},
updateChart() {
if (!this.system.history_1m) {
return;
}
this.setValues();
this.chart.update();
},
entity_count() {
if (!this.system || !this.system.current) {
return;
}
return this.system.current.matched_entity_count;
}
},
template: `
<div class="system-entity-graph">
<div class="system-graph-label system-graph-label-filled">
{{entity_count()}}
</div>
<canvas :id="'system-entity-' + system.name + '-graph'" :data-fps="tick"></canvas>
</div>`
});

View File

@ -1,170 +0,0 @@
const system_time_chart = {
type: 'line',
data: {
labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 12, 26, 27, 28, 29, 30,
31, 32, 33, 34, 13, 36, 37, 38, 39, 40,
41, 42, 43, 44, 14, 46, 47, 48, 49, 50,
51, 52, 53, 54, 15, 56, 57, 58, 59, 60],
datasets: [
{
label: 'Matched entities',
data: [],
backgroundColor: [ '#26537F' ],
borderColor: [
'#4596E5',
],
borderWidth: 2,
pointRadius: 0,
yAxisID: "entities"
}
]
},
options: {
title: {
display: false
},
responsive: true,
maintainAspectRatio: false,
lineTension: 1,
animation: {
duration: 0
},
hover: {
animationDuration: 0 // duration of animations when hovering an item
},
responsiveAnimationDuration: 0,
legend: {
display: false
},
elements: {
line: {
tension: 0 // disables bezier curves
}
},
scales: {
yAxes: [{
id: 'entities',
position: 'right',
gridLines: {
display:false
},
ticks: {
display: false,
beginAtZero: true,
maxTicksLimit: 2
}
}],
xAxes: [{
gridLines: {
display:false
},
ticks: {
display: false
}
}]
}
}
}
Vue.component('system-time-graph', {
props: ['tick', 'system', 'system_time', 'percentage'],
updated() {
this.updateChart();
},
data: function() {
return {
chart: undefined
}
},
methods: {
setValues() {
if (!this.system.history_1m) {
return;
}
if (!this.chart) {
this.createChart();
}
system_time_chart.data.datasets[0].data = this.system.history_1m.time_spent.avg;
},
createChart() {
const ctx = document.getElementById('system-time-' + this.system.name + '-graph');
this.chart = new Chart(ctx, {
type: system_time_chart.type,
data: system_time_chart.data,
options: system_time_chart.options
});
},
updateChart() {
if (!this.system.history_1m) {
return;
}
this.setValues();
this.chart.update();
},
time_spent() {
let time_spent = this.system.current.time_spent;
if (time_spent >= 1.0) {
return time_spent.toFixed(0) + "s";
}
time_spent *= 1000;
if (time_spent >= 1.0) {
return time_spent.toFixed(0) + "ms";
}
time_spent *= 1000;
if (time_spent >= 1.0) {
return time_spent.toFixed(0) + "us";
}
time_spent *= 1000;
if (time_spent >= 1.0) {
return time_spent.toFixed(0) + "ns";
} else if (time_spent >= 0.01) {
return time_spent.toFixed(2) + "ns";
} else {
return "0ns";
}
},
percentage_formatted() {
let pct = this.percentage;
if (pct > 10.0) {
return pct.toFixed(0) + "%"
} else {
return pct.toFixed(1) + "%"
}
},
percentage_class() {
let pct = this.percentage;
if (pct < 1.0) {
return "pct-lowest";
} else if (pct < 5.0) {
return "pct-low";
} else if (pct < 10.0) {
return "pct-medium";
} else if (pct < 20.0) {
return "pct-high";
} else {
return "pct-highest";
}
}
},
template: `
<div class="system-time-graph">
<div class="system-graph-label">
{{time_spent()}}
</div>
<div :class="'system-graph-label-secondary ' + percentage_class()">
{{percentage_formatted()}}
</div>
<canvas :id="'system-time-' + system.name + '-graph'" :data-fps="tick"></canvas>
</div>`
});

View File

@ -1,173 +0,0 @@
Vue.component('app-systems', {
mounted: function() {
this.startRequesting("");
},
beforeDestroy: function() {
this.stopRequesting();
},
data: function() {
return {
data: {},
data_request: undefined,
error: false,
tick: 0
}
},
methods: {
request_data() {
var url = "metrics?pipeline=yes";
this.tick ++;
app.get(url, (msg) => {
this.data = msg;
this.error = false;
}, (Http) => {
if (Http.status == 404) {
this.error = true;
}
});
},
// Stop periodically requesting the scope
stopRequesting() {
this.data = [];
clearInterval(this.data_request);
},
// Start periodically requesting a scope
startRequesting(scope) {
this.stopRequesting();
// Initial request
this.request_data();
// Start periodic request
this.data_request = window.setInterval(function() {
this.request_data();
}.bind(this), 1000);
},
format_num(val) {
if (val < 1.0) {
return val.toFixed(2);
} else if (val < 10.0) {
return val.toFixed(1);
} else {
return val.toFixed(0);
}
},
to_unit(val) {
if (val >= 1.0) {
return this.format_num(val) + "s";
}
val *= 1000;
if (val >= 1.0) {
return this.format_num(val) + "ms";
}
val *= 1000;
if (val >= 1.0) {
return this.format_num(val) + "us";
}
val *= 1000;
if (val >= 1.0) {
return this.format_num(val) + "ns";
} else {
return this.format_num(val) + "ns";
}
}
},
computed: {
systems() {
if (!this.data.pipeline) {
return [];
}
return this.data.pipeline.systems;
},
fps() {
if (!this.data.pipeline) {
return 0;
}
return this.data.pipeline.fps.toFixed(1) + "Hz";
},
frame_time() {
if (!this.data.pipeline) {
return 0;
}
return this.to_unit(this.data.pipeline.frame_time_total);
},
system_time() {
if (!this.data.pipeline) {
return 0;
}
return this.to_unit(this.data.pipeline.system_time_total);
},
merge_time() {
if (!this.data.pipeline) {
return 0;
}
return this.to_unit(this.data.pipeline.merge_time_total);
}
},
template: `
<div class="systems">
<link rel="stylesheet" href="apps/systems/style.css">
<div class="metric-row">
<div class="metric metric-column">
<div class="metric-header">fps</div>
<div class="metric-content">
{{fps}}
</div>
</div>
<div class="metric metric-column">
<div class="metric-header">frame time</div>
<div class="metric-content">
{{frame_time}}
</div>
</div>
<div class="metric metric-column">
<div class="metric-header">system time</div>
<div class="metric-content">
{{system_time}}
</div>
</div>
<div class="metric metric-column">
<div class="metric-header">merge time</div>
<div class="metric-content">
{{merge_time}}
</div>
</div>
</div>
<div style="position: relative;">
<div v-for="s in systems">
<div v-if="s">
<system :system="s" :system_time="data.pipeline.system_time_total" :tick="tick"></system>
</div>
<div v-else class="merge">
Merge
</div>
</div>
</div>
</div>
`
});
app.app_loaded("systems", [{
name: "system-entity-graph",
url: "apps/systems/system_entity_graph.js"
}, {
name: "system-time-graph",
url: "apps/systems/system_time_graph.js"
}, {
name: "system",
url: "apps/systems/system.js"
}, {
name: "system-component",
url: "apps/systems/system_component.js"
}]);

View File

@ -1,253 +0,0 @@
body {
font-family: Roboto,Noto Sans,Noto,sans-serif;
font-size: 1.0em;
font-size: 16px;
background-color: #37393E;
color: #eee;
margin: 0px;
}
#app {
position: relative;
}
div.sidebar {
position: absolute;
background-color: #1E2022;
top: 40px;
width: 70px;
height: calc(100vh - 50px);
padding-top: 12px;
}
div.sidebar-button-container {
position: relative;
height: 45px;
margin-bottom: 12px;
}
div.sidebar-button {
border-radius: 10px;
margin-left: 12px;
width: 45px;
height: 45px;
cursor: pointer;
text-align: center;
}
div.sidebar-selector {
position: absolute;
background-color: #5BE595;
top: 17px;
left: -5px;
height: 10px;
width: 4px;
border-radius: 0px 4px 4px 0px;
transition: left 100ms, height 150ms, top 150ms;
}
div.sidebar-selector-active {
top: 2.3px;
height: 40px;
left: 0px;
}
div.sidebar-selector-hover {
top: 12px;
height: 20px;
left: 0px;
}
img.sidebar-icon {
width: 40px;
height: 40px;
margin-top: 3;
}
div.content {
position: absolute;
width: calc(100% - 70px);
height: calc(100vh - 40px);
top: 40px;
left: 70px;
overflow-y: auto;
}
div.play-controls {
background-color: rgb(23, 24, 26);;
position: absolute;
top: 0px;
width: 100%;
height: 40px;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.2);
}
div.play-buttons {
position: relative;
width: 100px;
margin: 0 auto;
padding: 4px;
}
div.play-button img {
width: 30px;
height: 30px;
float: left;
cursor: pointer;
background-color: #37393E;
margin: 1px;
border-radius: 2px;
transition: background-color 100ms;
}
div.player-active img {
background-color: #777;
}
div.perf-summary {
position: absolute;
top: 0px;
right: 0px;
width: 200px;
height: 40px;
}
div.perf-clock {
position: relative;
float: left;
margin: 0 auto;
left: 10px;
top: -1px;
padding: 2px;
padding-left: 10px;
padding-right: 4px;
font-size: 18px;
border-radius: 5px;
border-color: #37393E;
border-style: solid;
border-width: 1px;
background-color: #000;
color: #777;
}
div.perf-clock-active {
color: #5BE595;
}
div.perf-clock img {
position: relative;
top: 2px;
/* float: left; */
height: 17px;
}
div.perf-fps {
position: relative;
top: 7px;
right: 35px;
width: 80px;
background-color: #000;
border-color: #37393E;
border-style: solid;
border-width: 1px;
border-radius: 5px;
min-height: 25px;
}
span.perf-fps-value {
position: absolute;
border-radius: 5px;
background-color: #000;
font-size: 18px;
padding: 2px;
padding-left: 8px;
}
span.perf-label {
position: absolute;
right: 0px;
background-color: #37393E;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
font-size: 12px;
padding: 5px;
padding-top: 6px;
}
span.perf-fps-ok {
color: #5BE595;
}
div.perf-load {
position: absolute;
right: 10px;
top: 7px;
height: 25px;
width: 130px;
border-color: #37393E;
border-style: solid;
border-width: 1px;
border-radius: 5px;
background-color: #000;
}
div.perf-load svg {
position: absolute;
background-color: #000;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
div.metric-row:after {
position: relative;
}
div.metric-row:after {
content: "";
display: table;
clear: both;
margin: 0px;
padding: 0px;
}
div.metric-column {
float: left;
width: 10%;
min-width: 150px;
margin-right: 0px !important;
margin-bottom: 0px !important;
}
div.metric {
background-color: #1E2022;
color: #eee;
font-size: 18px;
padding: 10px;
margin: 10px;
border-left-color: #181818;
border-left-style: solid;
border-left-width: 5px;
}
div.metric-content {
font-size: 36px;
color: #aaa;
}
div.metric-header {
text-transform: uppercase;
}
input {
-webkit-appearance: none;
-moz-appearance: none;
border-style: none;
outline: none;
padding: 5px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,27 +0,0 @@
<html>
<head>
<title>Flecs Dash</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600" rel="stylesheet" type="text/css">
<link rel="icon" href="favicon.png" sizes="16x16" type="image/png">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="app">
<sidebar v-on:app-select="app_select" :app="app" :apps="apps"></sidebar>
<appcontent :app="app"></appcontent>
<play-controls></play-controls>
</div>
<script src="js/vue.js"></script>
<script src="js/periodic_request.js"></script>
<script src="js/sidebar.js"></script>
<script src="js/sidebar-button.js"></script>
<script src="js/appcontent.js"></script>
<script src="js/perf_summary.js"></script>
<script src="js/play_controls.js"></script>
<script src="js/app.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
</body>
</html>

View File

@ -1,136 +0,0 @@
Vue.config.devtools = true;
var app = new Vue({
el: '#app',
methods: {
// Request
request(method, url, onmsg, onloadend) {
const Http = new XMLHttpRequest();
Http.open(method, "http://" + url);
if (onloadend) {
Http.onloadend = function() { onloadend(Http); };
}
Http.send();
Http.onreadystatechange = (e)=>{
if (Http.readyState == 4) {
if (Http.responseText && Http.responseText.length) {
if (onmsg) {
onmsg(JSON.parse(Http.responseText));
}
}
}
}
},
// Request to own server
request_self(method, url, onmsg, onloadend) {
this.request(method, this.host + "/" + url, onmsg, onloadend);
},
set_host(host) {
this.host = host;
this.init();
},
get(url, onmsg, onloadend) {
this.request_self("GET", url, onmsg, onloadend);
},
put(url, onmsg, onloadend) {
this.request_self("PUT", url, onmsg, onloadend);
},
post(url, onmsg, onloadend) {
this.request_self("POST", url, onmsg, onloadend);
},
delete(url, onmsg, onloadend) {
this.request_self("DELETE", url, onmsg, onloadend);
},
path_to_url(path) {
return path.replace(/\./g, "/");
},
// Initialize application, send initial request for id of self
init() {
this.get("this", (msg) => {
this.server_id = msg.server_id;
this.get("scope/" + this.path_to_url(this.server_id) + "?include=flecs.dash.App", (msg) => {
this.apps = msg;
});
});
},
// Anything that needs to appen periodically
refresh() { },
// App select event selects a different application
app_select(event) {
this.app_load(event.name);
},
import(url) {
var app = document.getElementById(url);
if (!app) {
app = document.createElement("script");
app.setAttribute("id", url);
app.setAttribute("src", url);
document.body.appendChild(app);
return false
} else {
return true;
}
},
component_loaded(id) {
return Vue.options.components[id] != undefined;
},
// Load app resources
app_load(app_id) {
if (this.import("apps/" + app_id + "/" + app_id + ".js")) {
this.app_loaded(app_id);
}
},
// App is loaded
app_loaded(app_id, dependencies) {
var dependencies_resolved = true;
// Load dependent components if any
if (dependencies) {
dependencies.forEach(d => {
if (!this.component_loaded(d.name)) {
app.import(d.url);
dependencies_resolved = false;
}
});
}
// If all dependencies are resolved, just load the app
if (dependencies_resolved) {
this.app = app_id;
} else {
// If not all dependencies are loaded, try again in a little bit
window.setTimeout(function() {
this.app_loaded(app_id, dependencies);
}.bind(this), 32);
}
}
},
data: {
host: window.location.host,
app: "",
apps: [],
server_id: ""
}
});
window.onload = function() {
app.init();
window.setInterval(app.refresh, 1000);
}

View File

@ -1,18 +0,0 @@
Vue.component('appcontent', {
props: ['world', 'app'],
render: function(h) {
if (!this.app || this.app === "") {
return h('div');
} else {
return h('div', {
attrs: {class: "content"}
}, [
h('app-' + this.app, {
props: {
world: this.world
}
})
]);
}
}
});

View File

@ -1,182 +0,0 @@
Vue.component('perf-fps', {
props: ['data'],
data: function() {
return {
last_tick: 0,
last_fps: 0,
last_update: 0,
last_result: 0
}
},
computed: {
fps() {
if (!this.data) {
return 0;
}
var d = new Date();
var t = d.getTime();
if (t - this.last_update < 1000) {
return this.last_result;
}
this.last_update = t;
const fps = this.data.data["flecs.dash.monitor.Fps"];
const tick = this.data.data["flecs.dash.monitor.Tick"];
let result = (fps.count - this.last_fps) / (tick.count - this.last_tick);
this.last_fps = fps.count;
this.last_tick = tick.count;
if (result < 0) {
result = 0;
}
if (result > 1000000) {
this.last_result = (result / 1000000).toFixed(2) + "M";
} else
if (result > 1000) {
this.last_result = (result / 1000).toFixed(2) + "K";
} else if (result > 100) {
this.last_result = result.toFixed(0);
} else {
this.last_result = result.toFixed(1);
}
return this.last_result;
},
css() {
if (!this.data) {
return "";
}
const fps = this.data.data["flecs.dash.monitor.Fps"];
if (this.fps >= fps.target * 0.9) {
return "perf-fps-ok";
}
}
},
template: `
<div>
<div class="perf-fps">
<span :class="'perf-fps-value ' + css">{{ fps }}</span>
<span class="perf-label">FPS</span>
</div>
</div>
`
});
Vue.component('perf-load', {
props: ['data'],
data: function() {
return {
last_total: 0,
last_frame: 0
}
},
computed: {
load() {
if (!this.data) {
return 100;
}
const load = this.data.data["flecs.dash.monitor.Load"];
let result = (load.frame_time_count - this.last_frame) / (load.total_time_count - this.last_total);
this.last_frame = load.frame_time_count;
this.last_total = load.total_time_count;
if (result < 0) {
result = 0;
}
if (!result) {
result = 1;
}
return (result * 100);
},
css() {
return "";
}
},
template: `
<div :class="'perf-load'">
<svg width="100px" height="25px">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" :x2="1.0 / (load / 100)" y2="0%">
<stop offset="0%" style="stop-color:#5BE595;stop-opacity:1" />
<stop offset="100%" style="stop-color:#4981B5;stop-opacity:1" />
</linearGradient>
</defs>
<rect x="0" y="0" :width="5 + (load * 0.95)" height="25" fill="url(#grad1)" />
</svg>
<span class="perf-label">Load</span>
</div>
`
});
Vue.component('perf-clock', {
props: ['data'],
methods: {
digit(num) {
if (num < 10) {
return "0" + num;
} else {
return num;
}
}
},
computed: {
clock() {
if (!this.data) {
return "00:00:00.00";
}
const clock = this.data.data["flecs.dash.monitor.WorldClock"];
let hours = Math.floor(clock.world_time / 3600);
let minutes = Math.floor((clock.world_time - hours * 3600) / 60);
let seconds = Math.floor(clock.world_time - hours * 3600 - minutes * 60);
let remainder = Math.floor((clock.world_time - Math.floor(clock.world_time)) * 10);
return this.digit(hours) + ":" + this.digit(minutes) + ":" +
this.digit(seconds) + "." + remainder;
},
css() {
if (!this.data) {
return "";
}
const clock = this.data.data["flecs.dash.monitor.WorldClock"];
if (clock.world_time != 0) {
return "perf-clock-active";
} else {
return "";
}
}
},
template: `
<div :class="'perf-clock ' + css">
{{ clock }}
<img src="images/clock.png">
</div>
`
});
Vue.component('perf-summary', {
props: ['data'],
template: `
<div>
<perf-clock :data="data"></perf-clock>
<div class="perf-summary">
<perf-fps :data="data"></perf-fps>
<perf-load :data="data"></perf-load>
</div>
</div>
`
});

View File

@ -1,36 +0,0 @@
class PeriodicRequest {
constructor(interval, callback, err_callback) {
this.error = false;
this.timer = undefined;
this.result = undefined;
this.interval = interval;
this.callback = callback;
this.err_callback = err_callback;
}
request(url) {
app.get(url, (msg) => {
this.result = msg;
this.error = false;
this.callback(msg);
}, (Http) => {
if (!Http.status || Http.status >= 400) {
this.err_callback(Http.status);
this.error = true;
}
});
}
stopRequesting() {
this.result = undefined;
clearInterval(this.timer);
}
startRequesting(url) {
this.stopRequesting();
this.timer = window.setInterval(function() {
this.request(url);
}.bind(this), this.interval);
}
}

View File

@ -1,69 +0,0 @@
Vue.component('play-button', {
props: ["state", "icon"],
methods: {
clicked: function() {
this.$emit('click', {event: this.icon});
},
css() {
let result = "play-button";
if (this.state == this.icon) {
result += " player-active";
}
return result;
}
},
template: `
<div :class="css()">
<img :src="'images/' + icon + '.png'" v-on:click="clicked">
</div>
`
});
Vue.component('play-controls', {
mounted: function() {
this.request.startRequesting("entity/flecs/core/World");
},
beforeDestroy: function() {
this.stopRequesting();
},
data: function() {
return {
state: undefined,
request: new PeriodicRequest(100, this.on_recv_world, this.on_recv_err),
data: undefined
}
},
methods: {
play: function(e) {
this.state = e.event;
app.put("player/" + e.event, (msg) => { });
},
on_recv_world(data) {
this.data = data;
if (!this.state) {
const state = this.data.data["flecs.player.Player"];
if (state.state == "EcsPlayerPlay") {
this.state = "play";
} else if (state.state == "EcsPlayerStop") {
this.state = "stop";
} else if (state.state == "EcsPlayerPause") {
this.state = "pause";
}
}
},
on_recv_err() {
this.state = undefined;
}
},
template: `
<div class="play-controls">
<div class="play-buttons">
<play-button icon="stop" :state="state" v-on:click="play"></play-button>
<play-button icon="play" :state="state" v-on:click="play"></play-button>
<play-button icon="pause" :state="state" v-on:click="play"></play-button>
</div>
<perf-summary :data="data"></perf-summary>
</div>
`
});

View File

@ -1,46 +0,0 @@
Vue.component('sidebar-button', {
props: {
app: String,
icon: String,
active_app: String
},
data: function() {
return {
hover: false
}
},
methods: {
selector_css() {
let result = "sidebar-selector";
if (this.app == this.active_app) {
result += " sidebar-selector-active";
} else if (this.hover) {
result += " sidebar-selector-hover";
}
return result;
},
button_css() {
return "sidebar-button";
},
select_app: function() {
this.$emit('select-app', {name: this.app});
},
set_hover(hover) {
this.hover = hover;
}
},
template: `
<div class="sidebar-button-container">
<div :class="selector_css()">
</div>
<div :class="button_css()"
v-on:click="select_app"
v-on:mouseover="set_hover(true)"
v-on:mouseleave="set_hover(false)">
<img :src="icon" class="sidebar-icon">
</div>
</div>
`
});

View File

@ -1,59 +0,0 @@
Vue.component('sidebar', {
props: {
app: String,
apps: Array
},
methods: {
select_app(event) {
this.$emit("app-select", event);
}
},
computed: {
app_names: function() {
let ids = [];
if (!this.apps) {
return;
}
this.apps.forEach((table) => {
if (table.data && table.data.Name) {
table.data.Name.forEach((app) => {
ids.push(app.value);
});
}
});
return ids;
},
app_icons: function() {
let ids = [];
if (!this.apps) {
return;
}
this.apps.forEach((table) => {
if (table.data && table.data["flecs.dash.App"]) {
table.data["flecs.dash.App"].forEach((app) => {
ids.push(app.icon);
});
}
});
return ids;
}
},
template: `
<div class="sidebar">
<sidebar-button v-for="(el, i) in app_names"
:app="el"
:icon="app_icons[i]"
:active_app="app"
:key="el"
v-on:select-app="select_app">
</sidebar-button>
</div>
`
});

File diff suppressed because one or more lines are too long