diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index 5ff960b7ed..44c6321e90 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -1,331 +1,350 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'celerity_mapper.php' to rebuild
  * it.
  * @generated
  */
 
 celerity_register_resource_map(array(
   'aphront-dialog-view-css' =>
   array(
     'uri' => '/res/771b987d/rsrc/css/aphront/dialog-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/dialog-view.css',
   ),
   'aphront-form-view-css' =>
   array(
     'uri' => '/res/785ac1c6/rsrc/css/aphront/form-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/form-view.css',
   ),
   'aphront-panel-view-css' =>
   array(
-    'uri' => '/res/fe62e634/rsrc/css/aphront/panel-view.css',
+    'uri' => '/res/63672373/rsrc/css/aphront/panel-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/panel-view.css',
   ),
   'aphront-request-failure-view-css' =>
   array(
-    'uri' => '/res/d7df3b42/rsrc/css/aphront/request-failure-view.css',
+    'uri' => '/res/97b8337a/rsrc/css/aphront/request-failure-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/request-failure-view.css',
   ),
   'aphront-side-nav-view-css' =>
   array(
     'uri' => '/res/0fc0545c/rsrc/css/aphront/side-nav-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/side-nav-view.css',
   ),
   'aphront-table-view-css' =>
   array(
     'uri' => '/res/52b0191f/rsrc/css/aphront/table-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/table-view.css',
   ),
   'aphront-tokenizer-control-css' =>
   array(
     'uri' => '/res/a3d23074/rsrc/css/aphront/tokenizer.css',
     'type' => 'css',
     'requires' =>
     array(
       0 => 'aphront-typeahead-control-css',
     ),
     'disk' => '/rsrc/css/aphront/tokenizer.css',
   ),
   'aphront-typeahead-control-css' =>
   array(
     'uri' => '/res/928df9f0/rsrc/css/aphront/typeahead.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/typeahead.css',
   ),
   'phabricator-standard-page-view' =>
   array(
     'uri' => '/res/fb02fb0e/rsrc/css/application/base/standard-page-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/base/standard-page-view.css',
   ),
+  'differential-revision-add-comment-css' =>
+  array(
+    'uri' => '/res/623fef21/rsrc/css/application/differential/add-comment.css',
+    'type' => 'css',
+    'requires' =>
+    array(
+    ),
+    'disk' => '/rsrc/css/application/differential/add-comment.css',
+  ),
   'differential-changeset-view-css' =>
   array(
     'uri' => '/res/658d181a/rsrc/css/application/differential/changeset-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/changeset-view.css',
   ),
   'differential-core-view-css' =>
   array(
     'uri' => '/res/525d1a12/rsrc/css/application/differential/core.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/core.css',
   ),
   'differential-revision-comment-list-css' =>
   array(
     'uri' => '/res/a1c117db/rsrc/css/application/differential/revision-comment-list.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-comment-list.css',
   ),
   'differential-revision-comment-css' =>
   array(
     'uri' => '/res/bf6369c6/rsrc/css/application/differential/revision-comment.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-comment.css',
   ),
   'differential-revision-detail-css' =>
   array(
     'uri' => '/res/230a67c6/rsrc/css/application/differential/revision-detail.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-detail.css',
   ),
   'differential-revision-history-css' =>
   array(
     'uri' => '/res/755f3da3/rsrc/css/application/differential/revision-history.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-history.css',
   ),
   'differential-table-of-contents-css' =>
   array(
     'uri' => '/res/a4a7b2b5/rsrc/css/application/differential/table-of-contents.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/table-of-contents.css',
   ),
   'phabricator-directory-css' =>
   array(
     'uri' => '/res/6a000601/rsrc/css/application/directory/phabricator-directory.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/directory/phabricator-directory.css',
   ),
   'phabricator-core-buttons-css' =>
   array(
     'uri' => '/res/6e348ba4/rsrc/css/core/buttons.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/buttons.css',
   ),
   'phabricator-core-css' =>
   array(
     'uri' => '/res/39ce37c2/rsrc/css/core/core.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/core.css',
   ),
   'phabricator-remarkup-css' =>
   array(
     'uri' => '/res/786989c3/rsrc/css/core/remarkup.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/remarkup.css',
   ),
   'syntax-highlighting-css' =>
   array(
     'uri' => '/res/fb673ece/rsrc/css/core/syntax.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/syntax.css',
   ),
   'javelin-behavior-aphront-basic-tokenizer' =>
   array(
     'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-lib-dev',
     ),
     'disk' => '/rsrc/js/application/core/behavior-tokenizer.js',
   ),
+  'javelin-behavior-differential-feedback-preview' =>
+  array(
+    'uri' => '/res/34fbb670/rsrc/js/application/differential/behavior-comment-preview.js',
+    'type' => 'js',
+    'requires' =>
+    array(
+      0 => 'javelin-lib-dev',
+    ),
+    'disk' => '/rsrc/js/application/differential/behavior-comment-preview.js',
+  ),
   'javelin-behavior-differential-populate' =>
   array(
     'uri' => '/res/9982573c/rsrc/js/application/differential/behavior-populate.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-lib-dev',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-populate.js',
   ),
   'javelin-init-dev' =>
   array(
     'uri' => '/res/c57a9e89/rsrc/js/javelin/init.dev.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/init.dev.js',
   ),
   'javelin-init-prod' =>
   array(
     'uri' => '/res/f0172c54/rsrc/js/javelin/init.min.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/init.min.js',
   ),
   'javelin-lib-dev' =>
   array(
     'uri' => '/res/3e747182/rsrc/js/javelin/javelin.dev.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/javelin.dev.js',
   ),
   'javelin-lib-prod' =>
   array(
     'uri' => '/res/9438670e/rsrc/js/javelin/javelin.min.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/javelin.min.js',
   ),
   'javelin-typeahead-dev' =>
   array(
     'uri' => '/res/c81c0f01/rsrc/js/javelin/typeahead.dev.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/typeahead.dev.js',
   ),
   'javelin-typeahead-prod' =>
   array(
     'uri' => '/res/1da2d984/rsrc/js/javelin/typeahead.min.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/typeahead.min.js',
   ),
 ), array (
   'packages' =>
   array (
-    '364a306c' =>
+    'd348c79d' =>
     array (
       'name' => 'core.pkg.css',
       'symbols' =>
       array (
         0 => 'phabricator-core-css',
         1 => 'phabricator-core-buttons-css',
         2 => 'phabricator-standard-page-view',
         3 => 'aphront-dialog-view-css',
         4 => 'aphront-form-view-css',
         5 => 'aphront-panel-view-css',
         6 => 'aphront-side-nav-view-css',
         7 => 'aphront-table-view-css',
         8 => 'aphront-tokenizer-control-css',
         9 => 'aphront-typeahead-control-css',
         10 => 'phabricator-directory-css',
       ),
-      'uri' => '/res/pkg/364a306c/core.pkg.css',
+      'uri' => '/res/pkg/d348c79d/core.pkg.css',
       'type' => 'css',
     ),
     '69b11588' =>
     array (
       'name' => 'differential.pkg.css',
       'symbols' =>
       array (
         0 => 'differential-core-view-css',
         1 => 'differential-changeset-view-css',
         2 => 'differential-revision-detail-css',
         3 => 'differential-revision-history-css',
         4 => 'differential-table-of-contents-css',
       ),
       'uri' => '/res/pkg/69b11588/differential.pkg.css',
       'type' => 'css',
     ),
   ),
   'reverse' =>
   array (
-    'phabricator-core-css' => '364a306c',
-    'phabricator-core-buttons-css' => '364a306c',
-    'phabricator-standard-page-view' => '364a306c',
-    'aphront-dialog-view-css' => '364a306c',
-    'aphront-form-view-css' => '364a306c',
-    'aphront-panel-view-css' => '364a306c',
-    'aphront-side-nav-view-css' => '364a306c',
-    'aphront-table-view-css' => '364a306c',
-    'aphront-tokenizer-control-css' => '364a306c',
-    'aphront-typeahead-control-css' => '364a306c',
-    'phabricator-directory-css' => '364a306c',
+    'phabricator-core-css' => 'd348c79d',
+    'phabricator-core-buttons-css' => 'd348c79d',
+    'phabricator-standard-page-view' => 'd348c79d',
+    'aphront-dialog-view-css' => 'd348c79d',
+    'aphront-form-view-css' => 'd348c79d',
+    'aphront-panel-view-css' => 'd348c79d',
+    'aphront-side-nav-view-css' => 'd348c79d',
+    'aphront-table-view-css' => 'd348c79d',
+    'aphront-tokenizer-control-css' => 'd348c79d',
+    'aphront-typeahead-control-css' => 'd348c79d',
+    'phabricator-directory-css' => 'd348c79d',
     'differential-core-view-css' => '69b11588',
     'differential-changeset-view-css' => '69b11588',
     'differential-revision-detail-css' => '69b11588',
     'differential-revision-history-css' => '69b11588',
     'differential-table-of-contents-css' => '69b11588',
   ),
 ));
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 261d63ec43..9a5aacd4d4 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,342 +1,344 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
  * @generated
  */
 
 phutil_register_library_map(array(
   'class' =>
   array(
     'Aphront400Response' => 'aphront/response/400',
     'Aphront404Response' => 'aphront/response/404',
     'AphrontAjaxResponse' => 'aphront/response/ajax',
     'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
     'AphrontController' => 'aphront/controller',
     'AphrontDatabaseConnection' => 'storage/connection/base',
     'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration',
     'AphrontDefaultApplicationController' => 'aphront/default/controller',
     'AphrontDialogResponse' => 'aphront/response/dialog',
     'AphrontDialogView' => 'view/dialog',
     'AphrontErrorView' => 'view/form/error',
     'AphrontException' => 'aphront/exception/base',
     'AphrontFileResponse' => 'aphront/response/file',
     'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
     'AphrontFormControl' => 'view/form/control/base',
     'AphrontFormFileControl' => 'view/form/control/file',
     'AphrontFormMarkupControl' => 'view/form/control/markup',
     'AphrontFormPasswordControl' => 'view/form/control/password',
     'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha',
     'AphrontFormSelectControl' => 'view/form/control/select',
     'AphrontFormStaticControl' => 'view/form/control/static',
     'AphrontFormSubmitControl' => 'view/form/control/submit',
     'AphrontFormTextAreaControl' => 'view/form/control/textarea',
     'AphrontFormTextControl' => 'view/form/control/text',
     'AphrontFormTokenizerControl' => 'view/form/control/tokenizer',
     'AphrontFormView' => 'view/form/base',
     'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
     'AphrontNullView' => 'view/null',
     'AphrontPageView' => 'view/page/base',
     'AphrontPanelView' => 'view/layout/panel',
     'AphrontQueryConnectionException' => 'storage/exception/connection',
     'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost',
     'AphrontQueryCountException' => 'storage/exception/count',
     'AphrontQueryDuplicateKeyException' => 'storage/exception/duplicatekey',
     'AphrontQueryException' => 'storage/exception/base',
     'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing',
     'AphrontQueryParameterException' => 'storage/exception/parameter',
     'AphrontQueryRecoverableException' => 'storage/exception/recoverable',
     'AphrontRedirectException' => 'aphront/exception/redirect',
     'AphrontRedirectResponse' => 'aphront/response/redirect',
     'AphrontRequest' => 'aphront/request',
     'AphrontRequestFailureView' => 'view/page/failure',
     'AphrontResponse' => 'aphront/response/base',
     'AphrontSideNavView' => 'view/layout/sidenav',
     'AphrontTableView' => 'view/control/table',
     'AphrontURIMapper' => 'aphront/mapper',
     'AphrontView' => 'view/base',
     'AphrontWebpageResponse' => 'aphront/response/webpage',
     'CelerityAPI' => 'infrastructure/celerity/api',
     'CelerityResourceController' => 'infrastructure/celerity/controller',
     'CelerityResourceMap' => 'infrastructure/celerity/map',
     'CelerityStaticResourceResponse' => 'infrastructure/celerity/response',
     'ConduitAPIMethod' => 'applications/conduit/method/base',
     'ConduitAPIRequest' => 'applications/conduit/protocol/request',
     'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect',
     'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff',
     'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty',
     'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
     'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
     'ConduitException' => 'applications/conduit/protocol/exception',
     'DifferentialAction' => 'applications/differential/constants/action',
     'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
     'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
     'DifferentialChangeType' => 'applications/differential/constants/changetype',
     'DifferentialChangeset' => 'applications/differential/storage/changeset',
     'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview',
     'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview',
     'DifferentialChangesetParser' => 'applications/differential/parser/changeset',
     'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview',
     'DifferentialComment' => 'applications/differential/storage/comment',
     'DifferentialCommentEditor' => 'applications/differential/editor/comment',
     'DifferentialCommentMail' => 'applications/differential/mail/comment',
+    'DifferentialCommentPreviewController' => 'applications/differential/controller/commentpreview',
     'DifferentialCommentSaveController' => 'applications/differential/controller/commentsave',
     'DifferentialController' => 'applications/differential/controller/base',
     'DifferentialDAO' => 'applications/differential/storage/base',
     'DifferentialDiff' => 'applications/differential/storage/diff',
     'DifferentialDiffContentMail' => 'applications/differential/mail/diffcontent',
     'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty',
     'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents',
     'DifferentialDiffViewController' => 'applications/differential/controller/diffview',
     'DifferentialHunk' => 'applications/differential/storage/hunk',
     'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
     'DifferentialMail' => 'applications/differential/mail/base',
     'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup',
     'DifferentialNewDiffMail' => 'applications/differential/mail/newdiff',
     'DifferentialReviewRequestMail' => 'applications/differential/mail/reviewrequest',
     'DifferentialRevision' => 'applications/differential/storage/revision',
     'DifferentialRevisionCommentListView' => 'applications/differential/view/revisioncommentlist',
     'DifferentialRevisionCommentView' => 'applications/differential/view/revisioncomment',
     'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem',
     'DifferentialRevisionDetailView' => 'applications/differential/view/revisiondetail',
     'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit',
     'DifferentialRevisionEditor' => 'applications/differential/editor/revision',
     'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist',
     'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
     'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus',
     'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
     'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
     'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus',
     'Javelin' => 'infrastructure/javelin/api',
     'LiskDAO' => 'storage/lisk/dao',
     'Phabricator404Controller' => 'applications/base/controller/404',
     'PhabricatorAuthController' => 'applications/auth/controller/base',
     'PhabricatorConduitAPIController' => 'applications/conduit/controller/api',
     'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog',
     'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console',
     'PhabricatorConduitController' => 'applications/conduit/controller/base',
     'PhabricatorConduitDAO' => 'applications/conduit/storage/base',
     'PhabricatorConduitLogController' => 'applications/conduit/controller/log',
     'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog',
     'PhabricatorController' => 'applications/base/controller/base',
     'PhabricatorDirectoryCategory' => 'applications/directory/storage/category',
     'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete',
     'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit',
     'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist',
     'PhabricatorDirectoryController' => 'applications/directory/controller/base',
     'PhabricatorDirectoryDAO' => 'applications/directory/storage/base',
     'PhabricatorDirectoryItem' => 'applications/directory/storage/item',
     'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete',
     'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit',
     'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist',
     'PhabricatorDirectoryMainController' => 'applications/directory/controller/main',
     'PhabricatorEmailLoginController' => 'applications/auth/controller/email',
     'PhabricatorEmailTokenController' => 'applications/auth/controller/emailtoken',
     'PhabricatorEnv' => 'infrastructure/env',
     'PhabricatorFacebookAuthController' => 'applications/auth/controller/facebookauth',
     'PhabricatorFacebookAuthDiagnosticsController' => 'applications/auth/controller/facebookauth/diagnostics',
     'PhabricatorFile' => 'applications/files/storage/file',
     'PhabricatorFileController' => 'applications/files/controller/base',
     'PhabricatorFileDAO' => 'applications/files/storage/base',
     'PhabricatorFileListController' => 'applications/files/controller/list',
     'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob',
     'PhabricatorFileURI' => 'applications/files/uri',
     'PhabricatorFileUploadController' => 'applications/files/controller/upload',
     'PhabricatorFileViewController' => 'applications/files/controller/view',
     'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
     'PhabricatorLoginController' => 'applications/auth/controller/login',
     'PhabricatorLogoutController' => 'applications/auth/controller/logout',
     'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite',
     'PhabricatorMetaMTAController' => 'applications/metamta/controller/base',
     'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base',
     'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list',
     'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail',
     'PhabricatorMetaMTAMailingList' => 'applications/metamta/storage/mailinglist',
     'PhabricatorMetaMTAMailingListEditController' => 'applications/metamta/controller/mailinglistedit',
     'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists',
     'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send',
     'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
     'PhabricatorObjectHandle' => 'applications/phid/handle',
     'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
     'PhabricatorPHID' => 'applications/phid/storage/phid',
     'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate',
     'PhabricatorPHIDController' => 'applications/phid/controller/base',
     'PhabricatorPHIDDAO' => 'applications/phid/storage/base',
     'PhabricatorPHIDListController' => 'applications/phid/controller/list',
     'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup',
     'PhabricatorPHIDType' => 'applications/phid/storage/type',
     'PhabricatorPHIDTypeEditController' => 'applications/phid/controller/typeedit',
     'PhabricatorPHIDTypeListController' => 'applications/phid/controller/typelist',
     'PhabricatorPeopleController' => 'applications/people/controller/base',
     'PhabricatorPeopleEditController' => 'applications/people/controller/edit',
     'PhabricatorPeopleListController' => 'applications/people/controller/list',
     'PhabricatorPeopleProfileController' => 'applications/people/controller/profile',
     'PhabricatorStandardPageView' => 'view/page/standard',
     'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common',
     'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
     'PhabricatorUser' => 'applications/people/storage/user',
     'PhabricatorUserDAO' => 'applications/people/storage/base',
   ),
   'function' =>
   array(
     '_qsprintf_check_scalar_type' => 'storage/qsprintf',
     '_qsprintf_check_type' => 'storage/qsprintf',
     'celerity_generate_unique_node_id' => 'infrastructure/celerity/api',
     'celerity_register_resource_map' => 'infrastructure/celerity/map',
     'javelin_render_tag' => 'infrastructure/javelin/markup',
     'phabricator_format_relative_time' => 'view/utils',
     'phabricator_format_timestamp' => 'view/utils',
     'phabricator_format_units_generic' => 'view/utils',
     'qsprintf' => 'storage/qsprintf',
     'queryfx' => 'storage/queryfx',
     'queryfx_all' => 'storage/queryfx',
     'queryfx_one' => 'storage/queryfx',
     'require_celerity_resource' => 'infrastructure/celerity/api',
     'vqsprintf' => 'storage/qsprintf',
     'vqueryfx' => 'storage/queryfx',
     'vqueryfx_all' => 'storage/queryfx',
     'xsprintf_query' => 'storage/qsprintf',
   ),
   'requires_class' =>
   array(
     'Aphront400Response' => 'AphrontResponse',
     'Aphront404Response' => 'AphrontResponse',
     'AphrontAjaxResponse' => 'AphrontResponse',
     'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
     'AphrontDefaultApplicationController' => 'AphrontController',
     'AphrontDialogResponse' => 'AphrontResponse',
     'AphrontDialogView' => 'AphrontView',
     'AphrontErrorView' => 'AphrontView',
     'AphrontFileResponse' => 'AphrontResponse',
     'AphrontFormCheckboxControl' => 'AphrontFormControl',
     'AphrontFormControl' => 'AphrontView',
     'AphrontFormFileControl' => 'AphrontFormControl',
     'AphrontFormMarkupControl' => 'AphrontFormControl',
     'AphrontFormPasswordControl' => 'AphrontFormControl',
     'AphrontFormRecaptchaControl' => 'AphrontFormControl',
     'AphrontFormSelectControl' => 'AphrontFormControl',
     'AphrontFormStaticControl' => 'AphrontFormControl',
     'AphrontFormSubmitControl' => 'AphrontFormControl',
     'AphrontFormTextAreaControl' => 'AphrontFormControl',
     'AphrontFormTextControl' => 'AphrontFormControl',
     'AphrontFormTokenizerControl' => 'AphrontFormControl',
     'AphrontFormView' => 'AphrontView',
     'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
     'AphrontNullView' => 'AphrontView',
     'AphrontPageView' => 'AphrontView',
     'AphrontPanelView' => 'AphrontView',
     'AphrontQueryConnectionException' => 'AphrontQueryException',
     'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
     'AphrontQueryCountException' => 'AphrontQueryException',
     'AphrontQueryDuplicateKeyException' => 'AphrontQueryException',
     'AphrontQueryObjectMissingException' => 'AphrontQueryException',
     'AphrontQueryParameterException' => 'AphrontQueryException',
     'AphrontQueryRecoverableException' => 'AphrontQueryException',
     'AphrontRedirectException' => 'AphrontException',
     'AphrontRedirectResponse' => 'AphrontResponse',
     'AphrontRequestFailureView' => 'AphrontView',
     'AphrontSideNavView' => 'AphrontView',
     'AphrontTableView' => 'AphrontView',
     'AphrontWebpageResponse' => 'AphrontResponse',
     'CelerityResourceController' => 'AphrontController',
     'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod',
     'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
     'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
     'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
     'DifferentialAddCommentView' => 'AphrontView',
     'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
     'DifferentialChangeset' => 'DifferentialDAO',
     'DifferentialChangesetDetailView' => 'AphrontView',
     'DifferentialChangesetListView' => 'AphrontView',
     'DifferentialChangesetViewController' => 'DifferentialController',
     'DifferentialComment' => 'DifferentialDAO',
     'DifferentialCommentMail' => 'DifferentialMail',
+    'DifferentialCommentPreviewController' => 'DifferentialController',
     'DifferentialCommentSaveController' => 'DifferentialController',
     'DifferentialController' => 'PhabricatorController',
     'DifferentialDAO' => 'PhabricatorLiskDAO',
     'DifferentialDiff' => 'DifferentialDAO',
     'DifferentialDiffContentMail' => 'DifferentialMail',
     'DifferentialDiffProperty' => 'DifferentialDAO',
     'DifferentialDiffTableOfContentsView' => 'AphrontView',
     'DifferentialDiffViewController' => 'DifferentialController',
     'DifferentialHunk' => 'DifferentialDAO',
     'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
     'DifferentialReviewRequestMail' => 'DifferentialMail',
     'DifferentialRevision' => 'DifferentialDAO',
     'DifferentialRevisionCommentListView' => 'AphrontView',
     'DifferentialRevisionCommentView' => 'AphrontView',
     'DifferentialRevisionDetailView' => 'AphrontView',
     'DifferentialRevisionEditController' => 'DifferentialController',
     'DifferentialRevisionListController' => 'DifferentialController',
     'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
     'DifferentialRevisionViewController' => 'DifferentialController',
     'Phabricator404Controller' => 'PhabricatorController',
     'PhabricatorAuthController' => 'PhabricatorController',
     'PhabricatorConduitAPIController' => 'PhabricatorConduitController',
     'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO',
     'PhabricatorConduitConsoleController' => 'PhabricatorConduitController',
     'PhabricatorConduitController' => 'PhabricatorController',
     'PhabricatorConduitDAO' => 'PhabricatorLiskDAO',
     'PhabricatorConduitLogController' => 'PhabricatorConduitController',
     'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
     'PhabricatorController' => 'AphrontController',
     'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO',
     'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryController' => 'PhabricatorController',
     'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
     'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
     'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController',
     'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
     'PhabricatorEmailLoginController' => 'PhabricatorAuthController',
     'PhabricatorEmailTokenController' => 'PhabricatorAuthController',
     'PhabricatorFacebookAuthController' => 'PhabricatorAuthController',
     'PhabricatorFacebookAuthDiagnosticsController' => 'PhabricatorAuthController',
     'PhabricatorFile' => 'PhabricatorFileDAO',
     'PhabricatorFileController' => 'PhabricatorController',
     'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
     'PhabricatorFileListController' => 'PhabricatorFileController',
     'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
     'PhabricatorFileUploadController' => 'PhabricatorFileController',
     'PhabricatorFileViewController' => 'PhabricatorFileController',
     'PhabricatorLiskDAO' => 'LiskDAO',
     'PhabricatorLoginController' => 'PhabricatorAuthController',
     'PhabricatorLogoutController' => 'PhabricatorAuthController',
     'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
     'PhabricatorMetaMTAController' => 'PhabricatorController',
     'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
     'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO',
     'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO',
     'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
     'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
     'PhabricatorPHID' => 'PhabricatorPHIDDAO',
     'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
     'PhabricatorPHIDController' => 'PhabricatorController',
     'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO',
     'PhabricatorPHIDListController' => 'PhabricatorPHIDController',
     'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController',
     'PhabricatorPHIDType' => 'PhabricatorPHIDDAO',
     'PhabricatorPHIDTypeEditController' => 'PhabricatorPHIDController',
     'PhabricatorPHIDTypeListController' => 'PhabricatorPHIDController',
     'PhabricatorPeopleController' => 'PhabricatorController',
     'PhabricatorPeopleEditController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleListController' => 'PhabricatorPeopleController',
     'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
     'PhabricatorStandardPageView' => 'AphrontPageView',
     'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
     'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
     'PhabricatorUser' => 'PhabricatorUserDAO',
     'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
   ),
   'requires_interface' =>
   array(
   ),
 ));
diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
index a7dbbe58cf..046b99f300 100644
--- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
@@ -1,195 +1,195 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 /**
  * @group aphront
  */
 class AphrontDefaultApplicationConfiguration
   extends AphrontApplicationConfiguration {
 
   public function getApplicationName() {
     return 'aphront-default';
   }
 
   public function getURIMap() {
     return array(
       '/repository/' => array(
         '$'                     => 'RepositoryListController',
         'new/$'                 => 'RepositoryEditController',
         'edit/(?<id>\d+)/$'     => 'RepositoryEditController',
         'delete/(?<id>\d+)/$'   => 'RepositoryDeleteController',
       ),
       '/' => array(
         '$'                     => 'PhabricatorDirectoryMainController',
       ),
       '/directory/' => array(
         'item/$'
           => 'PhabricatorDirectoryItemListController',
         'item/edit/(?:(?<id>\d+)/)?$'
           => 'PhabricatorDirectoryItemEditController',
         'item/delete/(?<id>\d+)/'
           => 'PhabricatorDirectoryItemDeleteController',
         'category/$'
           => 'PhabricatorDirectoryCategoryListController',
         'category/edit/(?:(?<id>\d+)/)?$'
           => 'PhabricatorDirectoryCategoryEditController',
         'category/delete/(?<id>\d+)/'
           => 'PhabricatorDirectoryCategoryDeleteController',
       ),
       '/file/' => array(
         '$' => 'PhabricatorFileListController',
         'upload/$' => 'PhabricatorFileUploadController',
         '(?<view>info)/(?<phid>[^/]+)/' => 'PhabricatorFileViewController',
         '(?<view>view)/(?<phid>[^/]+)/' => 'PhabricatorFileViewController',
         '(?<view>download)/(?<phid>[^/]+)/' => 'PhabricatorFileViewController',
       ),
       '/phid/' => array(
         '$' => 'PhabricatorPHIDLookupController',
         'list/$' => 'PhabricatorPHIDListController',
         'type/$' => 'PhabricatorPHIDTypeListController',
         'type/edit/(?:(?<id>\d+)/)?$' => 'PhabricatorPHIDTypeEditController',
         'new/$' => 'PhabricatorPHIDAllocateController',
       ),
       '/people/' => array(
         '$' => 'PhabricatorPeopleListController',
         'edit/(?:(?<username>\w+)/)?$' => 'PhabricatorPeopleEditController',
       ),
       '/p/(?<username>\w+)/$' => 'PhabricatorPeopleProfileController',
       '/conduit/' => array(
         '$' => 'PhabricatorConduitConsoleController',
         'method/(?<method>[^/]+)$' => 'PhabricatorConduitConsoleController',
         'log/$' => 'PhabricatorConduitLogController',
       ),
       '/api/(?<method>[^/]+)$' => 'PhabricatorConduitAPIController',
 
 
       '/D(?<id>\d+)' => 'DifferentialRevisionViewController',
       '/differential/' => array(
         '$' => 'DifferentialRevisionListController',
         'filter/(?<filter>\w+)/$' => 'DifferentialRevisionListController',
         'diff/(?<id>\d+)/$'       => 'DifferentialDiffViewController',
         'changeset/(?<id>\d+)/$'  => 'DifferentialChangesetViewController',
         'revision/edit/(?:(?<id>\d+)/)?$'
           => 'DifferentialRevisionEditController',
         'comment/' => array(
-          'preview/$' => 'DifferentialCommentPreviewController',
+          'preview/(?<id>\d+)/$' => 'DifferentialCommentPreviewController',
           'save/$' => 'DifferentialCommentSaveController',
           'inline/' => array(
             'preview/$' => 'DifferentialInlineCommentPreviewController',
             'edit/$' => 'DifferentialInlineCommentEditController',
           ),
         ),
       ),
 
       '/res/' => array(
         '(?<package>pkg/)?(?<hash>[a-f0-9]{8})/(?<path>.+\.(?:css|js))$'
           => 'CelerityResourceController',
       ),
 
       '/typeahead/' => array(
         'common/(?<type>\w+)/$'
           => 'PhabricatorTypeaheadCommonDatasourceController',
       ),
 
       '/mail/' => array(
         '$' => 'PhabricatorMetaMTAListController',
         'send/$' => 'PhabricatorMetaMTASendController',
         'view/(?<id>\d+)/$' => 'PhabricatorMetaMTAViewController',
         'lists/$' => 'PhabricatorMetaMTAMailingListsController',
         'lists/edit/(?:(?<id>\d+)/)?$'
           => 'PhabricatorMetaMTAMailingListEditController',
       ),
 
       '/login/' => array(
         '$' => 'PhabricatorLoginController',
         'email/$' => 'PhabricatorEmailLoginController',
         'etoken/(?<token>\w+)/$' => 'PhabricatorEmailTokenController',
       ),
       '/logout/$' => 'PhabricatorLogoutController',
       '/facebook-auth/' => array(
         '$' => 'PhabricatorFacebookAuthController',
         'diagnose/$' => 'PhabricatorFacebookAuthDiagnosticsController',
       ),
     );
   }
 
   public function buildRequest() {
     $request = new AphrontRequest($this->getHost(), $this->getPath());
     $request->setRequestData($_GET + $_POST);
     return $request;
   }
 
   public function handleException(Exception $ex) {
 
     $class    = phutil_escape_html(get_class($ex));
     $message  = phutil_escape_html($ex->getMessage());
 
     $content =
       '<div class="aphront-unhandled-exception">'.
         '<h1>Unhandled Exception "'.$class.'": '.$message.'</h1>'.
         '<code>'.phutil_escape_html((string)$ex).'</code>'.
       '</div>';
 
     $view = new PhabricatorStandardPageView();
     $view->appendChild($content);
 
     $response = new AphrontWebpageResponse();
     $response->setContent($view->render());
 
     return $response;
   }
 
   public function willSendResponse(AphrontResponse $response) {
     $request = $this->getRequest();
     if ($response instanceof AphrontDialogResponse) {
       if (!$request->isAjax()) {
         $view = new PhabricatorStandardPageView();
         $view->appendChild(
           '<div style="padding: 2em 0;">'.
             $response->buildResponseString().
           '</div>');
         $response = new AphrontWebpageResponse();
         $response->setContent($view->render());
         return $response;
       }
     } else if ($response instanceof Aphront404Response) {
 
       $failure = new AphrontRequestFailureView();
       $failure->setHeader('404 Not Found');
       $failure->appendChild(
         '<p>The page you requested was not found.</p>');
 
       $view = new PhabricatorStandardPageView();
       $view->setTitle('404 Not Found');
       $view->appendChild($failure);
 
       $response = new AphrontWebpageResponse();
       $response->setContent($view->render());
       $response->setHTTPResponseCode(404);
       return $response;
     }
 
     return $response;
   }
 
   public function build404Controller() {
     return array(new Phabricator404Controller($this->getRequest()), array());
   }
 
 
 }
diff --git a/src/applications/conduit/method/differential/creatediff/__init__.php b/src/applications/conduit/method/differential/creatediff/__init__.php
index f43bd3996d..160deafd17 100644
--- a/src/applications/conduit/method/differential/creatediff/__init__.php
+++ b/src/applications/conduit/method/differential/creatediff/__init__.php
@@ -1,21 +1,22 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('arcanist', 'parser/diff/change');
 
 phutil_require_module('phabricator', 'applications/conduit/method/base');
 phutil_require_module('phabricator', 'applications/differential/constants/lintstatus');
 phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus');
 phutil_require_module('phabricator', 'applications/differential/constants/unitstatus');
 phutil_require_module('phabricator', 'applications/differential/storage/diff');
 phutil_require_module('phabricator', 'applications/differential/storage/revision');
+phutil_require_module('phabricator', 'infrastructure/env');
 
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('ConduitAPI_differential_creatediff_Method.php');
diff --git a/src/applications/differential/controller/commentpreview/DifferentialCommentPreviewController.php b/src/applications/differential/controller/commentpreview/DifferentialCommentPreviewController.php
new file mode 100644
index 0000000000..96ef550e44
--- /dev/null
+++ b/src/applications/differential/controller/commentpreview/DifferentialCommentPreviewController.php
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class DifferentialCommentPreviewController extends DifferentialController {
+
+  private $id;
+
+  public function willProcessRequest(array $data) {
+    $this->id = $data['id'];
+  }
+
+  public function processRequest() {
+
+    $request = $this->getRequest();
+
+    $author_phid = $request->getUser()->getPHID();
+
+    $handles = id(new PhabricatorObjectHandleData(array($author_phid)))
+      ->loadHandles();
+
+    $factory = new DifferentialMarkupEngineFactory();
+    $engine = $factory->newDifferentialCommentMarkupEngine();
+
+    $comment = new DifferentialComment();
+    $comment->setContent($request->getStr('content'));
+    $comment->setAction($request->getStr('action'));
+    $comment->setAuthorPHID($author_phid);
+
+    $view = new DifferentialRevisionCommentView();
+    $view->setComment($comment);
+    $view->setHandles($handles);
+    $view->setMarkupEngine($engine);
+    $view->setPreview(true);
+
+    return id(new AphrontAjaxResponse())
+      ->setContent($view->render());
+  }
+
+}
diff --git a/src/applications/conduit/method/differential/creatediff/__init__.php b/src/applications/differential/controller/commentpreview/__init__.php
similarity index 51%
copy from src/applications/conduit/method/differential/creatediff/__init__.php
copy to src/applications/differential/controller/commentpreview/__init__.php
index f43bd3996d..0d1ba25e1e 100644
--- a/src/applications/conduit/method/differential/creatediff/__init__.php
+++ b/src/applications/differential/controller/commentpreview/__init__.php
@@ -1,21 +1,19 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
-phutil_require_module('arcanist', 'parser/diff/change');
-
-phutil_require_module('phabricator', 'applications/conduit/method/base');
-phutil_require_module('phabricator', 'applications/differential/constants/lintstatus');
-phutil_require_module('phabricator', 'applications/differential/constants/revisionstatus');
-phutil_require_module('phabricator', 'applications/differential/constants/unitstatus');
-phutil_require_module('phabricator', 'applications/differential/storage/diff');
-phutil_require_module('phabricator', 'applications/differential/storage/revision');
+phutil_require_module('phabricator', 'aphront/response/ajax');
+phutil_require_module('phabricator', 'applications/differential/controller/base');
+phutil_require_module('phabricator', 'applications/differential/parser/markup');
+phutil_require_module('phabricator', 'applications/differential/storage/comment');
+phutil_require_module('phabricator', 'applications/differential/view/revisioncomment');
+phutil_require_module('phabricator', 'applications/phid/handle/data');
 
 phutil_require_module('phutil', 'utils');
 
 
-phutil_require_source('ConduitAPI_differential_creatediff_Method.php');
+phutil_require_source('DifferentialCommentPreviewController.php');
diff --git a/src/applications/differential/mail/base/__init__.php b/src/applications/differential/mail/base/__init__.php
index e37c404c5a..6fa6f9c631 100644
--- a/src/applications/differential/mail/base/__init__.php
+++ b/src/applications/differential/mail/base/__init__.php
@@ -1,12 +1,13 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'applications/metamta/storage/mail');
+phutil_require_module('phabricator', 'infrastructure/env');
 
 
 phutil_require_source('DifferentialMail.php');
diff --git a/src/applications/differential/view/addcomment/DifferentialAddCommentView.php b/src/applications/differential/view/addcomment/DifferentialAddCommentView.php
index b390c5a083..1fc3def9ee 100644
--- a/src/applications/differential/view/addcomment/DifferentialAddCommentView.php
+++ b/src/applications/differential/view/addcomment/DifferentialAddCommentView.php
@@ -1,76 +1,99 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 final class DifferentialAddCommentView extends AphrontView {
 
   private $revision;
   private $actions;
   private $actionURI;
   private $user;
 
   public function setRevision($revision) {
     $this->revision = $revision;
     return $this;
   }
 
   public function setActions(array $actions) {
     $this->actions = $actions;
     return $this;
   }
 
   public function setActionURI($uri) {
     $this->actionURI = $uri;
   }
 
   public function setUser(PhabricatorUser $user) {
     $this->user = $user;
   }
 
   public function render() {
+
+    require_celerity_resource('differential-revision-add-comment-css');
+
     $revision = $this->revision;
 
     $actions = array();
     foreach ($this->actions as $action) {
       $actions[$action] = DifferentialAction::getActionVerb($action);
     }
 
     $form = new AphrontFormView();
     $form
       ->setUser($this->user)
       ->setAction($this->actionURI)
       ->addHiddenInput('revision_id', $revision->getID())
       ->appendChild(
         id(new AphrontFormSelectControl())
           ->setLabel('Action')
           ->setName('action')
+          ->setID('comment-action')
           ->setOptions($actions))
       ->appendChild(
         id(new AphrontFormTextAreaControl())
           ->setName('comment')
+          ->setID('comment-content')
           ->setLabel('Comment'))
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->setValue('Comment'));
 
+    Javelin::initBehavior(
+      'differential-feedback-preview',
+      array(
+        'uri'     => '/differential/comment/preview/'.$revision->getID().'/',
+        'preview' => 'comment-preview',
+        'action'  => 'comment-action',
+        'content' => 'comment-content',
+      ));
+
     return
-      '<div class="differential-panel">'.
-        '<h1>Add Comment</h1>'.
-        $form->render().
+      '<div class="differential-add-comment-panel">'.
+        '<div class="differential-panel">'.
+          '<h1>Add Comment</h1>'.
+          $form->render().
+        '</div>'.
+        '<div class="differential-comment-preview">'.
+          '<div id="comment-preview">'.
+            '<span class="differential-loading-text">'.
+              'Loading comment preview...'.
+            '</span>'.
+          '</div>'.
+        '</div>'.
       '</div>';
   }
 }
diff --git a/src/applications/differential/view/addcomment/__init__.php b/src/applications/differential/view/addcomment/__init__.php
index 67d21d5c5e..1597617bf0 100644
--- a/src/applications/differential/view/addcomment/__init__.php
+++ b/src/applications/differential/view/addcomment/__init__.php
@@ -1,17 +1,19 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'applications/differential/constants/action');
+phutil_require_module('phabricator', 'infrastructure/celerity/api');
+phutil_require_module('phabricator', 'infrastructure/javelin/api');
 phutil_require_module('phabricator', 'view/base');
 phutil_require_module('phabricator', 'view/form/base');
 phutil_require_module('phabricator', 'view/form/control/submit');
 
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('DifferentialAddCommentView.php');
diff --git a/src/applications/differential/view/revisioncomment/DifferentialRevisionCommentView.php b/src/applications/differential/view/revisioncomment/DifferentialRevisionCommentView.php
index cb9bab5604..2ef005669a 100644
--- a/src/applications/differential/view/revisioncomment/DifferentialRevisionCommentView.php
+++ b/src/applications/differential/view/revisioncomment/DifferentialRevisionCommentView.php
@@ -1,96 +1,106 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 final class DifferentialRevisionCommentView extends AphrontView {
 
   private $comment;
   private $handles;
   private $markupEngine;
+  private $preview;
 
   public function setComment($comment) {
     $this->comment = $comment;
     return $this;
   }
 
   public function setHandles(array $handles) {
     $this->handles = $handles;
     return $this;
   }
 
   public function setMarkupEngine($markup_engine) {
     $this->markupEngine = $markup_engine;
     return $this;
   }
 
+  public function setPreview($preview) {
+    $this->preview = $preview;
+    return $this;
+  }
+
   public function render() {
 
     require_celerity_resource('phabricator-remarkup-css');
     require_celerity_resource('differential-revision-comment-css');
 
     $comment = $this->comment;
 
     $action = $comment->getAction();
 
     $action_class = 'differential-comment-action-'.phutil_escape_html($action);
 
-    $date = date('F jS, Y g:i:s A', $comment->getDateCreated());
+    if ($this->preview) {
+      $date = 'COMMENT PREVIEW';
+    } else {
+      $date = date('F jS, Y g:i:s A', $comment->getDateCreated());
+    }
 
     $author = $this->handles[$comment->getAuthorPHID()];
     $author_link = $author->renderLink();
 
     $verb = DifferentialAction::getActionPastTenseVerb($comment->getAction());
     $verb = phutil_escape_html($verb);
 
     $content = $comment->getContent();
     if (strlen(rtrim($content))) {
       $title = "{$author_link} {$verb} this revision:";
       $content =
         '<div class="phabricator-remarkup">'.
           $this->markupEngine->markupText($content).
         '</div>';
     } else {
       $title = null;
       $content =
         '<div class="differential-comment-nocontent">'.
           "<p>{$author_link} {$verb} this revision.</p>".
         '</div>';
     }
-    
+
     $background = null;
     $uri = $author->getImageURI();
     if ($uri) {
       $background = "background-image: url('{$uri}');";
-    } 
+    }
 
     return
       '<div class="differential-comment '.$action_class.'">'.
         '<div class="differential-comment-head">'.
           '<div class="differential-comment-date">'.$date.'</div>'.
           '<div class="differential-comment-title">'.$title.'</div>'.
         '</div>'.
         '<div class="differential-comment-body" style="'.$background.'">'.
           '<div class="differential-comment-core">'.
             '<div class="differential-comment-content">'.
               $content.
             '</div>'.
           '</div>'.
         '</div>'.
       '</div>';
   }
 
 }
diff --git a/src/view/form/control/base/AphrontFormControl.php b/src/view/form/control/base/AphrontFormControl.php
index b35988d64b..48c494b2d8 100755
--- a/src/view/form/control/base/AphrontFormControl.php
+++ b/src/view/form/control/base/AphrontFormControl.php
@@ -1,145 +1,155 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 abstract class AphrontFormControl extends AphrontView {
 
   private $label;
   private $caption;
   private $error;
   private $name;
   private $value;
   private $disabled;
+  private $id;
+
+  public function setID($id) {
+    $this->id = $id;
+    return $this;
+  }
+
+  public function getID() {
+    return $this->id;
+  }
 
   public function setLabel($label) {
     $this->label = $label;
     return $this;
   }
 
   public function getLabel() {
     return $this->label;
   }
 
   public function setCaption($caption) {
     $this->caption = $caption;
     return $this;
   }
 
   public function getCaption() {
     return $this->caption;
   }
 
   public function setError($error) {
     $this->error = $error;
     return $this;
   }
 
   public function getError() {
     return $this->error;
   }
 
   public function setName($name) {
     $this->name = $name;
     return $this;
   }
 
   public function getName() {
     return $this->name;
   }
 
   public function setValue($value) {
     $this->value = $value;
     return $this;
   }
 
   public function getValue() {
     return $this->value;
   }
 
   public function setDisabled($disabled) {
     $this->disabled = $disabled;
     return $this;
   }
 
   public function getDisabled() {
     return $this->disabled;
   }
 
   abstract protected function renderInput();
   abstract protected function getCustomControlClass();
 
   protected function shouldRender() {
     return true;
   }
 
   final public function render() {
     if (!$this->shouldRender()) {
       return null;
     }
 
     $custom_class = $this->getCustomControlClass();
 
     if (strlen($this->getLabel())) {
       $label =
         '<label class="aphront-form-label">'.
           phutil_escape_html($this->getLabel()).
           ':'.
         '</label>';
     } else {
       $label = null;
       $custom_class .= ' aphront-form-control-nolabel';
     }
 
     $input =
       '<div class="aphront-form-input">'.
         $this->renderInput().
       '</div>';
 
     if (strlen($this->getError())) {
       $error = $this->getError();
       if ($error === true) {
         $error = '*';
       } else {
         $error = "\xC2\xAB ".$error;
       }
       $error =
         '<div class="aphront-form-error">'.
           phutil_escape_html($error).
         '</div>';
     } else {
       $error = null;
     }
 
     if (strlen($this->getCaption())) {
       $caption =
         '<div class="aphront-form-caption">'.
           $this->getCaption().
         '</div>';
     } else {
       $caption = null;
     }
 
     return
       '<div class="aphront-form-control '.$custom_class.'">'.
         $error.
         $label.
         $input.
         $caption.
         '<div style="clear: both;"></div>'.
       '</div>';
   }
 }
diff --git a/src/view/form/control/password/AphrontFormPasswordControl.php b/src/view/form/control/password/AphrontFormPasswordControl.php
index cb0f259fbb..ae02ea73f4 100755
--- a/src/view/form/control/password/AphrontFormPasswordControl.php
+++ b/src/view/form/control/password/AphrontFormPasswordControl.php
@@ -1,36 +1,37 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class AphrontFormPasswordControl extends AphrontFormControl {
 
   protected function getCustomControlClass() {
     return 'aphront-form-control-password';
   }
 
   protected function renderInput() {
     return phutil_render_tag(
       'input',
       array(
         'type'      => 'password',
         'name'      => $this->getName(),
         'value'     => $this->getValue(),
         'disabled'  => $this->getDisabled() ? 'disabled' : null,
+        'id'        => $this->getID(),
       ));
   }
 
 }
diff --git a/src/view/form/control/select/AphrontFormSelectControl.php b/src/view/form/control/select/AphrontFormSelectControl.php
index d2ffaad74d..fdb6a58080 100755
--- a/src/view/form/control/select/AphrontFormSelectControl.php
+++ b/src/view/form/control/select/AphrontFormSelectControl.php
@@ -1,57 +1,58 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class AphrontFormSelectControl extends AphrontFormControl {
 
   protected function getCustomControlClass() {
     return 'aphront-form-control-select';
   }
 
   private $options;
 
   public function setOptions(array $options) {
     $this->options = $options;
     return $this;
   }
 
   public function getOptions() {
     return $this->options;
   }
 
   protected function renderInput() {
     $options = array();
     foreach ($this->getOptions() as $value => $label) {
       $options[] = phutil_render_tag(
         'option',
         array(
           'selected' => ($value == $this->getValue()) ? 'selected' : null,
           'value'    => $value,
         ),
         phutil_escape_html($label));
     }
 
     return phutil_render_tag(
       'select',
       array(
         'name'      => $this->getName(),
         'disabled'  => $this->getDisabled() ? 'disabled' : null,
+        'id'        => $this->getID(),
       ),
       implode("\n", $options));
   }
 
 }
diff --git a/src/view/form/control/text/AphrontFormTextControl.php b/src/view/form/control/text/AphrontFormTextControl.php
index f8265188af..185ffd6a7e 100755
--- a/src/view/form/control/text/AphrontFormTextControl.php
+++ b/src/view/form/control/text/AphrontFormTextControl.php
@@ -1,36 +1,37 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class AphrontFormTextControl extends AphrontFormControl {
 
   protected function getCustomControlClass() {
     return 'aphront-form-control-text';
   }
 
   protected function renderInput() {
     return phutil_render_tag(
       'input',
       array(
         'type'      => 'text',
         'name'      => $this->getName(),
         'value'     => $this->getValue(),
         'disabled'  => $this->getDisabled() ? 'disabled' : null,
+        'id'        => $this->getID(),
       ));
   }
 
 }
diff --git a/src/view/form/control/textarea/AphrontFormTextAreaControl.php b/src/view/form/control/textarea/AphrontFormTextAreaControl.php
index 55ec143551..36b64a8b53 100755
--- a/src/view/form/control/textarea/AphrontFormTextAreaControl.php
+++ b/src/view/form/control/textarea/AphrontFormTextAreaControl.php
@@ -1,55 +1,56 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class AphrontFormTextAreaControl extends AphrontFormControl {
 
   const HEIGHT_VERY_SHORT = 'very-short';
   const HEIGHT_SHORT      = 'short';
-  
+
   private $height;
 
   public function setHeight($height) {
     $this->height = $height;
     return $this;
   }
 
   protected function getCustomControlClass() {
     return 'aphront-form-control-textarea';
   }
 
   protected function renderInput() {
 
     $height_class = null;
     switch ($this->height) {
       case self::HEIGHT_VERY_SHORT:
       case self::HEIGHT_SHORT:
         $height_class = 'aphront-textarea-'.$this->height;
         break;
     }
 
     return phutil_render_tag(
       'textarea',
       array(
         'name'      => $this->getName(),
         'disabled'  => $this->getDisabled() ? 'disabled' : null,
         'class'     => $height_class,
+        'id'        => $this->getID(),
       ),
       phutil_escape_html($this->getValue()));
   }
 
 }
diff --git a/webroot/rsrc/css/application/differential/add-comment.css b/webroot/rsrc/css/application/differential/add-comment.css
new file mode 100644
index 0000000000..99be1e9f7f
--- /dev/null
+++ b/webroot/rsrc/css/application/differential/add-comment.css
@@ -0,0 +1,23 @@
+/**
+ * @provides differential-revision-add-comment-css
+ */
+
+.differential-add-comment-panel .aphront-form-view {
+  background: #fffff5;
+}
+
+.differential-add-comment-panel .differential-panel {
+  margin-bottom: 0;
+}
+
+.differential-comment-preview {
+  background: #f0f0f0;
+  border-bottom: 1px solid #aaaaaa;
+  margin-bottom: 2em;
+  max-width: 1120px;
+  padding: 15px 20px;
+}
+
+.differential-comment-preview .differential-loading-text {
+  color: #aaaaaa;
+}
diff --git a/webroot/rsrc/js/application/differential/behavior-comment-preview.js b/webroot/rsrc/js/application/differential/behavior-comment-preview.js
new file mode 100644
index 0000000000..41bdec070b
--- /dev/null
+++ b/webroot/rsrc/js/application/differential/behavior-comment-preview.js
@@ -0,0 +1,50 @@
+/**
+ * @provides javelin-behavior-differential-feedback-preview
+ * @requires javelin-lib-dev
+ */
+
+JX.behavior('differential-feedback-preview', function(config) {
+
+  var action = JX.$(config.action);
+  var content = JX.$(config.content);
+  var preview = JX.$(config.preview);
+
+  var aval = null;//action.value;
+  var cval = null;//content.value;
+  var defer = null;
+  var min = null;
+  var request = null;
+
+  function check() {
+    if (request || (min && (new Date().getTime() < min))) {
+      // Waiting on an async or just got one back, rate-limit.
+      return;
+    }
+
+    defer && defer.stop();
+
+    if (action.value !== aval || content.value !== cval) {
+      aval = action.value;
+      cval = content.value;
+
+      request = new JX.Request(config.uri, function(r) {
+        preview && JX.DOM.setContent(preview, JX.HTML(r));
+        min = new Date().getTime() + 500;
+        defer && defer.stop();
+        defer = JX.defer(check, 500);
+      });
+      request.listen('finally', function() { request = null; });
+      request.setData({action : aval, content : cval});
+      // If we don't get a response back soon, retry on the next action.
+      request.setTimeout(2000);
+      request.send();
+    } else {
+      defer = JX.defer(check, 2000);
+    }
+  }
+
+  JX.DOM.listen(content, 'keydown', null, check);
+  JX.DOM.listen(action,  'change',  null, check);
+
+  check();
+});