.
58
app/assets/bootstrap/bootstrap.less
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Core variables and mixins
|
||||
@import "../../../node_modules/bootstrap/less/variables.less";
|
||||
@import "variables.less";
|
||||
@import "../../../node_modules/bootstrap/less/mixins.less";
|
||||
//@import "mixin-overrides.less";
|
||||
|
||||
// Reset and dependencies
|
||||
@import "../../../node_modules/bootstrap/less/normalize.less";
|
||||
@import "../../../node_modules/bootstrap/less/print.less";
|
||||
@import "../../../node_modules/bootstrap/less/glyphicons.less";
|
||||
|
||||
// Core CSS
|
||||
@import "../../../node_modules/bootstrap/less/scaffolding.less";
|
||||
@import "../../../node_modules/bootstrap/less/type.less";
|
||||
@import "../../../node_modules/bootstrap/less/code.less";
|
||||
@import "../../../node_modules/bootstrap/less/grid.less";
|
||||
@import "../../../node_modules/bootstrap/less/tables.less";
|
||||
@import "../../../node_modules/bootstrap/less/forms.less";
|
||||
@import "../../../node_modules/bootstrap/less/buttons.less";
|
||||
|
||||
// Components
|
||||
@import "../../../node_modules/bootstrap/less/component-animations.less";
|
||||
@import "../../../node_modules/bootstrap/less/dropdowns.less";
|
||||
@import "../../../node_modules/bootstrap/less/button-groups.less";
|
||||
@import "../../../node_modules/bootstrap/less/input-groups.less";
|
||||
@import "../../../node_modules/bootstrap/less/navs.less";
|
||||
@import "../../../node_modules/bootstrap/less/navbar.less";
|
||||
@import "../../../node_modules/bootstrap/less/breadcrumbs.less";
|
||||
@import "../../../node_modules/bootstrap/less/pagination.less";
|
||||
@import "../../../node_modules/bootstrap/less/pager.less";
|
||||
@import "../../../node_modules/bootstrap/less/labels.less";
|
||||
@import "../../../node_modules/bootstrap/less/badges.less";
|
||||
@import "../../../node_modules/bootstrap/less/jumbotron.less";
|
||||
@import "../../../node_modules/bootstrap/less/thumbnails.less";
|
||||
@import "../../../node_modules/bootstrap/less/alerts.less";
|
||||
@import "../../../node_modules/bootstrap/less/progress-bars.less";
|
||||
@import "../../../node_modules/bootstrap/less/media.less";
|
||||
@import "../../../node_modules/bootstrap/less/list-group.less";
|
||||
@import "../../../node_modules/bootstrap/less/panels.less";
|
||||
@import "../../../node_modules/bootstrap/less/responsive-embed.less";
|
||||
@import "../../../node_modules/bootstrap/less/wells.less";
|
||||
@import "../../../node_modules/bootstrap/less/close.less";
|
||||
|
||||
// Components w/ JavaScript
|
||||
@import "../../../node_modules/bootstrap/less/modals.less";
|
||||
@import "../../../node_modules/bootstrap/less/tooltip.less";
|
||||
@import "../../../node_modules/bootstrap/less/popovers.less";
|
||||
@import "../../../node_modules/bootstrap/less/carousel.less";
|
||||
|
||||
// Utility classes
|
||||
@import "../../../node_modules/bootstrap/less/utilities.less";
|
||||
@import "../../../node_modules/bootstrap/less/responsive-utilities.less";
|
||||
|
||||
@import "overrides.less";
|
||||
|
||||
body {
|
||||
background: transparent;
|
||||
}
|
4
app/assets/bootstrap/include.less
Normal file
@@ -0,0 +1,4 @@
|
||||
// Core variables and mixins
|
||||
@import "../../../node_modules/bootstrap/less/variables.less";
|
||||
@import "variables.less";
|
||||
@import "../../../node_modules/bootstrap/less/mixins.less";
|
541
app/assets/bootstrap/overrides.less
Normal file
@@ -0,0 +1,541 @@
|
||||
@import (less, reference) "../../../node_modules/font-awesome/css/font-awesome.css";
|
||||
|
||||
|
||||
.glyphicon:extend(.fa all) {
|
||||
&.glyphicon-chevron-right:extend(.fa-chevron-right all) {};
|
||||
&.glyphicon-chevron-left:extend(.fa-chevron-left all) {};
|
||||
&.glyphicon-chevron-up:extend(.fa-chevron-up all) {};
|
||||
&.glyphicon-chevron-down:extend(.fa-chevron-down all) {};
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
color: #EFEAB1;
|
||||
transition: opacity 0.125s, background 0.125s, color0.125s ;
|
||||
|
||||
&:hover {
|
||||
color: #FFF79A;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.block-element {
|
||||
display: block;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: none !important;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.125);
|
||||
|
||||
&:active {
|
||||
outline: 5px auto @brand-info;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.active {
|
||||
.box-shadow(@control-shadow-active);
|
||||
}
|
||||
|
||||
-webkit-transition: all 0.125s ease-out;
|
||||
-moz-transition: all 0.125s ease-out;
|
||||
-ms-transition: all 0.125s ease-out;
|
||||
-o-transition: all 0.125s ease-out;
|
||||
transition: all 0.125s ease-out;
|
||||
|
||||
&[disabled] {
|
||||
cursor: default !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-ink {
|
||||
background: transparent !important;
|
||||
box-shadow: none;
|
||||
|
||||
&.btn-danger {
|
||||
color: #FF4832;
|
||||
}
|
||||
|
||||
&.btn-xs {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-toolbar {
|
||||
margin: 0;
|
||||
|
||||
.btn, .btn-group, [uib-dropdown] {
|
||||
float: none;
|
||||
}
|
||||
|
||||
> .btn-group > * {
|
||||
float: left;
|
||||
}
|
||||
|
||||
> .btn, > .btn-group, > [uib-dropdown] {
|
||||
margin: 0 5px 5px 0px;
|
||||
vertical-align: top;
|
||||
|
||||
> .btn {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-xs-max) {
|
||||
.btn-toolbar-collapsible {
|
||||
.btn {
|
||||
font-size: 0;
|
||||
|
||||
.fa::before, .caret {
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[uib-dropdown] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
[uib-dropdown-menu], .dropdown-menu {
|
||||
.box-shadow(@control-dropdown-shadow);
|
||||
top: 25px;
|
||||
|
||||
> li > * {
|
||||
display: block;
|
||||
padding: 5px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: @line-height-base;
|
||||
color: @dropdown-link-color;
|
||||
white-space: nowrap;
|
||||
|
||||
&[disabled] {
|
||||
color: #888;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
> li.disabled a {
|
||||
color: #666;
|
||||
&:hover {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[uib-dropdown-menu] > .active > * {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @dropdown-link-active-color;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
background-color: @dropdown-link-active-bg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.form-control {
|
||||
border: none;
|
||||
|
||||
&[checkbox] {
|
||||
border: none;
|
||||
background: transparent;
|
||||
display: inline-block;
|
||||
margin: @padding-base-vertical 0 0;
|
||||
}
|
||||
|
||||
border-radius: 0;
|
||||
.box-shadow(~"0 1px 1px rgba(0, 0, 0, 0.51)");
|
||||
|
||||
&:focus {
|
||||
.box-shadow(~"0 1px 1px rgba(0, 0, 0, 0.51)");
|
||||
}
|
||||
|
||||
.transition(0.25s background);
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
.box-shadow(~"0 1px 1px rgba(0, 0, 0, 0.51)");
|
||||
.form-control {
|
||||
.box-shadow(none);
|
||||
}
|
||||
}
|
||||
|
||||
.input-group-addon {
|
||||
border: none;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
@media (min-width: @screen-sm-min) {
|
||||
.control-label {
|
||||
font-size: 12px;
|
||||
padding-top: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.form-group .control-label {
|
||||
font-size: 12px;
|
||||
padding-top: 10px;
|
||||
font-weight: bold;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
label.form-control-static {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
width: 100%;
|
||||
|
||||
*:focus + .input-group-addon,
|
||||
*:focus + .input-group-btn {
|
||||
//border-bottom: 1px solid @brand-primary;
|
||||
}
|
||||
|
||||
.input-group-addon, .input-group-btn {
|
||||
border: none;
|
||||
|
||||
&.input-sm {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
text-decoration: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 76%;
|
||||
position: relative;
|
||||
padding: 5px 4px 4px;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
.box-shadow(@control-shadow);
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border: none;
|
||||
border-top: 1px solid @list-group-line-border;
|
||||
cursor: default;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #f7e61d;
|
||||
border-right: 2px solid #f7e61d;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item.combi {
|
||||
padding: 0;
|
||||
clear: both;
|
||||
|
||||
tr& {
|
||||
a.main, .btn {
|
||||
display: table-cell !important;
|
||||
}
|
||||
}
|
||||
|
||||
& > .main {
|
||||
display: block;
|
||||
|
||||
h4 {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
video-thumbnail, asset-thumbnail {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
& > a.main {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
& > a.main, & > a.btn, & > button, & > [uib-dropdown] > button {
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
color: @list-group-link-hover-color;
|
||||
background-color: @list-group-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
& > a.btn, & > button, & > [uib-dropdown] > button {
|
||||
background-color: @list-group-bg;
|
||||
position: relative; // more z-index
|
||||
z-index: 2;
|
||||
display: block;
|
||||
float: right;
|
||||
border: none;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
& > .drag-handle {
|
||||
float: left;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&.single > .main {
|
||||
height: 40px;
|
||||
line-height: 39px;
|
||||
padding: 0 15px;
|
||||
|
||||
[checkbox] {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.label-lg {
|
||||
top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
&.double > .main {
|
||||
height: 68px;
|
||||
line-height: 25px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
&.single {
|
||||
a.btn, button, .drag-handle {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&.double {
|
||||
a.btn, button, .drag-handle {
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.form-control-focus(@color: @input-border-focus) {
|
||||
@color-rgba: rgba(red(@color), green(@color), blue(@color), .3);
|
||||
}
|
||||
|
||||
.modal {
|
||||
background-color: rgba(0,0,0,.5);
|
||||
position: fixed !important;
|
||||
|
||||
|
||||
.modal-dialog {
|
||||
margin: 0 auto;
|
||||
top: 50px;
|
||||
|
||||
.modal-content {
|
||||
background-color: @body-bg;
|
||||
|
||||
|
||||
.modal-body {
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
padding: 25px 15px;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid #222;
|
||||
padding: 0;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
.btn.btn-default {
|
||||
background: @input-bg;
|
||||
border: none !important;
|
||||
cursor: pointer;
|
||||
box-shadow: none !important;
|
||||
padding: 10px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(0,0,0,.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-backdrop {
|
||||
background: rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
|
||||
.navbar-fixed-top {
|
||||
border-width: 0 0 2px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.navbar-form {
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.nav-tabs-justified,
|
||||
.nav-tabs {
|
||||
border-bottom: 1px solid @nav-tabs-border-color;
|
||||
|
||||
> li {
|
||||
> a {
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
color: @text-color;
|
||||
&:hover {
|
||||
background-color: @nav-tabs-active-link-hover-bg;
|
||||
border-bottom: 1px solid @nav-tabs-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
// Active state, and its :hover to override normal :hover
|
||||
&.active > a {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus {
|
||||
border-radius: 0;
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid @nav-tabs-active-link-hover-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-justified {
|
||||
> li {
|
||||
display: table-cell;
|
||||
width: 1%;
|
||||
> a {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.popover-content {
|
||||
//padding: 0;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
background: transparent;
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.table-condensed {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
background: @table-bg;
|
||||
.box-shadow(@control-shadow);
|
||||
&.no-margin {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
> thead,
|
||||
> tbody,
|
||||
> tfoot {
|
||||
> tr {
|
||||
background-color: transparent;
|
||||
> th,
|
||||
> td {
|
||||
border-top: 1px solid @table-line-border-color;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
th, td {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> thead > tr > th {
|
||||
border-bottom: 2px solid @table-line-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.table-bordered {
|
||||
> thead,
|
||||
> tbody,
|
||||
> tfoot {
|
||||
> tr {
|
||||
> th,
|
||||
> td {
|
||||
border: 1px solid @table-line-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.help-block {
|
||||
color: darken(@text-color, 10%);
|
||||
}
|
||||
|
||||
|
||||
.label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
.datepicker-dropdown {
|
||||
&.datepicker-orient-top:before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&.datepicker-orient-top:after {
|
||||
border-bottom: 7px solid @dropdown-bg !important;
|
||||
}
|
||||
|
||||
&.datepicker-orient-bottom:after {
|
||||
border-top: 7px solid @dropdown-bg !important;
|
||||
}
|
||||
|
||||
table tr td.day:hover {
|
||||
background: #555 !important;
|
||||
}
|
||||
|
||||
table tr td.active {
|
||||
background: @brand-primary !important;
|
||||
color: #222 !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
}
|
141
app/assets/bootstrap/variables.less
Normal file
@@ -0,0 +1,141 @@
|
||||
@brand-primary: #f7e61d;
|
||||
@brand-success: #5cb85c;
|
||||
@brand-info: #5bc0de;
|
||||
@brand-warning: #f0ad4e;
|
||||
@brand-danger: #FF1C01;
|
||||
|
||||
// New
|
||||
@brand-primary: #f7e61d;
|
||||
@brand-success: #42B500;
|
||||
@brand-info: #01BAEF;
|
||||
@brand-warning: #DB8A00;
|
||||
@brand-danger: #EF2F00;
|
||||
|
||||
|
||||
@control-shadow: 0 1px 1px rgba(0,0,0,.25);
|
||||
@control-shadow-active: 0 1px 1px rgba(0,0,0,.25) inset, @control-shadow;
|
||||
@control-dropdown-shadow: 0 0 50px rgba(0,0,0,.5), @control-shadow;
|
||||
@form-accent: #DBCA00;
|
||||
@form-accent-bright: @brand-primary;
|
||||
|
||||
|
||||
@body-bg: #1D272D;
|
||||
@text-color: #aaa;
|
||||
|
||||
@font-family-sans-serif: "Source Sans Pro", "PT Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
@icon-font-path: "../fonts/";
|
||||
@component-active-color: rgba(0,0,0,.15);
|
||||
@component-active-color: darken(@component-active-bg, 30%);
|
||||
@table-bg: #444;
|
||||
@table-bg-accent: rgba(255,255,255,.15);
|
||||
@table-bg-hover: #666;
|
||||
@table-border-color: #2e2e2e;
|
||||
@table-line-border-color: #4f4f4f;
|
||||
|
||||
@btn-default-color: @text-color;
|
||||
@btn-default-bg: #243D49;
|
||||
@btn-default-border: transparent;
|
||||
@btn-primary-color: @component-active-color;
|
||||
@btn-primary-border: #584E00;
|
||||
@btn-danger-border: rgba(0,0,0,.5);
|
||||
@btn-danger-color: white;
|
||||
@btn-danger-bg: #FF4630;
|
||||
@btn-danger-border: transparent;//@brand-danger;
|
||||
@btn-link-disabled-color: darken(@text-color, 20%);
|
||||
|
||||
@input-bg: #11181C;
|
||||
@input-bg-disabled: #2a2f31;
|
||||
@input-color: #bbb;
|
||||
@input-border: #3a3a3a;
|
||||
@input-group-addon-border-color: @input-bg;
|
||||
@input-color-placeholder: #777;
|
||||
@input-group-addon-bg: @input-bg;
|
||||
|
||||
@dropdown-bg: rgba(64,64,64,.95); //@body-bg;
|
||||
@dropdown-link-color: @text-color;
|
||||
@dropdown-link-hover-color: #ddd;
|
||||
@dropdown-link-hover-bg: #444;
|
||||
@dropdown-link-disabled-color: darken(@text-color, 5%);
|
||||
@navbar-default-bg: #23272A;
|
||||
@navbar-default-border: #111;
|
||||
|
||||
|
||||
@nav-tabs-border-color: #666;
|
||||
@nav-tabs-link-hover-border-color: transparent;
|
||||
|
||||
@nav-tabs-active-link-hover-bg: rgba(255,255,255,.1);
|
||||
@nav-tabs-active-link-hover-color: @brand-primary;
|
||||
@nav-tabs-active-link-border-color: @brand-primary;
|
||||
@nav-tabs-active-link-hover-border-color: @brand-primary;
|
||||
|
||||
|
||||
@pagination-color: @btn-default-color;
|
||||
@pagination-bg: @btn-default-bg;
|
||||
@pagination-border: @btn-default-border;
|
||||
|
||||
@pagination-hover-color: @btn-default-color;
|
||||
@pagination-hover-bg: lighten(@btn-default-bg, 5%);
|
||||
@pagination-hover-border: @btn-default-border;
|
||||
|
||||
@pagination-active-color: @brand-primary;
|
||||
@pagination-active-bg: darken(@btn-default-bg, 5%);
|
||||
@pagination-active-border: @btn-default-border;
|
||||
|
||||
@pagination-disabled-color: @btn-link-disabled-color;
|
||||
@pagination-disabled-bg: darken(@btn-default-bg, 5%);
|
||||
@pagination-disabled-border: @btn-default-border;
|
||||
//@state-success-bg: #234116; //#dff0d8;
|
||||
//@state-info-bg: #0C3A50; //#d9edf7;
|
||||
//@state-danger-bg: #9E3B3B;///#f2dede;
|
||||
@popover-bg: rgba(64,64,64,.95);
|
||||
@popover-arrow-color: #444;
|
||||
@progress-bg: #555;
|
||||
@list-group-bg: rgba(255,255,255,.1);
|
||||
@list-group-disabled-bg: #333;
|
||||
@list-group-border: transparent;
|
||||
@list-group-line-border: rgba(255,255,255,.1);
|
||||
@list-group-hover-bg: rgba(255,255,255,.2);
|
||||
@list-group-link-color: @text-color;
|
||||
@list-group-link-heading-color: @text-color;
|
||||
@list-group-active-bg: rgba(255,255,255,.3);
|
||||
@list-group-active-border: transparent;
|
||||
|
||||
|
||||
@panel-bg: @table-bg;
|
||||
@panel-inner-border: @table-border-color;
|
||||
@panel-footer-bg: #666;
|
||||
@panel-default-text: #ddd;
|
||||
@panel-default-border: @table-border-color;
|
||||
@panel-default-heading-bg: #666;
|
||||
@well-bg: #222;
|
||||
@badge-bg: #333;
|
||||
@badge-active-bg: #333;
|
||||
@code-bg: #222;
|
||||
@pre-bg: #222;
|
||||
@pre-color: #bbb;
|
||||
@blockquote-border-color: #444;
|
||||
@page-header-border-color: #444;
|
||||
|
||||
|
||||
@alert-bg: #2A2A2A;
|
||||
|
||||
@state-success-text: @brand-success;
|
||||
@state-success-bg: @alert-bg;
|
||||
@state-success-border: @state-success-text;
|
||||
|
||||
@state-info-text: @brand-info;
|
||||
@state-info-bg: @alert-bg;
|
||||
@state-info-border: @state-info-text;
|
||||
|
||||
@state-warning-text: #F27208;
|
||||
@state-warning-bg: @alert-bg;
|
||||
@state-warning-border: @state-warning-text;
|
||||
|
||||
@state-danger-text: @brand-danger;
|
||||
@state-danger-bg: @alert-bg;
|
||||
@state-danger-border: @state-danger-text;
|
||||
|
||||
|
||||
@border-radius-base: 1px;
|
||||
@border-radius-large: 3px;
|
||||
@border-radius-small: 1px;
|
BIN
app/assets/img/disk.icns
Normal file
BIN
app/assets/img/disk.ico
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
app/assets/img/icon.png
Normal file
After Width: | Height: | Size: 213 KiB |
BIN
app/assets/img/logo.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
107
app/assets/img/logo.svg
Normal file
@@ -0,0 +1,107 @@
|
||||
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="536.82501"
|
||||
height="126.525"
|
||||
viewBox="0 0 536.82501 126.525"
|
||||
sodipodi:docname="elements_wortmarke+bildmarke_gelb+weiÃ_rz.svg"
|
||||
inkscape:export-filename="/home/eugene/Downloads/logo.png"
|
||||
inkscape:export-xdpi="42.677204"
|
||||
inkscape:export-ydpi="42.677204"><metadata
|
||||
id="metadata8"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1845"
|
||||
inkscape:window-height="1025"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1586643"
|
||||
inkscape:cx="143.54613"
|
||||
inkscape:cy="-3.8338411"
|
||||
inkscape:window-x="75"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g10" /><g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="ink_ext_XXXXXX"
|
||||
transform="matrix(1.25,0,0,-1.25,0,126.525)"><g
|
||||
id="g12"
|
||||
transform="scale(0.1,0.1)"><path
|
||||
d="m 202.457,809.793 404.891,0 0,202.457 -404.891,0 0,-202.457 z"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path14"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 0,607.375 202.445,0 0,202.457 -202.445,0 0,-202.457 z"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path16"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 202.457,404.918 404.891,0 0,202.457 -404.891,0 0,-202.457 z"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path18"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 0,202.461 202.445,0 0,202.457 -202.445,0 0,-202.457 z"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path20"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 202.457,0 404.891,0 0,202.461 -404.891,0 0,-202.461 z"
|
||||
style="fill:#fff200;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path22"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 1072.96,482.414 -148.108,0 0,48.219 148.108,0 0,-48.219 z m 39.27,-234.23 -302.441,0 0,516.679 302.441,0 0,-48.218 -247.32,0 0,-420.243 247.32,0 0,-48.218"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path24"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 1518.04,248.184 -187.4,0 0,48.218 187.4,0 0,-48.218 z m -247.34,0 -55.1,0 0,516.679 55.1,0 0,-516.679"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path26"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 1875.61,482.414 -148.12,0 0,48.219 148.12,0 0,-48.219 z m 39.27,-234.23 -302.43,0 0,516.679 302.43,0 0,-48.218 -247.33,0 0,-420.243 247.33,0 0,-48.218"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path28"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 2515.65,248.184 -55.12,0 0,333.425 55.12,0 0,-333.425 z m -432.66,0 -55.1,0 0,333.425 55.1,0 0,-333.425"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path30"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 2899.41,482.414 -148.12,0 0,48.219 148.12,0 0,-48.219 z m 39.28,-234.23 -302.44,0 0,516.679 302.44,0 0,-48.218 -247.34,0 0,-420.243 247.34,0 0,-48.218"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path32"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 3438.17,419.605 -55.11,0 0,345.258 55.11,0 0,-345.258 z m -334.12,-171.421 -55.12,0 0,344.457 55.12,0 0,-344.457 z m 336.1,19.675 -44.09,-28.949 -352.46,505.524 44.79,28.933 351.76,-505.508"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path34"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 3732.38,248.184 -55.1,0 0,408.523 55.1,0 0,-408.523 z m 160.52,468.461 -376.14,0 0,48.218 376.14,0 0,-48.218"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path36"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 4243.6,638.805 c -18.61,62.683 -73.02,92.308 -126.76,92.308 -56.49,0 -108.85,-33.754 -108.85,-95.761 0,-37.891 23.43,-75.786 73.02,-86.801 l 16.54,-4.141 -13.1,-48.91 -15.16,3.453 c -75.77,17.902 -115.04,76.461 -115.04,135.02 0,95.757 80.6,144.683 162.59,144.683 73.71,0 148.81,-39.273 171.54,-122.64 L 4243.6,638.805 Z M 4122.34,234.406 c -75.78,0 -153.63,38.571 -181.18,124.696 l 46.84,19.285 c 19.99,-66.137 75.79,-95.762 132.27,-95.762 63.39,0 121.26,37.203 121.26,104.027 0,46.856 -28.24,81.297 -83.37,92.313 l -13.77,2.758 13.08,48.91 19.99,-4.129 c 76.46,-15.848 117.12,-69.586 117.12,-137.777 0,-85.438 -68.9,-154.321 -172.24,-154.321"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path38"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
d="m 2528.61,742.93 -44.08,35.597 -211.89,-224.117 -212.58,224.731 -44.08,-35.598 249.15,-263.246 7.51,-7.883 7.51,7.883 248.46,262.633"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path40"
|
||||
inkscape:connector-curvature="0" /></g></g></svg>
|
After Width: | Height: | Size: 6.0 KiB |
BIN
app/assets/img/shortcut.ico
Normal file
After Width: | Height: | Size: 361 KiB |
BIN
app/assets/img/tray-darwin.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
app/assets/img/tray-darwin@2x.png
Normal file
After Width: | Height: | Size: 241 B |
BIN
app/assets/img/tray-linux.png
Normal file
After Width: | Height: | Size: 261 B |
BIN
app/assets/img/tray-win32.png
Normal file
After Width: | Height: | Size: 261 B |
BIN
app/assets/img/user.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
23
app/assets/toaster-custom.less
Normal file
@@ -0,0 +1,23 @@
|
||||
app > toaster-container > #toast-container {
|
||||
width: 100% !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
top: 50px !important;
|
||||
|
||||
.toast {
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
opacity: 1 !important;
|
||||
border-radius: 0 !important;
|
||||
width: 100% !important;
|
||||
padding: 10px !important;
|
||||
|
||||
.toaster-icon {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.toast-title {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
}
|
||||
}
|
13
app/index.pug
Normal file
@@ -0,0 +1,13 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
meta(charset='UTF-8')
|
||||
title ELEMENTS Benchmark
|
||||
base(href='index.html')
|
||||
script.
|
||||
console.timeStamp('index')
|
||||
window.nodeRequire = require
|
||||
script(src='./preload.js')
|
||||
script(src='./bundle.js', defer)
|
||||
body(style='background-image: radial-gradient(circle, #2b3840 0%, #1D272D 100%); min-height: 100vh')
|
||||
app
|
130
app/main.js
Normal file
@@ -0,0 +1,130 @@
|
||||
const Config = require('electron-config')
|
||||
const electron = require('electron')
|
||||
const platform = require('os').platform()
|
||||
require('electron-debug')({enabled: true, showDevTools: process.argv.indexOf('--debug') != -1})
|
||||
|
||||
let app = electron.app
|
||||
let windowConfig = new Config({name: 'window'})
|
||||
|
||||
|
||||
setupWindowManagement = () => {
|
||||
let windowCloseable
|
||||
|
||||
app.window.on('close', (e) => {
|
||||
windowConfig.set('windowBoundaries', app.window.getBounds())
|
||||
if (!windowCloseable) {
|
||||
app.window.hide()
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
|
||||
app.window.on('closed', () => {
|
||||
app.window = null
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-closeable', (event, flag) => {
|
||||
windowCloseable = flag
|
||||
})
|
||||
|
||||
electron.ipcMain.on('window-focus', () => {
|
||||
app.window.show()
|
||||
app.window.focus()
|
||||
})
|
||||
|
||||
app.on('before-quit', () => windowCloseable = true)
|
||||
}
|
||||
|
||||
|
||||
setupMenu = () => {
|
||||
var template = [{
|
||||
label: "Application",
|
||||
submenu: [
|
||||
{ type: "separator" },
|
||||
{ label: "Quit", accelerator: "CmdOrCtrl+Q", click: () => {
|
||||
app.window.webContents.send('host:quit-request')
|
||||
}}
|
||||
]}, {
|
||||
label: "Edit",
|
||||
submenu: [
|
||||
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
|
||||
{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
|
||||
{ type: "separator" },
|
||||
{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
|
||||
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
|
||||
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
|
||||
{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
|
||||
]
|
||||
}]
|
||||
|
||||
electron.Menu.setApplicationMenu(electron.Menu.buildFromTemplate(template))
|
||||
}
|
||||
|
||||
|
||||
start = () => {
|
||||
let t0 = Date.now()
|
||||
|
||||
let secondInstance = app.makeSingleInstance((argv) => {
|
||||
app.window.focus()
|
||||
})
|
||||
|
||||
if (secondInstance) {
|
||||
app.quit()
|
||||
return
|
||||
}
|
||||
|
||||
let options = {
|
||||
width: 800,
|
||||
height: 400,
|
||||
icon: `${app.getAppPath()}/assets/img/icon.png`,
|
||||
title: 'ELEMENTS Benchmark',
|
||||
minWidth: 800,
|
||||
minHeight: 400,
|
||||
'web-preferences': {'web-security': false},
|
||||
//- background to avoid the flash of unstyled window
|
||||
backgroundColor: '#1D272D',
|
||||
}
|
||||
Object.assign(options, windowConfig.get('windowBoundaries'))
|
||||
|
||||
if (platform == 'darwin') {
|
||||
options.titleBarStyle = 'hidden'
|
||||
} else {
|
||||
options.frame = false
|
||||
}
|
||||
|
||||
app.commandLine.appendSwitch('--disable-http-cache')
|
||||
|
||||
app.window = new electron.BrowserWindow(options)
|
||||
app.window.loadURL(`file://${app.getAppPath()}/assets/webpack/index.html`, {extraHeaders: "pragma: no-cache\n"})
|
||||
|
||||
if (platform != 'darwin') {
|
||||
app.window.setMenu(null)
|
||||
}
|
||||
|
||||
app.window.show()
|
||||
app.window.focus()
|
||||
|
||||
setupWindowManagement()
|
||||
setupMenu()
|
||||
|
||||
console.info(`Host startup: ${Date.now() - t0}ms`)
|
||||
t0 = Date.now()
|
||||
electron.ipcMain.on('app:ready', () => {
|
||||
console.info(`App startup: ${Date.now() - t0}ms`)
|
||||
})
|
||||
}
|
||||
|
||||
app.on('ready', start)
|
||||
|
||||
app.on('activate', () => {
|
||||
if (!app.window)
|
||||
start()
|
||||
else {
|
||||
app.window.show()
|
||||
app.window.focus()
|
||||
}
|
||||
})
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
console.log(err)
|
||||
app.window.webContents.send('uncaughtException', err)
|
||||
})
|
14
app/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "term",
|
||||
"version": "1.0.0",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"child-process-promise": "^2.1.3",
|
||||
"devtron": "^1.4.0",
|
||||
"electron-config": "^0.2.1",
|
||||
"electron-debug": "^1.0.1",
|
||||
"electron-is-dev": "^0.1.2",
|
||||
"path": "^0.12.7",
|
||||
"pty.js": "https://github.com/Tyriar/pty.js/tarball/c75c2dcb6dcad83b0cb3ef2ae42d0448fb912642"
|
||||
}
|
||||
}
|
15
app/src/app.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
declare var nodeRequire: any
|
||||
interface IPromise {}
|
||||
|
||||
declare interface Window {
|
||||
require: any
|
||||
process: any
|
||||
__dirname: any
|
||||
__platform: any
|
||||
}
|
||||
|
||||
declare var window: Window
|
||||
|
||||
declare interface Console {
|
||||
timeStamp(...args: any[])
|
||||
}
|
60
app/src/app.module.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { BrowserModule } from '@angular/platform-browser'
|
||||
import { HttpModule } from '@angular/http'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { ToasterModule } from 'angular2-toaster'
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { PerfectScrollbarModule } from 'angular2-perfect-scrollbar'
|
||||
import { PerfectScrollbarConfigInterface } from 'angular2-perfect-scrollbar'
|
||||
|
||||
const PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {
|
||||
suppressScrollX: true
|
||||
}
|
||||
|
||||
|
||||
import { ConfigService } from 'services/config'
|
||||
import { ElectronService } from 'services/electron'
|
||||
import { HostAppService } from 'services/hostApp'
|
||||
import { LogService } from 'services/log'
|
||||
import { ModalService } from 'services/modal'
|
||||
import { NotifyService } from 'services/notify'
|
||||
import { QuitterService } from 'services/quitter'
|
||||
import { LocalStorageService } from 'angular2-localstorage/LocalStorageEmitter'
|
||||
|
||||
import { AppComponent } from 'components/app'
|
||||
import { CheckboxComponent } from 'components/checkbox'
|
||||
import { SettingsModalComponent } from 'components/settingsModal'
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HttpModule,
|
||||
FormsModule,
|
||||
ToasterModule,
|
||||
NgbModule.forRoot(),
|
||||
PerfectScrollbarModule.forRoot(PERFECT_SCROLLBAR_CONFIG),
|
||||
],
|
||||
providers: [
|
||||
ConfigService,
|
||||
ElectronService,
|
||||
HostAppService,
|
||||
LogService,
|
||||
ModalService,
|
||||
NotifyService,
|
||||
QuitterService,
|
||||
LocalStorageService,
|
||||
],
|
||||
entryComponents: [
|
||||
SettingsModalComponent,
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
CheckboxComponent,
|
||||
SettingsModalComponent,
|
||||
],
|
||||
bootstrap: [
|
||||
AppComponent
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
132
app/src/components/app.less
Normal file
@@ -0,0 +1,132 @@
|
||||
@import "~bootstrap/less/variables.less";
|
||||
@import "~bootstrap/variables.less";
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
-webkit-user-select: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: @body-bg;
|
||||
}
|
||||
|
||||
|
||||
.navbar {
|
||||
flex: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
-webkit-app-region: drag;
|
||||
margin: 0;
|
||||
|
||||
background: @navbar-default-bg;
|
||||
:host.platform-darwin & {
|
||||
background: fade(@navbar-default-bg, 50%);
|
||||
}
|
||||
|
||||
.navbar-btn-big {
|
||||
margin: 0;
|
||||
padding: 15px 20px 15px;
|
||||
background-color: #333;
|
||||
text-align: left;
|
||||
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&.btn-profile {
|
||||
padding: 7px 14px 7px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 150px;
|
||||
|
||||
:host.platform-darwin & {
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
>img {
|
||||
flex: none;
|
||||
float: left;
|
||||
margin: 2px 10px 4px 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
:host.platform-darwin & {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
>div {
|
||||
flex: auto;
|
||||
|
||||
:host.platform-darwin & {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.username {
|
||||
}
|
||||
|
||||
.settings {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-close {
|
||||
font-size: 38px;
|
||||
padding: 1px 13px 2px;
|
||||
line-height: 47px;
|
||||
|
||||
:host.platform-darwin & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.navbar-btn-big:hover {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
button.navbar-btn-big:active {
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
|
||||
.navbar-brand {
|
||||
padding: 11px 15px;
|
||||
width: 150px;
|
||||
|
||||
:host.platform-darwin & {
|
||||
display: block;
|
||||
margin: auto;
|
||||
float: none;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[scrollable] {
|
||||
width: 100vw;
|
||||
flex: auto;
|
||||
display: flex;
|
||||
|
||||
.nano {
|
||||
flex: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
display: block;
|
||||
flex: none;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
perfect-scrollbar {
|
||||
flex: auto;
|
||||
min-height: 0;
|
||||
}
|
19
app/src/components/app.pug
Normal file
@@ -0,0 +1,19 @@
|
||||
div.navbar.navbar-default.draggable
|
||||
button.btn.btn-default.navbar-btn.navbar-btn-big.btn-close.pull-right((click)='hide()', title='Hide')
|
||||
| ×
|
||||
button.btn.btn-default.navbar-btn.navbar-btn-big.pull-right((click)='showSettings()', title='Settings')
|
||||
i.fa.fa-cog
|
||||
div.navbar-brand
|
||||
img.logo(src=require("img/logo.svg"))
|
||||
|
||||
perfect-scrollbar
|
||||
div.container
|
||||
div#term(style='width: 300px; height: 300px;')
|
||||
|
||||
|
||||
footer
|
||||
|
||||
toaster-container([toasterconfig]="toasterconfig")
|
||||
template(ngbModalContainer)
|
||||
|
||||
div.window-resizer.window-resizer-tl
|
87
app/src/components/app.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Component, ElementRef } from '@angular/core'
|
||||
import { ModalService } from 'services/modal'
|
||||
import { ElectronService } from 'services/electron'
|
||||
import { HostAppService } from 'services/hostApp'
|
||||
import { LogService } from 'services/log'
|
||||
import { QuitterService } from 'services/quitter'
|
||||
import { ToasterConfig } from 'angular2-toaster'
|
||||
|
||||
import { SettingsModalComponent } from 'components/settingsModal'
|
||||
|
||||
import 'angular2-toaster/lib/toaster.css'
|
||||
import 'global.less'
|
||||
|
||||
const hterm = require('hterm-commonjs')
|
||||
var pty = require('pty.js');
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
template: require('./app.pug'),
|
||||
styles: [require('./app.less')],
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(
|
||||
private hostApp: HostAppService,
|
||||
private modal: ModalService,
|
||||
private electron: ElectronService,
|
||||
element: ElementRef,
|
||||
log: LogService,
|
||||
_quitter: QuitterService,
|
||||
) {
|
||||
console.timeStamp('AppComponent ctor')
|
||||
|
||||
let logger = log.create('main')
|
||||
logger.info('ELEMENTS client', electron.app.getVersion())
|
||||
|
||||
this.toasterConfig = new ToasterConfig({
|
||||
mouseoverTimerStop: true,
|
||||
preventDuplicates: true,
|
||||
timeout: 4000,
|
||||
})
|
||||
}
|
||||
|
||||
toasterConfig: ToasterConfig
|
||||
|
||||
ngOnInit () {
|
||||
let io
|
||||
hterm.hterm.defaultStorage = new hterm.lib.Storage.Memory()
|
||||
let t = new hterm.hterm.Terminal()
|
||||
t.onTerminalReady = function() {
|
||||
t.installKeyboard()
|
||||
io = t.io.push();
|
||||
//#t.decorate(element.nativeElement);
|
||||
|
||||
var cmd = pty.spawn('bash', [], {
|
||||
name: 'xterm-color',
|
||||
cols: 80,
|
||||
rows: 30,
|
||||
cwd: process.env.HOME,
|
||||
env: process.env
|
||||
});
|
||||
cmd.on('data', function(data) {
|
||||
io.writeUTF8(data);
|
||||
});
|
||||
|
||||
|
||||
io.onVTKeystroke = function(str) {
|
||||
cmd.write(str)
|
||||
};
|
||||
io.sendString = function(str) {
|
||||
cmd.write(str)
|
||||
};
|
||||
io.onTerminalResize = function(columns, rows) {
|
||||
cmd.resize(columns, rows)
|
||||
};
|
||||
};
|
||||
console.log(document.querySelector('#term'))
|
||||
t.decorate(document.querySelector('#term'));
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
}
|
||||
|
||||
showSettings() {
|
||||
this.modal.open(SettingsModalComponent)
|
||||
}
|
||||
}
|
53
app/src/components/checkbox.less
Normal file
@@ -0,0 +1,53 @@
|
||||
:host {
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
background: rgba(255,255,255,.05);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: rgba(255,255,255,.1);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
flex: none;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1px;
|
||||
transition: 0.25s opacity;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
i.on {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
i.on, &.active i.off {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
i.off, &.active i.on {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
flex: auto;
|
||||
}
|
||||
}
|
4
app/src/components/checkbox.pug
Normal file
@@ -0,0 +1,4 @@
|
||||
.icon((click)='click()', tabindex='0', [class.active]='model', (keyup.space)='click()')
|
||||
i.fa.fa-square-o.off
|
||||
i.fa.fa-check-square.on
|
||||
.text((click)='click()') {{text}}
|
25
app/src/components/checkbox.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NgZone, Component, Input, Output, EventEmitter } from '@angular/core'
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'checkbox',
|
||||
template: require('./checkbox.pug'),
|
||||
styles: [require('./checkbox.less')]
|
||||
})
|
||||
export class CheckboxComponent {
|
||||
public click() {
|
||||
NgZone.assertInAngularZone()
|
||||
if (this.disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
this.model = !this.model
|
||||
this.modelChange.emit(this.model)
|
||||
}
|
||||
|
||||
@Input() model: boolean
|
||||
@Output() modelChange = new EventEmitter()
|
||||
@Input() disabled: boolean
|
||||
|
||||
@Input() text: string
|
||||
}
|
69
app/src/components/settingsModal.less
Normal file
@@ -0,0 +1,69 @@
|
||||
:host {
|
||||
>.modal-body {
|
||||
padding: 0 0 20px !important;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.version-info {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
padding: 10px 0;
|
||||
-webkit-user-select: text;
|
||||
transition: .25s all;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.status-line {
|
||||
display: flex;
|
||||
padding: 5px 10px;
|
||||
|
||||
&.clickable {
|
||||
&:hover {
|
||||
background: rgba(0,0,0,.5);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex: none;
|
||||
padding: 7px 10px 0 0px;
|
||||
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
i {
|
||||
width: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: auto;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
|
||||
.title {
|
||||
flex: none;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: auto;
|
||||
font-size: 16px;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
112
app/src/components/settingsModal.pug
Normal file
@@ -0,0 +1,112 @@
|
||||
div.modal-body
|
||||
ngb-tabset(type='tabs nav-justified')
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
i.fa.fa-cog
|
||||
| General
|
||||
template(ngbTabContent)
|
||||
.status-line.clickable(*ngIf='connectionHost', (click)='openWeb()')
|
||||
.icon
|
||||
i.fa.fa-rss.fa-2x.fa-live
|
||||
.main
|
||||
.title Server
|
||||
.value {{connectionHost}}
|
||||
|
||||
.status-line(*ngIf='!connectionHost')
|
||||
.icon
|
||||
i.fa.fa-rss.fa-2x
|
||||
.main
|
||||
.title Server
|
||||
.value Not connected
|
||||
|
||||
.status-line(*ngIf='!userInfo?.user')
|
||||
.icon
|
||||
img(src=require("img/user.png"))
|
||||
.main
|
||||
.title Login
|
||||
.value Not logged in
|
||||
|
||||
.status-line(*ngIf='userInfo?.user')
|
||||
.icon
|
||||
img([src]='userInfo.user.avatar || "../../assets/img/user.png"')
|
||||
.main
|
||||
.title Login
|
||||
.value {{ userInfo.user.full_name || userInfo.user.username }}
|
||||
|
||||
br
|
||||
|
||||
div.form-group
|
||||
checkbox(text='Remember connected workspaces', '[(model)]'='config.store.rememberWorkspaces')
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
i.fa.fa-wrench
|
||||
| Advanced
|
||||
template(ngbTabContent)
|
||||
div.form-group(*ngIf='isWindows || isLinux')
|
||||
div.input-group
|
||||
input.form-control(type='text', placeholder='SNFS projects folder', '[(ngModel)]'='config.store.snfsPath')
|
||||
div.input-group-btn
|
||||
button.btn.btn-default((click)='selectSNFSPath()')
|
||||
i.fa.fa-folder-open
|
||||
div.form-group(*ngIf='isWindows')
|
||||
label First drive letter to use
|
||||
select.form-control('[(ngModel)]'='config.store.firstDrive')
|
||||
option(*ngFor='let x of drives', value='{{x}}') {{x}}:
|
||||
div.form-group(*ngIf='isMac')
|
||||
label Extra NFS options
|
||||
input.form-control(type='text', '[(ngModel)]'='config.store.extraNFSOptions')
|
||||
div.form-group(*ngIf='isMac')
|
||||
label Extra AFP options
|
||||
input.form-control(type='text', '[(ngModel)]'='config.store.extraAFPOptions')
|
||||
div.form-group(*ngIf='isMac')
|
||||
label Extra SMB options
|
||||
input.form-control(type='text', '[(ngModel)]'='config.store.extraSMBOptions')
|
||||
div.form-group(*ngIf='isLinux')
|
||||
label Extra NFS options
|
||||
input.form-control(type='text', '[(ngModel)]'='config.store.extraLinuxNFSOptions')
|
||||
div.form-group(*ngIf='isLinux')
|
||||
label Extra SMB options
|
||||
input.form-control(type='text', '[(ngModel)]'='config.store.extraLinuxSMBOptions')
|
||||
|
||||
ngb-tab(*ngIf="apiServer.authorizedKeysStore.length > 0")
|
||||
template(ngbTabTitle)
|
||||
i.fa.fa-plug
|
||||
| Apps
|
||||
template(ngbTabContent)
|
||||
.list-group
|
||||
.list-group-item(*ngFor="let key of apiServer.authorizedKeysStore")
|
||||
button.btn.btn-default((click)='apiServer.deauthorizeKey(key)')
|
||||
i.fa.fa-times
|
||||
span Disconnect this app
|
||||
div {{key.name}}
|
||||
|
||||
ngb-tab
|
||||
template(ngbTabTitle)
|
||||
i.fa.fa-info-circle
|
||||
| About
|
||||
template(ngbTabContent)
|
||||
.form-group
|
||||
h1 ELEMENTS Client
|
||||
div syslink GmbH © {{year}}
|
||||
|
||||
.form-group
|
||||
label Version
|
||||
div {{version}}
|
||||
|
||||
.form-group
|
||||
button.btn.btn-default((click)='copyDiagnostics()') Copy diagnostic info
|
||||
|
||||
div.modal-footer
|
||||
div.btn-group.btn-group-justified
|
||||
a.btn.btn-default((click)='logout()', *ngIf='elementsClient.userInfo')
|
||||
i.fa.fa-fw.fa-arrow-left
|
||||
br
|
||||
| Log out
|
||||
a.btn.btn-default((click)='quit()')
|
||||
i.fa.fa-fw.fa-power-off
|
||||
br
|
||||
| Quit
|
||||
a.btn.btn-default((click)='close()')
|
||||
i.fa.fa-fw.fa-check
|
||||
br
|
||||
| Done
|
44
app/src/components/settingsModal.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Component } from '@angular/core'
|
||||
import { ElectronService } from 'services/electron'
|
||||
import { HostAppService, PLATFORM_WINDOWS, PLATFORM_LINUX, PLATFORM_MAC } from 'services/hostApp'
|
||||
import { ConfigService } from 'services/config'
|
||||
import { QuitterService } from 'services/quitter'
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
import * as os from 'os'
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'settings-modal',
|
||||
template: require('./settingsModal.pug'),
|
||||
styles: [require('./settingsModal.less')],
|
||||
})
|
||||
export class SettingsModalComponent {
|
||||
constructor(
|
||||
private modalInstance: NgbActiveModal,
|
||||
private hostApp: HostAppService,
|
||||
private electron: ElectronService,
|
||||
private quitter: QuitterService,
|
||||
public config: ConfigService,
|
||||
) {
|
||||
this.isWindows = hostApp.platform == PLATFORM_WINDOWS
|
||||
this.isMac = hostApp.platform == PLATFORM_MAC
|
||||
this.isLinux = hostApp.platform == PLATFORM_LINUX
|
||||
this.version = electron.app.getVersion()
|
||||
this.year = new Date().getFullYear()
|
||||
}
|
||||
|
||||
isWindows: boolean
|
||||
isMac: boolean
|
||||
isLinux: boolean
|
||||
year: number
|
||||
version: string
|
||||
|
||||
ngOnDestroy() {
|
||||
this.config.save()
|
||||
}
|
||||
|
||||
close() {
|
||||
this.modalInstance.close()
|
||||
}
|
||||
}
|
6
app/src/entry.preload.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'source-sans-pro'
|
||||
|
||||
import 'font-awesome/css/font-awesome.css'
|
||||
|
||||
import '../assets/toaster-custom.less'
|
||||
import '../assets/bootstrap/bootstrap.less'
|
25
app/src/entry.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
console.timeStamp('entry point')
|
||||
|
||||
import 'core-js'
|
||||
import 'zone.js/dist/zone.js'
|
||||
import 'core-js/es7/reflect'
|
||||
import 'jquery'
|
||||
|
||||
// Always land on the start view
|
||||
location.hash = ''
|
||||
|
||||
import { AppModule } from 'app.module'
|
||||
import { enableProdMode } from '@angular/core'
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
|
||||
|
||||
if (nodeRequire('electron-is-dev')) {
|
||||
console.warn('Running in debug mode')
|
||||
} else {
|
||||
enableProdMode()
|
||||
}
|
||||
|
||||
console.timeStamp('angular bootstrap started')
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
|
||||
|
||||
process.emitWarning = function () { console.log(arguments) }
|
176
app/src/global.less
Normal file
@@ -0,0 +1,176 @@
|
||||
@import "~bootstrap/include.less";
|
||||
|
||||
|
||||
html.platform-win32 {
|
||||
body.focused {
|
||||
//border: 1px solid #9c9c00 !important;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
border: 1px solid #131313;
|
||||
transition: 0.5s border;
|
||||
overflow: hidden;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.no-drag, a, button, checkbox, .form-control, #toast-container {
|
||||
-webkit-app-region: no-drag;
|
||||
outline: 0 !important;
|
||||
|
||||
* {
|
||||
outline: 0 !important;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control {
|
||||
-webkit-user-select: initial;
|
||||
}
|
||||
|
||||
.window-resizer {
|
||||
-webkit-app-region: no-drag;
|
||||
position: fixed;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.window-resizer-tl {
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.no-wrap {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.word-wrap {
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
#toast-container.toast-top-full-width {
|
||||
width: 100%;
|
||||
top: 50px;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 2px rgba(0,0,0,.75);
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin: 20px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50px;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.fa-live {
|
||||
color: #7aff00;
|
||||
|
||||
.list-group-item &.fa-2x {
|
||||
top: 10px;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.otp-input {
|
||||
height: 50px;
|
||||
|
||||
input {
|
||||
height: 50px;
|
||||
font-size: 41px;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngb-modal-backdrop {
|
||||
// ngbmodalwindow has its own, properly animated backdrop
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
ngb-modal-window.fade.in {
|
||||
&.out {
|
||||
opacity: 0;
|
||||
|
||||
.modal-dialog {
|
||||
transform: translate(0, -25%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngb-tabset {
|
||||
>ul.nav-tabs.nav-justified {
|
||||
border-bottom: none;
|
||||
margin-bottom: 10px;
|
||||
background: rgba(0,0,0,.25);
|
||||
|
||||
.nav-item .nav-link {
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
&.active {
|
||||
background: rgba(0,0,0,.5);
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
|
||||
i {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
>.tab-content {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn {
|
||||
i + * {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
margin: none;
|
||||
|
||||
> .btn {
|
||||
float: right;
|
||||
margin: -7px -11px 0 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0,0,0,.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ps-container.ps-in-scrolling>.ps-scrollbar-y-rail,
|
||||
.ps-container:hover>.ps-scrollbar-y-rail:hover {
|
||||
background: rgba(0,0,0,.5) !important;
|
||||
}
|
88
app/src/services/config.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HostAppService, PLATFORM_MAC, PLATFORM_WINDOWS } from 'services/hostApp'
|
||||
const Config = nodeRequire('electron-config')
|
||||
const exec = nodeRequire('child-process-promise').exec
|
||||
import * as fs from 'fs'
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
constructor(
|
||||
private hostApp: HostAppService,
|
||||
) {
|
||||
this.config = new Config({name: 'config'})
|
||||
this.load()
|
||||
}
|
||||
|
||||
private config: any
|
||||
private store: any
|
||||
|
||||
migrate() {
|
||||
if (!this.has('migrated')) {
|
||||
if (this.hostApp.platform == PLATFORM_WINDOWS) {
|
||||
let configPath = `${this.hostApp.getPath('documents')}\\.elements.conf`
|
||||
let config = null
|
||||
try {
|
||||
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
||||
console.log('Migrating configuration:', config)
|
||||
this.set('host', config.Hostname)
|
||||
this.set('username', config.Username)
|
||||
this.set('firstDrive', config.FirstDrive)
|
||||
} catch (err) {
|
||||
console.error('Could not migrate the config:', err)
|
||||
}
|
||||
this.set('migrated', 1)
|
||||
this.save()
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (this.hostApp.platform == PLATFORM_MAC) {
|
||||
return Promise.all([
|
||||
exec('defaults read ~/Library/Preferences/com.syslink.Elements.plist connection_host').then((result) => {
|
||||
this.set('host', result.stdout.trim())
|
||||
}),
|
||||
exec('defaults read ~/Library/Preferences/com.syslink.Elements.plist connection_username').then((result) => {
|
||||
this.set('username', result.stdout.trim())
|
||||
}),
|
||||
]).then(() => {
|
||||
this.set('migrated', 1)
|
||||
this.save()
|
||||
}).catch((err) => {
|
||||
console.error('Could not migrate the config:', err)
|
||||
this.set('migrated', 1)
|
||||
this.save()
|
||||
})
|
||||
}
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
set(key: string, value: any) {
|
||||
this.save()
|
||||
this.config.set(key, value)
|
||||
this.load()
|
||||
}
|
||||
|
||||
get(key: string): any {
|
||||
this.save()
|
||||
return this.config.get(key)
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
this.save()
|
||||
return this.config.has(key)
|
||||
}
|
||||
|
||||
delete(key: string) {
|
||||
this.save()
|
||||
this.config.delete(key)
|
||||
this.load()
|
||||
}
|
||||
|
||||
load() {
|
||||
this.store = this.config.store
|
||||
}
|
||||
|
||||
save() {
|
||||
this.config.store = this.store
|
||||
}
|
||||
}
|
39
app/src/services/electron.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ElectronService {
|
||||
constructor() {
|
||||
if (process.env.TEST_ENV) {
|
||||
this.initTest()
|
||||
} else {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
this.electron = require('electron')
|
||||
this.remoteElectron = this.remoteRequire('electron')
|
||||
this.app = this.remoteElectron.app
|
||||
this.dialog = this.remoteElectron.dialog
|
||||
this.shell = this.electron.shell
|
||||
this.clipboard = this.electron.clipboard
|
||||
this.ipcRenderer = this.electron.ipcRenderer
|
||||
}
|
||||
|
||||
initTest() {
|
||||
;
|
||||
}
|
||||
|
||||
remoteRequire(name: string): any {
|
||||
return this.electron.remote.require(name)
|
||||
}
|
||||
|
||||
app: any
|
||||
ipcRenderer: any
|
||||
shell: any
|
||||
dialog: any
|
||||
clipboard: any
|
||||
private electron: any
|
||||
private remoteElectron: any
|
||||
}
|
69
app/src/services/hostApp.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Injectable, NgZone, EventEmitter } from '@angular/core'
|
||||
import { ElectronService } from 'services/electron'
|
||||
import { Logger, LogService } from 'services/log'
|
||||
|
||||
export const PLATFORM_WINDOWS = 'win32'
|
||||
export const PLATFORM_MAC = 'darwin'
|
||||
export const PLATFORM_LINUX = 'linux'
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class HostAppService {
|
||||
constructor(
|
||||
private zone: NgZone,
|
||||
private electron: ElectronService,
|
||||
log: LogService,
|
||||
) {
|
||||
this.platform = require('os').platform()
|
||||
this.logger = log.create('hostApp')
|
||||
|
||||
electron.ipcRenderer.on('host:quit-request', () => this.zone.run(() => this.quitRequested.emit()))
|
||||
|
||||
electron.ipcRenderer.on('uncaughtException', function(err) {
|
||||
console.error('Unhandled exception:', err)
|
||||
})
|
||||
|
||||
this.ready.subscribe(() => {
|
||||
electron.ipcRenderer.send('app:ready')
|
||||
})
|
||||
}
|
||||
|
||||
platform: string;
|
||||
quitRequested = new EventEmitter<any>()
|
||||
ready = new EventEmitter<any>()
|
||||
|
||||
private logger: Logger;
|
||||
|
||||
getWindow() {
|
||||
return this.electron.app.window
|
||||
}
|
||||
|
||||
getShell() {
|
||||
return this.electron.shell
|
||||
}
|
||||
|
||||
getAppPath() {
|
||||
return this.electron.app.getAppPath()
|
||||
}
|
||||
|
||||
getPath(type: string) {
|
||||
return this.electron.app.getPath(type)
|
||||
}
|
||||
|
||||
openDevTools() {
|
||||
this.electron.app.webContents.openDevTools()
|
||||
}
|
||||
|
||||
setWindowCloseable(flag: boolean) {
|
||||
this.electron.ipcRenderer.send('window-closeable', flag)
|
||||
}
|
||||
|
||||
focusWindow() {
|
||||
this.electron.ipcRenderer.send('window-focus')
|
||||
}
|
||||
|
||||
quit() {
|
||||
this.logger.info('Quitting')
|
||||
this.electron.app.quit()
|
||||
}
|
||||
}
|
25
app/src/services/log.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
|
||||
|
||||
export class Logger {
|
||||
constructor(
|
||||
private name: string,
|
||||
) {}
|
||||
|
||||
log(level: string, ...args: any[]) {
|
||||
args.splice(0, 0, this.name + ':')
|
||||
console[level](...args)
|
||||
}
|
||||
|
||||
debug(...args: any[]) { this.log('debug', ...args) }
|
||||
info(...args: any[]) { this.log('info', ...args) }
|
||||
warn(...args: any[]) { this.log('warn', ...args) }
|
||||
error(...args: any[]) { this.log('error', ...args) }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class LogService {
|
||||
create (name: string): Logger {
|
||||
return new Logger(name)
|
||||
}
|
||||
}
|
29
app/src/services/modal.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ModalService {
|
||||
constructor(
|
||||
private zone: NgZone,
|
||||
private ngbModal: NgbModal,
|
||||
) {}
|
||||
|
||||
open(content: any, config?: any) {
|
||||
config = config || {}
|
||||
config.windowClass = 'out'
|
||||
let modal = this.ngbModal.open(content, config)
|
||||
|
||||
let fx = (<any>modal)._removeModalElements.bind(modal);
|
||||
|
||||
(<any>modal)._removeModalElements = () => {
|
||||
(<any>modal)._windowCmptRef.instance.windowClass = 'out'
|
||||
setTimeout(() => fx(), 500)
|
||||
}
|
||||
setTimeout(() => {
|
||||
(<any>modal)._windowCmptRef.instance.windowClass = ''
|
||||
}, 1)
|
||||
|
||||
return modal
|
||||
}
|
||||
}
|
48
app/src/services/notify.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { ToasterService } from 'angular2-toaster'
|
||||
import { LogService } from 'services/log'
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class NotifyService {
|
||||
constructor(
|
||||
private toaster: ToasterService,
|
||||
private log: LogService,
|
||||
) {}
|
||||
|
||||
pop(options) {
|
||||
this.toaster.pop(options)
|
||||
}
|
||||
|
||||
info(title: string, body: string = null) {
|
||||
return this.pop({
|
||||
type: 'info',
|
||||
title, body,
|
||||
timeout: 4000,
|
||||
})
|
||||
}
|
||||
|
||||
success(title: string, body: string = null) {
|
||||
return this.pop({
|
||||
type: 'success',
|
||||
title, body,
|
||||
timeout: 4000,
|
||||
})
|
||||
}
|
||||
|
||||
warning(title: string, body: string = null) {
|
||||
return this.pop({
|
||||
type: 'warning',
|
||||
title, body,
|
||||
timeout: 4000,
|
||||
})
|
||||
}
|
||||
|
||||
error(title: string, body: string = null) {
|
||||
return this.pop({
|
||||
type: 'error',
|
||||
title, body,
|
||||
timeout: 4000,
|
||||
})
|
||||
}
|
||||
}
|
21
app/src/services/quitter.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Injectable } from '@angular/core'
|
||||
import { HostAppService } from 'services/hostApp'
|
||||
import { ElectronService } from 'services/electron'
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class QuitterService {
|
||||
constructor(
|
||||
private electron: ElectronService,
|
||||
private hostApp: HostAppService,
|
||||
) {
|
||||
hostApp.quitRequested.subscribe(() => {
|
||||
this.quit()
|
||||
})
|
||||
}
|
||||
|
||||
quit() {
|
||||
this.hostApp.setWindowCloseable(true)
|
||||
this.hostApp.quit()
|
||||
}
|
||||
}
|