This repository has no description
1// the css side of pierre theming lives in src/pierre.css
2
3import {
4 getSharedHighlighter,
5 registerCustomCSSVariableTheme,
6 type FileDiffOptions,
7 type FileOptions
8} from "@pierre/diffs";
9import { browser } from "$app/environment";
10
11// pierre's two diff layouts, threaded from the ?diff= url param down to the
12// FileDiff instances
13export type DiffStyle = NonNullable<FileDiffOptions<undefined>["diffStyle"]>;
14
15// pierre's theming hook is a registered shiki css-variable theme. the token
16// colors come from --diffs-token-* properties in pierre.css, they flip
17// with the media query. the name is pinned by app.css's @utility or tailwind
18// tree-shakes those rules away
19export const PIERRE_THEME_NAME = "tangled";
20
21registerCustomCSSVariableTheme(PIERRE_THEME_NAME, {
22 // pierre.css defines the real tokens on diffs-container, these are just
23 // fallbacks for the generated bindings
24 background: "#ffffff",
25 foreground: "#111827",
26 "token-comment": "#6b7280",
27 "token-string": "#16a34a",
28 "token-string-expression": "#16a34a",
29 "token-keyword": "#c084fc",
30 "token-function": "#60a5fa",
31 "token-constant": "#f59e0b",
32 "token-parameter": "#111827",
33 "token-punctuation": "#4b5563",
34 "token-link": "#4338ca",
35 "token-inserted": "#16a34a",
36 "token-deleted": "#dc2626",
37 "token-changed": "#d97706"
38});
39
40// pierre skips highlighting lines past 1e3 chars by default.
41// lockfiles however do not care...
42export const PIERRE_TOKENIZE_MAX_LINE_LENGTH = 5000;
43
44// "line-info" separators carry pierre's "N unmodified lines" text, bare
45// "simple" ones get a "···"
46export const PIERRE_SEPARATOR_CSS =
47 // flush against our own file header, pierre's is disabled
48 ":host{--diffs-gap-block:0}" +
49 // overflow auto so the bottom scrollbar only exists when a line overflows
50 ":host{--diffs-overflow-override:auto}" +
51 "[data-separator]{block-size:21px;font-size:14px;line-height:21px;" +
52 "color:var(--diffs-fg-number-override,#9ca3af)}" +
53 // kill pierre's block gap, padding and rounded chip, the separator reads
54 // as one full-width row
55 '[data-separator="line-info"]{margin-block:0}' +
56 '[data-unified] [data-separator="line-info"] [data-separator-wrapper]{padding-inline:0}' +
57 '[data-gutter] [data-separator="line-info"] [data-separator-wrapper]{padding-left:0}' +
58 // pierre full-widths the unified separator and the split additions bar but
59 // not the split deletions one, that one carries the count text and clips in
60 // the 44px gutter
61 '[data-deletions] [data-gutter] [data-separator="line-info"] [data-separator-wrapper]{width:100cqi}' +
62 '[data-separator="line-info"] [data-separator-content]{border-radius:0}' +
63 '[data-separator="line-info"] [data-expand-button]{border-radius:0}' +
64 '[data-additions] [data-gutter] [data-separator="line-info"] [data-separator-wrapper]{border-radius:0}' +
65 "[data-separator-content]{justify-content:center}" +
66 '[data-separator="simple"]{display:flex;align-items:center;justify-content:center}' +
67 '[data-separator="simple"]::after{content:"···"}';
68
69export const PIERRE_BASE_OPTIONS = {
70 theme: PIERRE_THEME_NAME,
71 themeType: "system",
72 tokenizeMaxLineLength: PIERRE_TOKENIZE_MAX_LINE_LENGTH
73} as const;
74
75// shared by the components and the ssr preloads, the prerendered markup is
76// only adoptable when both sides build the same options
77export const pierreDiffOptions = (style: DiffStyle): FileDiffOptions<undefined> => ({
78 ...PIERRE_BASE_OPTIONS,
79 diffStyle: style,
80 diffIndicators: "classic",
81 lineDiffType: "word-alt",
82 // "line-info" separators size the bar to span the whole diff.
83 // "line-info-basic" never gets that, its count text clips in the 44px
84 // gutter cell
85 hunkSeparators: "line-info",
86 disableFileHeader: true,
87 overflow: "scroll",
88 unsafeCSS: PIERRE_SEPARATOR_CSS
89});
90
91export const pierreFileOptions = (wrap: boolean): FileOptions<undefined> => ({
92 ...PIERRE_BASE_OPTIONS,
93 disableFileHeader: true,
94 overflow: wrap ? "wrap" : "scroll"
95});
96
97// a renderer with a missing language paints plain text first and swaps in
98// the highlight, but only once the shared highlighter is up. before that the
99// first render waits on the shiki import and paints nothing, so warm it here
100if (browser) void getSharedHighlighter({ themes: [PIERRE_THEME_NAME], langs: [] });