@ascribed/review 0.1.2-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +183 -0
  3. package/dist/github/index.d.ts +7 -0
  4. package/dist/github/index.js +8 -0
  5. package/dist/github/index.js.map +1 -0
  6. package/dist/github/marker.d.ts +63 -0
  7. package/dist/github/marker.js +156 -0
  8. package/dist/github/marker.js.map +1 -0
  9. package/dist/github/queries.d.ts +16 -0
  10. package/dist/github/queries.js +174 -0
  11. package/dist/github/queries.js.map +1 -0
  12. package/dist/github/read.d.ts +84 -0
  13. package/dist/github/read.js +141 -0
  14. package/dist/github/read.js.map +1 -0
  15. package/dist/github/repository.d.ts +30 -0
  16. package/dist/github/repository.js +77 -0
  17. package/dist/github/repository.js.map +1 -0
  18. package/dist/github/requests.d.ts +65 -0
  19. package/dist/github/requests.js +274 -0
  20. package/dist/github/requests.js.map +1 -0
  21. package/dist/github/session.d.ts +143 -0
  22. package/dist/github/session.js +581 -0
  23. package/dist/github/session.js.map +1 -0
  24. package/dist/github/transport.d.ts +33 -0
  25. package/dist/github/transport.js +206 -0
  26. package/dist/github/transport.js.map +1 -0
  27. package/dist/marks/index.d.ts +92 -0
  28. package/dist/marks/index.js +766 -0
  29. package/dist/marks/index.js.map +1 -0
  30. package/dist/overlay/index.d.ts +4 -0
  31. package/dist/overlay/index.js +7 -0
  32. package/dist/overlay/index.js.map +1 -0
  33. package/dist/overlay/markdown.d.ts +4 -0
  34. package/dist/overlay/markdown.js +368 -0
  35. package/dist/overlay/markdown.js.map +1 -0
  36. package/dist/overlay/overlay.d.ts +47 -0
  37. package/dist/overlay/overlay.js +1710 -0
  38. package/dist/overlay/overlay.js.map +1 -0
  39. package/dist/overlay/style.d.ts +1 -0
  40. package/dist/overlay/style.js +570 -0
  41. package/dist/overlay/style.js.map +1 -0
  42. package/dist/overlay/text.d.ts +33 -0
  43. package/dist/overlay/text.js +200 -0
  44. package/dist/overlay/text.js.map +1 -0
  45. package/dist/overlay/types.d.ts +60 -0
  46. package/dist/overlay/types.js +2 -0
  47. package/dist/overlay/types.js.map +1 -0
  48. package/dist/place/anchor.d.ts +23 -0
  49. package/dist/place/anchor.js +49 -0
  50. package/dist/place/anchor.js.map +1 -0
  51. package/dist/place/index.d.ts +4 -0
  52. package/dist/place/index.js +6 -0
  53. package/dist/place/index.js.map +1 -0
  54. package/dist/place/lines.d.ts +56 -0
  55. package/dist/place/lines.js +280 -0
  56. package/dist/place/lines.js.map +1 -0
  57. package/dist/place/place.d.ts +77 -0
  58. package/dist/place/place.js +182 -0
  59. package/dist/place/place.js.map +1 -0
  60. package/dist/shared/errors.d.ts +40 -0
  61. package/dist/shared/errors.js +22 -0
  62. package/dist/shared/errors.js.map +1 -0
  63. package/dist/shared/git.d.ts +4 -0
  64. package/dist/shared/git.js +29 -0
  65. package/dist/shared/git.js.map +1 -0
  66. package/dist/shared/types.d.ts +56 -0
  67. package/dist/shared/types.js +3 -0
  68. package/dist/shared/types.js.map +1 -0
  69. package/package.json +61 -0
  70. package/src/marks/marks.css +361 -0
@@ -0,0 +1,29 @@
1
+ // Running `git` with a fixed argument list, never through a shell.
2
+ import { execFile } from "node:child_process";
3
+ import { ReviewError } from "./errors.js";
4
+ /** Runs `git` in `cwd` and resolves with its standard output. */
5
+ export function git(cwd, args) {
6
+ return new Promise((resolve, reject) => {
7
+ execFile("git", ["-c", "core.quotepath=off", ...args], { cwd, maxBuffer: 256 * 1024 * 1024, encoding: "utf8" }, (error, stdout, stderr) => {
8
+ if (error === null) {
9
+ resolve(stdout);
10
+ return;
11
+ }
12
+ const missing = error.code === "ENOENT";
13
+ const detail = stderr.trim() || error.message;
14
+ reject(new ReviewError("git", missing ? "`git` isn't on the path." : `\`git ${args.join(" ")}\` failed: ${detail}`, { cause: error }));
15
+ });
16
+ });
17
+ }
18
+ /** Like `git`, but resolves with `undefined` instead of rejecting when git exits non-zero. */
19
+ export async function gitMaybe(cwd, args) {
20
+ try {
21
+ return await git(cwd, args);
22
+ }
23
+ catch (error) {
24
+ if (error instanceof ReviewError && error.message.startsWith("`git` isn't"))
25
+ throw error;
26
+ return undefined;
27
+ }
28
+ }
29
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/shared/git.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,iEAAiE;AACjE,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,IAAuB;IACtD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CACN,KAAK,EACL,CAAC,IAAI,EAAE,oBAAoB,EAAE,GAAG,IAAI,CAAC,EACrC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EACvD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO,CAAC,MAAM,CAAC,CAAC;gBAChB,OAAO;YACT,CAAC;YACD,MAAM,OAAO,GAAI,KAA+B,CAAC,IAAI,KAAK,QAAQ,CAAC;YACnE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC;YAC9C,MAAM,CACJ,IAAI,WAAW,CACb,KAAK,EACL,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,MAAM,EAAE,EACpF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CACF,CAAC;QACJ,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAuB;IACjE,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC;YAAE,MAAM,KAAK,CAAC;QACzF,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,56 @@
1
+ /** A comment's author: `null` when GitHub reports none (a deleted account). */
2
+ export interface Author {
3
+ login: string;
4
+ avatarUrl: string | undefined;
5
+ }
6
+ /** A comment in a thread. `body` is Markdown from other people, unrendered. */
7
+ export interface ThreadComment {
8
+ id: string;
9
+ author: Author | null;
10
+ body: string;
11
+ createdAt: string;
12
+ url: string;
13
+ /** Whether it's in the viewer's pending review: only the viewer sees it. */
14
+ pending: boolean;
15
+ }
16
+ /** Which side of the pull request's diff a thread is on: new text, or removed text. */
17
+ export type Side = "RIGHT" | "LEFT";
18
+ /**
19
+ * A review thread, or a conversation comment with a hidden anchor marker
20
+ * (`kind: "conversation"`), which GitHub can't resolve.
21
+ */
22
+ export interface Thread {
23
+ id: string;
24
+ kind: "review" | "conversation";
25
+ /** The file's path in the repository, with `/`. */
26
+ repositoryPath: string;
27
+ /** `"file"` for a thread on a whole file, which has no lines. */
28
+ subject: "line" | "file";
29
+ side: Side;
30
+ /** The thread's last line at the pull request's head, or `null` when it's outdated. */
31
+ line: number | null;
32
+ /** Its first line, for a multi-line thread. */
33
+ startLine: number | null;
34
+ /** Its last line at `originalCommit`. */
35
+ originalLine: number | null;
36
+ originalStartLine: number | null;
37
+ /** The commit `line` is counted at. */
38
+ commit: string | undefined;
39
+ /** The commit the thread was made at. */
40
+ originalCommit: string | undefined;
41
+ resolved: boolean;
42
+ outdated: boolean;
43
+ canResolve: boolean;
44
+ canUnresolve: boolean;
45
+ canReply: boolean;
46
+ comments: ThreadComment[];
47
+ /** GitHub's diff hunk ending at the thread's line. */
48
+ diffHunk: string | undefined;
49
+ /** For a conversation comment: the block source and build its marker names. */
50
+ marker: {
51
+ source: string;
52
+ build: string | undefined;
53
+ } | undefined;
54
+ /** The text the thread is on, when known: quoted by a conversation comment, or read from git. */
55
+ quote: string | undefined;
56
+ }
@@ -0,0 +1,3 @@
1
+ // The shapes threads take, shared by `./github` and `./place`.
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,+DAA+D"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@ascribed/review",
3
+ "version": "0.1.2-next.2",
4
+ "description": "Reviewing Ascribe pages as readers see them: changed blocks marked on the rendered page, and a pull request's review threads beside them.",
5
+ "type": "module",
6
+ "license": "MPL-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ascribed-dev/ascribe.git",
10
+ "directory": "packages/review"
11
+ },
12
+ "homepage": "https://github.com/ascribed-dev/ascribe/tree/main/packages/review#readme",
13
+ "bugs": "https://github.com/ascribed-dev/ascribe/issues",
14
+ "sideEffects": false,
15
+ "files": [
16
+ "dist",
17
+ "src/marks/marks.css",
18
+ "README.md"
19
+ ],
20
+ "exports": {
21
+ "./marks": {
22
+ "types": "./dist/marks/index.d.ts",
23
+ "default": "./dist/marks/index.js"
24
+ },
25
+ "./marks.css": "./src/marks/marks.css",
26
+ "./overlay": {
27
+ "types": "./dist/overlay/index.d.ts",
28
+ "default": "./dist/overlay/index.js"
29
+ },
30
+ "./github": {
31
+ "types": "./dist/github/index.d.ts",
32
+ "default": "./dist/github/index.js"
33
+ },
34
+ "./place": {
35
+ "types": "./dist/place/index.d.ts",
36
+ "default": "./dist/place/index.js"
37
+ }
38
+ },
39
+ "engines": {
40
+ "node": ">=24"
41
+ },
42
+ "devDependencies": {
43
+ "@ascribed/elements": "0.1.2-next.2",
44
+ "@types/node": "^24.0.0",
45
+ "esbuild": "^0.28.2",
46
+ "jsdom": "^30.1.1"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "dependencies": {
52
+ "smol-toml": "^1.9.0"
53
+ },
54
+ "gitHead": "1291a4b1db8edd88650e70f417d8037e26bceb38",
55
+ "scripts": {
56
+ "build": "tsc -p tsconfig.build.json",
57
+ "typecheck": "tsc --noEmit",
58
+ "test": "vitest run",
59
+ "embed": "node embed.ts"
60
+ }
61
+ }
@@ -0,0 +1,361 @@
1
+ /*
2
+ * @ascribed/review/marks.css: how changed blocks look on a rendered page.
3
+ *
4
+ * `markChanges` (@ascribed/review/marks) puts the marks on the page and
5
+ * `setShow` picks what it shows, with `data-ascribe-show` on the page's root:
6
+ * `changes`, `will` (the page as it will be), or `was` (as it was). Every
7
+ * mark has a label as well as a bar of its own shape, so no change is told by
8
+ * color alone. Theme it with the --ascribe-review-* custom properties.
9
+ *
10
+ * The colors follow the reader's color scheme. A page whose own colors don't
11
+ * sets `data-ascribe-scheme="light"` or `"dark"` on its root element.
12
+ */
13
+
14
+ :root {
15
+ --ascribe-review-added: #1a7f37;
16
+ --ascribe-review-added-background: #dcf7e3;
17
+ --ascribe-review-changed: #8a5a00;
18
+ --ascribe-review-changed-background: #fff1c2;
19
+ --ascribe-review-removed: #c4222d;
20
+ --ascribe-review-removed-background: #ffe4e2;
21
+ --ascribe-review-moved: #7a3fd0;
22
+ --ascribe-review-moved-background: #f0e6ff;
23
+ --ascribe-review-muted: #5b6475;
24
+ --ascribe-review-surface: #ffffff;
25
+ --ascribe-review-flash: #fff6bf;
26
+ --ascribe-review-focus: #1f6feb;
27
+ --ascribe-review-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
28
+ /* The review overlay's threads and controls (@ascribed/review/overlay). */
29
+ --ascribe-review-text: #1d2330;
30
+ --ascribe-review-border: #d3d8e0;
31
+ --ascribe-review-thread: #f5f7fb;
32
+ --ascribe-review-input: #ffffff;
33
+ --ascribe-review-accent-text: #ffffff;
34
+ --ascribe-review-blend: multiply;
35
+ }
36
+
37
+ @media (prefers-color-scheme: dark) {
38
+ :root:not([data-ascribe-scheme="light"]) {
39
+ --ascribe-review-added: #4fc572;
40
+ --ascribe-review-added-background: #133520;
41
+ --ascribe-review-changed: #dcb13a;
42
+ --ascribe-review-changed-background: #3a2f0d;
43
+ --ascribe-review-removed: #ff8279;
44
+ --ascribe-review-removed-background: #401918;
45
+ --ascribe-review-moved: #c59bff;
46
+ --ascribe-review-moved-background: #2d2440;
47
+ --ascribe-review-muted: #929bab;
48
+ --ascribe-review-surface: #1e2026;
49
+ --ascribe-review-flash: #3b3514;
50
+ --ascribe-review-focus: #5aa2ff;
51
+ --ascribe-review-text: #d9dde5;
52
+ --ascribe-review-border: #343a45;
53
+ --ascribe-review-thread: #272b33;
54
+ --ascribe-review-input: #1e2026;
55
+ --ascribe-review-accent-text: #0b1220;
56
+ --ascribe-review-blend: screen;
57
+ }
58
+ }
59
+
60
+ :root[data-ascribe-scheme="dark"] {
61
+ --ascribe-review-added: #4fc572;
62
+ --ascribe-review-added-background: #133520;
63
+ --ascribe-review-changed: #dcb13a;
64
+ --ascribe-review-changed-background: #3a2f0d;
65
+ --ascribe-review-removed: #ff8279;
66
+ --ascribe-review-removed-background: #401918;
67
+ --ascribe-review-moved: #c59bff;
68
+ --ascribe-review-moved-background: #2d2440;
69
+ --ascribe-review-muted: #929bab;
70
+ --ascribe-review-surface: #1e2026;
71
+ --ascribe-review-flash: #3b3514;
72
+ --ascribe-review-focus: #5aa2ff;
73
+ --ascribe-review-text: #d9dde5;
74
+ --ascribe-review-border: #343a45;
75
+ --ascribe-review-thread: #272b33;
76
+ --ascribe-review-input: #1e2026;
77
+ --ascribe-review-accent-text: #0b1220;
78
+ --ascribe-review-blend: screen;
79
+ }
80
+
81
+ .ascribe-marks {
82
+ position: relative;
83
+ }
84
+
85
+ /* The bar: a left border whose color and style differ by kind. */
86
+ .ascribe-marks [data-ascribe-change] {
87
+ position: relative;
88
+ border-inline-start: 3px solid transparent;
89
+ padding-inline-start: 9px;
90
+ margin-inline-start: -12px;
91
+ border-radius: 0 3px 3px 0;
92
+ }
93
+
94
+ .ascribe-marks li[data-ascribe-change] {
95
+ margin-inline-start: 0;
96
+ padding-inline-start: 6px;
97
+ }
98
+
99
+ [data-ascribe-show="changes"] [data-ascribe-change="added"] {
100
+ border-inline-start-color: var(--ascribe-review-added);
101
+ background: var(--ascribe-review-added-background);
102
+ }
103
+
104
+ [data-ascribe-show="changes"] [data-ascribe-change="changed"] {
105
+ border-inline-start-color: var(--ascribe-review-changed);
106
+ border-inline-start-style: dashed;
107
+ }
108
+
109
+ [data-ascribe-show="changes"] [data-ascribe-change="moved"] {
110
+ border-inline-start-color: var(--ascribe-review-moved);
111
+ border-inline-start-style: double;
112
+ background: var(--ascribe-review-moved-background);
113
+ }
114
+
115
+ [data-ascribe-show="changes"] [data-ascribe-change="removed"] {
116
+ border-inline-start-color: var(--ascribe-review-removed);
117
+ border-inline-start-style: dotted;
118
+ background: var(--ascribe-review-removed-background);
119
+ }
120
+
121
+ /* The labels: Added, Changed, Removed, Moved. */
122
+ .ascribe-label {
123
+ display: none;
124
+ font-size: 10.5px;
125
+ font-weight: 600;
126
+ letter-spacing: 0.04em;
127
+ text-transform: uppercase;
128
+ margin-inline-end: 6px;
129
+ font-style: normal;
130
+ }
131
+
132
+ [data-ascribe-show="changes"] .ascribe-label {
133
+ display: inline-block;
134
+ }
135
+
136
+ [data-ascribe-show="changes"] .ascribe-label-before {
137
+ display: block;
138
+ margin-block: 8px -4px;
139
+ }
140
+
141
+ .ascribe-label[data-ascribe-label="added"] {
142
+ color: var(--ascribe-review-added);
143
+ }
144
+
145
+ .ascribe-label[data-ascribe-label="changed"] {
146
+ color: var(--ascribe-review-changed);
147
+ }
148
+
149
+ .ascribe-label[data-ascribe-label="removed"] {
150
+ color: var(--ascribe-review-removed);
151
+ }
152
+
153
+ .ascribe-label[data-ascribe-label="moved"] {
154
+ color: var(--ascribe-review-moved);
155
+ }
156
+
157
+ /* On a tab's label and a <details>' summary: what's changed inside. */
158
+ .ascribe-hint {
159
+ display: none;
160
+ margin-inline-start: 2px;
161
+ font-size: 11px;
162
+ font-weight: 600;
163
+ font-style: normal;
164
+ }
165
+
166
+ [data-ascribe-show="changes"] .ascribe-hint {
167
+ display: inline;
168
+ }
169
+
170
+ .ascribe-hint[data-ascribe-hint="added"] {
171
+ color: var(--ascribe-review-added);
172
+ }
173
+
174
+ .ascribe-hint[data-ascribe-hint="changed"] {
175
+ color: var(--ascribe-review-changed);
176
+ }
177
+
178
+ /* Words inside a changed block. */
179
+ .ascribe-ins {
180
+ text-decoration: none;
181
+ }
182
+
183
+ .ascribe-del {
184
+ display: none;
185
+ text-decoration: none;
186
+ }
187
+
188
+ [data-ascribe-show="changes"] .ascribe-ins {
189
+ background: var(--ascribe-review-added-background);
190
+ box-shadow: inset 0 -2px 0 var(--ascribe-review-added);
191
+ }
192
+
193
+ [data-ascribe-show="changes"] .ascribe-del {
194
+ display: inline;
195
+ background: var(--ascribe-review-removed-background);
196
+ text-decoration: line-through;
197
+ color: var(--ascribe-review-muted);
198
+ }
199
+
200
+ /* A removed block: collapsed to one line, struck through, with Show. */
201
+ .ascribe-removed {
202
+ display: none;
203
+ list-style: none;
204
+ }
205
+
206
+ [data-ascribe-show="changes"] .ascribe-removed {
207
+ display: flex;
208
+ gap: 6px;
209
+ align-items: baseline;
210
+ margin-block: 8px;
211
+ }
212
+
213
+ [data-ascribe-show="changes"] .ascribe-removed-body {
214
+ flex: 1;
215
+ min-width: 0;
216
+ max-height: 1.6em;
217
+ overflow: hidden;
218
+ text-decoration: line-through;
219
+ color: var(--ascribe-review-muted);
220
+ }
221
+
222
+ [data-ascribe-show="changes"] .ascribe-removed-body > * {
223
+ margin-block: 0;
224
+ }
225
+
226
+ [data-ascribe-show="changes"] .ascribe-removed:not(.ascribe-open) .ascribe-removed-body,
227
+ [data-ascribe-show="changes"] .ascribe-removed:not(.ascribe-open) .ascribe-removed-body > * {
228
+ overflow: hidden;
229
+ white-space: nowrap;
230
+ text-overflow: ellipsis;
231
+ }
232
+
233
+ [data-ascribe-show="changes"] .ascribe-removed.ascribe-open .ascribe-removed-body {
234
+ max-height: none;
235
+ }
236
+
237
+ [data-ascribe-show="changes"] .ascribe-removed.ascribe-open .ascribe-removed-body > * {
238
+ margin-block: 0.5em;
239
+ }
240
+
241
+ .ascribe-toggle,
242
+ .ascribe-move-link {
243
+ font: inherit;
244
+ font-size: 12px;
245
+ white-space: nowrap;
246
+ color: var(--ascribe-review-focus);
247
+ background: none;
248
+ border: 0;
249
+ padding: 0 2px;
250
+ cursor: pointer;
251
+ text-decoration: underline;
252
+ text-underline-offset: 2px;
253
+ }
254
+
255
+ [data-ascribe-show="was"] .ascribe-removed {
256
+ display: block;
257
+ }
258
+
259
+ [data-ascribe-show="was"] .ascribe-removed > .ascribe-label,
260
+ [data-ascribe-show="was"] .ascribe-toggle {
261
+ display: none;
262
+ }
263
+
264
+ /* A move: the block where it is now, and a stub where it was. */
265
+ .ascribe-move-note {
266
+ display: none;
267
+ font-size: 12px;
268
+ color: var(--ascribe-review-moved);
269
+ }
270
+
271
+ [data-ascribe-show="changes"] .ascribe-move-note {
272
+ display: inline;
273
+ }
274
+
275
+ [data-ascribe-show="changes"] .ascribe-move-after {
276
+ display: block;
277
+ margin-block: -4px 8px;
278
+ }
279
+
280
+ .ascribe-moved-from {
281
+ display: none;
282
+ list-style: none;
283
+ }
284
+
285
+ [data-ascribe-show="changes"] .ascribe-moved-from {
286
+ display: block;
287
+ border-inline-start: 3px double var(--ascribe-review-moved);
288
+ padding-inline-start: 9px;
289
+ margin: 6px 0 6px -12px;
290
+ }
291
+
292
+ [data-ascribe-show="was"] .ascribe-moved-from {
293
+ display: block;
294
+ }
295
+
296
+ [data-ascribe-show="was"] .ascribe-moved-from > .ascribe-move-note {
297
+ display: none;
298
+ }
299
+
300
+ /* What only the page as it was shows: a changed block before, a moved block
301
+ at its old place. */
302
+ [data-ascribe-was-only] {
303
+ display: none;
304
+ }
305
+
306
+ [data-ascribe-show="was"] [data-ascribe-was-only] {
307
+ display: block;
308
+ }
309
+
310
+ [data-ascribe-show="was"] li[data-ascribe-was-only] {
311
+ display: list-item;
312
+ }
313
+
314
+ [data-ascribe-show="was"] [data-ascribe-change="added"],
315
+ [data-ascribe-show="was"] [data-ascribe-change="moved"],
316
+ [data-ascribe-show="was"] [data-ascribe-change="moved"] + .ascribe-move-after,
317
+ [data-ascribe-show="was"] [data-ascribe-has-was],
318
+ [data-ascribe-show="was"] .ascribe-label-before,
319
+ [data-ascribe-show="was"] .ascribe-ins {
320
+ display: none;
321
+ }
322
+
323
+ [data-ascribe-show="was"] .ascribe-del {
324
+ display: inline;
325
+ }
326
+
327
+ /* A block's source, on hover and focus. */
328
+ .ascribe-where {
329
+ position: absolute;
330
+ inset-inline-end: 0;
331
+ transform: translateY(-100%);
332
+ font-family: var(--ascribe-review-mono);
333
+ font-size: 10.5px;
334
+ color: var(--ascribe-review-muted);
335
+ background: var(--ascribe-review-surface);
336
+ padding: 0 4px;
337
+ pointer-events: none;
338
+ white-space: nowrap;
339
+ z-index: 1;
340
+ }
341
+
342
+ .ascribe-flash {
343
+ animation: ascribe-flash 1.4s ease-out;
344
+ }
345
+
346
+ .ascribe-marks [tabindex="-1"]:focus {
347
+ outline: none;
348
+ }
349
+
350
+ @keyframes ascribe-flash {
351
+ from {
352
+ background: var(--ascribe-review-flash);
353
+ }
354
+ }
355
+
356
+ @media (prefers-reduced-motion: reduce) {
357
+ .ascribe-flash {
358
+ animation: none;
359
+ outline: 2px solid var(--ascribe-review-focus);
360
+ }
361
+ }