@tanishqkancharla/diffmap 0.1.5 → 0.2.0
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.
- package/README.md +57 -57
- package/index.html +18 -0
- package/package.json +7 -5
- package/src/annotations.ts +36 -29
- package/src/changes.ts +229 -0
- package/src/cli.ts +1 -0
- package/src/errors.ts +5 -0
- package/src/gist/GistApp.tsx +36 -18
- package/src/gist/fetchGist.ts +23 -0
- package/src/git.test.ts +402 -0
- package/src/git.ts +498 -0
- package/src/github/GitHubApp.tsx +39 -22
- package/src/github/fetchRepo.test.ts +59 -1
- package/src/github/fetchRepo.ts +99 -43
- package/src/github/patch.test.ts +18 -9
- package/src/github/patch.ts +30 -19
- package/src/host/HostHome.tsx +20 -7
- package/src/host/home.md +17 -55
- package/src/host/home.test.ts +4 -3
- package/src/index.ts +1 -0
- package/src/parseFence.ts +6 -6
- package/src/parseViewer.test.ts +87 -0
- package/src/parseViewer.ts +103 -77
- package/src/serve.ts +34 -1
- package/src/share.ts +18 -0
- package/src/viewer/CallStackDiff.tsx +15 -3
- package/src/viewer/ChangesCard.tsx +267 -0
- package/src/viewer/ComarkView.tsx +83 -15
- package/src/viewer/Fence.tsx +9 -1
- package/src/viewer/MermaidBlock.tsx +99 -11
- package/src/viewer/RangePicker.tsx +526 -0
- package/src/viewer/SourceDiffPanel.tsx +253 -116
- package/src/viewer/TableOfContents.tsx +177 -93
- package/src/viewer/ViewerApp.tsx +34 -114
- package/src/viewer/changesSource.ts +91 -0
- package/src/viewer/sourceHeader.test.ts +2 -87
- package/src/viewer/sourceHeader.ts +4 -63
- package/src/viewer/sourceNavigation.ts +0 -43
- package/vite.config.ts +1 -0
- package/vite.gist.config.ts +57 -0
- package/src/viewer/TocButton.tsx +0 -27
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
1
|
+
import { useEffect, useMemo, useRef, useState, type MouseEvent } from "react";
|
|
2
2
|
import { CodeView, type CodeViewHandle } from "@pierre/diffs/react";
|
|
3
|
-
import type {
|
|
4
|
-
CodeViewDiffItem,
|
|
5
|
-
CodeViewOptions,
|
|
6
|
-
CodeViewLineSelection,
|
|
7
|
-
} from "@pierre/diffs";
|
|
3
|
+
import type { CodeViewOptions, CodeViewLineSelection } from "@pierre/diffs";
|
|
8
4
|
import {
|
|
9
5
|
backgroundColor,
|
|
10
6
|
border,
|
|
11
7
|
borderColor,
|
|
8
|
+
colors,
|
|
12
9
|
Button,
|
|
10
|
+
ChevronDown,
|
|
13
11
|
flex,
|
|
14
12
|
spacing,
|
|
15
13
|
text,
|
|
@@ -21,21 +19,17 @@ import {
|
|
|
21
19
|
type SourceAnnotation,
|
|
22
20
|
type SourceReference,
|
|
23
21
|
} from "../annotations.js";
|
|
22
|
+
import { changedFileItem, type Rev } from "../changes.js";
|
|
24
23
|
import type { SourceDefinition } from "../definitions.js";
|
|
25
24
|
import { useGistPin } from "../gist/pin.ts";
|
|
26
25
|
import { useGitHubPin } from "../github/pin.ts";
|
|
27
26
|
import { useHostFiles } from "../host/files.ts";
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from "./sourceNavigation.js";
|
|
27
|
+
import { ChangesSummary } from "./ChangesCard.tsx";
|
|
28
|
+
import { useChanges } from "./changesSource.ts";
|
|
29
|
+
import { RangePicker } from "./RangePicker.tsx";
|
|
30
|
+
import { requestSource, useSourceReference } from "./sourceNavigation.js";
|
|
33
31
|
import { pierreDiffOptions, sourceSelectionCss } from "./pierre.js";
|
|
34
|
-
import {
|
|
35
|
-
sourcePanelOrigin,
|
|
36
|
-
sourcePanelPath,
|
|
37
|
-
sourcePanelPin,
|
|
38
|
-
} from "./sourceHeader.ts";
|
|
32
|
+
import { sourcePanelPin } from "./sourceHeader.ts";
|
|
39
33
|
import { useViewerMode } from "./viewerMode.ts";
|
|
40
34
|
|
|
41
35
|
export type SourceSelection = {
|
|
@@ -50,7 +44,10 @@ type DefinitionNavigation = {
|
|
|
50
44
|
};
|
|
51
45
|
|
|
52
46
|
export function SourceDiffPanel(props: {
|
|
53
|
-
|
|
47
|
+
/** The changes to show; references with their own rev open that rev instead. */
|
|
48
|
+
rev: Rev;
|
|
49
|
+
/** Local viewers pick other changes with the range picker. */
|
|
50
|
+
onRevChange?: (rev: Rev) => void;
|
|
54
51
|
selection: SourceSelection | undefined;
|
|
55
52
|
onSelect: (selection: SourceSelection) => void;
|
|
56
53
|
}) {
|
|
@@ -79,51 +76,86 @@ export function SourceDiffPanel(props: {
|
|
|
79
76
|
const pinClass = useStyles(styles.pin);
|
|
80
77
|
const links = useStyles(styles.links);
|
|
81
78
|
const code = useStyles(styles.code);
|
|
79
|
+
const summary = useStyles(styles.summary);
|
|
80
|
+
const codeWrap = useStyles(styles.codeWrap);
|
|
81
|
+
const collapseIcon = useStyles(styles.collapseIcon);
|
|
82
82
|
const github = useGitHubPin();
|
|
83
83
|
const gist = useGistPin();
|
|
84
84
|
const hostFiles = useHostFiles();
|
|
85
85
|
const selection = props.selection;
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
86
|
+
const reference = selection?.reference;
|
|
87
|
+
const changes = useChanges(props.rev);
|
|
88
|
+
const items = useMemo(
|
|
89
89
|
() =>
|
|
90
|
-
|
|
91
|
-
?
|
|
92
|
-
:
|
|
93
|
-
[
|
|
90
|
+
changes === undefined || changes instanceof Error
|
|
91
|
+
? []
|
|
92
|
+
: changes.files.flatMap((changed) => changedFileItem(changed) ?? []),
|
|
93
|
+
[changes],
|
|
94
94
|
);
|
|
95
|
+
// A rev-less reference resolves against current files; a commit reference names its lines directly.
|
|
96
|
+
const source = useSourceReference(
|
|
97
|
+
reference?.kind === "file" && reference.rev === undefined
|
|
98
|
+
? reference
|
|
99
|
+
: undefined,
|
|
100
|
+
);
|
|
101
|
+
const resolvedFile = source instanceof Error ? undefined : source;
|
|
102
|
+
const inChanges =
|
|
103
|
+
resolvedFile !== undefined &&
|
|
104
|
+
items.some((item) => item.id === resolvedFile.path);
|
|
95
105
|
const file =
|
|
96
|
-
definition
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
: definition;
|
|
106
|
+
definition ??
|
|
107
|
+
(resolvedFile !== undefined && changes !== undefined && !inChanges
|
|
108
|
+
? resolvedFile
|
|
109
|
+
: undefined);
|
|
101
110
|
const status =
|
|
102
111
|
navigation.status !== undefined
|
|
103
112
|
? navigation.status
|
|
104
113
|
: source instanceof Error
|
|
105
114
|
? source.message
|
|
106
|
-
:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
: changes instanceof Error
|
|
116
|
+
? changes.message
|
|
117
|
+
: changes === undefined
|
|
118
|
+
? "Loading changes…"
|
|
119
|
+
: reference?.kind === "file" &&
|
|
120
|
+
reference.rev === undefined &&
|
|
121
|
+
source === undefined &&
|
|
122
|
+
(mode === "local" ||
|
|
123
|
+
mode === "github" ||
|
|
124
|
+
hostFiles !== undefined)
|
|
125
|
+
? "Opening source…"
|
|
126
|
+
: items.length === 0 && file === undefined
|
|
127
|
+
? "No changes."
|
|
128
|
+
: undefined;
|
|
111
129
|
const selectedLines = useMemo<CodeViewLineSelection | null>(() => {
|
|
112
130
|
if (file !== undefined)
|
|
113
131
|
return { id: file.path, range: { start: file.start, end: file.end } };
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
const focus =
|
|
133
|
+
resolvedFile ??
|
|
134
|
+
(reference?.kind === "file" && reference.start !== undefined
|
|
135
|
+
? {
|
|
136
|
+
path: reference.path,
|
|
137
|
+
start: reference.start,
|
|
138
|
+
end: reference.end ?? reference.start,
|
|
139
|
+
}
|
|
140
|
+
: undefined);
|
|
116
141
|
// oxlint-disable-next-line unicorn/no-null -- Pierre uses null for a controlled empty selection.
|
|
117
|
-
if (
|
|
142
|
+
if (focus === undefined) return null;
|
|
118
143
|
return {
|
|
119
|
-
id:
|
|
120
|
-
range: {
|
|
121
|
-
start: ref.start,
|
|
122
|
-
end: ref.end,
|
|
123
|
-
side: ref.side === "old" ? "deletions" : "additions",
|
|
124
|
-
},
|
|
144
|
+
id: focus.path,
|
|
145
|
+
range: { start: focus.start, end: focus.end, side: "additions" },
|
|
125
146
|
};
|
|
126
|
-
}, [
|
|
147
|
+
}, [reference, file, resolvedFile]);
|
|
148
|
+
const focusedPath =
|
|
149
|
+
file?.path ?? (reference?.kind === "file" ? reference.path : undefined);
|
|
150
|
+
// Collapsed files reset when the rev or the selected reference changes, so a reference always shows its file.
|
|
151
|
+
const [collapse, setCollapse] = useState<{
|
|
152
|
+
rev: Rev;
|
|
153
|
+
selection: SourceSelection | undefined;
|
|
154
|
+
paths: ReadonlySet<string>;
|
|
155
|
+
}>({ rev: props.rev, selection, paths: new Set() });
|
|
156
|
+
if (collapse.rev !== props.rev || collapse.selection !== selection) {
|
|
157
|
+
setCollapse({ rev: props.rev, selection, paths: new Set() });
|
|
158
|
+
}
|
|
127
159
|
const rangeId = selectedLines?.id;
|
|
128
160
|
const rangeStart = selectedLines?.range.start;
|
|
129
161
|
const rangeEnd = selectedLines?.range.end;
|
|
@@ -131,35 +163,84 @@ export function SourceDiffPanel(props: {
|
|
|
131
163
|
selectedLines !== null && "side" in selectedLines.range
|
|
132
164
|
? selectedLines.range.side
|
|
133
165
|
: undefined;
|
|
166
|
+
const shownItems = useMemo(
|
|
167
|
+
() =>
|
|
168
|
+
items.map((item) => {
|
|
169
|
+
// The referenced file shows enough unchanged context to reveal its range.
|
|
170
|
+
const changed =
|
|
171
|
+
item.id === rangeId &&
|
|
172
|
+
rangeSide === "additions" &&
|
|
173
|
+
changes !== undefined &&
|
|
174
|
+
!(changes instanceof Error)
|
|
175
|
+
? changes.files.find((entry) => entry.path === item.id)
|
|
176
|
+
: undefined;
|
|
177
|
+
const shown =
|
|
178
|
+
changed === undefined
|
|
179
|
+
? item
|
|
180
|
+
: (changedFileItem(changed, {
|
|
181
|
+
start: rangeStart!,
|
|
182
|
+
end: rangeEnd!,
|
|
183
|
+
}) ?? item);
|
|
184
|
+
const collapsed = collapse.paths.has(item.id);
|
|
185
|
+
// Pierre only rereads an item when its version changes.
|
|
186
|
+
return {
|
|
187
|
+
...shown,
|
|
188
|
+
collapsed,
|
|
189
|
+
version: (shown.version ?? 0) * 2 + (collapsed ? 1 : 0),
|
|
190
|
+
};
|
|
191
|
+
}),
|
|
192
|
+
[items, changes, collapse.paths, rangeId, rangeStart, rangeEnd, rangeSide],
|
|
193
|
+
);
|
|
194
|
+
const toggleCollapsed = (path: string) => {
|
|
195
|
+
const paths = new Set(collapse.paths);
|
|
196
|
+
if (!paths.delete(path)) paths.add(path);
|
|
197
|
+
setCollapse({ ...collapse, paths });
|
|
198
|
+
};
|
|
199
|
+
// File headers live in Pierre's shadow DOM; a click inside one names its file through the prefix slot.
|
|
200
|
+
const onHeaderClick = (event: MouseEvent) => {
|
|
201
|
+
if (file !== undefined) return;
|
|
202
|
+
const fileHeader = event.nativeEvent
|
|
203
|
+
.composedPath()
|
|
204
|
+
.find(
|
|
205
|
+
(target): target is Element =>
|
|
206
|
+
target instanceof Element && target.hasAttribute("data-diffs-header"),
|
|
207
|
+
);
|
|
208
|
+
const root = fileHeader?.getRootNode();
|
|
209
|
+
if (!(root instanceof ShadowRoot)) return;
|
|
210
|
+
const path = root.host
|
|
211
|
+
.querySelector("[data-collapse-path]")
|
|
212
|
+
?.getAttribute("data-collapse-path");
|
|
213
|
+
if (path !== null && path !== undefined) toggleCollapsed(path);
|
|
214
|
+
};
|
|
134
215
|
|
|
135
|
-
const openPath = sourcePanelPath({
|
|
136
|
-
filePath: file?.path,
|
|
137
|
-
reference: selection?.reference,
|
|
138
|
-
items: props.items,
|
|
139
|
-
linkedDiffId: linkedDiff?.id,
|
|
140
|
-
});
|
|
141
|
-
const origin = sourcePanelOrigin({
|
|
142
|
-
showingFile: file !== undefined,
|
|
143
|
-
reference: selection?.reference,
|
|
144
|
-
items: props.items,
|
|
145
|
-
linkedDiffId: linkedDiff?.id,
|
|
146
|
-
});
|
|
147
216
|
const pin =
|
|
148
|
-
|
|
217
|
+
file === undefined
|
|
149
218
|
? { kind: "none" as const }
|
|
150
|
-
: sourcePanelPin({
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
github,
|
|
154
|
-
gist,
|
|
155
|
-
});
|
|
219
|
+
: sourcePanelPin({ path: file.path, github, gist });
|
|
220
|
+
// Diffs keep Pierre's sticky per-file headers; a single file shows its path in the panel title.
|
|
221
|
+
const showTitle = file !== undefined;
|
|
156
222
|
const options = pierreDiffOptions({
|
|
157
223
|
themeType: resolvedTheme,
|
|
158
|
-
disableFileHeader:
|
|
224
|
+
disableFileHeader: showTitle,
|
|
159
225
|
});
|
|
226
|
+
const loadedChanges =
|
|
227
|
+
changes === undefined || changes instanceof Error ? undefined : changes;
|
|
228
|
+
|
|
229
|
+
// Scroll again once the changes arrive, since Pierre cannot scroll to items it does not have yet.
|
|
230
|
+
const ready = file !== undefined || loadedChanges !== undefined;
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (!ready || focusedPath === undefined || rangeId !== undefined) return;
|
|
233
|
+
viewer.current?.scrollTo({
|
|
234
|
+
type: "item",
|
|
235
|
+
id: focusedPath,
|
|
236
|
+
align: "start",
|
|
237
|
+
behavior: "instant",
|
|
238
|
+
});
|
|
239
|
+
}, [ready, focusedPath, rangeId]);
|
|
160
240
|
|
|
161
241
|
useEffect(() => {
|
|
162
242
|
if (
|
|
243
|
+
!ready ||
|
|
163
244
|
rangeId === undefined ||
|
|
164
245
|
rangeStart === undefined ||
|
|
165
246
|
rangeEnd === undefined
|
|
@@ -177,7 +258,7 @@ export function SourceDiffPanel(props: {
|
|
|
177
258
|
align: "center",
|
|
178
259
|
behavior: "instant",
|
|
179
260
|
});
|
|
180
|
-
}, [rangeId, rangeStart, rangeEnd, rangeSide]);
|
|
261
|
+
}, [ready, rangeId, rangeStart, rangeEnd, rangeSide]);
|
|
181
262
|
|
|
182
263
|
const onTokenClick: CodeViewOptions<undefined, undefined>["onTokenClick"] = (
|
|
183
264
|
token,
|
|
@@ -234,11 +315,43 @@ export function SourceDiffPanel(props: {
|
|
|
234
315
|
|
|
235
316
|
return (
|
|
236
317
|
<aside id="source-diff-panel" aria-label="Source changes" className={panel}>
|
|
237
|
-
<div
|
|
238
|
-
{
|
|
318
|
+
<div
|
|
319
|
+
className={header}
|
|
320
|
+
data-diffmap-kind="source-header"
|
|
321
|
+
// Pierre's file headers draw their own top border, which divides them from this header.
|
|
322
|
+
data-file-headers={!showTitle && items.length > 0}
|
|
323
|
+
>
|
|
324
|
+
{!showTitle && props.onRevChange !== undefined && (
|
|
325
|
+
<div className={titleRow}>
|
|
326
|
+
<RangePicker rev={props.rev} onChange={props.onRevChange} />
|
|
327
|
+
{loadedChanges !== undefined && (
|
|
328
|
+
<ChangesSummary className={summary} changes={loadedChanges} />
|
|
329
|
+
)}
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
{!showTitle &&
|
|
333
|
+
props.onRevChange === undefined &&
|
|
334
|
+
loadedChanges !== undefined && (
|
|
335
|
+
<div className={titleRow}>
|
|
336
|
+
{loadedChanges.url === undefined ? (
|
|
337
|
+
<div className={titleClass} title={loadedChanges.title}>
|
|
338
|
+
{loadedChanges.title}
|
|
339
|
+
</div>
|
|
340
|
+
) : (
|
|
341
|
+
<a
|
|
342
|
+
className={titleClass}
|
|
343
|
+
href={loadedChanges.url}
|
|
344
|
+
title={loadedChanges.title}
|
|
345
|
+
>
|
|
346
|
+
{loadedChanges.title}
|
|
347
|
+
</a>
|
|
348
|
+
)}
|
|
349
|
+
</div>
|
|
350
|
+
)}
|
|
351
|
+
{showTitle && (
|
|
239
352
|
<div className={titleRow}>
|
|
240
|
-
<div className={titleClass} title={
|
|
241
|
-
{
|
|
353
|
+
<div className={titleClass} title={file.path}>
|
|
354
|
+
{file.path}
|
|
242
355
|
</div>
|
|
243
356
|
{definition !== undefined && (
|
|
244
357
|
<Button
|
|
@@ -277,73 +390,78 @@ export function SourceDiffPanel(props: {
|
|
|
277
390
|
{pin.label}
|
|
278
391
|
</a>
|
|
279
392
|
)}
|
|
280
|
-
{pin.kind === "spec" && (
|
|
281
|
-
<span className={pinClass} data-diffmap-pin="spec">
|
|
282
|
-
{pin.label}
|
|
283
|
-
</span>
|
|
284
|
-
)}
|
|
285
|
-
{mode === "local" && (
|
|
286
|
-
<span>⌘-click or Ctrl-click a symbol to go to its definition.</span>
|
|
287
|
-
)}
|
|
288
393
|
{status !== undefined && <span role="status">{status}</span>}
|
|
289
|
-
{selection
|
|
290
|
-
<span>
|
|
291
|
-
Select a linked stack row or diagram element to open its source.
|
|
292
|
-
</span>
|
|
293
|
-
) : (
|
|
394
|
+
{selection !== undefined &&
|
|
294
395
|
selection.annotation.references.length > 1 && (
|
|
295
396
|
<div className={links} aria-label="Linked changes">
|
|
296
|
-
{selection.annotation.references.map((
|
|
397
|
+
{selection.annotation.references.map((option, index) => (
|
|
297
398
|
<Button
|
|
298
399
|
key={index}
|
|
299
400
|
variantColor="blue"
|
|
300
|
-
variant={
|
|
301
|
-
reference === selection.reference ? "primary" : "quiet"
|
|
302
|
-
}
|
|
401
|
+
variant={option === selection.reference ? "primary" : "quiet"}
|
|
303
402
|
onClick={() =>
|
|
304
403
|
props.onSelect({
|
|
305
404
|
annotation: selection.annotation,
|
|
306
|
-
reference,
|
|
405
|
+
reference: option,
|
|
307
406
|
})
|
|
308
407
|
}
|
|
309
408
|
>
|
|
310
|
-
{referenceLabel(
|
|
409
|
+
{referenceLabel(option)}
|
|
311
410
|
</Button>
|
|
312
411
|
))}
|
|
313
412
|
</div>
|
|
314
|
-
)
|
|
315
|
-
)}
|
|
413
|
+
)}
|
|
316
414
|
</div>
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
415
|
+
{/* oxlint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- Pierre's file headers are the click targets; this only catches their clicks. */}
|
|
416
|
+
<div className={codeWrap} onClick={onHeaderClick}>
|
|
417
|
+
<CodeView
|
|
418
|
+
ref={viewer}
|
|
419
|
+
className={code}
|
|
420
|
+
items={
|
|
421
|
+
file === undefined
|
|
422
|
+
? shownItems
|
|
423
|
+
: [
|
|
424
|
+
{
|
|
425
|
+
type: "file",
|
|
426
|
+
id: file.path,
|
|
427
|
+
file: {
|
|
428
|
+
name: file.path,
|
|
429
|
+
contents: file.contents,
|
|
430
|
+
},
|
|
330
431
|
},
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
432
|
+
]
|
|
433
|
+
}
|
|
434
|
+
selectedLines={selectedLines}
|
|
435
|
+
disableWorkerPool
|
|
436
|
+
options={{
|
|
437
|
+
...options,
|
|
438
|
+
unsafeCSS:
|
|
439
|
+
options.unsafeCSS + sourceSelectionCss + panelBackgroundCss,
|
|
440
|
+
// Files stack flush under the header, with no gaps between them.
|
|
441
|
+
layout: { paddingTop: 0, paddingBottom: 0, gap: 0 },
|
|
442
|
+
stickyHeaders: true,
|
|
443
|
+
onTokenClick,
|
|
444
|
+
}}
|
|
445
|
+
renderHeaderPrefix={(item) => (
|
|
446
|
+
<span
|
|
447
|
+
className={collapseIcon}
|
|
448
|
+
data-collapse-path={item.id}
|
|
449
|
+
data-collapsed={collapse.paths.has(item.id)}
|
|
450
|
+
aria-hidden="true"
|
|
451
|
+
>
|
|
452
|
+
<ChevronDown size="sm" />
|
|
453
|
+
</span>
|
|
454
|
+
)}
|
|
455
|
+
/>
|
|
456
|
+
</div>
|
|
343
457
|
</aside>
|
|
344
458
|
);
|
|
345
459
|
}
|
|
346
460
|
|
|
461
|
+
/** Pierre paints its own white; the panel's background shows through instead. */
|
|
462
|
+
const panelBackgroundCss = `:host { --diffs-light-bg: ${backgroundColor.app}; --diffs-dark-bg: ${backgroundColor.app}; }
|
|
463
|
+
[data-diffs-header] { border-top: 1px solid ${borderColor.border}; cursor: pointer; }`;
|
|
464
|
+
|
|
347
465
|
const styles = {
|
|
348
466
|
panel: style(flex({ direction: "column" }), border(["left"], "border"), {
|
|
349
467
|
gridArea: "diff",
|
|
@@ -359,10 +477,14 @@ const styles = {
|
|
|
359
477
|
}),
|
|
360
478
|
header: style(
|
|
361
479
|
flex({ direction: "column" }),
|
|
362
|
-
|
|
480
|
+
// Same vertical padding as the page header; the inline padding lines the
|
|
481
|
+
// picker's icon and label up with Pierre's file headers below.
|
|
482
|
+
spacing.padding({ y: 3 }),
|
|
483
|
+
{ paddingInline: "7px" },
|
|
363
484
|
border(["bottom"], "border"),
|
|
364
485
|
text({ size: "sm", color: "lowContrast" }),
|
|
365
486
|
{
|
|
487
|
+
"&[data-file-headers='true']": { borderBottom: "none" },
|
|
366
488
|
gap: "6px",
|
|
367
489
|
minWidth: 0,
|
|
368
490
|
maxHeight: "35%",
|
|
@@ -370,6 +492,8 @@ const styles = {
|
|
|
370
492
|
flexShrink: 0,
|
|
371
493
|
},
|
|
372
494
|
),
|
|
495
|
+
// Pierre's file headers pad 16px; this header pads 7px.
|
|
496
|
+
summary: style({ marginLeft: "auto", marginRight: "9px" }),
|
|
373
497
|
titleRow: style(flex({ direction: "row", alignItems: "center" }), {
|
|
374
498
|
gap: "6px",
|
|
375
499
|
minWidth: 0,
|
|
@@ -392,6 +516,19 @@ const styles = {
|
|
|
392
516
|
},
|
|
393
517
|
}),
|
|
394
518
|
links: style(flex({ direction: "row" }), { gap: "6px", flexWrap: "wrap" }),
|
|
519
|
+
codeWrap: style({
|
|
520
|
+
display: "flex",
|
|
521
|
+
flexDirection: "column",
|
|
522
|
+
flex: "1 1 auto",
|
|
523
|
+
minWidth: 0,
|
|
524
|
+
minHeight: 0,
|
|
525
|
+
}),
|
|
526
|
+
collapseIcon: style({
|
|
527
|
+
display: "flex",
|
|
528
|
+
color: colors.gray[11],
|
|
529
|
+
transition: "transform 150ms ease-out",
|
|
530
|
+
"&[data-collapsed='true']": { transform: "rotate(-90deg)" },
|
|
531
|
+
}),
|
|
395
532
|
code: style({
|
|
396
533
|
flex: "1 1 auto",
|
|
397
534
|
minWidth: 0,
|