The extension provides several composables that encapsulate reusable logic for storage, UI utilities, and data fetching.
useWebExtensionStorage
A reactive wrapper around the Chrome Storage API. See the Storage page for complete documentation.
Quick Reference
function useWebExtensionStorage<T>(
key: string,
initialValue: MaybeRefOrGetter<T>,
options?: WebExtensionStorageOptions<T>
): { data: RemovableRef<T>, dataReady: Promise<T> }
Location: src/composables/useWebExtensionStorage.ts:46-146
Example
interface Settings {
theme: string
fontSize: number
}
const { data: settings, dataReady } = useWebExtensionStorage<Settings>(
'my_settings',
{ theme: 'dark', fontSize: 16 },
{ mergeDefaults: true }
)
await dataReady
console.log(settings.value.theme) // 'dark'
useUI
Provides UI utility functions, including a color brightness detector.
Function Signature
function useUI(): {
lightOrDark: (color: string) => 'light' | 'dark'
}
Location: src/composables/useUI.ts:1-76
lightOrDark
Determines whether a color is light or dark using the HSP (Highly Sensitive Poo) color model.
Parameters
Color value in hex format (#RRGGBB) or RGB format (rgb(r, g, b))
Returns
Returns 'light' if the color’s perceived brightness is > 127.5, otherwise 'dark'
Algorithm
From useUI.ts:16-46:
const lightOrDark = (color: string) => {
let r, g, b, hsp
// Parse RGB or HEX format
if (color?.match(/^rgb/)) {
color = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/)
r = color[1]
g = color[2]
b = color[3]
} else {
color = +`0x${color?.slice(1).replace(color.length < 5 && /./g, '$&$&')}`
r = color >> 16
g = (color >> 8) & 255
b = color & 255
}
// HSP equation from http://alienryderflex.com/hsp.html
hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b))
return hsp > 127.5 ? 'light' : 'dark'
}
The HSP formula weighs colors according to human perception:
- Red: 29.9%
- Green: 58.7% (humans are most sensitive to green)
- Blue: 11.4%
Usage Example
From NewTab.vue:71-75:
const { lightOrDark } = useUI()
const skeletonClass = computed(() =>
lightOrDark(config.value.bgColor as string) === 'dark' ? 'bg-zinc-700' : 'bg-zinc-300',
)
This dynamically adjusts skeleton loader colors based on the background color brightness.
The composable also disables page scrolling on mount (lines 48-70). This is useful for full-screen new tab pages:
onMounted(async () => {
const wheelOpt = supportsPassive ? { passive: false } : false
const wheelEvent = 'onwheel' in document.createElement('div') ? 'wheel' : 'mousewheel'
window.addEventListener('DOMMouseScroll', preventDefault, false)
window.addEventListener(wheelEvent, preventDefault, wheelOpt)
window.addEventListener('touchmove', preventDefault, wheelOpt)
window.addEventListener('keydown', preventDefaultForScrollKeys, false)
})
The scroll prevention runs automatically when you use useUI() in a component. You don’t need to call any methods to enable it.
fetcher
A simple wrapper around the Fetch API that parses JSON responses.
Function Signature
function fetcher(key: string): Promise<any>
Location: src/logic/fetcher.ts:1-11
Parameters
Returns
Parsed JSON response data
Implementation
export function fetcher(key: string) {
return fetch(key)
.then((resp) => {
return resp && resp.json()
})
.then((data) => {
if (data.message) throw new Error(data.message)
return data
})
}
Error Handling
The function throws an error if the response includes a message property, following common API error conventions.
Usage Example
From NewTab.vue:89-93:
import { fetcher } from '~/logic/fetcher'
async function fetchQuote() {
const url = 'https://gist.githubusercontent.com/fuongz/dc7bdaffc9181e7ef0b176f1f025ab22/raw/0d62f619d5ff9457e8e9f710c9fdefd463a0ee7c/quotes.json'
const res = await fetcher(url)
if (res) quote.value = getRandomQuote(res)
}
Type Definitions
All composables are fully typed with TypeScript.
WebExtensionStorageOptions
export type WebExtensionStorageOptions<T> = UseStorageAsyncOptions<T>
Extends VueUse’s UseStorageAsyncOptions with these properties:
flush?: 'pre' | 'post' | 'sync'
deep?: boolean
listenToStorageChanges?: boolean
writeDefaults?: boolean
mergeDefaults?: boolean | ((stored: T, defaults: T) => T)
shallow?: boolean
serializer?: Serializer<T>
onError?: (error: unknown) => void
RemovableRef
From @vueuse/core:
type RemovableRef<T> = Ref<T> & {
// Set to null to remove from storage
}
Additional Utilities
setupApp
An application setup function used in the extension entry point.
Location: src/logic/common-setup.ts:3-15
function setupApp(app: App): void
Configures the Vue app with global properties and plugins. This function:
- Injects a globally available
$app object in templates
- Provides access to
app via inject('app') in script setup
- Can be extended with additional plugins (i18n, router, etc.)
Environment Utilities
Location: src/env.ts:1-15
function isForbiddenUrl(url: string): boolean
const isFirefox: boolean
- isForbiddenUrl: Checks if a URL is restricted (chrome://, edge://, etc.)
- isFirefox: Boolean constant detecting Firefox browser
Best Practices
const { data: config, dataReady } = useWebExtensionStorage('key', defaults)
// Wait before accessing data
await dataReady
console.log(config.value) // Safe
Without awaiting, config.value may contain only the defaults, not the stored value.Use mergeDefaults for evolving schemas
// v1: { theme: 'dark' }
// v2: { theme: 'dark', fontSize: 16 }
const { data } = useWebExtensionStorage(
'settings',
{ theme: 'dark', fontSize: 16 },
{ mergeDefaults: true } // Existing users get fontSize: 16
)
This adds new properties to existing configs without overwriting user preferences.
interface Config {
bgColor: string
fontSize: number
}
const { data } = useWebExtensionStorage<Config>('config', defaults)
TypeScript will enforce the shape and catch property access errors.Use lightOrDark for accessible contrast
const { lightOrDark } = useUI()
const textColor = computed(() =>
lightOrDark(bgColor.value) === 'dark' ? 'text-white' : 'text-black'
)
This ensures text remains readable on any background color.