use markdown for description in blueprint composer

on type quickinfo from palette, and when type config editor shows, and on config in that editor
diff --git a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
index 3cf0cc9..a78c9fd 100644
--- a/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
+++ b/ui-modules/blueprint-composer/app/components/catalog-selector/catalog-selector.template.html
@@ -162,7 +162,9 @@
             <p><i class="mini-icon fa fa-fw fa-bookmark"></i> <samp class="type-symbolic-name">{{popover.symbolicName}}</samp></p>
             <p ng-if="popover.version"><i class="mini-icon fa fa-fw fa-code-fork"></i> {{popover.version}}</p>
         </div>
-        <p class="quick-info-description" ng-if="popover.description">{{popover.description}}</p>
+        <p class="quick-info-description">
+            <md-first-line raw-data="::popover.description" raw-item="::popover"></md-first-line>
+        </p>
         <p class="quick-info-description" ng-if="popover == freeFormTile">This is an ad hoc tile for an item entered by the user not known in the catalog.</p>
         <div class="quick-info-metadata bundle">
             <p ng-if="lastUsedText(popover)"><i class="mini-icon fa fa-clock-o"></i> {{ lastUsedText(popover) }}
diff --git a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
index a798d9c..19794ab 100644
--- a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
+++ b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js
@@ -584,6 +584,9 @@
             version: data.containingBundle.split(':')[1]
         });
         entity.miscData.set('typeName', data.displayName || data.symbolicName);
+        entity.miscData.set('displayName', data.displayName);
+        entity.miscData.set('symbolicName', data.symbolicName);
+        entity.miscData.set('description', data.description);
         entity.miscData.set('config', data.config || []);
         entity.miscData.set('parameters', data.parameters || []);
         entity.miscData.set('sensors', data.sensors || []);
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
index 9106aa8..9da1581 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js
@@ -35,7 +35,7 @@
 const REPLACED_DSL_ENTITYSPEC = '___brooklyn:entitySpec';
 
 angular.module(MODULE_NAME, [onEnter, autoGrow, blurOnEnter, brooklynDslEditor, brooklynDslViewer])
-    .directive('specEditor', ['$rootScope', '$templateCache', '$injector', '$sanitize', '$filter', '$log', '$sce', '$timeout', '$document', '$state', '$compile', 'blueprintService', 'composerOverrides', specEditorDirective])
+    .directive('specEditor', ['$rootScope', '$templateCache', '$injector', '$sanitize', '$filter', '$log', '$sce', '$timeout', '$document', '$state', '$compile', 'blueprintService', 'composerOverrides', 'mdHelper', specEditorDirective])
     .filter('specEditorConfig', specEditorConfigFilter)
     .filter('specEditorType', specEditorTypeFilter)
     .run(['$templateCache', templateCache]);
@@ -89,7 +89,7 @@
     'string', 'boolean', 'integer', 'double', 'duration', ' port'
     ];
 
-export function specEditorDirective($rootScope, $templateCache, $injector, $sanitize, $filter, $log, $sce, $timeout, $document, $state, $compile, blueprintService, composerOverrides) {
+export function specEditorDirective($rootScope, $templateCache, $injector, $sanitize, $filter, $log, $sce, $timeout, $document, $state, $compile, blueprintService, composerOverrides, mdHelper) {
     return {
         restrict: 'E',
         scope: {
@@ -118,6 +118,7 @@
         scope.REPLACED_DSL_ENTITYSPEC = REPLACED_DSL_ENTITYSPEC;
         scope.parameters = [];
         scope.config = {};
+        specEditor.descriptionVisible = false;
         specEditor.paramTypes = PARAM_TYPES;
 
         specEditor.getParameter = getParameter;
@@ -214,6 +215,11 @@
         // Model
         scope.$watch('model', (newVal, oldVal) => {
             if (newVal && !newVal.equals(oldVal)) {
+                scope.modelDescription = mdHelper.analyzeDescription({
+                    description: newVal.miscData.get('description'),
+                    symbolicName: newVal.miscData.get('symbolicName'),
+                    displayName: newVal.miscData.get('displayName'),
+                });
                 loadLocalConfigFromModel();
                 loadLocalParametersFromModel();
             }
@@ -470,16 +476,6 @@
             return issues.some(issue => issue.level === ISSUE_LEVEL.ERROR) ? 'badge-danger' : 'badge-warning';
         };
 
-        specEditor.descriptionHtml = (text) => {
-            let out = [];
-            for (let item of text.split(/\n\n+/)) {
-                out.push('<div class="paragraph-spacing"></div>');
-                out.push($sanitize(item));
-            }
-            out.splice(0, 1);
-            return $sce.trustAsHtml(out.join("\n"));
-        };
-
         function getConfigWidgetModeInternal(item, val) {
             if (angular.element($document[0].activeElement).hasClass("form-control") && item.widgetMode) {
                 // don't switch mode in mid-edit, e.g. if you are manually typing $brooklyn:component("x").config("y")
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less
index 3a6e045..68fd6e3 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less
@@ -32,7 +32,7 @@
     cursor: pointer;
     transition: 0.1s ease all;
     &:hover {
-      color: @brand-primary;
+      color: @brand-primary !important;
     }
   }
   .remove-affordance() {
@@ -903,6 +903,15 @@
         order: 99;
    }
   }
+
+  .type-description {
+    .toolbar-button-affordance();
+    padding-top: 1ex;
+    float: right;
+    &.active {
+      color: @gray;
+    }
+  }
 }
 
 .popover.spec-editor-popover {
diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
index 6c46db0..19b0371 100644
--- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
+++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html
@@ -41,16 +41,32 @@
             <img ng-src="{{model.icon}}" alt="{{model | entityName}} logo" class="media-object" />
         </div>
         <div class="media-body panel-header-body">
-            <div title="{{ model.miscData.get('typeName') }}"><i class="fa fa-bookmark panel-header-icon"></i><samp class="entity-type-header">{{model.type}}</samp> <span class="label label-primary version">{{model | entityVersion}}</span></div>
+            <div title="{{ model.miscData.get('typeName') }}">
+                <i class="fa fa-question-circle type-description"
+                   ng-class="{ 'active': specEditor.descriptionVisible }"
+                   title="{{ specEditor.descriptionVisible ? 'Hide description' : 'Show description' }}"
+                   ng-if="modelDescription.isPresent"
+                   ng-click="specEditor.descriptionVisible = !specEditor.descriptionVisible"></i>
+                <i class="fa fa-bookmark panel-header-icon"></i><samp class="entity-type-header">{{model.type}}</samp>
+                <span class="label label-primary version">{{model | entityVersion}}</span>
+            </div>
             <div ng-if="['POLICY', 'ENRICHER'].indexOf(model.family.id) === -1" class="identifier">
                 <i class="fa fa-id-card-o panel-header-icon"></i>
                 <input class="form-control editable" ng-model="model.id" placeholder="(no reference ID)" blur-on-enter />
                 </div>
+            <md-if-oneline ng-show="specEditor.descriptionVisible && modelDescription.isNonMultiline" data="modelDescription"></md-if-oneline>
         </div>
     </div>
   </div>
 </section>
 
+<br-collapsible state="true" ng-show="specEditor.descriptionVisible && modelDescription.isMultiline">
+    <heading>Description</heading>
+
+    <div style="margin-left: 3px;">
+        <md-if-multiline ng-show="specEditor.descriptionVisible" data="modelDescription"></md-if-multiline>
+    </div>
+</br-collapsible>
 
 <!-- ENTITY PARAMETERS -->
 <br-collapsible state="state.parameters.open" ng-if="!model.parent">  <!-- the ng-if is needed to make state update?! -->
@@ -672,7 +688,9 @@
             <p><i class="mini-icon fa fa-fw fa-cog"></i> <samp class="type-symbolic-name">{{item.name}}</samp>
                 <span class="config-type label-color column-for-type oneline label label-success">{{item.type}}</span></p>
         </div>
-        <p class="quick-info-description" ng-if="item.description" ng-bind-html="specEditor.descriptionHtml(item.description)"></p>
+        <p class="quick-info-description">
+            <md-field raw-item="item"/>
+        </p>
         <div class="quick-info-metadata config-default" ng-if="item.defaultValue"></i>Default value: <samp>{{item.defaultValue}}</samp></div>
     </div>
 
@@ -686,7 +704,9 @@
             <p><i class="mini-icon fa fa-fw fa-cog"></i> <samp class="type-symbolic-name">{{item.name}}</samp>
             <span class="config-type label-color column-for-type oneline label label-success">{{item.type}}</span></p>
         </div>
-        <p class="quick-info-description" ng-if="item.description" ng-bind-html="specEditor.descriptionHtml(item.description)"></p>
+        <p class="quick-info-description" ng-if="item.description">
+            <md-field raw-item="item"/>
+        </p>
         <div class="quick-info-metadata config-default" ng-if="item.defaultValue"></i>Default value: <samp>{{item.defaultValue}}</samp></div>
         <div class="quick-info-metadata config-required" ng-if="item.constraints.required"><i>This field is required.</div>
     </div>
diff --git a/ui-modules/blueprint-composer/app/index.js b/ui-modules/blueprint-composer/app/index.js
index e45c07f..7504627 100755
--- a/ui-modules/blueprint-composer/app/index.js
+++ b/ui-modules/blueprint-composer/app/index.js
@@ -35,6 +35,7 @@
 import brooklynUserManagement from 'brooklyn-ui-utils/user-management/user-management';
 import brYamlEditor from 'brooklyn-ui-utils/yaml-editor/yaml-editor';
 import brUtils from 'brooklyn-ui-utils/utils/general';
+import mdHelper from 'brooklyn-ui-utils/md-helper';
 
 import brSpecEditor from './components/spec-editor/spec-editor.directive';
 import brooklynCatalogSaver from './components/catalog-saver/catalog-saver.directive';
@@ -79,7 +80,7 @@
 
 angular.module('brooklynBlueprintComposer', [ngAnimate, ngResource, ngCookies, ngClipboard, uiRouter, 'ui.router.state.events', brCore,
     brServerStatus, brAutoFocus, brIconGenerator, brInterstitialSpinner, brooklynModuleLinks, brooklynUserManagement,
-    brYamlEditor, brUtils, brSpecEditor, brooklynCatalogSaver, brooklynApi, bottomSheet, stackViewer, brDragndrop,
+    brYamlEditor, brUtils, brSpecEditor, brooklynCatalogSaver, brooklynApi, bottomSheet, stackViewer, brDragndrop, mdHelper,
     customActionDirective, customConfigSuggestionDropdown, paletteApiProvider, paletteServiceProvider, blueprintLoaderApiProvider,
     breadcrumbs, catalogSelector, designer, objectCache, entityFilters, locationFilter, actionService, tabService, composerOverrides, blueprintService,
     dslService, paletteDragAndDropService, recentlyUsedService, scriptTagDecorator, brandAngularJs])
diff --git a/ui-modules/utils/md-helper/index.js b/ui-modules/utils/md-helper/index.js
index d462c3b..4890749 100644
--- a/ui-modules/utils/md-helper/index.js
+++ b/ui-modules/utils/md-helper/index.js
@@ -37,22 +37,16 @@
         scope: {
             data: '<',
             rawData: '<',
+            rawItem: '<',
         },
+        controller: ['$scope', populateData],
         template: `
             <div>
                 <md-if-oneline data="data"></md-if-oneline>
                 <md-if-multiline data="data"></md-if-multiline>
             </div>
         `,
-        controller: ['$scope', function ($scope) {
-            if ($scope.rawData && !$scope.data) {
-                $scope.data = analyze($scope.rawData);
-            }
-        }],
     };
-
-    function link(scope, element, attrs) {
-    }
 }
 
 // prints out one line of data -- in future could use markdown formatting, but currently does not
@@ -61,14 +55,14 @@
         restrict: 'E',
         scope: {
             data: '<',
+            rawData: '<',
+            rawItem: '<',
         },
+        controller: ['$scope', populateData],
         template: `
             <span>{{::data.oneline}}</span>
         `,
     };
-
-    function link(scope, element, attrs) {
-    }
 }
 
 // prints out full, formatted, _if_ it is one line of data
@@ -77,7 +71,10 @@
         restrict: 'E',
         scope: {
             data: '<',
+            rawData: '<',
+            rawItem: '<',
         },
+        controller: ['$scope', populateData],
         template: `
             <div ng-if="data.isNonMultiline">
                 <p ng-if="data.unformatted">{{data.unformatted}}</p>
@@ -85,9 +82,6 @@
             </div>
         `,
     };
-
-    function link(scope, element, attrs) {
-    }
 }
 
 // prints out all the data, formatted, if multiline
@@ -96,19 +90,37 @@
         restrict: 'E',
         scope: {
             data: '<',
+            rawData: '<',
+            rawItem: '<',
         },
+        controller: ['$scope', populateData],
+        // for multiline, collapse margin from children eg an <h1> first element, inserting then removing a 24px margin
         template: `
             <div ng-if="data.isMultiline">
                 <pre ng-if="data.unformatted">{{data.unformatted}}</pre>
-                <div ng-if="data.markdownFormatted" ng-bind-html="data.markdownFormatted"></div>
+                <div ng-if="data.markdownFormatted" style="margin-top: -24px;"><div style="margin-top: 24px;" ng-bind-html="data.markdownFormatted"></div></div>
             </div>
         `,
     };
 }
 
+function nameFieldValues(src) {
+    src = src || {};
+    return ['symbolicName', 'displayName', 'typeName', 'name'].map(x => src[x]);
+}
+
+function populateData($scope) {
+    if (!$scope.data) {
+        if (!$scope.rawData && $scope.rawItem) {
+            $scope.rawData = $scope.rawItem.description;
+        }
+        $scope.data = analyze($scope.rawData, nameFieldValues($scope.rawItem));
+    }
+}
+
 export function analyzeDescription(input) {
     input = input || {};
-    return analyze(input.description, [input.symbolicName, input.displayName, input.name]);
+    return analyze(input.description, nameFieldValues(input));
 }
 
 export function analyze(field, names) {
@@ -121,6 +133,7 @@
         try {
             result.markdownFormatted = marked(field);
         } catch (e) {
+            console.log("could not convert markdown; treading as unformatted", e);
             // not markdown
             result.unformatted = field;
         }