Skip to content

Instantly share code, notes, and snippets.

View pugson's full-sized avatar

pugson pugson

View GitHub Profile
@pugson
pugson / oura-sleep-debt.md
Created August 16, 2026 12:29
Oura Sleep Debt isn't in the API — but you can calculate it from sleep data. Formula, reference implementation, and how to verify it against your own app readings.

Reconstructing Oura's Sleep Debt from the public API

Sleep Debt is not available through the Oura API — but you can calculate it yourself from the sleep data that is.

Everything you need is already in /v2/usercollection/sleep: the nightly total_sleep_duration values. The debt figure is just a weighted sum over the last 14 of them, against one personal constant you have to calibrate once. This gist gives you the formula, the reference implementation, and — the part that actually matters — how to verify your version is right rather than merely plausible.

Oura's app shows a Sleep Debt figure on its Sleep screen. It appears nowhere in the public API, and no amount of proxying the app will find it: Oura computes it on-device, in a native engine called ecore, and writes it straight to the app's local database. Opening the Sleep Debt screen generates no network traffic at all. So there is nothing to intercept — but there is enough in the API to rebuild the number.

Result: **13 of 15 re

@pugson
pugson / _readme.md
Created March 24, 2026 01:00
react-native-bottom-tabs Liquid Glass minimizeBehavior with LegendList, FlashList, etc.
demo.mp4

iOS 26 Liquid Glass Tab Bar Minimize on Scroll in React Native

Liquid Glass tab bars can minimize into a compact floating pill when you scroll. SwiftUI makes this easy with .tabBarMinimizeBehavior(.onScrollDown) but if you're using React Native with react-native-bottom-tabs, it doesn't work virtualized list libraries like LegendList or FlashList. Here is a simple patch to get it working.

Usage

Apply the patch with your package manager (bun, pnpm, yarn, etc.) and make a new native build, then pass minimizeBehavior to your tab navigator:

@pugson
pugson / withExpoP3.ts
Created March 10, 2025 12:48 — forked from lauridskern/withExpoP3.ts
Expo config plugin to enable P3 colors
const { withAppDelegate } = require("expo/config-plugins");
const {
mergeContents,
} = require("@expo/config-plugins/build/utils/generateCode");
const withExpoP3 = (config) => {
return withAppDelegate(config, (config) => {
if (
config.modResults.language === "objc" ||
config.modResults.language === "objcpp"
@pugson
pugson / _readme.md
Created November 11, 2024 21:54
Interactive component showing sleep data from Oura Ring API

Preview

preview

See the demo on Twitter

Rough prototype of visualizing my sleep data with Visx and Framer Motion.

This is a code snippet from a Next.js 14 app. Uses the Oura v2 API to get data from your account.

@pugson
pugson / fix-svg-jsx.mjs
Created June 4, 2024 09:38
fix svg in jsx with a script
import fs from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const APPS_DIRECTORY = path.resolve(__dirname, "../../apps");
// Common SVG attributes that need renaming to camelCase for JSX
const attributeReplacements = {
blueprint:
name: Philips Hue Tap Dial Light Brightness Control
description: Use the Philips Hue Tap Dial to control the brightness of a light.
domain: automation
input:
tap_dial:
name: Tap Dial
description: The tap dial to use. Select the battery sensor that is exposed.
selector:
entity:
@pugson
pugson / image-with-avg-color.tsx
Created June 2, 2024 09:32
client component that uses FastAverageColor
"use client";
import { FastAverageColor } from "fast-average-color";
import { useEffect, useState } from "react";
const fac = new FastAverageColor();
export function ImageWithAvgColor({ src }: { src: string }) {
const [avgColor, setAvgColor] = useState<string>("transparent");
useEffect(() => {
@pugson
pugson / constants.ts
Created April 13, 2024 23:09 — forked from matthewmorek/constants.ts
Handling BASE_URLs in Vercel env
const { CI, PORT = 3000, VERCEL_ENV, NEXT_PUBLIC_VERCEL_ENV, VERCEL_URL, NEXT_PUBLIC_VERCEL_URL } = process.env;
export const ENVIRONMENT = VERCEL_ENV || NEXT_PUBLIC_VERCEL_ENV;
const baseDomainSource = CI ? VERCEL_URL : NEXT_PUBLIC_VERCEL_URL;
const baseDomain = baseDomainSource;
let BASE_URL: string;
if (ENVIRONMENT === 'preview') {
@pugson
pugson / props.md
Created January 29, 2024 00:27
SVG → JSX
SVG Attribute JSX Property
class className
fill-opacity fillOpacity
stroke-opacity strokeOpacity
stroke-width strokeWidth
stroke-linecap strokeLinecap
stroke-linejoin strokeLinejoin
stroke-dasharray strokeDasharray
stroke-dashoffset strokeDashoffset
import * as React from 'react';
const useIsFirstRender = (): boolean => {
const isFirst = React.useRef(true);
if (isFirst.current) {
isFirst.current = false;
return true;
} else {