+
@@ -55,20 +59,92 @@ DIFF_STYLE_TOGGLE = r'''
'''
MAIN_CSS = r'''
-@layer base-style {
- html, body {
- margin: 0;
- padding: 0;
- font-family: sans-serif;
+
+@media (prefers-color-scheme: light) {
+ html {
+ --c-bg-primary: #ffffff;
+ --c-fg-primary: #000000;
+ --c-bg-auxiliary: #f8f8f8;
+ --c-fg-auxiliary: #a0a0a0;
+ --c-border-line: #e0e0e0;
+ --c-bg-insert: #ecfdf0;
+ --c-bg-delete: #fbe9eb;
+ --c-bg-delete-lineno: #f9d7dc;
+ --c-fg-delete-lineno: #ae969a;
+ --c-bg-delete-word: #fac5cd;
+ --c-fg-delete-word: #400000;
+ --c-fg-insert-word: #004000;
+ --c-bg-insert-word: #c7f0d2;
+ --c-fg-insert-lineno: #9bb0a1;
+ --c-bg-insert-lineno: #ddfbe6;
+ --c-bg-empty: #f0f0f0;
+ --c-fg-foldline: #bbbbbb;
+ --c-border-delete: #e0c8c8; /* pick a darker border color inside the light red gutter */
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ html {
+ --c-bg-primary: #010409;
+ --c-fg-primary: #a0a0a0;
+ --c-bg-auxiliary: #0d1117;
+ --c-fg-auxiliary: #f0f6fc;
+ --c-fg-foldline: #bbbbbb;
+ --c-border-line: #3d444d;
+ --c-bg-insert: #223738;
+ --c-bg-delete: #280d1f;
+ --c-bg-delete-lineno: #421632;
+ --c-fg-delete-lineno: #ae969a;
+ --c-bg-delete-word: #421632;
+ --c-fg-delete-word: #fac5cd;
+ --c-fg-insert-word: #c7f0d2;
+ --c-bg-insert-word: #325148;
+ --c-fg-insert-lineno: #9bb0a1;
+ --c-bg-insert-lineno: #325148;
+ --c-bg-empty: #080b0f;
+ --c-border-delete: #e0c8c8;
+ }
+}
+
+@media print {
+ html {
+ /* Copy of the light theme, but we clip all light gray backgrounds to white. */
+ --c-bg-primary: #ffffff;
+ --c-fg-primary: #000000;
+ --c-bg-auxiliary: #ffffff;
+ --c-fg-auxiliary: #a0a0a0;
+ --c-border-line: #e0e0e0;
+ --c-bg-insert: #ecfdf0;
+ --c-bg-delete: #fbe9eb;
+ --c-bg-delete-lineno: #f9d7dc;
+ --c-fg-delete-lineno: #ae969a;
+ --c-bg-delete-word: #fac5cd;
+ --c-fg-delete-word: #400000;
+ --c-fg-insert-word: #004000;
+ --c-bg-insert-word: #c7f0d2;
+ --c-fg-insert-lineno: #9bb0a1;
+ --c-bg-insert-lineno: #ddfbe6;
+ --c-bg-empty: #ffffff;
+ --c-fg-foldline: #bbbbbb;
+ --c-border-delete: #e0c8c8;
+ }
+}
+
+@layer wsd-base-style {
+ html {
+ background-color: var(--c-bg-primary);
+ height: 100%;
+ width: 100%;
}
- #js-controls {
+ #wsd-js-controls {
display: none;
- background-color: #f8f8f8;
+ color: var(--c-fg-primary);
+ background-color: var(--c-bg-auxiliary);
padding: 5px 20px;
font-size: 10pt;
font-weight: bold;
- border: 1px solid #e0e0e0;
+ border: 1px solid var(--c-border-line);
position: sticky;
top: 0;
z-index: 1;
@@ -76,59 +152,59 @@ MAIN_CSS = r'''
}
@media screen and (max-width: 40em) {
- #js-controls {
+ #wsd-js-controls {
position: initial;
}
- .diff {
+ .wsd-diff {
border-top: none;
}
- .file-title {
- background-color: #f8f8f8;
- border-bottom: solid 1px #e0e0e0;
+ .wsd-file-title {
+ background-color: var(--c-bg-auxiliary);
+ border-bottom: solid 1px var(--c-border-line);
}
}
- input[type="checkbox"] {
+ #wsd-js-controls input[type="checkbox"] {
width: 20px;
height: 20px;
}
- input, label, .control-label {
+ #wsd-js-controls input, #wsd-js-controls label, #wsd-js-controls .control-label {
vertical-align: middle;
}
- .field-group {
+ .wsd-field-group {
display: inline-block;
}
- .field {
+ .wsd-field {
white-space: nowrap;
display: inline-block;
}
- label {
+ #wsd-js-controls label {
font-weight: normal;
margin-right: .5em;
margin-left: 5px;
}
- .control-label {
+ .wsd-control-label {
margin-right: .5em;
margin-left: 5px;
padding-bottom: 3px;
}
- .file-container {
+ .wsd-file-container {
font-family: monospace;
font-size: 9pt;
- background-color: #f8f8f8;
- border: solid 1px #e0e0e0;
+ background-color: var(--c-bg-auxiliary);
+ border: solid 1px var(--c-border-line);
margin: 15px;
}
- .file-title {
+ .wsd-file-title {
padding: 10px 20px;
font-size: 10pt;
font-weight: bold;
@@ -138,7 +214,7 @@ MAIN_CSS = r'''
display: flex;
}
- .filename {
+ .wsd-filename {
max-width: 30em;
text-overflow: ellipsis;
overflow: hidden;
@@ -146,58 +222,64 @@ MAIN_CSS = r'''
direction: rtl;
}
- .diff {
+ .wsd-diff-files {
+ color: var(--c-fg-primary);
+ }
+
+ .wsd-diff {
+ background-color: var(--c-bg-primary);
overflow-x: auto;
display: grid;
align-items: start;
- border-top: 1px solid #e0e0e0;
+ border-top: 1px solid var(--c-border-line);
}
- .line {
+ .wsd-line {
padding-left: calc(4em + 5px);
text-indent: -4em;
padding-top: 2px;
+ align-self: stretch; /* Make sure empty lines don't collapse */
}
/* Make individual syntax tokens wrap anywhere */
- .line > span {
+ .wsd-line > span {
overflow-wrap: anywhere;
white-space: pre-wrap;
}
- .line {
+ .wsd-line {
min-width: 15em;
}
- .line.left.change, .line.left.insert {
- background-color: #fbe9eb;
+ .wsd-line.wsd-left.wsd-change, .wsd-line.wsd-left.wsd-insert {
+ background-color: var(--c-bg-delete);
}
- .line.right.change, .line.right.insert {
- background-color: #ecfdf0;
+ .wsd-line.wsd-right.wsd-change, .wsd-line.wsd-right.wsd-insert {
+ background-color: var(--c-bg-insert);
}
- .lineno.left.change, .lineno.left.insert {
- background-color: #f9d7dc;
- color: #ae969a;
+ .wsd-lineno.wsd-left.wsd-change, .wsd-lineno.wsd-left.wsd-insert {
+ background-color: var(--c-bg-delete-lineno);
+ color: var(--c-fg-delete-lineno);
}
- .lineno.right.change, .lineno.right.insert {
- background-color: #ddfbe6;
- color: #9bb0a1;
+ .wsd-lineno.wsd-right.wsd-change, .wsd-lineno.wsd-right.wsd-insert {
+ background-color: var(--c-bg-insert-lineno);
+ color: var(--c-fg-insert-lineno);
}
- .right > .word_change {
- background-color: #c7f0d2;
- color: #004000;
+ .wsd-right > .wsd-word-change {
+ background-color: var(--c-bg-insert-word);
+ color: var(--c-fg-insert-word);
}
- .left > .word_change {
- background-color: #fac5cd;
- color: #400000;
+ .wsd-left > .wsd-word-change {
+ background-color: var(--c-bg-delete-word);
+ color: var(--c-fg-delete-word);
}
- .lineno {
+ .wsd-lineno {
word-break: keep-all;
margin: 0;
padding-left: 30px;
@@ -205,57 +287,89 @@ MAIN_CSS = r'''
overflow: clip;
position: relative;
text-align: right;
- color: #a0a0a0;
- background-color: #f8f8f8;
- border-right: 1px solid #e0e0e0;
+ color: var(--c-fg-auxiliary);
+ background-color: var(--c-bg-auxiliary);
+ border-right: 1px solid var(--c-border-line);
align-self: stretch;
}
- .lineno.change, .lineno.insert {
- color: #000000;
- }
-
- .lineno::after {
+ .wsd-lineno::after {
position: absolute;
right: 0;
content: "\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳";
white-space: pre;
- color: #a0a0a0;
+ color: var(--c-fg-auxiliary);
}
/* Default rules for split diff for wide screens (laptops) */
- .diff {
+ .wsd-diff {
grid-template-columns: min-content 1fr min-content 1fr;
}
- .empty {
- background-color: #f0f0f0;
+ .wsd-empty {
+ background-color: var(--c-bg-empty);
align-self: stretch;
}
/* line continuation arrows only in non-empty lines */
- .lineno.empty::after {
+ .wsd-lineno.wsd-empty::after {
content: "";
}
+
+ .wsd-lineno, .wsd-left {
+ user-select: none;
+ }
+
+ /* Collapsing runs of unchanged lines */
+ .wsd-collapse {
+ grid-column: 1 / span 4;
+ display: grid;
+ grid-template-columns: subgrid;
+ }
+
+ .wsd-collapse-controls {
+ grid-column: 1 / span 4;
+ display: flex;
+ justify-content: center;
+ color: var(--c-fg-auxiliary);
+
+ background-image: radial-gradient(var(--c-fg-foldline) 1px, transparent 0);
+ background-size: 10px 10px;
+ background-position: center;
+ background-repeat: repeat-x;
+ background-color: var(--c-bg-auxiliary)
+ }
+
+ .wsd-collapse-controls > label {
+ background-color: var(--c-bg-auxiliary);
+ }
+
+ .wsd-collapse:has(input[type="checkbox"]:checked) > span {
+ display: none;
+ }
}
-@layer automatic-media-rule {
+@layer wsd-automatic-media-rule {
/* Unified diff for narrow screens (phones) */
@media screen and (max-width: 70em) {
- .diff {
+ .wsd-diff {
grid-auto-flow: dense;
grid-template-columns: min-content min-content 1fr;
}
- .lineno {
+ .wsd-collapse, .wsd-collapse-controls {
+ grid-column: 1 / span 3;
+ }
+
+ .wsd-lineno {
padding-left: 1em;
}
- .lineno.left {
+ .wsd-lineno.wsd-left {
grid-column: 1;
}
- .lineno.left.change, .lineno.right.change {
+ .wsd-lineno.wsd-left.wsd-change, .wsd-lineno.wsd-right.wsd-change {
grid-column: 1 / span 2;
display: grid;
grid-template-columns: 1fr 1fr;
@@ -266,31 +380,31 @@ MAIN_CSS = r'''
column-gap: 10px;
}
- .lineno.right.change::before {
+ .wsd-lineno.wsd-right.wsd-change::before {
content: "";
align-self: stretch;
grid-column: 1;
- border-right: 1px solid #e0e0e0;
+ border-right: 1px solid var(--c-border-line);
margin-right: -6px; /* move border into column gap, and 1px over to align with other borders */
}
- .lineno.left.change::before {
+ .wsd-lineno.wsd-left.wsd-change::before {
content: "";
align-self: stretch;
grid-column: 2;
- border-left: 1px solid #e0c8c8; /* pick a darker border color inside the light red gutter */
+ border-left: 1px solid var(--c-border-delete);
margin-left: -5px;
}
- .lineno.left.insert {
- border-right: 1px solid #e0c8c8;
+ .wsd-lineno.wsd-left.wsd-insert {
+ border-right: 1px solid var(--c-border-delete);
}
- .lineno.right.change::after {
+ .wsd-lineno.wsd-right.wsd-change::after {
grid-column: 2;
}
- .lineno.left.insert {
+ .wsd-lineno.wsd-left.wsd-insert {
grid-column: 1 / span 2;
display: grid;
grid-template-columns: 1fr 1fr;
@@ -300,46 +414,46 @@ MAIN_CSS = r'''
padding-right: 0;
}
- .lineno.right {
+ .wsd-lineno.wsd-right {
grid-column: 2;
}
- .lineno.right.insert {
+ .wsd-lineno.wsd-right.wsd-insert {
grid-column: 2;
}
- .line.left, .line.right.empty {
+ .wsd-line.wsd-left, .wsd-line.wsd-right.wsd-empty {
display: none;
}
- .line {
+ .wsd-line {
grid-column: 3;
}
- .line.left.insert {
+ .wsd-line.wsd-left.wsd-insert {
display: block;
}
- .line.left.change {
+ .wsd-line.wsd-left.wsd-change {
display: block;
}
- .lineno.right.empty {
+ .wsd-lineno.wsd-right.wsd-empty {
display: none;
}
- .lineno.left.empty {
- background-color: #ddfbe6;
+ .wsd-lineno.wsd-left.wsd-empty {
+ background-color: var(--c-bg-insert-lineno);
}
/* line continuation arrows only in right line number column */
- .lineno.left.insert::after {
+ .wsd-lineno.wsd-left.wsd-insert::after {
}
- .lineno.left.insert::before {
+ .wsd-lineno.wsd-left.wsd-insert::before {
content: "";
grid-column: 2;
- border-left: 1px solid #e0c8c8; /* pick a darker border color inside the light red gutter */
+ border-left: 1px solid var(--c-border-delete); /* pick a darker border color inside the light red gutter */
margin-left: -5px;
}
}
@@ -351,13 +465,13 @@ DIFF_STYLE_SCRIPT = r'''
const findRule = ((stylesheet, name) => Array.from(stylesheet.cssRules).find(
element => (element instanceof CSSLayerBlockRule && element.name == name)).cssRules[0]);
- const automaticMediaElement = findRule(findStylesheet('main-style'), 'automatic-media-rule');
+ const automaticMediaElement = findRule(findStylesheet('wsd-main-style'), 'wsd-automatic-media-rule');
const automaticMediaRule = automaticMediaElement.media[0];
const impossibleMediaRule = "screen and (max-width: 0px)";
const tautologicalMediaRule = "screen and (min-width: 0px)";
- const toggleAuto = document.getElementById("toggle-split-auto");
- const toggleForce = document.getElementById("toggle-split-force");
+ const toggleAuto = document.getElementById("wsd-toggle-split-auto");
+ const toggleForce = document.getElementById("wsd-toggle-split-force");
toggleAuto.checked = true;
toggleForce.disabled = true;
@@ -398,11 +512,10 @@ DIFF_STYLE_SCRIPT = r'''
});
toggleForce.checked = !mediaMatch.matches;
- document.getElementById('js-controls').style = 'display: flex';
+ document.getElementById('wsd-js-controls').style = 'display: flex';
'''
-HTML_TEMPLATE = r'''
-
+HTML_TEMPLATE = r'''
@@ -410,7 +523,13 @@ HTML_TEMPLATE = r'''
-