Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ html.light .shiki {
}
}

/* Settings-based configuration for code ligatures. On by default. */
:root[data-code-ligatures='false'] code {
font-variant-ligatures: none;
}

/* Inline code in package descriptions */
p > span > code,
.line-clamp-2 code {
Expand Down
33 changes: 33 additions & 0 deletions app/composables/useSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export interface AppSettings {
autoOpenURL: boolean
}
codeContainerFull: boolean
/** Enable/disable ligatures in code */
codeLigatures: boolean
sidebar: {
collapsed: string[]
}
Expand All @@ -65,6 +67,7 @@ const DEFAULT_SETTINGS: AppSettings = {
autoOpenURL: false,
},
codeContainerFull: false,
codeLigatures: true,
sidebar: {
collapsed: [],
},
Expand Down Expand Up @@ -253,3 +256,33 @@ export function useCodeContainer() {
toggleCodeContainer,
}
}

export const useCodeLigatures = createSharedComposable(function useCodeLigatures() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const useCodeLigatures = createSharedComposable(function useCodeLigatures() {
export const useCodeLigatures = createSharedComposable(() => {

Copy link
Copy Markdown
Author

@Kiwow Kiwow Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw this in the useKeyboardShortcuts composable and it made sense to me as a way to get nicer stack traces in logs. I think it's nice to have a named function for that reason.

To be fair, in prod builds, it won't make a difference, so I'm up for either solution. Your version is definitely more readable.

To compare, here's the outputs of `console.trace` (running in dev) when called at the start of the composable and the start of the watcher function, respectively:

Named function expression:

Two stack traces. Both contain 'useCodeLigatures' as part of the trace, as well as Vue-specific functions

Anonymous arrow function:

Two stack traces. Outside of Vue-specific functions, the traces only contain references to anonymous functions

const { settings } = useSettings()

const codeLigatures = computed(() => settings.value.codeLigatures)

if (import.meta.client) {
// Sync the data attribute on root to the setting
watch(
codeLigatures,
value => {
if (value) {
delete document.documentElement.dataset.codeLigatures
} else {
document.documentElement.dataset.codeLigatures = 'false'
}
},
{ immediate: true },
)
}

function toggleCodeLigatures() {
settings.value.codeLigatures = !settings.value.codeLigatures
}

return {
codeLigatures,
toggleCodeLigatures,
}
})
11 changes: 11 additions & 0 deletions app/pages/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { locale: currentLocale, locales, setLocale: setNuxti18nLocale } = useI18n
const colorMode = useColorMode()
const { currentLocaleStatus, isSourceLocale } = useI18nStatus()
const keyboardShortcutsEnabled = useKeyboardShortcuts()
const { toggleCodeLigatures } = useCodeLigatures()

// Escape to go back (but not when focused on form elements or modal is open)
onKeyStroke(
Expand Down Expand Up @@ -143,6 +144,16 @@ const setLocale: typeof setNuxti18nLocale = newLocale => {
:description="$t('settings.enable_graph_pulse_loop_description')"
v-model="settings.enableGraphPulseLooping"
/>

<!-- Divider -->
<div class="border-t border-border my-4" />

<!-- Code ligatures toggle -->
<SettingsToggle
:label="$t('settings.enable_code_ligatures')"
:modelValue="settings.codeLigatures"
@update:modelValue="() => toggleCodeLigatures()"
/>
</div>
</section>

Expand Down
5 changes: 5 additions & 0 deletions app/utils/prehydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,10 @@ export function initPreferencesOnPrehydrate() {
if (settings.keyboardShortcuts === false) {
document.documentElement.dataset.kbdShortcuts = 'false'
}

// Code font ligatures (default: true)
if (settings.codeLigatures === false) {
document.documentElement.dataset.codeLigatures = 'false'
}
})
}
3 changes: 2 additions & 1 deletion i18n/locales/cs-CZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@
"black": "Černá"
},
"keyboard_shortcuts_enabled": "Povolit klávesové zkratky",
"keyboard_shortcuts_enabled_description": "Klávesové zkratky lze zakázat, pokud se střetávají s jinými zkratkami prohlížeče nebo systému"
"keyboard_shortcuts_enabled_description": "Klávesové zkratky lze zakázat, pokud se střetávají s jinými zkratkami prohlížeče nebo systému",
"enable_code_ligatures": "Zapnout ligatury v kódu"
},
"i18n": {
"missing_keys": "{count} chybějící překlad | {count} chybějící překlady | {count} chybějících překladů",
Expand Down
3 changes: 2 additions & 1 deletion i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@
"black": "Black"
},
"keyboard_shortcuts_enabled": "Enable keyboard shortcuts",
"keyboard_shortcuts_enabled_description": "Keyboard shortcuts can be disabled if they conflict with other browser or system shortcuts"
"keyboard_shortcuts_enabled_description": "Keyboard shortcuts can be disabled if they conflict with other browser or system shortcuts",
"enable_code_ligatures": "Enable ligatures in code"
},
"i18n": {
"missing_keys": "{count} missing translation | {count} missing translations",
Expand Down
3 changes: 3 additions & 0 deletions i18n/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,9 @@
},
"keyboard_shortcuts_enabled_description": {
"type": "string"
},
"enable_code_ligatures": {
"type": "string"
}
},
"additionalProperties": false
Expand Down
28 changes: 28 additions & 0 deletions test/nuxt/composables/use-settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,31 @@ describe('useSettings - keyboardShortcuts', () => {
})
})
})

describe('useSettings - codeLigatures', () => {
beforeEach(() => {
vi.resetModules()
})

it('has a default value of true', async () => {
const { useSettings } = await import('~/composables/useSettings')
const codeLigatures = useSettings().settings.value.codeLigatures
expect(codeLigatures).toBe(true)
})

describe('useCodeLigatures', () => {
it('has a default value of true', async () => {
const { useCodeLigatures } = await import('~/composables/useSettings')
const codeLigatures = useCodeLigatures().codeLigatures
expect(codeLigatures.value).toBe(true)
})

it('updates after toggle', async () => {
const { useCodeLigatures } = await import('~/composables/useSettings')
const { codeLigatures, toggleCodeLigatures } = useCodeLigatures()
expect(codeLigatures.value).toBe(true)
toggleCodeLigatures()
expect(codeLigatures.value).toBe(false)
})
})
})
Loading