Skip to content

Commit f4cd5cd

Browse files
committed
Move GlobalOptions to own file
1 parent 82ab124 commit f4cd5cd

File tree

6 files changed

+135
-152
lines changed

6 files changed

+135
-152
lines changed

web/src/lib/diff-viewer-multi-file.svelte.ts

Lines changed: 2 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,8 @@ import {
99
parseMultiFilePatchGithub,
1010
} from "./github.svelte";
1111
import { type StructuredPatch } from "diff";
12-
import {
13-
ConciseDiffViewCachedState,
14-
DEFAULT_THEME_DARK,
15-
DEFAULT_THEME_LIGHT,
16-
isNoNewlineAtEofLine,
17-
parseSinglePatch,
18-
patchHeaderDiffOnly,
19-
} from "$lib/components/diff/concise-diff-view.svelte";
20-
import type { BundledTheme } from "shiki";
21-
import { browser } from "$app/environment";
22-
import { getEffectiveGlobalTheme } from "$lib/theme.svelte";
23-
import {
24-
countOccurrences,
25-
type FileTreeNodeData,
26-
makeFileTree,
27-
type LazyPromise,
28-
lazyPromise,
29-
watchLocalStorage,
30-
animationFramePromise,
31-
yieldToBrowser,
32-
} from "$lib/util";
12+
import { ConciseDiffViewCachedState, isNoNewlineAtEofLine, parseSinglePatch, patchHeaderDiffOnly } from "$lib/components/diff/concise-diff-view.svelte";
13+
import { countOccurrences, type FileTreeNodeData, makeFileTree, type LazyPromise, lazyPromise, animationFramePromise, yieldToBrowser } from "$lib/util";
3314
import { onDestroy, tick } from "svelte";
3415
import { type TreeNode, TreeState } from "$lib/components/tree/index.svelte";
3516
import { VList } from "virtua/svelte";
@@ -40,126 +21,6 @@ import { ProgressBarState } from "$lib/components/progress-bar/index.svelte";
4021
export const GITHUB_URL_PARAM = "github_url";
4122
export const PATCH_URL_PARAM = "patch_url";
4223

43-
export type SidebarLocation = "left" | "right";
44-
45-
export class GlobalOptions {
46-
static readonly key = "diff-viewer-global-options";
47-
private static readonly context = new Context<GlobalOptions>(GlobalOptions.key);
48-
49-
static init(cookie?: string) {
50-
const opts = new GlobalOptions();
51-
if (!browser) {
52-
GlobalOptions.context.set(opts);
53-
if (cookie) {
54-
opts.deserialize(cookie);
55-
}
56-
return opts;
57-
}
58-
const serialized = localStorage.getItem(GlobalOptions.key);
59-
if (serialized !== null) {
60-
opts.deserialize(serialized);
61-
}
62-
GlobalOptions.context.set(opts);
63-
return opts;
64-
}
65-
66-
static get() {
67-
return GlobalOptions.context.get();
68-
}
69-
70-
syntaxHighlighting = $state(true);
71-
syntaxHighlightingThemeLight: BundledTheme = $state(DEFAULT_THEME_LIGHT);
72-
syntaxHighlightingThemeDark: BundledTheme = $state(DEFAULT_THEME_DARK);
73-
wordDiffs = $state(true);
74-
lineWrap = $state(true);
75-
omitPatchHeaderOnlyHunks = $state(true);
76-
sidebarLocation: SidebarLocation = $state("left");
77-
78-
private constructor() {
79-
$effect(() => {
80-
this.save();
81-
});
82-
83-
watchLocalStorage(GlobalOptions.key, (newValue) => {
84-
if (newValue) {
85-
this.deserialize(newValue);
86-
}
87-
});
88-
}
89-
90-
get syntaxHighlightingTheme() {
91-
switch (getEffectiveGlobalTheme()) {
92-
case "dark":
93-
return this.syntaxHighlightingThemeDark;
94-
case "light":
95-
return this.syntaxHighlightingThemeLight;
96-
}
97-
}
98-
99-
private save() {
100-
if (!browser) {
101-
return;
102-
}
103-
localStorage.setItem(GlobalOptions.key, this.serialize());
104-
document.cookie = `${GlobalOptions.key}=${encodeURIComponent(this.serializeCookie())}; path=/; max-age=31536000; SameSite=Lax`;
105-
}
106-
107-
private serialize() {
108-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
109-
const cereal: any = {
110-
syntaxHighlighting: this.syntaxHighlighting,
111-
omitPatchHeaderOnlyHunks: this.omitPatchHeaderOnlyHunks,
112-
wordDiff: this.wordDiffs,
113-
lineWrap: this.lineWrap,
114-
sidebarLocation: this.sidebarLocation,
115-
};
116-
if (this.syntaxHighlightingThemeLight !== DEFAULT_THEME_LIGHT) {
117-
cereal.syntaxHighlightingThemeLight = this.syntaxHighlightingThemeLight;
118-
}
119-
if (this.syntaxHighlightingThemeDark !== DEFAULT_THEME_DARK) {
120-
cereal.syntaxHighlightingThemeDark = this.syntaxHighlightingThemeDark;
121-
}
122-
return JSON.stringify(cereal);
123-
}
124-
125-
private serializeCookie() {
126-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
127-
const cereal: any = {
128-
sidebarLocation: this.sidebarLocation,
129-
};
130-
return JSON.stringify(cereal);
131-
}
132-
133-
private deserialize(serialized: string) {
134-
const jsonObject = JSON.parse(serialized);
135-
if (jsonObject.syntaxHighlighting !== undefined) {
136-
this.syntaxHighlighting = jsonObject.syntaxHighlighting;
137-
}
138-
if (jsonObject.syntaxHighlightingThemeLight !== undefined) {
139-
this.syntaxHighlightingThemeLight = jsonObject.syntaxHighlightingThemeLight as BundledTheme;
140-
} else {
141-
this.syntaxHighlightingThemeLight = DEFAULT_THEME_LIGHT;
142-
}
143-
if (jsonObject.syntaxHighlightingThemeDark !== undefined) {
144-
this.syntaxHighlightingThemeDark = jsonObject.syntaxHighlightingThemeDark as BundledTheme;
145-
} else {
146-
this.syntaxHighlightingThemeDark = DEFAULT_THEME_DARK;
147-
}
148-
if (jsonObject.omitPatchHeaderOnlyHunks !== undefined) {
149-
this.omitPatchHeaderOnlyHunks = jsonObject.omitPatchHeaderOnlyHunks;
150-
}
151-
if (jsonObject.wordDiff !== undefined) {
152-
this.wordDiffs = jsonObject.wordDiff;
153-
}
154-
if (jsonObject.lineWrap !== undefined) {
155-
this.lineWrap = jsonObject.lineWrap;
156-
}
157-
if (jsonObject.sidebarLocation !== undefined) {
158-
this.sidebarLocation = jsonObject.sidebarLocation;
159-
}
160-
}
161-
}
162-
16324
export const staticSidebar = new MediaQuery("(width >= 64rem)");
16425

16526
export type AddOrRemove = "add" | "remove";
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { DEFAULT_THEME_DARK, DEFAULT_THEME_LIGHT } from "$lib/components/diff/concise-diff-view.svelte";
2+
import type { BundledTheme } from "shiki";
3+
import { browser } from "$app/environment";
4+
import { getEffectiveGlobalTheme } from "$lib/theme.svelte";
5+
import { watchLocalStorage } from "$lib/util";
6+
import { Context } from "runed";
7+
8+
export type SidebarLocation = "left" | "right";
9+
10+
export class GlobalOptions {
11+
static readonly key = "diff-viewer-global-options";
12+
private static readonly context = new Context<GlobalOptions>(GlobalOptions.key);
13+
14+
static init(cookie?: string) {
15+
const opts = new GlobalOptions();
16+
if (!browser) {
17+
GlobalOptions.context.set(opts);
18+
if (cookie) {
19+
opts.deserialize(cookie);
20+
}
21+
return opts;
22+
}
23+
const serialized = localStorage.getItem(GlobalOptions.key);
24+
if (serialized !== null) {
25+
opts.deserialize(serialized);
26+
}
27+
GlobalOptions.context.set(opts);
28+
return opts;
29+
}
30+
31+
static get() {
32+
return GlobalOptions.context.get();
33+
}
34+
35+
syntaxHighlighting = $state(true);
36+
syntaxHighlightingThemeLight: BundledTheme = $state(DEFAULT_THEME_LIGHT);
37+
syntaxHighlightingThemeDark: BundledTheme = $state(DEFAULT_THEME_DARK);
38+
wordDiffs = $state(true);
39+
lineWrap = $state(true);
40+
omitPatchHeaderOnlyHunks = $state(true);
41+
sidebarLocation: SidebarLocation = $state("left");
42+
43+
private constructor() {
44+
$effect(() => {
45+
this.save();
46+
});
47+
48+
watchLocalStorage(GlobalOptions.key, (newValue) => {
49+
if (newValue) {
50+
this.deserialize(newValue);
51+
}
52+
});
53+
}
54+
55+
get syntaxHighlightingTheme() {
56+
switch (getEffectiveGlobalTheme()) {
57+
case "dark":
58+
return this.syntaxHighlightingThemeDark;
59+
case "light":
60+
return this.syntaxHighlightingThemeLight;
61+
}
62+
}
63+
64+
private save() {
65+
if (!browser) {
66+
return;
67+
}
68+
localStorage.setItem(GlobalOptions.key, this.serialize());
69+
document.cookie = `${GlobalOptions.key}=${encodeURIComponent(this.serializeCookie())}; path=/; max-age=31536000; SameSite=Lax`;
70+
}
71+
72+
private serialize() {
73+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
74+
const cereal: any = {
75+
syntaxHighlighting: this.syntaxHighlighting,
76+
omitPatchHeaderOnlyHunks: this.omitPatchHeaderOnlyHunks,
77+
wordDiff: this.wordDiffs,
78+
lineWrap: this.lineWrap,
79+
sidebarLocation: this.sidebarLocation,
80+
};
81+
if (this.syntaxHighlightingThemeLight !== DEFAULT_THEME_LIGHT) {
82+
cereal.syntaxHighlightingThemeLight = this.syntaxHighlightingThemeLight;
83+
}
84+
if (this.syntaxHighlightingThemeDark !== DEFAULT_THEME_DARK) {
85+
cereal.syntaxHighlightingThemeDark = this.syntaxHighlightingThemeDark;
86+
}
87+
return JSON.stringify(cereal);
88+
}
89+
90+
private serializeCookie() {
91+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
92+
const cereal: any = {
93+
sidebarLocation: this.sidebarLocation,
94+
};
95+
return JSON.stringify(cereal);
96+
}
97+
98+
private deserialize(serialized: string) {
99+
const jsonObject = JSON.parse(serialized);
100+
if (jsonObject.syntaxHighlighting !== undefined) {
101+
this.syntaxHighlighting = jsonObject.syntaxHighlighting;
102+
}
103+
if (jsonObject.syntaxHighlightingThemeLight !== undefined) {
104+
this.syntaxHighlightingThemeLight = jsonObject.syntaxHighlightingThemeLight as BundledTheme;
105+
} else {
106+
this.syntaxHighlightingThemeLight = DEFAULT_THEME_LIGHT;
107+
}
108+
if (jsonObject.syntaxHighlightingThemeDark !== undefined) {
109+
this.syntaxHighlightingThemeDark = jsonObject.syntaxHighlightingThemeDark as BundledTheme;
110+
} else {
111+
this.syntaxHighlightingThemeDark = DEFAULT_THEME_DARK;
112+
}
113+
if (jsonObject.omitPatchHeaderOnlyHunks !== undefined) {
114+
this.omitPatchHeaderOnlyHunks = jsonObject.omitPatchHeaderOnlyHunks;
115+
}
116+
if (jsonObject.wordDiff !== undefined) {
117+
this.wordDiffs = jsonObject.wordDiff;
118+
}
119+
if (jsonObject.lineWrap !== undefined) {
120+
this.lineWrap = jsonObject.lineWrap;
121+
}
122+
if (jsonObject.sidebarLocation !== undefined) {
123+
this.sidebarLocation = jsonObject.sidebarLocation;
124+
}
125+
}
126+
}

web/src/routes/+layout.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { GlobalOptions } from "$lib/global-options.svelte";
12
import type { LayoutServerLoad } from "./$types";
2-
import { GlobalOptions } from "$lib/diff-viewer-multi-file.svelte";
33

44
export const load: LayoutServerLoad = async ({ cookies }) => {
55
return {

web/src/routes/+page.svelte

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@
22
import ConciseDiffView from "$lib/components/diff/ConciseDiffView.svelte";
33
import { type FileTreeNodeData } from "$lib/util";
44
import { VList } from "virtua/svelte";
5-
import {
6-
type FileDetails,
7-
getFileStatusProps,
8-
GlobalOptions,
9-
MultiFileDiffViewerState,
10-
requireEitherImage,
11-
staticSidebar,
12-
} from "$lib/diff-viewer-multi-file.svelte";
5+
import { type FileDetails, getFileStatusProps, MultiFileDiffViewerState, requireEitherImage, staticSidebar } from "$lib/diff-viewer-multi-file.svelte";
136
import Tree from "$lib/components/tree/Tree.svelte";
147
import Spinner from "$lib/components/Spinner.svelte";
158
import { type TreeNode } from "$lib/components/tree/index.svelte";
@@ -34,6 +27,7 @@
3427
import SidebarToggle from "./SidebarToggle.svelte";
3528
import type { PageProps } from "./$types";
3629
import ProgressBar from "$lib/components/progress-bar/ProgressBar.svelte";
30+
import { GlobalOptions } from "$lib/global-options.svelte";
3731
3832
let { data }: PageProps = $props();
3933
const globalOptions = GlobalOptions.init(data.globalOptions);

web/src/routes/FileHeader.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts">
22
import DiffStats from "$lib/components/diff/DiffStats.svelte";
33
import LabeledCheckbox from "$lib/components/LabeledCheckbox.svelte";
4-
import { type FileDetails, GlobalOptions, MultiFileDiffViewerState } from "$lib/diff-viewer-multi-file.svelte";
4+
import { type FileDetails, MultiFileDiffViewerState } from "$lib/diff-viewer-multi-file.svelte";
5+
import { GlobalOptions } from "$lib/global-options.svelte";
56
import { Popover, Button } from "bits-ui";
67
import { tick } from "svelte";
78

web/src/routes/SidebarToggle.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script lang="ts">
2-
import { GlobalOptions, MultiFileDiffViewerState } from "$lib/diff-viewer-multi-file.svelte";
2+
import { MultiFileDiffViewerState } from "$lib/diff-viewer-multi-file.svelte";
33
import { Button, mergeProps } from "bits-ui";
44
import { type RestProps } from "$lib/types";
5+
import { GlobalOptions } from "$lib/global-options.svelte";
56
67
let { ...restProps }: RestProps = $props();
78

0 commit comments

Comments
 (0)