@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Put inline previews in remarkup textareas

Summary:
Ref T3967. This gives us a reasonable baseline for doing remarkup previews inline in all contexts, and works in weird/constrained context including:

- inline comments;
- conpherence; and
- custom fields.

It would be nicer to go beyond this in contexts like Phame posts, but this is a start, at least.

Test Plan:
{F1040877}

{F1040878}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T3967

Differential Revision: https://secure.phabricator.com/D14855

+195 -31
+15 -15
resources/celerity/map.php
··· 7 7 */ 8 8 return array( 9 9 'names' => array( 10 - 'core.pkg.css' => '4cf32aa0', 11 - 'core.pkg.js' => '821768c9', 10 + 'core.pkg.css' => '1a2d5480', 11 + 'core.pkg.js' => 'cf262309', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => '2de124c9', 14 14 'differential.pkg.js' => '64e69521', ··· 104 104 'rsrc/css/application/tokens/tokens.css' => '3d0f239e', 105 105 'rsrc/css/application/uiexample/example.css' => '528b19de', 106 106 'rsrc/css/core/core.css' => 'a76cefc9', 107 - 'rsrc/css/core/remarkup.css' => '275e362f', 107 + 'rsrc/css/core/remarkup.css' => '72024fc6', 108 108 'rsrc/css/core/syntax.css' => '9fd11da8', 109 109 'rsrc/css/core/z-index.css' => '57ddcaa2', 110 110 'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa', ··· 487 487 'rsrc/js/core/behavior-object-selector.js' => '49b73b36', 488 488 'rsrc/js/core/behavior-oncopy.js' => '2926fff2', 489 489 'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03', 490 - 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '461fd61b', 490 + 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'ecddcbe2', 491 491 'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b', 492 492 'rsrc/js/core/behavior-remarkup-preview.js' => 'f7379f45', 493 493 'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e', ··· 640 640 'javelin-behavior-phabricator-notification-example' => '8ce821c5', 641 641 'javelin-behavior-phabricator-object-selector' => '49b73b36', 642 642 'javelin-behavior-phabricator-oncopy' => '2926fff2', 643 - 'javelin-behavior-phabricator-remarkup-assist' => '461fd61b', 643 + 'javelin-behavior-phabricator-remarkup-assist' => 'ecddcbe2', 644 644 'javelin-behavior-phabricator-reveal-content' => '60821bc7', 645 645 'javelin-behavior-phabricator-search-typeahead' => '048330fa', 646 646 'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6', ··· 759 759 'phabricator-object-selector-css' => '85ee8ce6', 760 760 'phabricator-phtize' => 'd254d646', 761 761 'phabricator-prefab' => '666c80c5', 762 - 'phabricator-remarkup-css' => '275e362f', 762 + 'phabricator-remarkup-css' => '72024fc6', 763 763 'phabricator-search-results-css' => '7dea472c', 764 764 'phabricator-shaped-request' => '7cbe244b', 765 765 'phabricator-side-menu-view-css' => 'bec2458e', ··· 1100 1100 'javelin-behavior', 1101 1101 'javelin-dom', 1102 1102 ), 1103 - '461fd61b' => array( 1104 - 'javelin-behavior', 1105 - 'javelin-stratcom', 1106 - 'javelin-dom', 1107 - 'phabricator-phtize', 1108 - 'phabricator-textareautils', 1109 - 'javelin-workflow', 1110 - 'javelin-vector', 1111 - ), 1112 1103 '469c0d9e' => array( 1113 1104 'javelin-behavior', 1114 1105 'javelin-dom', ··· 1960 1951 'javelin-aphlict', 1961 1952 'phabricator-phtize', 1962 1953 'javelin-dom', 1954 + ), 1955 + 'ecddcbe2' => array( 1956 + 'javelin-behavior', 1957 + 'javelin-stratcom', 1958 + 'javelin-dom', 1959 + 'phabricator-phtize', 1960 + 'phabricator-textareautils', 1961 + 'javelin-workflow', 1962 + 'javelin-vector', 1963 1963 ), 1964 1964 'edd1ba66' => array( 1965 1965 'javelin-behavior',
+2
src/__phutil_library_map__.php
··· 1644 1644 'PhabricatorApplicationTransactionNoEffectResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionNoEffectResponse.php', 1645 1645 'PhabricatorApplicationTransactionPublishWorker' => 'applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php', 1646 1646 'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php', 1647 + 'PhabricatorApplicationTransactionRemarkupPreviewController' => 'applications/transactions/controller/PhabricatorApplicationTransactionRemarkupPreviewController.php', 1647 1648 'PhabricatorApplicationTransactionReplyHandler' => 'applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php', 1648 1649 'PhabricatorApplicationTransactionResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionResponse.php', 1649 1650 'PhabricatorApplicationTransactionShowOlderController' => 'applications/transactions/controller/PhabricatorApplicationTransactionShowOlderController.php', ··· 5760 5761 'PhabricatorApplicationTransactionNoEffectResponse' => 'AphrontProxyResponse', 5761 5762 'PhabricatorApplicationTransactionPublishWorker' => 'PhabricatorWorker', 5762 5763 'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 5764 + 'PhabricatorApplicationTransactionRemarkupPreviewController' => 'PhabricatorApplicationTransactionController', 5763 5765 'PhabricatorApplicationTransactionReplyHandler' => 'PhabricatorMailReplyHandler', 5764 5766 'PhabricatorApplicationTransactionResponse' => 'AphrontProxyResponse', 5765 5767 'PhabricatorApplicationTransactionShowOlderController' => 'PhabricatorApplicationTransactionController',
+2
src/applications/transactions/application/PhabricatorTransactionsApplication.php
··· 33 33 => 'PhabricatorApplicationTransactionShowOlderController', 34 34 '(?P<value>old|new)/(?<phid>[^/]+)/' 35 35 => 'PhabricatorApplicationTransactionValueController', 36 + 'remarkuppreview/' 37 + => 'PhabricatorApplicationTransactionRemarkupPreviewController', 36 38 'editengine/' => array( 37 39 $this->getQueryRoutePattern() 38 40 => 'PhabricatorEditEngineListController',
+25
src/applications/transactions/controller/PhabricatorApplicationTransactionRemarkupPreviewController.php
··· 1 + <?php 2 + 3 + final class PhabricatorApplicationTransactionRemarkupPreviewController 4 + extends PhabricatorApplicationTransactionController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $viewer = $this->getViewer(); 12 + 13 + $corpus = $request->getStr('corpus'); 14 + 15 + $remarkup = new PHUIRemarkupView($viewer, $corpus); 16 + 17 + $content = array( 18 + 'content' => hsprintf('%s', $remarkup), 19 + ); 20 + 21 + return id(new AphrontAjaxResponse()) 22 + ->setContent($content); 23 + } 24 + 25 + }
+19 -4
src/view/form/control/PhabricatorRemarkupControl.php
··· 42 42 )); 43 43 } 44 44 45 + $root_id = celerity_generate_unique_node_id(); 46 + 45 47 Javelin::initBehavior( 46 48 'phabricator-remarkup-assist', 47 49 array( ··· 56 58 'URL' => pht('URL'), 57 59 ), 58 60 'disabled' => $this->getDisabled(), 61 + 'rootID' => $root_id, 59 62 )); 60 63 Javelin::initBehavior('phabricator-tooltips', array()); 61 64 ··· 114 117 ); 115 118 } 116 119 120 + $actions['fa-eye'] = array( 121 + 'tip' => pht('Preview'), 122 + 'align' => 'right', 123 + ); 124 + 125 + $actions[] = array( 126 + 'spacer' => true, 127 + 'align' => 'right', 128 + ); 129 + 117 130 $actions['fa-life-bouy'] = array( 118 - 'tip' => pht('Help'), 119 - 'align' => 'right', 120 - 'href' => PhabricatorEnv::getDoclink('Remarkup Reference'), 121 - ); 131 + 'tip' => pht('Help'), 132 + 'align' => 'right', 133 + 'href' => PhabricatorEnv::getDoclink('Remarkup Reference'), 134 + ); 135 + 122 136 123 137 if (!$this->disableFullScreen) { 124 138 $actions[] = array( ··· 230 244 array( 231 245 'sigil' => 'remarkup-assist-control', 232 246 'class' => $this->getDisabled() ? 'disabled-control' : null, 247 + 'id' => $root_id, 233 248 ), 234 249 array( 235 250 $buttons,
+33
webroot/rsrc/css/core/remarkup.css
··· 498 498 height: auto; 499 499 border-width: 1px 0 0 0; 500 500 outline: none; 501 + resize: none; 501 502 } 502 503 503 504 .phabricator-image-macro-hero { ··· 523 524 background-color: {$lightviolet}; 524 525 padding: 0 4px; 525 526 } 527 + 528 + .remarkup-inline-preview { 529 + display: block; 530 + position: relative; 531 + background: #fff; 532 + overflow-y: auto; 533 + box-sizing: border-box; 534 + width: 100%; 535 + border: 1px solid {$sky}; 536 + resize: vertical; 537 + padding: 4px 6px; 538 + } 539 + 540 + .remarkup-control-fullscreen-mode .remarkup-inline-preview { 541 + resize: none; 542 + } 543 + 544 + .remarkup-inline-preview * { 545 + resize: none; 546 + } 547 + 548 + .remarkup-assist-button.preview-active { 549 + background: {$sky}; 550 + } 551 + 552 + .remarkup-assist-button.preview-active .phui-icon-view { 553 + color: #ffffff; 554 + } 555 + 556 + .remarkup-assist-button.preview-active:hover .phui-icon-view { 557 + color: {$lightsky}; 558 + }
+99 -12
webroot/rsrc/js/core/behavior-phabricator-remarkup-assist.js
··· 11 11 12 12 JX.behavior('phabricator-remarkup-assist', function(config) { 13 13 var pht = JX.phtize(config.pht); 14 + var root = JX.$(config.rootID); 15 + var area = JX.DOM.find(root, 'textarea'); 14 16 15 17 var edit_mode = 'normal'; 16 18 var edit_root = null; 19 + var preview = null; 17 20 18 21 function set_edit_mode(root, mode) { 19 22 if (mode == edit_mode) { ··· 26 29 JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', false); 27 30 JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', false); 28 31 } 29 - JX.DOM.find(edit_root, 'textarea').style.height = ''; 32 + 33 + area.style.height = ''; 34 + 35 + // If we're in preview mode, kick the preview back down to default 36 + // size. 37 + if (preview) { 38 + JX.DOM.show(area); 39 + resize_preview(); 40 + JX.DOM.hide(area); 41 + } 30 42 } 31 43 32 44 edit_root = root; ··· 36 48 if (mode == 'fa-arrows-alt') { 37 49 JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', true); 38 50 JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', true); 51 + 52 + // If we're in preview mode, expand the preview to full-size. 53 + if (preview) { 54 + JX.DOM.show(area); 55 + } 56 + 39 57 resizearea(); 58 + 59 + if (preview) { 60 + resize_preview(); 61 + JX.DOM.hide(area); 62 + } 40 63 } 41 64 42 - JX.DOM.focus(JX.DOM.find(edit_root, 'textarea')); 65 + JX.DOM.focus(area); 43 66 } 44 67 45 68 function resizearea() { ··· 54 77 // "top" and "bottom", and height "auto" renders as two lines high. Force 55 78 // it to the correct height with Javascript. 56 79 57 - var area = JX.DOM.find(edit_root, 'textarea'); 58 - 59 80 var v = JX.Vector.getViewport(); 60 81 v.x = null; 61 82 v.y -= 26; ··· 64 85 } 65 86 66 87 JX.Stratcom.listen('resize', null, resizearea); 67 - 68 88 69 89 JX.Stratcom.listen('keydown', null, function(e) { 70 90 if (e.getSpecialKey() != 'esc') { ··· 115 135 return sel.join('\n' + ch); 116 136 } 117 137 118 - function assist(area, action, root) { 138 + function assist(area, action, root, button) { 119 139 // If the user has some text selected, we'll try to use that (for example, 120 140 // if they have a word selected and want to bold it). Otherwise we'll insert 121 141 // generic text. ··· 180 200 set_edit_mode(root, 'normal'); 181 201 } else { 182 202 set_edit_mode(root, 'fa-arrows-alt'); 203 + } 204 + break; 205 + case 'fa-eye': 206 + if (!preview) { 207 + preview = JX.$N( 208 + 'div', 209 + { 210 + className: 'remarkup-inline-preview' 211 + }, 212 + null); 213 + 214 + area.parentNode.insertBefore(preview, area); 215 + JX.DOM.alterClass(button, 'preview-active', true); 216 + resize_preview(); 217 + JX.DOM.hide(area); 218 + 219 + update_preview(); 220 + } else { 221 + JX.DOM.show(area); 222 + resize_preview(true); 223 + JX.DOM.remove(preview); 224 + preview = null; 225 + 226 + JX.DOM.alterClass(button, 'preview-active', false); 183 227 } 184 228 break; 185 229 } 186 230 } 187 231 188 - JX.Stratcom.listen( 189 - ['click'], 232 + function resize_preview(restore) { 233 + if (!preview) { 234 + return; 235 + } 236 + 237 + var src; 238 + var dst; 239 + 240 + if (restore) { 241 + src = preview; 242 + dst = area; 243 + } else { 244 + src = area; 245 + dst = preview; 246 + } 247 + 248 + var d = JX.Vector.getDim(src); 249 + d.x = null; 250 + d.setDim(dst); 251 + } 252 + 253 + function update_preview() { 254 + var value = area.value; 255 + 256 + var data = { 257 + corpus: value 258 + }; 259 + 260 + var onupdate = function(r) { 261 + if (area.value !== value) { 262 + return; 263 + } 264 + 265 + if (!preview) { 266 + return; 267 + } 268 + 269 + JX.DOM.setContent(preview, JX.$H(r.content).getFragment()); 270 + }; 271 + 272 + new JX.Workflow('/transactions/remarkuppreview/', data) 273 + .setHandler(onupdate) 274 + .start(); 275 + } 276 + 277 + JX.DOM.listen( 278 + root, 279 + 'click', 190 280 'remarkup-assist', 191 281 function(e) { 192 282 var data = e.getNodeData('remarkup-assist'); ··· 200 290 return; 201 291 } 202 292 203 - var root = e.getNode('remarkup-assist-control'); 204 - var area = JX.DOM.find(root, 'textarea'); 205 - 206 - assist(area, data.action, root); 293 + assist(area, data.action, root, e.getNode('remarkup-assist')); 207 294 }); 208 295 209 296 });