mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
Pull out settings-view package into a separate repo
This commit is contained in:
@@ -85,6 +85,7 @@
|
||||
"link": "0.1.0",
|
||||
"markdown-preview": "0.1.0",
|
||||
"package-generator": "0.1.0",
|
||||
"settings-view": "0.1.0",
|
||||
"snippets": "0.1.0",
|
||||
"spell-check": "0.1.0",
|
||||
"status-bar": "0.1.0",
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
'body':
|
||||
'meta-,': 'settings-view:toggle'
|
||||
@@ -1,134 +0,0 @@
|
||||
{$$, View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
async = require 'async'
|
||||
AtomPackage = require 'atom-package'
|
||||
Editor = require 'editor'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class GeneralPanel extends View
|
||||
@content: ->
|
||||
@form id: 'general-panel', class: 'form-horizontal', =>
|
||||
@div outlet: "loadingElement", class: 'alert alert-info loading-area', "Loading settings"
|
||||
|
||||
initialize: ->
|
||||
window.setTimeout (=> @activatePackages => @showSettings()), 1
|
||||
|
||||
showSettings: ->
|
||||
@loadingElement.hide()
|
||||
@appendSettings(name, settings) for name, settings of config.getSettings()
|
||||
@bindFormFields()
|
||||
@bindEditors()
|
||||
|
||||
activatePackages: (finishedCallback) ->
|
||||
iterator = (pack, callback) ->
|
||||
try
|
||||
if pack instanceof AtomPackage and not pack.isActive()
|
||||
pack.activate({immediate: true})
|
||||
catch e
|
||||
console.error e
|
||||
finally
|
||||
callback()
|
||||
|
||||
async.each atom.getLoadedPackages(), iterator, finishedCallback
|
||||
|
||||
appendSettings: (namespace, settings) ->
|
||||
return if _.isEmpty(settings)
|
||||
|
||||
@append $$ ->
|
||||
@fieldset =>
|
||||
@legend "#{_.uncamelcase(namespace)} settings"
|
||||
appendSetting.call(this, namespace, name, value) for name, value of settings
|
||||
|
||||
bindFormFields: ->
|
||||
for input in @find('input[id]').toArray()
|
||||
do (input) =>
|
||||
input = $(input)
|
||||
name = input.attr('id')
|
||||
type = input.attr('type')
|
||||
|
||||
@observeConfig name, (value) ->
|
||||
if type is 'checkbox'
|
||||
input.attr('checked', value)
|
||||
else
|
||||
input.val(value) if value
|
||||
|
||||
input.on 'change', =>
|
||||
value = input.val()
|
||||
if type == 'checkbox'
|
||||
value = !!input.attr('checked')
|
||||
else
|
||||
value = @parseValue(type, value)
|
||||
config.set(name, value)
|
||||
|
||||
bindEditors: ->
|
||||
for editor in @find('.editor[id]').views()
|
||||
do (editor) =>
|
||||
name = editor.attr('id')
|
||||
type = editor.attr('type')
|
||||
|
||||
@observeConfig name, (value) ->
|
||||
return if value?.toString() == editor.getText()
|
||||
value ?= ""
|
||||
editor.setText(value.toString())
|
||||
|
||||
editor.getBuffer().on 'contents-modified', =>
|
||||
config.set(name, @parseValue(type, editor.getText()))
|
||||
|
||||
parseValue: (type, value) ->
|
||||
if value == ''
|
||||
value = undefined
|
||||
else if type == 'int'
|
||||
intValue = parseInt(value)
|
||||
value = intValue unless isNaN(intValue)
|
||||
else if type == 'float'
|
||||
floatValue = parseFloat(value)
|
||||
value = floatValue unless isNaN(floatValue)
|
||||
|
||||
value
|
||||
|
||||
###
|
||||
# Space Pen Helpers
|
||||
###
|
||||
|
||||
appendSetting = (namespace, name, value) ->
|
||||
return if namespace is 'core' and name is 'themes' # Handled in the Themes panel
|
||||
|
||||
@div class: 'control-group', =>
|
||||
@div class: 'controls', =>
|
||||
if _.isBoolean(value)
|
||||
appendCheckbox.call(this, namespace, name, value)
|
||||
else if _.isArray(value)
|
||||
appendArray.call(this, namespace, name, value)
|
||||
else
|
||||
appendEditor.call(this, namespace, name, value)
|
||||
|
||||
appendCheckbox = (namespace, name, value) ->
|
||||
englishName = _.uncamelcase(name)
|
||||
keyPath = "#{namespace}.#{name}"
|
||||
@div class: 'checkbox', =>
|
||||
@label for: keyPath, =>
|
||||
@input id: keyPath, type: 'checkbox'
|
||||
@text englishName
|
||||
|
||||
appendEditor = (namespace, name, value) ->
|
||||
englishName = _.uncamelcase(name)
|
||||
keyPath = "#{namespace}.#{name}"
|
||||
if _.isNumber(value)
|
||||
type = if value % 1 == 0 then 'int' else 'float'
|
||||
else
|
||||
type = 'string'
|
||||
|
||||
@label class: 'control-label', englishName
|
||||
@div class: 'controls', =>
|
||||
@subview keyPath.replace('.', ''), new Editor(mini: true, attributes: {id: keyPath, type: type})
|
||||
|
||||
appendArray = (namespace, name, value) ->
|
||||
englishName = _.uncamelcase(name)
|
||||
@label class: 'control-label', englishName
|
||||
@div class: 'controls', =>
|
||||
@text "readOnly: " + value.join(", ")
|
||||
@@ -1,78 +0,0 @@
|
||||
BufferedNodeProcess = require 'buffered-node-process'
|
||||
roaster = require 'roaster'
|
||||
async = require 'async'
|
||||
|
||||
### Internal ###
|
||||
|
||||
renderMarkdownInMetadata = (packages, callback) ->
|
||||
queue = async.queue (pack, callback) ->
|
||||
operations = []
|
||||
if pack.description
|
||||
operations.push (callback) ->
|
||||
roaster pack.description, {}, (error, html) ->
|
||||
pack.descriptionHtml = html
|
||||
callback()
|
||||
if pack.readme
|
||||
operations.push (callback) ->
|
||||
roaster pack.readme, {}, (error, html) ->
|
||||
pack.readmeHtml = html
|
||||
callback()
|
||||
async.waterfall(operations, callback)
|
||||
queue.push(pack) for pack in packages
|
||||
queue.drain = callback
|
||||
|
||||
getAvailable = (callback) ->
|
||||
command = require.resolve '.bin/apm'
|
||||
args = ['available', '--json']
|
||||
output = []
|
||||
stdout = (lines) -> output.push(lines)
|
||||
exit = (code) ->
|
||||
if code is 0
|
||||
try
|
||||
packages = JSON.parse(output.join()) ? []
|
||||
catch error
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if packages.length > 0
|
||||
renderMarkdownInMetadata packages, -> callback(null, packages)
|
||||
else
|
||||
callback(null, packages)
|
||||
else
|
||||
callback(new Error("apm failed with code: #{code}"))
|
||||
|
||||
new BufferedNodeProcess({command, args, stdout, exit})
|
||||
|
||||
install = ({name, version}, callback) ->
|
||||
activateOnSuccess = !atom.isPackageDisabled(name)
|
||||
activateOnFailure = atom.isPackageActive(name)
|
||||
atom.deactivatePackage(name) if atom.isPackageActive(name)
|
||||
atom.unloadPackage(name) if atom.isPackageLoaded(name)
|
||||
|
||||
command = require.resolve '.bin/apm'
|
||||
args = ['install', "#{name}@#{version}"]
|
||||
exit = (code) ->
|
||||
if code is 0
|
||||
atom.activatePackage(name) if activateOnSuccess
|
||||
callback()
|
||||
else
|
||||
atom.activatePackage(name) if activateOnFailure
|
||||
callback(new Error("Installing '#{name}' failed."))
|
||||
|
||||
new BufferedNodeProcess({command, args, exit})
|
||||
|
||||
uninstall = ({name}, callback) ->
|
||||
atom.deactivatePackage(name) if atom.isPackageActive(name)
|
||||
|
||||
command = require.resolve '.bin/apm'
|
||||
args = ['uninstall', name]
|
||||
exit = (code) ->
|
||||
if code is 0
|
||||
atom.unloadPackage(name) if atom.isPackageLoaded(name)
|
||||
callback()
|
||||
else
|
||||
callback(new Error("Uninstalling '#{name}' failed."))
|
||||
|
||||
new BufferedNodeProcess({command, args, exit})
|
||||
|
||||
module.exports = {renderMarkdownInMetadata, install, uninstall, getAvailable}
|
||||
@@ -1,127 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
{View, $$} = require 'space-pen'
|
||||
EventEmitter = require 'event-emitter'
|
||||
Editor = require 'editor'
|
||||
PackageView = require './package-view'
|
||||
packageManager = require './package-manager'
|
||||
stringScore = require 'stringscore'
|
||||
|
||||
|
||||
### Internal ###
|
||||
class PackageEventEmitter
|
||||
_.extend PackageEventEmitter.prototype, EventEmitter
|
||||
|
||||
module.exports =
|
||||
class PackagePanel extends View
|
||||
@content: ->
|
||||
@div class: 'package-panel', =>
|
||||
@legend 'Packages'
|
||||
@ul class: 'nav nav-tabs', =>
|
||||
@li class: 'active', =>
|
||||
@a 'Installed', =>
|
||||
@span class: 'badge pull-right', outlet: 'installedCount'
|
||||
@li =>
|
||||
@a 'Available', =>
|
||||
@span class: 'badge pull-right', outlet: 'availableCount'
|
||||
|
||||
@subview 'packageFilter', new Editor(mini: true, attributes: {id: 'package-filter'})
|
||||
@div outlet: 'installedPackages'
|
||||
@div outlet: 'availablePackages'
|
||||
|
||||
initialize: ->
|
||||
@packageEventEmitter = new PackageEventEmitter()
|
||||
|
||||
@availablePackages.hide()
|
||||
@loadInstalledViews()
|
||||
@loadAvailableViews()
|
||||
|
||||
@find('.nav-tabs li').on 'click', (event) =>
|
||||
return if $(event.currentTarget).hasClass('active')
|
||||
@find('.nav-tabs li').toggleClass('active')
|
||||
@availablePackages.toggle()
|
||||
@installedPackages.toggle()
|
||||
|
||||
@packageEventEmitter.on 'package-installed', (error, pack) =>
|
||||
@addInstalledPackage(pack) unless error?
|
||||
|
||||
@packageEventEmitter.on 'package-uninstalled', (error, pack) =>
|
||||
@removeInstalledPackage(pack) unless error?
|
||||
|
||||
@packageFilter.getBuffer().on 'contents-modified', =>
|
||||
@filterPackages(@packageFilter.getText())
|
||||
|
||||
loadInstalledViews: ->
|
||||
@installedPackages.empty()
|
||||
@installedPackages.append @createLoadingView('Loading installed packages\u2026')
|
||||
|
||||
packages = _.sortBy(atom.getAvailablePackageMetadata(), 'name')
|
||||
packageManager.renderMarkdownInMetadata packages, =>
|
||||
@installedPackages.empty()
|
||||
for pack in packages
|
||||
view = new PackageView(pack, @packageEventEmitter)
|
||||
@installedPackages.append(view)
|
||||
|
||||
@updateInstalledCount()
|
||||
|
||||
loadAvailableViews: ->
|
||||
@availablePackages.empty()
|
||||
@availablePackages.append @createLoadingView('Loading available packages\u2026')
|
||||
|
||||
packageManager.getAvailable (error, @packages=[]) =>
|
||||
@availablePackages.empty()
|
||||
if error?
|
||||
errorView = @createErrorView('Error fetching available packages.')
|
||||
errorView.on 'click', => @loadAvailableViews()
|
||||
@availablePackages.append errorView
|
||||
console.error(error.stack ? error)
|
||||
else
|
||||
for pack in @packages
|
||||
view = new PackageView(pack, @packageEventEmitter)
|
||||
@availablePackages.append(view)
|
||||
|
||||
@updateAvailableCount()
|
||||
|
||||
createLoadingView: (text) ->
|
||||
$$ ->
|
||||
@div class: 'alert alert-info loading-area', text
|
||||
|
||||
createErrorView: (text) ->
|
||||
$$ ->
|
||||
@div class: 'alert alert-error', =>
|
||||
@span text
|
||||
@button class: 'btn btn-mini btn-retry', 'Retry'
|
||||
|
||||
updateInstalledCount: ->
|
||||
@installedCount.text(@installedPackages.children().length)
|
||||
|
||||
updateAvailableCount: ->
|
||||
@availableCount.text(@availablePackages.children().length)
|
||||
|
||||
removeInstalledPackage: ({name}) ->
|
||||
@installedPackages.children("[name=#{name}]").remove()
|
||||
@updateInstalledCount()
|
||||
|
||||
addInstalledPackage: (pack) ->
|
||||
packageNames = [pack.name]
|
||||
@installedPackages.children().each (index, el) -> packageNames.push(el.getAttribute('name'))
|
||||
packageNames.sort()
|
||||
insertAfterIndex = packageNames.indexOf(pack.name) - 1
|
||||
|
||||
view = new PackageView(pack, @packageEventEmitter)
|
||||
if insertAfterIndex < 0
|
||||
@installedPackages.prepend(view)
|
||||
else
|
||||
@installedPackages.children(":eq(#{insertAfterIndex})").after(view)
|
||||
|
||||
@updateInstalledCount()
|
||||
|
||||
filterPackages: (filterString) ->
|
||||
for children in [@installedPackages.children(), @availablePackages.children()]
|
||||
for packageView in children
|
||||
name = packageView.getAttribute('name')
|
||||
continue unless name
|
||||
if /^\s*$/.test(filterString) or stringScore(name, filterString)
|
||||
$(packageView).show()
|
||||
else
|
||||
$(packageView).hide()
|
||||
@@ -1,187 +0,0 @@
|
||||
Package = require 'package'
|
||||
semver = require 'semver'
|
||||
packageManager = require './package-manager'
|
||||
_ = require 'underscore'
|
||||
{$$, View} = require 'space-pen'
|
||||
requireWithGlobals 'bootstrap/js/bootstrap-dropdown', jQuery: require 'jquery'
|
||||
|
||||
### Internal ###
|
||||
module.exports =
|
||||
class PackageView extends View
|
||||
@content: ->
|
||||
@div class: 'panel', =>
|
||||
@div outlet: 'heading', class: 'panel-heading', =>
|
||||
@span outlet: 'name'
|
||||
@span outlet: 'version', class: 'label'
|
||||
@span outlet: 'update', class: 'label label-info', 'Update Available'
|
||||
@span outlet: 'disabedLabel', class: 'label label-warning', 'Disabled'
|
||||
@div class: 'btn-group pull-right', =>
|
||||
@button outlet: 'defaultAction', class: 'btn btn-small btn-primary'
|
||||
@button outlet: 'dropdownButton', class: 'btn btn-small btn-primary dropdown-toggle', 'data-toggle': 'dropdown', =>
|
||||
@span class: 'caret'
|
||||
@ul outlet: 'dropdown', class: 'dropdown-menu', =>
|
||||
@li outlet: 'enableToggle', => @a 'Disable'
|
||||
@li outlet: 'homepage', => @a 'Visit homepage'
|
||||
@li outlet: 'issues', => @a 'Report issue'
|
||||
@div outlet: 'description'
|
||||
@ul class: 'list-group list-group-flush', =>
|
||||
@li outlet: 'readmeArea', class: 'list-group-item', =>
|
||||
@a 'Show README', outlet: 'readmeLink'
|
||||
@div class: 'readme', outlet: 'readme'
|
||||
|
||||
pack: null
|
||||
metadata: null
|
||||
installed: false
|
||||
disabled: false
|
||||
bundled: false
|
||||
updateAvailable: false
|
||||
|
||||
initialize: (pack, @packageEventEmitter) ->
|
||||
if pack instanceof Package
|
||||
@pack = pack
|
||||
@metadata = @pack.metadata
|
||||
else
|
||||
@metadata = pack
|
||||
|
||||
@updatePackageState()
|
||||
|
||||
@attr('name', @metadata.name)
|
||||
@name.text(@metadata.name)
|
||||
if version = semver.valid(@metadata.version)
|
||||
@version.text(version)
|
||||
else
|
||||
@version.hide()
|
||||
|
||||
if @metadata.descriptionHtml
|
||||
@description.html(@metadata.descriptionHtml)
|
||||
else if @metadata.description
|
||||
@description.text(@metadata.description)
|
||||
else
|
||||
@description.text('No further description available.')
|
||||
|
||||
@readme.hide()
|
||||
if @metadata.readmeHtml
|
||||
@readme.html(@metadata.readmeHtml)
|
||||
else if @metadata.readme
|
||||
@readme.text(@metadata.readme)
|
||||
else
|
||||
@readmeArea.hide()
|
||||
|
||||
@readmeLink.on 'click', =>
|
||||
if @readme.isVisible()
|
||||
@readme.hide()
|
||||
@readmeLink.text('Show README')
|
||||
else
|
||||
@readme.show()
|
||||
@readmeLink.text('Hide README')
|
||||
|
||||
homepage = @metadata.homepage
|
||||
unless homepage
|
||||
if _.isString(@metadata.repository)
|
||||
repoUrl = @metadata.repository
|
||||
else
|
||||
repoUrl = @metadata.repository?.url
|
||||
if repoUrl
|
||||
repoUrl = repoUrl.replace(/.git$/, '')
|
||||
homepage = repoUrl if require('url').parse(repoUrl).host is 'github.com'
|
||||
if homepage
|
||||
@homepage.find('a').attr('href', homepage)
|
||||
else
|
||||
@homepage.hide()
|
||||
|
||||
if issues = @metadata.bugs?.url
|
||||
@issues.find('a').attr('href', issues)
|
||||
else
|
||||
@issues.hide()
|
||||
|
||||
@defaultAction.on 'click', =>
|
||||
if @installed and @bundled
|
||||
@togglePackageEnablement()
|
||||
return
|
||||
|
||||
|
||||
@defaultAction.disable()
|
||||
if @installed
|
||||
if @updateAvailable
|
||||
@defaultAction.text('Upgrading\u2026')
|
||||
packageManager.install @metadata, (error) =>
|
||||
@packageEventEmitter.trigger('package-upgraded', error, @metadata)
|
||||
else
|
||||
@defaultAction.text('Uninstalling\u2026')
|
||||
packageManager.uninstall @metadata, (error) =>
|
||||
@packageEventEmitter.trigger('package-uninstalled', error, @metadata)
|
||||
else
|
||||
@defaultAction.text('Installing\u2026')
|
||||
packageManager.install @metadata, (error) =>
|
||||
@packageEventEmitter.trigger('package-installed', error, @metadata)
|
||||
|
||||
@updateDefaultAction()
|
||||
|
||||
@enableToggle.find('a').on 'click', => @togglePackageEnablement()
|
||||
|
||||
@observeConfig 'core.disabledPackages', =>
|
||||
@updatePackageState()
|
||||
@updateDefaultAction()
|
||||
@updateEnabledState()
|
||||
|
||||
@packageEventEmitter.on 'package-installed package-uninstalled package-upgraded', (error, metadata) =>
|
||||
if metadata?.name is @metadata.name
|
||||
@defaultAction.enable()
|
||||
@updatePackageState()
|
||||
@updateDefaultAction()
|
||||
|
||||
togglePackageEnablement: ->
|
||||
if @disabled
|
||||
config.removeAtKeyPath('core.disabledPackages', @metadata.name)
|
||||
else
|
||||
config.pushAtKeyPath('core.disabledPackages', @metadata.name)
|
||||
|
||||
updatePackageState: ->
|
||||
@disabled = atom.isPackageDisabled(@metadata.name)
|
||||
@updateAvailable = false
|
||||
@bundled = false
|
||||
loadedPackage = atom.getLoadedPackage(@metadata.name)
|
||||
packagePath = loadedPackage?.path ? atom.resolvePackagePath(@metadata.name)
|
||||
@installed = packagePath?
|
||||
if @installed
|
||||
for packageDirPath in config.bundledPackageDirPaths
|
||||
if packagePath.indexOf("#{packageDirPath}/") is 0
|
||||
@bundled = true
|
||||
break
|
||||
|
||||
version = loadedPackage?.metadata.version
|
||||
unless version
|
||||
try
|
||||
version = Package.loadMetadata(@metadata.name).version
|
||||
@updateAvailable = semver.gt(@metadata.version, version)
|
||||
|
||||
if @updateAvailable
|
||||
@update.show()
|
||||
else
|
||||
@update.hide()
|
||||
|
||||
updateEnabledState: ->
|
||||
enableLink = @enableToggle.find('a')
|
||||
if @disabled
|
||||
enableLink.text('Enable')
|
||||
@disabedLabel.show()
|
||||
else
|
||||
enableLink.text('Disable')
|
||||
@disabedLabel.hide()
|
||||
|
||||
@enableToggle.hide() unless @installed
|
||||
|
||||
updateDefaultAction: ->
|
||||
if @installed
|
||||
if @bundled
|
||||
if @disabled
|
||||
@defaultAction.text('Enable')
|
||||
else
|
||||
@defaultAction.text('Disable')
|
||||
else
|
||||
if @updateAvailable
|
||||
@defaultAction.text('Upgrade')
|
||||
else
|
||||
@defaultAction.text('Uninstall')
|
||||
else
|
||||
@defaultAction.text('Install')
|
||||
@@ -1,92 +0,0 @@
|
||||
{$$} = require 'space-pen'
|
||||
ScrollView = require 'scroll-view'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
Pane = require 'pane'
|
||||
GeneralPanel = require './general-panel'
|
||||
ThemePanel = require './theme-panel'
|
||||
PackagePanel = require './package-panel'
|
||||
Project = require 'project'
|
||||
|
||||
configUri = "atom://config"
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
module.exports =
|
||||
class SettingsView extends ScrollView
|
||||
registerDeserializer(this)
|
||||
|
||||
@activate: (state) ->
|
||||
Project.registerOpener (filePath) ->
|
||||
new SettingsView() if filePath is configUri
|
||||
|
||||
rootView.command 'settings-view:toggle', ->
|
||||
rootView.open(configUri)
|
||||
|
||||
$(window).on 'window:open-settings', ->
|
||||
rootView.open(configUri)
|
||||
|
||||
@deserialize: ({activePanelName}={}) ->
|
||||
new SettingsView(activePanelName)
|
||||
|
||||
@content: ->
|
||||
@div id: 'settings-view', tabindex: -1, =>
|
||||
@div id: 'config-menu', =>
|
||||
@ul id: 'panels-menu', class: 'nav nav-pills nav-stacked', outlet: 'panelMenu'
|
||||
@button "Open ~/.atom", id: 'open-dot-atom', class: 'btn btn-default btn-small'
|
||||
@div id: 'panels', outlet: 'panels'
|
||||
|
||||
activePanelName: null
|
||||
|
||||
initialize: (activePanelName) ->
|
||||
super
|
||||
@panelsByName = {}
|
||||
@on 'click', '#panels-menu li a', (e) =>
|
||||
@showPanel($(e.target).closest('li').attr('name'))
|
||||
|
||||
@on 'click', '#open-dot-atom', ->
|
||||
atom.open(config.configDirPath)
|
||||
|
||||
@addPanel('General', new GeneralPanel)
|
||||
@addPanel('Themes', new ThemePanel)
|
||||
@addPanel('Packages', new PackagePanel)
|
||||
@showPanel(activePanelName) if activePanelName
|
||||
|
||||
serialize: ->
|
||||
deserializer: 'SettingsView'
|
||||
activePanelName: @activePanelName
|
||||
|
||||
addPanel: (name, panel) ->
|
||||
panelItem = $$ -> @li name: name, => @a name
|
||||
@panelMenu.append(panelItem)
|
||||
panel.hide()
|
||||
@panelsByName[name] = panel
|
||||
@panels.append(panel)
|
||||
@showPanel(name) if @getPanelCount() is 1 or @panelToShow is name
|
||||
|
||||
getPanelCount: ->
|
||||
_.values(@panelsByName).length
|
||||
|
||||
showPanel: (name) ->
|
||||
if @panelsByName[name]
|
||||
@panels.children().hide()
|
||||
@panelMenu.children('.active').removeClass('active')
|
||||
@panelsByName[name].show()
|
||||
for editorElement in @panelsByName[name].find(".editor")
|
||||
$(editorElement).view().redraw()
|
||||
@panelMenu.children("[name='#{name}']").addClass('active')
|
||||
@activePanelName = name
|
||||
@panelToShow = null
|
||||
else
|
||||
@panelToShow = name
|
||||
|
||||
getTitle: ->
|
||||
"Settings"
|
||||
|
||||
getUri: ->
|
||||
configUri
|
||||
|
||||
isEqual: (other) ->
|
||||
other instanceof SettingsView
|
||||
@@ -1,72 +0,0 @@
|
||||
{View, $$} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
|
||||
###
|
||||
# Internal #
|
||||
###
|
||||
|
||||
window.jQuery = $
|
||||
require 'jqueryui-browser/ui/jquery.ui.core'
|
||||
require 'jqueryui-browser/ui/jquery.ui.widget'
|
||||
require 'jqueryui-browser/ui/jquery.ui.mouse'
|
||||
require 'jqueryui-browser/ui/jquery.ui.sortable'
|
||||
require 'jqueryui-browser/ui/jquery.ui.draggable'
|
||||
delete window.jQuery
|
||||
|
||||
module.exports =
|
||||
class ThemeConfigPanel extends View
|
||||
@content: ->
|
||||
@div id: 'themes-config', =>
|
||||
@legend "Themes"
|
||||
@div id: 'theme-picker', =>
|
||||
@div class: 'panel', =>
|
||||
@div class: 'panel-heading', "Enabled Themes"
|
||||
@ol id: 'enabled-themes', class: 'list-group list-group-flush', outlet: 'enabledThemes'
|
||||
@div class: 'panel', =>
|
||||
@div class: 'panel-heading', "Available Themes"
|
||||
@ol id: 'available-themes', class: 'list-group list-group-flush', outlet: 'availableThemes'
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
for name in atom.themes.getAvailableNames()
|
||||
@availableThemes.append(@buildThemeLi(name, draggable: true))
|
||||
|
||||
@observeConfig "core.themes", (enabledThemes) =>
|
||||
@enabledThemes.empty()
|
||||
for name in enabledThemes ? []
|
||||
@enabledThemes.append(@buildThemeLi(name))
|
||||
|
||||
@enabledThemes.sortable
|
||||
receive: (e, ui) => @enabledThemeReceived($(ui.helper))
|
||||
update: => @enabledThemesUpdated()
|
||||
|
||||
@on "click", "#enabled-themes .disable-theme", (e) =>
|
||||
$(e.target).closest('li').remove()
|
||||
@enabledThemesUpdated()
|
||||
|
||||
buildThemeLi: (name, {draggable} = {}) ->
|
||||
li = $$ ->
|
||||
@li class: 'list-group-item', name: name, =>
|
||||
@div class: 'disable-theme pull-right'
|
||||
@text name
|
||||
if draggable
|
||||
li.draggable
|
||||
connectToSortable: '#enabled-themes'
|
||||
appendTo: '#themes-config'
|
||||
helper: (e) ->
|
||||
target = $(e.target)
|
||||
target.clone().width(target.width())
|
||||
else
|
||||
li
|
||||
|
||||
enabledThemeReceived: (helper) ->
|
||||
name = helper.attr('name')
|
||||
@enabledThemes.find("[name='#{name}']:not('.ui-draggable')").remove()
|
||||
@enabledThemes.find(".ui-draggable").removeClass('ui-draggable')
|
||||
|
||||
enabledThemesUpdated: ->
|
||||
config.set('core.themes', @getEnabledThemeNames())
|
||||
|
||||
getEnabledThemeNames: ->
|
||||
$(li).attr('name') for li in @enabledThemes.children().toArray()
|
||||
@@ -1,3 +0,0 @@
|
||||
'main': './lib/settings-view'
|
||||
'deferredDeserializers': ['SettingsView']
|
||||
'description': 'GUI pane for Atom settings'
|
||||
@@ -1,153 +0,0 @@
|
||||
PackagePanel = require '../lib/package-panel'
|
||||
packageManager = require '../lib/package-manager'
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "PackagePanel", ->
|
||||
[panel, configObserver] = []
|
||||
|
||||
beforeEach ->
|
||||
installedPackages = [
|
||||
{
|
||||
name: 'p1'
|
||||
version: '3.2.1'
|
||||
}
|
||||
{
|
||||
name: 'p2'
|
||||
version: '1.2.3'
|
||||
}
|
||||
{
|
||||
name: 'p3'
|
||||
version: '5.8.5'
|
||||
}
|
||||
]
|
||||
|
||||
availablePackages = [
|
||||
{
|
||||
name: 'p4'
|
||||
version: '3.2.1'
|
||||
homepage: 'http://p4.io'
|
||||
}
|
||||
{
|
||||
name: 'p5'
|
||||
version: '1.2.3'
|
||||
repository: url: 'http://github.com/atom/p5.git'
|
||||
bugs: url: 'http://github.com/atom/p5/issues'
|
||||
}
|
||||
{
|
||||
name: 'p6'
|
||||
version: '5.8.5'
|
||||
}
|
||||
]
|
||||
|
||||
spyOn(packageManager, 'getAvailable').andCallFake (callback) ->
|
||||
callback(null, availablePackages)
|
||||
spyOn(packageManager, 'uninstall').andCallFake (pack, callback) ->
|
||||
_.remove(installedPackages, pack)
|
||||
callback()
|
||||
spyOn(packageManager, 'install').andCallFake (pack, callback) ->
|
||||
installedPackages.push(pack)
|
||||
callback()
|
||||
|
||||
spyOn(atom, 'getAvailablePackageMetadata').andReturn(installedPackages)
|
||||
spyOn(atom, 'resolvePackagePath').andCallFake (name) ->
|
||||
if _.contains(_.pluck(installedPackages, 'name'), name)
|
||||
"/tmp/atom-packages/#{name}"
|
||||
|
||||
configObserver = jasmine.createSpy("configObserver")
|
||||
observeSubscription = config.observe('core.disabledPackages', configObserver)
|
||||
config.set('core.disabledPackages', ['p1', 'p3'])
|
||||
configObserver.reset()
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
panel = new PackagePanel
|
||||
|
||||
waitsFor ->
|
||||
panel.installedPackages.children().length == 3
|
||||
|
||||
describe 'Installed tab', ->
|
||||
it "lists all installed packages with a link to enable or disable the package", ->
|
||||
p1View = panel.installedPackages.find("[name='p1']").view()
|
||||
expect(p1View).toExist()
|
||||
expect(p1View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
p2View = panel.installedPackages.find("[name='p2']").view()
|
||||
expect(p2View).toExist()
|
||||
expect(p2View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
p3View = panel.installedPackages.find("[name='p3']").view()
|
||||
expect(p3View).toExist()
|
||||
expect(p3View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
describe "when the core.disabledPackages array changes", ->
|
||||
it "updates the checkboxes for newly disabled / enabled packages", ->
|
||||
config.set('core.disabledPackages', ['p2'])
|
||||
p1View = panel.installedPackages.find("[name='p1']").view()
|
||||
expect(p1View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
p2View = panel.installedPackages.find("[name='p2']").view()
|
||||
expect(p2View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
p3View = panel.installedPackages.find("[name='p3']").view()
|
||||
expect(p3View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
describe "when the disable link is clicked", ->
|
||||
it "adds the package name to the disabled packages array", ->
|
||||
p2View = panel.installedPackages.find("[name='p2']").view()
|
||||
p2View.enableToggle.find('a').click()
|
||||
expect(configObserver).toHaveBeenCalledWith(['p1', 'p3', 'p2'])
|
||||
|
||||
describe "when the enable link is clicked", ->
|
||||
it "removes the package name from the disabled packages array", ->
|
||||
p3View = panel.installedPackages.find("[name='p3']").view()
|
||||
p3View.enableToggle.find('a').click()
|
||||
expect(configObserver).toHaveBeenCalledWith(['p1'])
|
||||
|
||||
describe "when Uninstall is clicked", ->
|
||||
it "removes the package from the tab", ->
|
||||
expect(panel.installedPackages.find("[name='p1']")).toExist()
|
||||
p1View = panel.installedPackages.find("[name='p1']").view()
|
||||
expect(p1View.defaultAction.text()).toBe 'Uninstall'
|
||||
p1View.defaultAction.click()
|
||||
expect(panel.installedPackages.find("[name='p1']")).not.toExist()
|
||||
|
||||
describe 'Available tab', ->
|
||||
it 'lists all available packages', ->
|
||||
panel.find("li a:contains(Available)").click()
|
||||
panel.attachToDom()
|
||||
|
||||
expect(panel.availablePackages.children('.panel').length).toBe 3
|
||||
p4View = panel.availablePackages.children('.panel:eq(0)').view()
|
||||
p5View = panel.availablePackages.children('.panel:eq(1)').view()
|
||||
p6View = panel.availablePackages.children('.panel:eq(2)').view()
|
||||
|
||||
expect(p4View.name.text()).toBe 'p4'
|
||||
expect(p5View.name.text()).toBe 'p5'
|
||||
expect(p6View.name.text()).toBe 'p6'
|
||||
|
||||
expect(p4View.version.text()).toBe '3.2.1'
|
||||
expect(p5View.version.text()).toBe '1.2.3'
|
||||
expect(p6View.version.text()).toBe '5.8.5'
|
||||
|
||||
p4View.dropdownButton.click()
|
||||
expect(p4View.homepage).toBeVisible()
|
||||
expect(p4View.homepage.find('a').attr('href')).toBe 'http://p4.io'
|
||||
expect(p4View.issues).toBeHidden()
|
||||
|
||||
p5View.dropdownButton.click()
|
||||
expect(p5View.homepage).toBeVisible()
|
||||
expect(p5View.homepage.find('a').attr('href')).toBe 'http://github.com/atom/p5'
|
||||
expect(p5View.issues).toBeVisible()
|
||||
expect(p5View.issues.find('a').attr('href')).toBe 'http://github.com/atom/p5/issues'
|
||||
|
||||
p6View.dropdownButton.click()
|
||||
expect(p6View.homepage).toBeHidden()
|
||||
expect(p6View.issues).toBeHidden()
|
||||
|
||||
describe "when Install is clicked", ->
|
||||
it "adds the package to the Installed tab", ->
|
||||
expect(panel.installedPackages.find("[name='p4']")).not.toExist()
|
||||
expect(panel.availablePackages.find("[name='p4']")).toExist()
|
||||
p4View = panel.availablePackages.find("[name='p4']").view()
|
||||
expect(p4View.defaultAction.text()).toBe 'Install'
|
||||
p4View.defaultAction.click()
|
||||
expect(panel.installedPackages.find("[name='p4']")).toExist()
|
||||
expect(p4View.defaultAction.text()).toBe 'Uninstall'
|
||||
@@ -1,80 +0,0 @@
|
||||
GeneralPanel = require '../lib/general-panel'
|
||||
Editor = require 'editor'
|
||||
|
||||
describe "GeneralPanel", ->
|
||||
panel = null
|
||||
|
||||
getValueForId = (id) ->
|
||||
element = panel.find("##{id.replace('.', '\\.')}")
|
||||
if element.is("input")
|
||||
element.attr('checked')?
|
||||
else
|
||||
element.view().getText()
|
||||
|
||||
setValueForId = (id, value) ->
|
||||
element = panel.find("##{id.replace('.', '\\.')}")
|
||||
if element.is("input")
|
||||
element.attr('checked', if value then 'checked' else '')
|
||||
element.change()
|
||||
else
|
||||
element.view().setText(value?.toString())
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
|
||||
|
||||
beforeEach ->
|
||||
config.set('foo.int', 22)
|
||||
config.set('foo.float', 0.1)
|
||||
config.set('foo.boolean', true)
|
||||
config.set('foo.string', 'hey')
|
||||
|
||||
panel = new GeneralPanel()
|
||||
spyOn(panel, "showSettings").andCallThrough()
|
||||
window.advanceClock(10000)
|
||||
waitsFor ->
|
||||
panel.showSettings.callCount > 0
|
||||
|
||||
it "automatically binds named fields to their corresponding config keys", ->
|
||||
expect(getValueForId('foo.int')).toBe '22'
|
||||
expect(getValueForId('foo.float')).toBe '0.1'
|
||||
expect(getValueForId('foo.boolean')).toBeTruthy()
|
||||
expect(getValueForId('foo.string')).toBe 'hey'
|
||||
|
||||
config.set('foo.int', 222)
|
||||
config.set('foo.float', 0.11)
|
||||
config.set('foo.boolean', false)
|
||||
config.set('foo.string', 'hey again')
|
||||
expect(getValueForId('foo.int')).toBe '222'
|
||||
expect(getValueForId('foo.float')).toBe '0.11'
|
||||
expect(getValueForId('foo.boolean')).toBeFalsy()
|
||||
expect(getValueForId('foo.string')).toBe 'hey again'
|
||||
|
||||
setValueForId('foo.int', 90)
|
||||
setValueForId('foo.float', 89.2)
|
||||
setValueForId('foo.string', "oh hi")
|
||||
setValueForId('foo.boolean', true)
|
||||
expect(config.get('foo.int')).toBe 90
|
||||
expect(config.get('foo.float')).toBe 89.2
|
||||
expect(config.get('foo.boolean')).toBe true
|
||||
expect(config.get('foo.string')).toBe 'oh hi'
|
||||
|
||||
setValueForId('foo.int', '')
|
||||
setValueForId('foo.float', '')
|
||||
setValueForId('foo.string', '')
|
||||
expect(config.get('foo.int')).toBeUndefined()
|
||||
expect(config.get('foo.float')).toBeUndefined()
|
||||
expect(config.get('foo.string')).toBeUndefined()
|
||||
|
||||
it "does not save the config value until it has been changed to a new value", ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
config.observe "foo.int", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
setValueForId('foo.int', 2)
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
observeHandler.reset()
|
||||
|
||||
setValueForId('foo.int', 2)
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
@@ -1,46 +0,0 @@
|
||||
SettingsView = require '../lib/settings-view'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
describe "SettingsView", ->
|
||||
settingsView = null
|
||||
|
||||
beforeEach ->
|
||||
settingsView = new SettingsView
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers which panel was visible", ->
|
||||
settingsView.showPanel('Packages')
|
||||
newSettingsView = deserialize(settingsView.serialize())
|
||||
settingsView.remove()
|
||||
newSettingsView.attachToDom()
|
||||
expect(newSettingsView.activePanelName).toBe 'Packages'
|
||||
|
||||
it "shows the previously active panel if it is added after deserialization", ->
|
||||
settingsView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
settingsView.showPanel('Panel 1')
|
||||
newSettingsView = deserialize(settingsView.serialize())
|
||||
settingsView.remove()
|
||||
newSettingsView.attachToDom()
|
||||
newSettingsView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
expect(newSettingsView.activePanelName).toBe 'Panel 1'
|
||||
|
||||
describe ".addPanel(name, view)", ->
|
||||
it "adds a menu entry to the left and a panel that can be activated by clicking it", ->
|
||||
settingsView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
settingsView.addPanel('Panel 2', $$ -> @div id: 'panel-2')
|
||||
|
||||
expect(settingsView.panelMenu.find('li a:contains(Panel 1)')).toExist()
|
||||
expect(settingsView.panelMenu.find('li a:contains(Panel 2)')).toExist()
|
||||
expect(settingsView.panelMenu.children(':first')).toHaveClass 'active'
|
||||
|
||||
settingsView.attachToDom()
|
||||
settingsView.panelMenu.find('li a:contains(Panel 1)').click()
|
||||
expect(settingsView.panelMenu.children('.active').length).toBe 1
|
||||
expect(settingsView.panelMenu.find('li:contains(Panel 1)')).toHaveClass('active')
|
||||
expect(settingsView.panels.find('#panel-1')).toBeVisible()
|
||||
expect(settingsView.panels.find('#panel-2')).toBeHidden()
|
||||
settingsView.panelMenu.find('li a:contains(Panel 2)').click()
|
||||
expect(settingsView.panelMenu.children('.active').length).toBe 1
|
||||
expect(settingsView.panelMenu.find('li:contains(Panel 2)')).toHaveClass('active')
|
||||
expect(settingsView.panels.find('#panel-1')).toBeHidden()
|
||||
expect(settingsView.panels.find('#panel-2')).toBeVisible()
|
||||
@@ -1,49 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
ThemePanel = require '../lib/theme-panel'
|
||||
|
||||
describe "ThemePanel", ->
|
||||
panel = null
|
||||
|
||||
beforeEach ->
|
||||
config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
|
||||
panel = new ThemePanel
|
||||
|
||||
describe "when an enabled theme is reloced in the themes list", ->
|
||||
it "updates the 'core.themes' config key to reflect the new order", ->
|
||||
li = panel.enabledThemes.children(':first').detach()
|
||||
panel.enabledThemes.append(li)
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-syntax', 'atom-dark-ui']
|
||||
|
||||
describe "when a theme is dragged into the enabled themes list", ->
|
||||
it "updates the 'core.themes' config key to reflect the themes in the enabled list", ->
|
||||
dragHelper = panel.availableThemes.find("li[name='atom-light-ui']").clone()
|
||||
panel.enabledThemes.prepend(dragHelper)
|
||||
panel.enabledThemes.sortable('option', 'receive')(null, helper: dragHelper[0])
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-light-ui', 'atom-dark-ui', 'atom-dark-syntax']
|
||||
|
||||
describe "when the theme is already present in the enabled list", ->
|
||||
it "removes the previous instance of the theme, updating the order based on the location of drag", ->
|
||||
dragHelper = panel.availableThemes.find("li[name='atom-dark-ui']").clone()
|
||||
panel.enabledThemes.append(dragHelper)
|
||||
panel.enabledThemes.sortable('option', 'receive')(null, helper: dragHelper[0])
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-syntax', 'atom-dark-ui']
|
||||
|
||||
dragHelper = panel.availableThemes.find("li[name='atom-dark-ui']").clone()
|
||||
panel.enabledThemes.prepend(dragHelper)
|
||||
panel.enabledThemes.sortable('option', 'receive')(null, helper: dragHelper[0])
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-ui', 'atom-dark-syntax']
|
||||
|
||||
describe "when the disable icon is clicked on a theme li", ->
|
||||
it "removes the theme from the list and the 'core.themes' array", ->
|
||||
panel.enabledThemes.find('li:first .disable-theme').click()
|
||||
expect(panel.getEnabledThemeNames()).toEqual ['atom-dark-syntax']
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-syntax']
|
||||
|
||||
describe "when the 'core.config' key is updated", ->
|
||||
it "refreshes the enabled themes list", ->
|
||||
config.set('core.themes', ['atom-light-ui', 'atom-light-syntax'])
|
||||
expect(panel.getEnabledThemeNames()).toEqual ['atom-light-ui', 'atom-light-syntax']
|
||||
@@ -1,151 +0,0 @@
|
||||
@import "bootstrap/less/variables.less";
|
||||
@import "octicon-mixins.less";
|
||||
|
||||
#settings-view {
|
||||
background: white;
|
||||
display: -webkit-flex;
|
||||
|
||||
#config-menu {
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-direction: column;
|
||||
|
||||
#panels-menu {
|
||||
width: 150px;
|
||||
margin-left: @line-height-base;
|
||||
margin-top: @line-height-base;
|
||||
-webkit-flex-grow: 1;
|
||||
|
||||
li a {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
#open-dot-atom {
|
||||
width: 150px;
|
||||
margin-left: @line-height-base;
|
||||
margin-bottom: @line-height-base;
|
||||
}
|
||||
}
|
||||
|
||||
#panels {
|
||||
-webkit-flex: 1;
|
||||
-webkit-flex-flow: column;
|
||||
display: -webkit-flex;
|
||||
padding: @line-height-base;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
|
||||
> div {
|
||||
-webkit-flex: 1;
|
||||
}
|
||||
|
||||
.editor {
|
||||
padding-left: 0.25em;
|
||||
border: 1px solid #aaa;
|
||||
box-shadow: 1px 1px 1px 0 #888 inset;
|
||||
}
|
||||
}
|
||||
|
||||
#themes-config {
|
||||
height: 100%;
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-flow: column;
|
||||
|
||||
#theme-picker {
|
||||
-webkit-flex: 1;
|
||||
display: -webkit-flex;
|
||||
min-height: 0;
|
||||
|
||||
.panel {
|
||||
-webkit-flex: 1;
|
||||
display: -webkit-flex;
|
||||
-webkit-flex-flow: column;
|
||||
min-height: 0;
|
||||
&:first-child { margin-right: @line-height-base / 2; }
|
||||
&:last-child { margin-left: @line-height-base / 2; }
|
||||
|
||||
.panel-heading {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
ol {
|
||||
margin-top: 0;
|
||||
-webkit-flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
|
||||
li {
|
||||
-webkit-user-select: none;
|
||||
cursor: default;
|
||||
padding-right: 15px;
|
||||
&:first-child { border-top: 0; }
|
||||
}
|
||||
}
|
||||
|
||||
#enabled-themes {
|
||||
li:hover .disable-theme {
|
||||
.octicon(x);
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
&:hover { color: #000; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li.ui-draggable-dragging, li.ui-sortable-helper {
|
||||
border: 0;
|
||||
box-sizing: content-box;
|
||||
background: white;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, .5);
|
||||
}
|
||||
}
|
||||
|
||||
#packages {
|
||||
white-space: nowrap;
|
||||
.package-enabled {
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
#package-filter {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.btn-retry {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.readme {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.panel-heading .label {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.loading-area {
|
||||
span {
|
||||
.octicon(hourglass);
|
||||
|
||||
&:before {
|
||||
font-size: 1.1em;
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.package-panel {
|
||||
.nav {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.badge {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user