diff --git a/package.json b/package.json index b99741b07c9..bf6c6e6b542 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "ampersand-state", "scout-server", "glob", - "electron-squirrel-startup" + "electron-squirrel-startup", + "ipc" ] }, "fonts": [ diff --git a/src/app.js b/src/app.js index c0f490b1436..c37e49c024c 100644 --- a/src/app.js +++ b/src/app.js @@ -223,13 +223,6 @@ var Application = View.extend({ event.preventDefault(); this.router.history.navigate(pathname); } - }, - sendMessage: function(msg) { - ipc.send('message', msg); - }, - onMessageReceived: function(msg) { - debug('message received from main process:', msg); - this.trigger(msg); } }); @@ -262,6 +255,13 @@ app.extend({ isFeatureEnabled: function(id) { return FEATURES[id] === true; }, + sendMessage: function(msg) { + ipc.send('message', msg); + }, + onMessageReceived: function(msg) { + debug('message received from main process:', msg); + this.trigger(msg); + }, init: function() { domReady(function() { state.render(); @@ -298,7 +298,7 @@ app.extend({ }); }); // set up ipc - ipc.on('message', state.onMessageReceived.bind(this)); + ipc.on('message', this.onMessageReceived.bind(this)); }, navigate: state.navigate.bind(state) }); diff --git a/src/collection-stats/index.js b/src/collection-stats/index.js index 25ec2c9680c..82648358156 100644 --- a/src/collection-stats/index.js +++ b/src/collection-stats/index.js @@ -34,7 +34,7 @@ var CollectionStatsView = AmpersandView.extend({ format: function(propertyName) { var value = this.model.get(propertyName) || 0; var precision = value <= 1000 ? '0' : '0.0'; - var format = propertyName.indexOf('_size') > -1 ? 'b' : 'a'; + var format = propertyName.indexOf('_size') > -1 ? ' b' : 'a'; return numeral(value).format(precision + format); }, derived: { diff --git a/src/collection-stats/index.less b/src/collection-stats/index.less index ae54b16c8b3..3e3bc22712d 100644 --- a/src/collection-stats/index.less +++ b/src/collection-stats/index.less @@ -4,42 +4,59 @@ position: absolute; right: 20px; text-align: right; - min-width: 450px; + min-width: 520px; } .collection-stats { display: inline-block; + position: relative; font-weight: 200; list-style: none; - padding: 0 30px 0 0; - margin: 11px 30px 0 0; - border-right: 1px solid @gray7; + padding: 0 20px 0 0; + margin: 0 20px 0 0; + + &::after { + content: ''; + display: block; + position: absolute; + right: 0; + top: 2px; + width: 1px; + height: 24px; + background: @gray7; + } &:last-child { - border-right: none; padding: 0; margin: 11px 0 0 0; + + &::after { + display: none; + } } } .collection-stats-item { display: inline-block; - margin-right: 10px; + margin-right: 12px; &:last-child { margin-right: 0; } } .collection-stats-primary-label { + display: inline-block; text-transform: uppercase; - letter-spacing: 1px; font-size: 12px; color: @gray3; + margin-right: 5px; } .collection-stats-primary-value { - font-size: 36px; - line-height: 36px; + display: inline-block; + font-size: 24px; + line-height: 24px; } .collection-stats-label { font-size: 11px; + line-height: 11px; color: @gray3; } .collection-stats-value { diff --git a/src/electron/config/windows.js b/src/electron/config/windows.js index 243ddf06418..e00d5e568c2 100644 --- a/src/electron/config/windows.js +++ b/src/electron/config/windows.js @@ -5,8 +5,8 @@ /** * The outer dimensions to use for new windows. */ -exports.DEFAULT_WIDTH = 1024; -exports.DEFAULT_HEIGHT = 700; +exports.DEFAULT_WIDTH = 1280; +exports.DEFAULT_HEIGHT = 800; /** * The outer window dimensions to use for new dialog diff --git a/src/electron/index.js b/src/electron/index.js index 5e15a4a802b..b57945ee1d5 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -4,11 +4,11 @@ // will be created in `/tmp/Compass` // (`~\AppData\Local\Temp\Compass` on Windows). require('./crash-reporter'); +var debug = require('debug')('electron:index'); if (!require('electron-squirrel-startup')) { var app = require('app'); var serverctl = require('./scout-server-ctl'); - var debug = require('debug')('scout-electron'); app.on('window-all-closed', function() { debug('All windows closed. Quitting app.'); diff --git a/src/electron/window-manager.js b/src/electron/window-manager.js index 51c81f649c7..53036db1cb5 100644 --- a/src/electron/window-manager.js +++ b/src/electron/window-manager.js @@ -128,3 +128,9 @@ app.on('show connect dialog', function(opts) { app.on('ready', function() { app.emit('show connect dialog'); }); + +var ipc = require('ipc'); +ipc.on('message', function(event, msg) { + debug('message received in main process', msg); + app.emit(msg); +}); diff --git a/src/field-list/field.jade b/src/field-list/field.jade index eff820e8c5d..bae68f47ec9 100644 --- a/src/field-list/field.jade +++ b/src/field-list/field.jade @@ -1,5 +1,4 @@ .schema-field.schema-field-basic - hr.field-divider .row .col-sm-4 .schema-field-name @@ -10,3 +9,4 @@ div(data-hook='minichart-container') div(data-hook='fields-subview') div(data-hook='arrayfields-subview') + hr.field-divider \ No newline at end of file diff --git a/src/home/collection.jade b/src/home/collection.jade index 0ada7fc24b2..fe4a41e672d 100644 --- a/src/home/collection.jade +++ b/src/home/collection.jade @@ -1,5 +1,4 @@ .collection-view.clearfix - div(data-hook='sampling-message-subview') div.modal.fade(tabindex='-1', role='dialog', arialabelledby='Share Schema Confirmation', data-hook='share-schema-confirmation') div.modal-dialog.modal-sm .modal-content diff --git a/src/home/collection.js b/src/home/collection.js index 69cdac8bab6..25bbe6f3166 100644 --- a/src/home/collection.js +++ b/src/home/collection.js @@ -3,7 +3,6 @@ var CollectionStatsView = require('../collection-stats'); var FieldListView = require('../field-list'); var DocumentListView = require('../document-list'); var RefineBarView = require('../refine-view'); -var SamplingMessageView = require('../sampling-message'); var MongoDBCollection = require('../models/mongodb-collection'); var SampledSchema = require('../models/sampled-schema'); var app = require('ampersand-app'); @@ -161,15 +160,6 @@ var MongoDBCollectionView = View.extend({ collection: this.schema.documents }); } - }, - sampling_message: { - hook: 'sampling-message-subview', - prepareView: function(el) { - return new SamplingMessageView({ - el: el, - parent: this - }); - } } } }); diff --git a/src/home/index.jade b/src/home/index.jade index ed324f00970..45183279ed9 100644 --- a/src/home/index.jade +++ b/src/home/index.jade @@ -4,4 +4,9 @@ div.report-zero-state(data-hook='report-zero-state') img(src='images/zero-state-arrow-collections.png', width="110") span Choose a collection to analyze + div.no-collections-zero-state(data-hook='no-collections-zero-state') + span + |The MongoDB instance you are connected to does not contain any collections.  + a.show-connect-window Connect to another instance + |. div(data-hook='sidebar') diff --git a/src/home/index.js b/src/home/index.js index 4e00d7a9363..c075d48f55c 100644 --- a/src/home/index.js +++ b/src/home/index.js @@ -16,18 +16,30 @@ var HomeView = View.extend({ allowNull: true, default: null }, - showZeroState: { + showDefaultZeroState: { type: 'boolean', - default: true + default: false + }, + showNoCollectionsZeroState: { + type: 'boolean', + default: false } }, bindings: { - showZeroState: { + showDefaultZeroState: { hook: 'report-zero-state', type: 'booleanClass', no: 'hidden' + }, + showNoCollectionsZeroState: { + hook: 'no-collections-zero-state', + type: 'booleanClass', + no: 'hidden' } }, + events: { + 'click a.show-connect-window': 'onClickShowConnectWindow' + }, initialize: function() { this.listenTo(app.instance, 'sync', this.onInstanceFetched); this.listenTo(app.connection, 'change:name', this.updateTitle); @@ -36,6 +48,11 @@ var HomeView = View.extend({ app.instance.fetch(); }, onInstanceFetched: function() { + if (app.instance.collections.length === 0) { + this.showNoCollectionsZeroState = true; + } else { + this.showDefaultZeroState = true; + } if (!this.ns) { app.instance.collections.unselectAll(); } else { @@ -58,11 +75,16 @@ var HomeView = View.extend({ this.ns = model.getId(); this.updateTitle(model); - this.showZeroState = false; + this.showDefaultZeroState = false; app.navigate(format('schema/%s', model.getId()), { silent: true }); }, + onClickShowConnectWindow: function() { + // code to close current connection window and open connect dialog + app.sendMessage('show connect dialog'); + window.close(); + }, template: require('./index.jade'), subviews: { _collection: { diff --git a/src/home/index.less b/src/home/index.less index 83094a2a833..28f122f2940 100644 --- a/src/home/index.less +++ b/src/home/index.less @@ -53,7 +53,7 @@ } .report-zero-state { - margin: 124px 0 0 20px; + margin: 190px 0 0 20px; img { margin-bottom: 7px; @@ -65,13 +65,28 @@ margin-left: 15px; } } +.no-collections-zero-state { + text-align: center; + margin-top: 200px; + padding: 50px 75px; + font-size: 18px; + color: @gray4; + font-weight: 200; + + a { + cursor: pointer; + } +} .collection-view { header { - padding: 12px 25px; + padding: 12px 25px 0; position: relative; z-index: 1; } + h1 { + margin-top: 0; + } .column-container { display: flex; overflow: hidden; diff --git a/src/models/sampled-schema.js b/src/models/sampled-schema.js index d45027c6078..93d84984b76 100644 --- a/src/models/sampled-schema.js +++ b/src/models/sampled-schema.js @@ -6,8 +6,24 @@ var filterableMixin = require('ampersand-collection-filterable'); var SampledDocumentCollection = require('./sampled-document-collection'); var es = require('event-stream'); var debug = require('debug')('scout:models:schema'); +var debugMetrics = require('debug')('scout:metrics'); var app = require('ampersand-app'); +// @todo: stub for metrics module. currently just logs debug messages with scout:metrics marker +var metrics = { + track: function(label, err, obj) { + if (!obj) { + obj = err; + err = null; + } + if (err) { + debugMetrics(label, err, obj); + } else { + debugMetrics(label, obj); + } + } +}; + /** * wrapping mongodb-schema's FieldCollection with a filterable mixin */ @@ -25,6 +41,8 @@ module.exports = Schema.extend({ } }, session: { + // total number of documents counted under the given query + total: 'number', is_fetching: { type: 'boolean', default: false @@ -96,43 +114,97 @@ module.exports = Schema.extend({ var model = this; wrapError(this, options); - var parse = function(doc, cb) { - model.parse(doc); - cb(null, doc); + var success = options.success; + options.success = function(resp) { + if (success) { + success(model, resp, options); + } + model.trigger('sync'); }; + var start = new Date(); + var timeAtFirstDoc; + var erroredOnDocs = []; - var docs = []; + // No results found + var onEmpty = function() { + model.is_fetching = false; + model.documents.reset(); + model.documents.trigger('sync'); + options.success({}); + }; + var docs = []; var addToDocuments = function(doc, cb) { docs.push(doc); cb(); }; + var parse = function(doc, cb) { + if (!timeAtFirstDoc) { + timeAtFirstDoc = new Date(); + } + try { + model.parse(doc); + } catch (err) { + erroredOnDocs.push(doc); + metrics.track('Schema: Error: Parse', err, { + doc: doc, + schema: model.serialize() + }); + } + cb(null, doc); + }; + var onEnd = function(err) { model.is_fetching = false; if (err) { + metrics.track('Schema: Error: End', err, { + schema: model + }); return options.error(model, err); } model.documents.reset(docs); model.documents.trigger('sync'); - options.success({}); - }; - var success = options.success; - options.success = function(resp) { - if (success) { - success(model, resp, options); - } - model.trigger('sync'); + // @note (imlucas): Any other metrics? Feedback on `Schema *`? + var totalTime = new Date() - start; + var timeToFirstDoc = timeAtFirstDoc - start; + + metrics.track('Schema: Complete', { + Duration: totalTime, + 'Total Document Count': model.total, + 'Sample Size': model.documents.length, + 'Errored Document Count': erroredOnDocs.length, + 'Time to First Doc': timeToFirstDoc, + 'Average Time Per Doc': (totalTime - timeToFirstDoc) / model.documents.length + // 'Schema Height': model.height, // # of top level keys + // 'Schema Width': model.width, // max nesting depth + // 'Schema Sparsity': model.sparsity // lots of fields missing or consistent + }); + options.success({}); }; model.trigger('request', {}, {}, options); - debug('creating sample stream'); - app.client.sample(model.ns, options) - .pipe(es.map(parse)) - .pipe(es.map(addToDocuments)) - .pipe(es.wait(onEnd)); + app.client.count(model.ns, options, function(err, count) { + if (err) { + metrics.track('Schema: Error: Count', err, { + schema: model + }); + return options.error(model, err); + } + debug('options', options, 'count', count.count); + model.total = count.count; + if (model.total === 0) { + return onEmpty(); + } + + debug('creating sample stream'); + app.client.sample(model.ns, options) + .pipe(es.map(parse)) + .pipe(es.map(addToDocuments)) + .pipe(es.wait(onEnd)); + }); }, serialize: function() { var res = this.getAttributes({ diff --git a/src/refine-view/index.jade b/src/refine-view/index.jade index 84183ca859d..e7a48a0100f 100644 --- a/src/refine-view/index.jade +++ b/src/refine-view/index.jade @@ -1,6 +1,10 @@ -.refine-view-container: .row: .col-md-12 - form: .input-group(data-hook='refine-input-group') +.refine-view-container + .query-input-container: .row: .col-md-12: form: .input-group(data-hook='refine-input-group') input.form-control.input-sm(type='text', data-hook='refine-input') span.input-group-btn button.btn.btn-info.btn-sm(type='button', data-hook='refine-button') Apply button.btn.btn-default.btn-sm(type='button', data-hook='reset-button') Reset + div(data-hook='sampling-message-subview') + + //- .sampling-message + //- span Query returned 9867 documents. This report is based on a sample of 1000 documents (10.13%). diff --git a/src/refine-view/index.js b/src/refine-view/index.js index e8a0103b6fb..206932b98e4 100644 --- a/src/refine-view/index.js +++ b/src/refine-view/index.js @@ -3,6 +3,8 @@ var EditableQuery = require('../models/editable-query'); var $ = require('jquery'); var EJSON = require('mongodb-extended-json'); var Query = require('mongodb-language-model').Query; +var SamplingMessageView = require('../sampling-message'); + // var debug = require('debug')('scout:refine-view:index'); module.exports = AmpersandView.extend({ @@ -55,6 +57,17 @@ module.exports = AmpersandView.extend({ } ] }, + subviews: { + sampling_message: { + hook: 'sampling-message-subview', + prepareView: function(el) { + return new SamplingMessageView({ + el: el, + parent: this + }); + } + } + }, events: { 'click [data-hook=refine-button]': 'refineClicked', 'click [data-hook=reset-button]': 'resetClicked', diff --git a/src/refine-view/index.less b/src/refine-view/index.less index 4df03906e44..b8f22def19e 100644 --- a/src/refine-view/index.less +++ b/src/refine-view/index.less @@ -1,19 +1,28 @@ .refine-view-container { - background: @gray8; - padding: 12px 25px; position: relative; z-index: 1; - input[type='text'] { - font-family: @font-family-monospace; - background: @pw; - height: 28px; - & + .input-group-btn { - padding-left: 10px; + .query-input-container { + padding: 12px 25px; + background: @gray8; - &:last-child > .btn { - margin-left: 2px; + input[type='text'] { + font-family: @font-family-monospace; + background: @pw; + height: 28px; + & + .input-group-btn { + padding-left: 10px; + + &:last-child > .btn { + margin-left: 2px; + } } } } + .sampling-message { + font-size: 12px; + font-weight: 200; + padding: 5px 25px; + border-bottom: 2px solid @gray7 + } } diff --git a/src/sampling-message/index.jade b/src/sampling-message/index.jade index 1f272cae0fa..357290ff3ea 100644 --- a/src/sampling-message/index.jade +++ b/src/sampling-message/index.jade @@ -1,5 +1,12 @@ -.sampling-status - span(data-hook='is_sample') This report is based on a sample of  - span(data-hook='is_query') Your query returned  - span(data-hook='sample_size_message') - |. +.sampling-message + if is_sample + | Query returned  + b #{formatted_total_count} + |  #{total_count_document}. + | This report is based on a sample of  + b #{formatted_sample_size} + |  #{sample_size_document}. + else + | Query returned  + b #{formatted_sample_size} + |  #{sample_size_document}. diff --git a/src/sampling-message/index.js b/src/sampling-message/index.js index c51a3aeee91..7f2d50c4f46 100644 --- a/src/sampling-message/index.js +++ b/src/sampling-message/index.js @@ -2,69 +2,83 @@ var View = require('ampersand-view'); var pluralize = require('pluralize'); var format = require('util').format; var app = require('ampersand-app'); +var numeral = require('numeral'); +var debug = require('debug')('scout:sampling-message:index'); var SamplingMessageView = View.extend({ + template: require('./index.jade'), session: { parent: 'state' }, - bindings: { - visible: { - type: 'booleanClass', - no: 'hidden' - }, - sample_size_message: { - hook: 'sample_size_message' - }, - is_sample: [ - { - hook: 'is_sample', - type: 'booleanClass', - no: 'hidden' - }, - { - hook: 'is_query', - type: 'booleanClass', - yes: 'hidden' - } - ] - }, props: { - schema_sample_size: { + sample_size: { + type: 'number', + default: 0 + }, + total_count: { type: 'number', default: 0 } }, derived: { visible: { - deps: ['schema_sample_size'], + deps: ['sample_size'], fn: function() { - return this.schema_sample_size > 0; + return this.sample_size > 0; } }, is_sample: { - deps: ['schema_sample_size'], + deps: ['sample_size'], + fn: function() { + return this.sample_size === app.queryOptions.size; + } + }, + formatted_total_count: { + deps: ['total_count'], fn: function() { - return this.schema_sample_size === app.queryOptions.size; + return numeral(this.total_count).format('0,0'); } }, - sample_size_message: { - deps: ['schema_sample_size'], + formatted_sample_size: { + deps: ['sample_size'], fn: function() { - return format('%d %s', this.parent.schema.sample_size, - pluralize('document', this.parent.schema.sample_size)); + return numeral(this.sample_size).format('0,0'); + } + }, + total_count_document: { + deps: ['total_count'], + fn: function() { + return pluralize('document', this.total_count); + } + }, + sample_size_document: { + deps: ['sample_size'], + fn: function() { + return pluralize('document', this.sample_size); } } }, - template: require('./index.jade'), + bindings: { + visible: { + type: 'booleanClass', + no: 'hidden' + } + }, initialize: function() { - this.listenTo(this.parent.schema, 'request', this.hide.bind(this)); - this.listenTo(this.parent.schema, 'sync', this.show.bind(this)); + this.listenTo(this.parent.parent.schema, 'request', this.hide.bind(this)); + this.listenTo(this.parent.parent.schema, 'sync', this.show.bind(this)); }, hide: function() { - this.schema_sample_size = 0; + this.sample_size = 0; + this.total_count = 0; }, show: function() { - this.schema_sample_size = this.parent.schema.sample_size; + this.sample_size = this.parent.parent.schema.sample_size; + this.total_count = this.parent.parent.schema.total; + this.render(); + }, + render: function() { + this.renderWithTemplate(this); } }); module.exports = SamplingMessageView; diff --git a/src/sidebar/index.jade b/src/sidebar/index.jade index fceda4a02a9..d95e7b07a90 100644 --- a/src/sidebar/index.jade +++ b/src/sidebar/index.jade @@ -1,5 +1,6 @@ div .sidebar.panel div.compass-logo + div(data-hook='instance-properties-subview') div(data-hook='collection-filter-subview') div(data-hook='collection-list-subview') diff --git a/src/sidebar/index.js b/src/sidebar/index.js index 3ba3da2d8ec..c3cdcc3e527 100644 --- a/src/sidebar/index.js +++ b/src/sidebar/index.js @@ -1,7 +1,9 @@ var View = require('ampersand-view'); var mousetrap = require('mousetrap'); +var InstancePropertiesView = require('./instance-properties'); var CollectionFilterView = require('./collection-filter'); var CollectionListView = require('./collection-list'); +var app = require('ampersand-app'); var SidebarView = View.extend({ props: { @@ -33,6 +35,16 @@ var SidebarView = View.extend({ }, template: require('./index.jade'), subviews: { + instance_properties: { + hook: 'instance-properties-subview', + prepareView: function(el) { + return new InstancePropertiesView({ + el: el, + parent: this, + instance: app.instance + }); + } + }, collections_filter: { hook: 'collection-filter-subview', prepareView: function(el) { diff --git a/src/sidebar/instance-properties.jade b/src/sidebar/instance-properties.jade new file mode 100644 index 00000000000..da3e380d234 --- /dev/null +++ b/src/sidebar/instance-properties.jade @@ -0,0 +1,14 @@ +div.instance-properties + span.hostname(data-hook='hostname') + span.version + | version  + span(data-hook='version') + div.db-stats + span.num-databases + strong(data-hook='num-databases') + |  DBs + span.num-collections + strong(data-hook='num-collections') + |  Collections + button.refresh-collections(data-hook='refresh-button') + i.fa(data-hook='refresh-icon') diff --git a/src/sidebar/instance-properties.js b/src/sidebar/instance-properties.js new file mode 100644 index 00000000000..2fb4dbd5ca3 --- /dev/null +++ b/src/sidebar/instance-properties.js @@ -0,0 +1,61 @@ +var View = require('ampersand-view'); +var app = require('ampersand-app'); +// var debug = require('debug')('scout:sidebar:instace-properties'); +var _ = require('lodash'); + +var InstancePropertiesView = module.exports = View.extend({ + template: require('./instance-properties.jade'), + session: { + numDatabases: 'number', + numCollections: 'number', + instance: 'state', + is_fetching: { + type: 'boolean', + default: true + } + }, + bindings: { + 'instance.host.hostname': { + type: 'text', + hook: 'hostname' + }, + 'instance.build.version': { + type: 'text', + hook: 'version' + }, + numCollections: { + type: 'text', + hook: 'num-collections' + }, + numDatabases: { + type: 'text', + hook: 'num-databases' + }, + is_fetching: { + type: 'booleanClass', + hook: 'refresh-icon', + yes: ['fa-refresh', 'fa-spin'], + no: 'fa-repeat' + } + }, + events: { + 'click button[data-hook=refresh-button]': 'onRefreshButtonClicked' + }, + initialize: function() { + this.listenTo(app.instance, 'sync', this.onInstanceFetched); + }, + onInstanceFetched: function() { + // delay switching the spinner back to static for 500ms, otherwise the reload is not noticable + _.delay(function() { + this.is_fetching = false; + }.bind(this), 500); + this.numDatabases = app.instance.databases.length; + this.numCollections = app.instance.collections.length; + }, + onRefreshButtonClicked: function() { + app.instance.fetch(); + this.is_fetching = true; + } +}); + +module.exports = InstancePropertiesView; diff --git a/styles/palette.less b/styles/palette.less index 840a0261318..29d27c15da6 100644 --- a/styles/palette.less +++ b/styles/palette.less @@ -19,8 +19,9 @@ @gray7: #ebebed; @gray8: #f5f6f7; -@slate0: #4c5259; -@slate1: #70757a; +@slate0: #42494f; +@slate1: #4c5259; +@slate2: #70757a; @blue3: #5b81a9; // TODO: deprecate @blue4: #84a1bf; // TODO: deprecate diff --git a/styles/sidebar.less b/styles/sidebar.less index 90cd562b8d5..00c0940b4aa 100644 --- a/styles/sidebar.less +++ b/styles/sidebar.less @@ -1,4 +1,4 @@ -@sidebar-bg: @slate0; +@sidebar-bg: @slate1; @sidebar-width: 220px; .sidebar { @@ -9,14 +9,70 @@ margin-bottom: 0; .compass-logo { - background: @slate1 url("images/logo-compass-on-dark.png") center center no-repeat; + background: @slate0 url("images/logo-compass-on-dark.png") center center no-repeat; background-size: 162px 28px; width: @sidebar-width; - height: 72px; + height: 60px; + } + + .instance-properties { + position: absolute; + top: 78px; + left: 10px; + + .hostname { + display: block; + font-size: 16px; + line-height: 16px; + font-weight: 200; + color: @pw; + } + .version { + display: block; + font-size: 13px; + line-height: 14px; + font-weight: normal; + color: @gray5; + } + .db-stats { + color: @gray5; + margin-top: 8px; + font-style: 13px; + line-height: 13px; + + .num-databases, + .num-collections { + display: inline-block; + border-right: 1px solid @slate2; + padding-right: 10px; + margin-right: 10px; + } + strong { + color: @gray7; + font-weight: bold; + } + button.refresh-collections { + background: none; + border: none; + color: @gray7; + font-size: 13px; + border-radius: 2px; + padding: 4px 5px; + transition: all 150ms ease; + + &:hover { + background: @slate0; + color: @pw; + } + } + } } .list-filter { - padding: 12px; + position: absolute; + top: 156px; + width: 100%; + // padding-bottom: 12px; box-shadow: 0 2px 0 rgba(0,0,0,0.2); i.search { @@ -24,19 +80,41 @@ position: absolute; margin-top: 9px; margin-left: 10px; - color: @gray5; + color: @gray7; } + // input { + // padding: 0; + // margin-left: 30px; + // margin-top: 7px; + // height: auto; + // background: @slate1; + // color: @pw; + // width: 174px; + // border: none; + // border-bottom: 1px solid lighten(@slate1, 5%); + // } + // input[type=search] { + // border-radius: 18px; + // } input { - padding: 5px 5px 5px 30px; + padding: 7px 5px 5px 30px; height: auto; - background: @slate0; + background: lighten(@slate1, 5%); color: @pw; width: 100%; - border: 1px solid lighten(@slate0, 5%); + border: none; + transition: all 150ms ease; + + &:focus { + background: lighten(@slate1, 8%); + } } - input[type=search] { - border-radius: 18px; + input::-webkit-input-placeholder { + color: @gray5; } + // input[type=search] { + // border-radius: 18px; + // } } .panel-title { @@ -45,7 +123,7 @@ .list-group { line-height: 24px; position: absolute; - top: 128px; + top: 189px; bottom: 0px; overflow-y: auto; width: 100%; @@ -60,10 +138,10 @@ color: @gray5; &:hover { - background: lighten(@slate0, 5%); + background: lighten(@slate1, 5%); a { color: @pw; - border-left: 4px solid lighten(@slate0, 5%); + border-left: 4px solid lighten(@slate1, 5%); text-decoration: none; } }