Skip to content

Instantly share code, notes, and snippets.

View kmelve's full-sized avatar
💬
is typing

Knut Melvær kmelve

💬
is typing
View GitHub Profile
@kmelve
kmelve / 00-README.md
Created April 2, 2026 16:08
Em Dash Easter Egg for Sanity + Portable Text — click any em dash to see if it was human-typed or AI-placed

Em Dash Easter Egg

An easter egg for blogs using Sanity and Portable Text: every em dash (—) becomes clickable, revealing whether it was typed by a human or placed by AI.

Human-certified dashes show playful messages like "Certified organic em dash" or "Artisanal punctuation." Uncertified dashes (assumed AI) show messages like "Machine-generated punctuation" or "Algorithmically optimized dash."

How it works

The feature has four layers:

@kmelve
kmelve / gist-bodyPortableText-schema.ts
Created March 17, 2026 20:06
Footnotes in Sanity Portable Text + Next.js — minimal reproducible implementation
// Schema: main body Portable Text with footnote annotation
// This shows how to register footnote as a mark annotation
import { BlockquoteIcon, LaunchIcon } from "@sanity/icons";
import { defineArrayMember, defineField, defineType } from "sanity";
export const bodyPortableText = defineType({
name: "bodyPortableText",
type: "array",
title: "Content",
@kmelve
kmelve / plan.md
Created January 31, 2026 18:31
Reusable Page Blocks in Sanity - Implementation Guide

Reusable Page Blocks in Sanity - Implementation Guide

A pattern for creating once, using everywhere: how to implement reusable content blocks in Sanity that can be shared across multiple pages.


Overview

This pattern allows content editors to create a page block (hero, CTA, feature section, etc.) once as a standalone document, then reference it from multiple pages. Changes to the reusable block automatically propagate everywhere it's used.

@kmelve
kmelve / BlockEditor.ts
Created May 24, 2024 11:10
Markdown to Portable Text paste handler
import { toPlainText } from "@portabletext/react"
import { BlockEditor as DefaultBlockEditor } from "sanity"
import { handlePaste } from "~/studio/components/blockEditor/handlePaste"
const wordsPerMinute = 200
export default function BlockEditor(props: any, ref) {
const value = props.value ?? []
const plainText = toPlainText(value)
const characterCount = plainText.length
#!/bin/bash
# Assumes that you have the Netlify and jq CLI tools installed and that you are logged in.
# Only been tested on macOS and zsh
# Will change the build image for all sites in a Netlify account to the specified image
#
# Usage: sh ./netlify-build-image-bulk.sh
# This assumes that you have logged in with the CLI fairly recently and that you're on macOS
# For other systems: https://github.com/sindresorhus/env-paths#pathsconfig
NETLIFY_AUTH=$(cat ~/Library/Preferences/netlify/config.json|jq -r ".users[].auth.token")
@kmelve
kmelve / stoppord_no
Created February 7, 2014 19:18
Norske stoppord
å
alle
andre
at
av
både
båe
bare
begge
ble
@kmelve
kmelve / entries-by-date-this-year.js
Created May 9, 2020 18:23
Structure definition for listing entries by date of the current year for Sanity Studio
import S from '@sanity/desk-tool/structure-builder'
import eachDayOfInterval from 'date-fns/eachDayOfInterval'
import startOfYear from 'date-fns/startOfYear'
import startOfDay from 'date-fns/startOfDay'
import endOfDay from 'date-fns/endOfDay'
let now = new Date()
export default () => S.listItem()
.title('Entries by day this year')
.child(
@kmelve
kmelve / LinkedInEmbedInput.tsx
Last active September 18, 2024 15:19
LinkedIn Embed Input for Sanity Studio
import React, { useState } from "react"
import { TextInput, Stack } from "@sanity/ui"
import { set, unset } from "sanity"
interface LinkedInEmbed {
_key: string
_type: string
postUrl: string
height: number
}
@kmelve
kmelve / BlockEditor.js
Created September 14, 2019 11:56
Custom portable text / block editor for Sanity with markdown paste and stats
import React, { Component, Fragment } from 'react'
import { BlockEditor } from 'part:@sanity/form-builder'
import Switch from 'part:@sanity/components/toggles/switch'
import css from './BlockEditor.module.css'
import { handlePaste } from './handlePaste'
export default class CustomEditor extends Component {
state = {
customPaste: false
}
@kmelve
kmelve / InputPicker.js
Last active August 16, 2024 21:15
CUSTOM SANITY STUDIO INPUT COMPONENT FOR FONT-AWESOME
/**
* CUSTOM SANITY STUDIO INPUT COMPONENT FOR FONT-AWESOME
*
* This is a proof of concept custom input component that loads the whole free library of font awesome icons and makes them
* searchable in a downshift component. If they're selected the font name will be saved as a string on the document.
*
* Install dependencies in your studio folder with:
* yarn add @fortawesome/fontawesome-svg-core @fortawesome/react-fontawesome @fortawesome/free-solid-svg-icons downshift
*
* Use as a custom input component in your shcema: