@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.

Render image diffs as abstract blocks diffs via DocumentEngine

Summary:
Depends on D20830. Ref T13425. Have the image engine elect into block rendering, then emit blocks.

This is rough (the blocks aren't actually diffed yet) but image diffs were already pretty rough so this is approximately a net improvement.

Test Plan: Viewed image diffs, saw nothing worse than before.

Maniphest Tasks: T13425

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

+349 -176
+6 -6
resources/celerity/map.php
··· 11 11 'conpherence.pkg.js' => '020aebcf', 12 12 'core.pkg.css' => 'c69171e6', 13 13 'core.pkg.js' => '6e5c894f', 14 - 'differential.pkg.css' => '8d8360fb', 14 + 'differential.pkg.css' => 'eef74643', 15 15 'differential.pkg.js' => '0b037a4f', 16 16 'diffusion.pkg.css' => '42c75c37', 17 17 'diffusion.pkg.js' => 'a98c0bf7', ··· 61 61 'rsrc/css/application/dashboard/dashboard.css' => '5a205b9d', 62 62 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 63 63 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', 64 - 'rsrc/css/application/differential/changeset-view.css' => 'bde53589', 64 + 'rsrc/css/application/differential/changeset-view.css' => '215129ef', 65 65 'rsrc/css/application/differential/core.css' => '7300a73e', 66 66 'rsrc/css/application/differential/phui-inline-comment.css' => '48acce5b', 67 67 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', ··· 554 554 'conpherence-thread-manager' => 'aec8e38c', 555 555 'conpherence-transaction-css' => '3a3f5e7e', 556 556 'd3' => '9d068042', 557 - 'differential-changeset-view-css' => 'bde53589', 557 + 'differential-changeset-view-css' => '215129ef', 558 558 'differential-core-view-css' => '7300a73e', 559 559 'differential-revision-add-comment-css' => '7e5900d9', 560 560 'differential-revision-comment-css' => '7dbc8d1d', ··· 1069 1069 'javelin-behavior', 1070 1070 'javelin-request', 1071 1071 ), 1072 + '215129ef' => array( 1073 + 'phui-inline-comment-view-css', 1074 + ), 1072 1075 '225bbb98' => array( 1073 1076 'javelin-install', 1074 1077 'javelin-reactor', ··· 1959 1962 'javelin-workflow', 1960 1963 'phabricator-drag-and-drop-file-upload', 1961 1964 'javelin-workboard-board', 1962 - ), 1963 - 'bde53589' => array( 1964 - 'phui-inline-comment-view-css', 1965 1965 ), 1966 1966 'c03f2fb4' => array( 1967 1967 'javelin-install',
+4
src/__phutil_library_map__.php
··· 3138 3138 'PhabricatorDividerProfileMenuItem' => 'applications/search/menuitem/PhabricatorDividerProfileMenuItem.php', 3139 3139 'PhabricatorDivinerApplication' => 'applications/diviner/application/PhabricatorDivinerApplication.php', 3140 3140 'PhabricatorDocumentEngine' => 'applications/files/document/PhabricatorDocumentEngine.php', 3141 + 'PhabricatorDocumentEngineBlock' => 'applications/files/diff/PhabricatorDocumentEngineBlock.php', 3142 + 'PhabricatorDocumentEngineBlocks' => 'applications/files/diff/PhabricatorDocumentEngineBlocks.php', 3141 3143 'PhabricatorDocumentRef' => 'applications/files/document/PhabricatorDocumentRef.php', 3142 3144 'PhabricatorDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorDocumentRenderingEngine.php', 3143 3145 'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php', ··· 9471 9473 'PhabricatorDividerProfileMenuItem' => 'PhabricatorProfileMenuItem', 9472 9474 'PhabricatorDivinerApplication' => 'PhabricatorApplication', 9473 9475 'PhabricatorDocumentEngine' => 'Phobject', 9476 + 'PhabricatorDocumentEngineBlock' => 'Phobject', 9477 + 'PhabricatorDocumentEngineBlocks' => 'Phobject', 9474 9478 'PhabricatorDocumentRef' => 'Phobject', 9475 9479 'PhabricatorDocumentRenderingEngine' => 'Phobject', 9476 9480 'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication',
+123 -83
src/applications/differential/parser/DifferentialChangesetParser.php
··· 1013 1013 ->setOldComments($old_comments) 1014 1014 ->setNewComments($new_comments); 1015 1015 1016 - $engine_view = $this->newDocumentEngineChangesetView(); 1017 - if ($engine_view !== null) { 1018 - return $engine_view; 1019 - } 1020 - 1021 - switch ($this->changeset->getFileType()) { 1022 - case DifferentialChangeType::FILE_IMAGE: 1023 - $old = null; 1024 - $new = null; 1025 - // TODO: Improve the architectural issue as discussed in D955 1026 - // https://secure.phabricator.com/D955 1027 - $reference = $this->getRenderingReference(); 1028 - $parts = explode('/', $reference); 1029 - if (count($parts) == 2) { 1030 - list($id, $vs) = $parts; 1031 - } else { 1032 - $id = $parts[0]; 1033 - $vs = 0; 1034 - } 1035 - $id = (int)$id; 1036 - $vs = (int)$vs; 1037 - 1038 - if (!$vs) { 1039 - $metadata = $this->changeset->getMetadata(); 1040 - $data = idx($metadata, 'attachment-data'); 1041 - 1042 - $old_phid = idx($metadata, 'old:binary-phid'); 1043 - $new_phid = idx($metadata, 'new:binary-phid'); 1044 - } else { 1045 - $vs_changeset = id(new DifferentialChangeset())->load($vs); 1046 - $old_phid = null; 1047 - $new_phid = null; 1048 - 1049 - // TODO: This is spooky, see D6851 1050 - if ($vs_changeset) { 1051 - $vs_metadata = $vs_changeset->getMetadata(); 1052 - $old_phid = idx($vs_metadata, 'new:binary-phid'); 1053 - } 1054 - 1055 - $changeset = id(new DifferentialChangeset())->load($id); 1056 - if ($changeset) { 1057 - $metadata = $changeset->getMetadata(); 1058 - $new_phid = idx($metadata, 'new:binary-phid'); 1059 - } 1060 - } 1061 - 1062 - if ($old_phid || $new_phid) { 1063 - // grab the files, (micro) optimization for 1 query not 2 1064 - $file_phids = array(); 1065 - if ($old_phid) { 1066 - $file_phids[] = $old_phid; 1067 - } 1068 - if ($new_phid) { 1069 - $file_phids[] = $new_phid; 1070 - } 1016 + $engine_blocks = $this->newDocumentEngineChangesetView(); 1017 + if ($engine_blocks !== null) { 1018 + $reference = $this->getRenderingReference(); 1019 + $parts = explode('/', $reference); 1020 + if (count($parts) == 2) { 1021 + list($id, $vs) = $parts; 1022 + } else { 1023 + $id = $parts[0]; 1024 + $vs = 0; 1025 + } 1071 1026 1072 - $files = id(new PhabricatorFileQuery()) 1073 - ->setViewer($this->getUser()) 1074 - ->withPHIDs($file_phids) 1075 - ->execute(); 1076 - foreach ($files as $file) { 1077 - if (empty($file)) { 1078 - continue; 1079 - } 1080 - if ($file->getPHID() == $old_phid) { 1081 - $old = $file; 1082 - } else if ($file->getPHID() == $new_phid) { 1083 - $new = $file; 1084 - } 1085 - } 1086 - } 1027 + // If we don't have an explicit "vs" changeset, it's the left side of 1028 + // the "id" changeset. 1029 + if (!$vs) { 1030 + $vs = $id; 1031 + } 1087 1032 1088 - $renderer->attachOldFile($old); 1089 - $renderer->attachNewFile($new); 1033 + return $renderer->renderDocumentEngineBlocks( 1034 + $engine_blocks, 1035 + (string)$id, 1036 + (string)$vs); 1037 + } 1090 1038 1091 - return $renderer->renderFileChange($old, $new, $id, $vs); 1039 + // If we've made it here with a type of file we don't know how to render, 1040 + // bail out with a default empty rendering. Normally, we'd expect a 1041 + // document engine to catch these changes before we make it this far. 1042 + switch ($this->changeset->getFileType()) { 1092 1043 case DifferentialChangeType::FILE_DIRECTORY: 1093 1044 case DifferentialChangeType::FILE_BINARY: 1045 + case DifferentialChangeType::FILE_IMAGE: 1094 1046 $output = $renderer->renderChangesetTable(null); 1095 1047 return $output; 1096 1048 } ··· 1699 1651 return null; 1700 1652 } 1701 1653 1702 - $old_data = $this->old; 1703 - $old_data = ipull($old_data, 'text'); 1704 - $old_data = implode('', $old_data); 1654 + $old_file = null; 1655 + $new_file = null; 1705 1656 1706 - $new_data = $this->new; 1707 - $new_data = ipull($new_data, 'text'); 1708 - $new_data = implode('', $new_data); 1657 + switch ($changeset->getFileType()) { 1658 + case DifferentialChangeType::FILE_IMAGE: 1659 + case DifferentialChangeType::FILE_BINARY: 1660 + list($old_file, $new_file) = $this->loadFileObjectsForChangeset(); 1661 + break; 1662 + } 1709 1663 1710 1664 $old_ref = id(new PhabricatorDocumentRef()) 1711 - ->setName($changeset->getOldFile()) 1712 - ->setData($old_data); 1665 + ->setName($changeset->getOldFile()); 1666 + if ($old_file) { 1667 + $old_ref->setFile($old_file); 1668 + } else { 1669 + $old_data = $this->old; 1670 + $old_data = ipull($old_data, 'text'); 1671 + $old_data = implode('', $old_data); 1672 + 1673 + $old_ref->setData($old_data); 1674 + } 1713 1675 1714 1676 $new_ref = id(new PhabricatorDocumentRef()) 1715 - ->setName($changeset->getFilename()) 1716 - ->setData($new_data); 1677 + ->setName($changeset->getFilename()); 1678 + if ($new_file) { 1679 + $new_ref->setFile($new_file); 1680 + } else { 1681 + $new_data = $this->new; 1682 + $new_data = ipull($new_data, 'text'); 1683 + $new_data = implode('', $new_data); 1684 + 1685 + $new_ref->setData($old_data); 1686 + } 1717 1687 1718 1688 $old_engines = PhabricatorDocumentEngine::getEnginesForRef( 1719 1689 $viewer, ··· 1741 1711 } 1742 1712 1743 1713 return null; 1714 + } 1715 + 1716 + private function loadFileObjectsForChangeset() { 1717 + $changeset = $this->changeset; 1718 + $viewer = $this->getViewer(); 1719 + 1720 + $old_file = null; 1721 + $new_file = null; 1722 + 1723 + // TODO: Improve the architectural issue as discussed in D955 1724 + // https://secure.phabricator.com/D955 1725 + $reference = $this->getRenderingReference(); 1726 + $parts = explode('/', $reference); 1727 + if (count($parts) == 2) { 1728 + list($id, $vs) = $parts; 1729 + } else { 1730 + $id = $parts[0]; 1731 + $vs = 0; 1732 + } 1733 + $id = (int)$id; 1734 + $vs = (int)$vs; 1735 + 1736 + if (!$vs) { 1737 + $metadata = $this->changeset->getMetadata(); 1738 + $data = idx($metadata, 'attachment-data'); 1739 + 1740 + $old_phid = idx($metadata, 'old:binary-phid'); 1741 + $new_phid = idx($metadata, 'new:binary-phid'); 1742 + } else { 1743 + $vs_changeset = id(new DifferentialChangeset())->load($vs); 1744 + $old_phid = null; 1745 + $new_phid = null; 1746 + 1747 + // TODO: This is spooky, see D6851 1748 + if ($vs_changeset) { 1749 + $vs_metadata = $vs_changeset->getMetadata(); 1750 + $old_phid = idx($vs_metadata, 'new:binary-phid'); 1751 + } 1752 + 1753 + $changeset = id(new DifferentialChangeset())->load($id); 1754 + if ($changeset) { 1755 + $metadata = $changeset->getMetadata(); 1756 + $new_phid = idx($metadata, 'new:binary-phid'); 1757 + } 1758 + } 1759 + 1760 + if ($old_phid || $new_phid) { 1761 + $file_phids = array(); 1762 + if ($old_phid) { 1763 + $file_phids[] = $old_phid; 1764 + } 1765 + if ($new_phid) { 1766 + $file_phids[] = $new_phid; 1767 + } 1768 + 1769 + $files = id(new PhabricatorFileQuery()) 1770 + ->setViewer($viewer) 1771 + ->withPHIDs($file_phids) 1772 + ->execute(); 1773 + 1774 + foreach ($files as $file) { 1775 + if ($file->getPHID() == $old_phid) { 1776 + $old_file = $file; 1777 + } else if ($file->getPHID() == $new_phid) { 1778 + $new_file = $file; 1779 + } 1780 + } 1781 + } 1782 + 1783 + return array($old_file, $new_file); 1744 1784 } 1745 1785 1746 1786 }
-13
src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
··· 608 608 return array($left_prefix, $right_prefix); 609 609 } 610 610 611 - protected function renderImageStage(PhabricatorFile $file) { 612 - return phutil_tag( 613 - 'div', 614 - array( 615 - 'class' => 'differential-image-stage', 616 - ), 617 - phutil_tag( 618 - 'img', 619 - array( 620 - 'src' => $file->getBestURI(), 621 - ))); 622 - } 623 - 624 611 }
-8
src/applications/differential/render/DifferentialChangesetOneUpMailRenderer.php
··· 31 31 return null; 32 32 } 33 33 34 - public function renderFileChange( 35 - $old_file = null, 36 - $new_file = null, 37 - $id = 0, 38 - $vs = 0) { 39 - return null; 40 - } 41 - 42 34 public function renderTextChange( 43 35 $range_start, 44 36 $range_len,
+7 -20
src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
··· 228 228 return null; 229 229 } 230 230 231 - public function renderFileChange( 232 - $old_file = null, 233 - $new_file = null, 234 - $id = 0, 235 - $vs = 0) { 231 + public function renderDocumentEngineBlocks( 232 + PhabricatorDocumentEngineBlocks $block_list, 233 + $old_changeset_key, 234 + $new_changeset_key) { 236 235 237 236 // TODO: This should eventually merge into the normal primitives pathway, 238 237 // but fake it for now and just share as much code as possible. 239 238 240 239 $primitives = array(); 241 - if ($old_file) { 240 + foreach ($block_list->newOneUpLayout() as $block) { 242 241 $primitives[] = array( 243 242 'type' => 'old-file', 244 - 'htype' => ($new_file ? 'new-file' : null), 245 - 'file' => $old_file, 243 + 'htype' => '', 246 244 'line' => 1, 247 - 'render' => $this->renderImageStage($old_file), 248 - ); 249 - } 250 - 251 - if ($new_file) { 252 - $primitives[] = array( 253 - 'type' => 'new-file', 254 - 'htype' => ($old_file ? 'old-file' : null), 255 - 'file' => $new_file, 256 - 'line' => 1, 257 - 'oline' => ($old_file ? 1 : null), 258 - 'render' => $this->renderImageStage($new_file), 245 + 'render' => $block->newContentView(), 259 246 ); 260 247 } 261 248
+7 -5
src/applications/differential/render/DifferentialChangesetRenderer.php
··· 378 378 $range_start, 379 379 $range_len, 380 380 $rows); 381 - abstract public function renderFileChange( 382 - $old = null, 383 - $new = null, 384 - $id = 0, 385 - $vs = 0); 381 + 382 + public function renderDocumentEngineBlocks( 383 + PhabricatorDocumentEngineBlocks $blocks, 384 + $old_changeset_key, 385 + $new_changeset_key) { 386 + return null; 387 + } 386 388 387 389 abstract protected function renderChangeTypeHeader($force); 388 390 abstract protected function renderUndershieldHeader();
-10
src/applications/differential/render/DifferentialChangesetTestRenderer.php
··· 134 134 return phutil_safe_html($out); 135 135 } 136 136 137 - 138 - public function renderFileChange( 139 - $old_file = null, 140 - $new_file = null, 141 - $id = 0, 142 - $vs = 0) { 143 - 144 - throw new PhutilMethodNotImplementedException(); 145 - } 146 - 147 137 }
+52 -29
src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
··· 364 364 return $this->wrapChangeInTable(phutil_implode_html('', $html)); 365 365 } 366 366 367 - public function renderFileChange( 368 - $old_file = null, 369 - $new_file = null, 370 - $id = 0, 371 - $vs = 0) { 367 + public function renderDocumentEngineBlocks( 368 + PhabricatorDocumentEngineBlocks $block_list, 369 + $old_changeset_key, 370 + $new_changeset_key) { 371 + 372 + $old_view = null; 373 + $new_view = null; 372 374 373 - $old = null; 374 - if ($old_file) { 375 - $old = $this->renderImageStage($old_file); 376 - } 375 + foreach ($block_list->newTwoUpLayout() as $row) { 376 + list($old, $new) = $row; 377 377 378 - $new = null; 379 - if ($new_file) { 380 - $new = $this->renderImageStage($new_file); 381 - } 378 + if ($old) { 379 + $old_view = $old->newContentView(); 380 + } else { 381 + $old_view = null; 382 + } 382 383 383 - // If we don't have an explicit "vs" changeset, it's the left side of the 384 - // "id" changeset. 385 - if (!$vs) { 386 - $vs = $id; 384 + if ($new) { 385 + $new_view = $new->newContentView(); 386 + } else { 387 + $new_view = null; 388 + } 387 389 } 388 390 389 391 $html_old = array(); ··· 405 407 } 406 408 } 407 409 408 - if (!$old) { 409 - $th_old = phutil_tag('th', array()); 410 + if ($old_view === null) { 411 + $old_id = null; 412 + $old_label = null; 410 413 } else { 411 - $th_old = phutil_tag('th', array('id' => "C{$vs}OL1"), 1); 414 + $old_id = "C{$old_changeset_key}OL1"; 415 + $old_label = '1'; 412 416 } 413 417 414 - if (!$new) { 415 - $th_new = phutil_tag('th', array()); 418 + $old_cell = phutil_tag( 419 + 'td', 420 + array( 421 + 'id' => $old_id, 422 + 'class' => 'n', 423 + ), 424 + $old_label); 425 + 426 + if ($new_view === null) { 427 + $new_id = null; 428 + $new_label = null; 416 429 } else { 417 - $th_new = phutil_tag('th', array('id' => "C{$id}NL1"), 1); 430 + $new_id = "C{$new_changeset_key}NL1"; 431 + $new_label = '1'; 418 432 } 419 433 434 + $new_cell = phutil_tag( 435 + 'td', 436 + array( 437 + 'id' => $new_id, 438 + 'class' => 'n', 439 + ), 440 + $new_label); 441 + 420 442 $output = hsprintf( 421 443 '<tr class="differential-image-diff">'. 422 444 '%s'. 423 - '<td class="differential-old-image">%s</td>'. 445 + '<td class="differential-old-image diff-image-cell">%s</td>'. 424 446 '%s'. 425 - '<td class="differential-new-image" colspan="3">%s</td>'. 447 + '<td class="differential-new-image diff-image-cell" '. 448 + 'colspan="3">%s</td>'. 426 449 '</tr>'. 427 450 '%s'. 428 451 '%s', 429 - $th_old, 430 - $old, 431 - $th_new, 432 - $new, 452 + $old_cell, 453 + $old_view, 454 + $new_cell, 455 + $new_view, 433 456 phutil_implode_html('', $html_old), 434 457 phutil_implode_html('', $html_new)); 435 458
+21
src/applications/files/diff/PhabricatorDocumentEngineBlock.php
··· 1 + <?php 2 + 3 + final class PhabricatorDocumentEngineBlock 4 + extends Phobject { 5 + 6 + private $content; 7 + 8 + public function setContent($content) { 9 + $this->content = $content; 10 + return $this; 11 + } 12 + 13 + public function getContent() { 14 + return $this->content; 15 + } 16 + 17 + public function newContentView() { 18 + return $this->getContent(); 19 + } 20 + 21 + }
+83
src/applications/files/diff/PhabricatorDocumentEngineBlocks.php
··· 1 + <?php 2 + 3 + final class PhabricatorDocumentEngineBlocks 4 + extends Phobject { 5 + 6 + private $lists = array(); 7 + 8 + public function addBlockList(PhabricatorDocumentRef $ref, array $blocks) { 9 + assert_instances_of($blocks, 'PhabricatorDocumentEngineBlock'); 10 + 11 + $this->lists[] = array( 12 + 'ref' => $ref, 13 + 'blocks' => array_values($blocks), 14 + ); 15 + 16 + return $this; 17 + } 18 + 19 + public function newTwoUpLayout() { 20 + $rows = array(); 21 + $lists = $this->lists; 22 + 23 + $idx = 0; 24 + while (true) { 25 + $found_any = false; 26 + 27 + $row = array(); 28 + foreach ($lists as $list) { 29 + $blocks = $list['blocks']; 30 + $cell = idx($blocks, $idx); 31 + 32 + if ($cell !== null) { 33 + $found_any = true; 34 + } 35 + 36 + $row[] = $cell; 37 + } 38 + 39 + if (!$found_any) { 40 + break; 41 + } 42 + 43 + $rows[] = $row; 44 + $idx++; 45 + } 46 + 47 + return $rows; 48 + } 49 + 50 + public function newOneUpLayout() { 51 + $rows = array(); 52 + $lists = $this->lists; 53 + 54 + $idx = 0; 55 + while (true) { 56 + $found_any = false; 57 + 58 + $row = array(); 59 + foreach ($lists as $list) { 60 + $blocks = $list['blocks']; 61 + $cell = idx($blocks, $idx); 62 + 63 + if ($cell !== null) { 64 + $found_any = true; 65 + } 66 + 67 + if ($cell) { 68 + $rows[] = $cell; 69 + } 70 + } 71 + 72 + if (!$found_any) { 73 + break; 74 + } 75 + 76 + $idx++; 77 + } 78 + 79 + return $rows; 80 + } 81 + 82 + 83 + }
+44
src/applications/files/document/PhabricatorImageDocumentEngine.php
··· 17 17 return (1024 * 1024 * 64); 18 18 } 19 19 20 + public function canDiffDocuments( 21 + PhabricatorDocumentRef $uref, 22 + PhabricatorDocumentRef $vref) { 23 + 24 + // For now, we can only render a rich image diff if both documents have 25 + // their data stored in Files already. 26 + 27 + return ($uref->getFile() && $vref->getFile()); 28 + } 29 + 30 + public function newDiffView( 31 + PhabricatorDocumentRef $uref, 32 + PhabricatorDocumentRef $vref) { 33 + 34 + $u_blocks = $this->newDiffBlocks($uref); 35 + $v_blocks = $this->newDiffBlocks($vref); 36 + 37 + return id(new PhabricatorDocumentEngineBlocks()) 38 + ->addBlockList($uref, $u_blocks) 39 + ->addBlockList($vref, $v_blocks); 40 + } 41 + 42 + private function newDiffBlocks(PhabricatorDocumentRef $ref) { 43 + $blocks = array(); 44 + 45 + $file = $ref->getFile(); 46 + 47 + $image_view = phutil_tag( 48 + 'div', 49 + array( 50 + 'class' => 'differential-image-stage', 51 + ), 52 + phutil_tag( 53 + 'img', 54 + array( 55 + 'src' => $file->getBestURI(), 56 + ))); 57 + 58 + $blocks[] = id(new PhabricatorDocumentEngineBlock()) 59 + ->setContent($image_view); 60 + 61 + return $blocks; 62 + } 63 + 20 64 protected function canRenderDocumentType(PhabricatorDocumentRef $ref) { 21 65 $file = $ref->getFile(); 22 66 if ($file) {
+2 -2
webroot/rsrc/css/application/differential/changeset-view.css
··· 282 282 font-weight: bold; 283 283 } 284 284 285 - .differential-diff .differential-image-diff { 285 + .differential-diff .diff-image-cell { 286 286 background-image: url(/rsrc/image/checker_light.png); 287 287 } 288 288 289 - .differential-diff .differential-image-diff:hover { 289 + .device-desktop .differential-diff .diff-image-cell:hover { 290 290 background-image: url(/rsrc/image/checker_dark.png); 291 291 } 292 292