+ {/if}
+{/if}
+
+
diff --git a/packages/viewer/src/components/Shared/index.ts b/packages/viewer/src/components/Shared/index.ts
index f7d893de..80d4cd27 100644
--- a/packages/viewer/src/components/Shared/index.ts
+++ b/packages/viewer/src/components/Shared/index.ts
@@ -5,6 +5,7 @@ export { default as Aliases } from './Aliases.svelte'
export { default as Filter } from './Filter.svelte'
export { default as Formula } from './Formula.svelte'
export { default as Id } from './Id.svelte'
+export { default as LeanLink } from './LeanLink.svelte'
export { default as Link } from './Link'
export { default as Loading } from './Loading.svelte'
export { default as NotFound } from './NotFound.svelte'
diff --git a/packages/viewer/src/components/Theorems/Show.svelte b/packages/viewer/src/components/Theorems/Show.svelte
index 8f469ca8..9ddf4692 100644
--- a/packages/viewer/src/components/Theorems/Show.svelte
+++ b/packages/viewer/src/components/Theorems/Show.svelte
@@ -1,6 +1,6 @@
-
Theorem
+
+ Theorem
+
+
diff --git a/packages/viewer/src/components/Traits/Related.svelte b/packages/viewer/src/components/Traits/Related.svelte
index 8e1c4d2c..d3685f7d 100644
--- a/packages/viewer/src/components/Traits/Related.svelte
+++ b/packages/viewer/src/components/Traits/Related.svelte
@@ -5,8 +5,8 @@
import context from '@/context'
import type { Property, Space, Trait, Traits } from '@/models'
import FilterButtons from './FilterButtons.svelte'
- import { defaultStorage } from '@/repositories'
import { writable } from 'svelte/store'
+ import { showRedundancy } from '@/stores/settings'
import urlSearchParam from '@/stores/urlSearchParam'
import { checkIfRedundant } from '@/stores/deduction'
@@ -21,8 +21,6 @@
let filterMode: 'all' | 'known' | 'asserted' | 'true' | 'false' | 'missing'
filterMode = 'known'
- let showRedundancy = defaultStorage.getItem('showRedundancy') != null
-
$: all = related($traits)
// all has type [Space, Property, Trait][]
// we need to index names in different positions depending on which kind we
@@ -109,10 +107,9 @@
diff --git a/packages/viewer/src/stores/settings.test.ts b/packages/viewer/src/stores/settings.test.ts
new file mode 100644
index 00000000..b86db960
--- /dev/null
+++ b/packages/viewer/src/stores/settings.test.ts
@@ -0,0 +1,85 @@
+import { beforeEach, describe, expect, it } from 'vitest'
+import { get } from 'svelte/store'
+import { persistedBoolean, showLeanLinks, showRedundancy } from './settings'
+
+function mockStorage() {
+ const data: Record = {}
+ return {
+ data,
+ getItem: (key: string) => data[key] ?? null,
+ setItem: (key: string, value: string) => {
+ data[key] = value
+ },
+ removeItem: (key: string) => {
+ delete data[key]
+ },
+ }
+}
+
+describe('persistedBoolean', () => {
+ it('defaults to the given default value when storage is empty', () => {
+ const storage = mockStorage()
+ const store = persistedBoolean('flag', false, storage)
+ expect(get(store)).toBe(false)
+ })
+
+ it('initializes to true when the key already exists in storage', () => {
+ const storage = mockStorage()
+ storage.data['flag'] = 'show'
+ const store = persistedBoolean('flag', false, storage)
+ expect(get(store)).toBe(true)
+ })
+
+ it('writes to storage when set to true', () => {
+ const storage = mockStorage()
+ const store = persistedBoolean('flag', false, storage)
+ store.set(true)
+ expect(storage.data['flag']).toBe('show')
+ })
+
+ it('removes from storage when set to false', () => {
+ const storage = mockStorage()
+ storage.data['flag'] = 'show'
+ const store = persistedBoolean('flag', true, storage)
+ store.set(false)
+ expect(storage.data['flag']).toBeUndefined()
+ })
+
+ it('reflects updates via subscribe', () => {
+ const storage = mockStorage()
+ const store = persistedBoolean('flag', false, storage)
+ const values: boolean[] = []
+ store.subscribe(v => values.push(v))
+
+ store.set(true)
+ store.set(false)
+
+ expect(values).toEqual([false, true, false])
+ })
+})
+
+describe('showLeanLinks', () => {
+ it('defaults to false', () => {
+ expect(get(showLeanLinks)).toBe(false)
+ })
+
+ it('can be toggled', () => {
+ showLeanLinks.set(true)
+ expect(get(showLeanLinks)).toBe(true)
+ showLeanLinks.set(false)
+ expect(get(showLeanLinks)).toBe(false)
+ })
+})
+
+describe('showRedundancy', () => {
+ it('defaults to false', () => {
+ expect(get(showRedundancy)).toBe(false)
+ })
+
+ it('can be toggled', () => {
+ showRedundancy.set(true)
+ expect(get(showRedundancy)).toBe(true)
+ showRedundancy.set(false)
+ expect(get(showRedundancy)).toBe(false)
+ })
+})
diff --git a/packages/viewer/src/stores/settings.ts b/packages/viewer/src/stores/settings.ts
new file mode 100644
index 00000000..f0a51117
--- /dev/null
+++ b/packages/viewer/src/stores/settings.ts
@@ -0,0 +1,25 @@
+import { writable } from 'svelte/store'
+import { defaultStorage } from '@/repositories'
+
+export function persistedBoolean(
+ key: string,
+ defaultValue: boolean,
+ storage: Pick = defaultStorage,
+) {
+ const store = writable(
+ storage.getItem(key) !== null ? true : defaultValue,
+ )
+
+ store.subscribe(v => {
+ if (v) {
+ storage.setItem(key, 'show')
+ } else {
+ storage.removeItem(key)
+ }
+ })
+
+ return store
+}
+
+export const showLeanLinks = persistedBoolean('showLeanLinks', false)
+export const showRedundancy = persistedBoolean('showRedundancy', false)