├── examples ├── next-static │ ├── .gitignore │ ├── pages │ │ ├── _app.tsx │ │ └── [[...index]].tsx │ ├── styles.css │ ├── next-env.d.ts │ ├── next.config.js │ ├── next.lock │ │ └── lock.json │ ├── schemas │ │ ├── plotSummaries.ts │ │ ├── plotSummary.ts │ │ ├── index.ts │ │ ├── castMember.ts │ │ ├── person.ts │ │ ├── crewMember.ts │ │ ├── screening.ts │ │ ├── movie.ts │ │ └── blockContent.ts │ ├── themer.d.ts │ ├── tsconfig.json │ ├── package.json │ ├── sanity.config.ts │ └── README.md ├── next-dynamic │ ├── .gitignore │ ├── pages │ │ ├── _app.tsx │ │ └── [[...index]].tsx │ ├── styles.css │ ├── next-env.d.ts │ ├── schemas │ │ ├── plotSummaries.ts │ │ ├── plotSummary.ts │ │ ├── index.ts │ │ ├── castMember.ts │ │ ├── person.ts │ │ ├── crewMember.ts │ │ ├── screening.ts │ │ ├── movie.ts │ │ └── blockContent.ts │ ├── themer.d.ts │ ├── tsconfig.json │ ├── package.json │ ├── sanity.config.ts │ └── README.md ├── advanced │ ├── static │ │ └── .gitkeep │ ├── sanity.cli.ts │ ├── schemas │ │ ├── plotSummaries.ts │ │ ├── plotSummary.ts │ │ ├── index.ts │ │ ├── castMember.ts │ │ ├── person.ts │ │ ├── crewMember.ts │ │ ├── screening.ts │ │ ├── movie.ts │ │ └── blockContent.ts │ ├── .gitignore │ ├── package.json │ ├── tsconfig.json │ ├── themer.d.ts │ ├── README.md │ ├── sanity.config.ts │ └── _document.tsx └── basic │ ├── static │ └── .gitkeep │ ├── sanity.cli.ts │ ├── schemas │ ├── plotSummaries.ts │ ├── plotSummary.ts │ ├── index.ts │ ├── castMember.ts │ ├── person.ts │ ├── crewMember.ts │ ├── screening.ts │ ├── movie.ts │ └── blockContent.ts │ ├── .gitignore │ ├── themer.d.ts │ ├── package.json │ ├── sanity.config.ts │ ├── tsconfig.json │ └── README.md ├── .github ├── CODEOWNERS ├── renovate.json └── workflows │ ├── lock.yml │ ├── prettier.yml │ └── ci.yml ├── apps ├── v1 │ ├── utils │ │ ├── ValidationError.ts │ │ ├── validMidPoints.ts │ │ ├── stringifyColorSearchParam.ts │ │ ├── isMidPoint.ts │ │ ├── roundMidPoint.ts │ │ ├── getMidPointFromLuminance.ts │ │ ├── applyHuesFromPreset.ts │ │ ├── __tests__ │ │ │ ├── snippets.test.ts │ │ │ ├── presets.test.ts │ │ │ ├── ServerTiming.test.ts │ │ │ └── applyHues.test.ts │ │ ├── colors.ts │ │ ├── types.ts │ │ ├── expandPresetSearchParams.ts │ │ ├── fetcher.ts │ │ ├── ServerTiming.ts │ │ ├── widenColorHue.ts │ │ ├── createTonesFromHues.ts │ │ ├── applyHues.ts │ │ ├── presets.tsx │ │ ├── shortenPresetSearchParams.ts │ │ └── parseHuesFromSearchParams.ts │ ├── public │ │ ├── 1x1.png │ │ ├── favicon.ico │ │ └── favicon.png │ ├── hooks │ │ ├── usePrettier.ts │ │ ├── useTheme.ts │ │ ├── useMemoHues.ts │ │ ├── useIdleCallback.ts │ │ └── useMagicRouter.ts │ ├── studios │ │ ├── index.ts │ │ ├── movies │ │ │ ├── schemas │ │ │ │ ├── plotSummaries.ts │ │ │ │ ├── plotSummary.ts │ │ │ │ ├── index.ts │ │ │ │ ├── person.ts │ │ │ │ ├── castMember.ts │ │ │ │ ├── crewMember.ts │ │ │ │ ├── screening.ts │ │ │ │ ├── movie.ts │ │ │ │ └── blockContent.ts │ │ │ └── index.tsx │ │ └── blog.tsx │ ├── next-env.d.ts │ ├── next.config.js │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── api │ │ │ ├── palette │ │ │ │ └── [projectId] │ │ │ │ │ └── [dataset] │ │ │ │ │ └── [id].ts │ │ │ └── hues.ts │ │ └── index.tsx │ ├── jest.config.js │ ├── tsconfig.json │ ├── components │ │ ├── SyncColorScheme.tsx │ │ ├── CopySnippetButton.tsx │ │ ├── CounterSnippet.tsx │ │ ├── ThemerFallback.tsx │ │ ├── ToggleView.tsx │ │ ├── HueColorInput.tsx │ │ ├── icons.tsx │ │ ├── ImageColorPaletteGrid.tsx │ │ ├── StudioPreview.tsx │ │ ├── Sidebar.styles.tsx │ │ ├── CodeSnippet.tsx │ │ ├── HeaderCard.tsx │ │ ├── ColorTintsPreview.tsx │ │ ├── ImportFromImage.styles.tsx │ │ ├── Head.tsx │ │ ├── SchemeMenu.tsx │ │ ├── ShareTab.tsx │ │ ├── Logo.tsx │ │ ├── StudioViewer.tsx │ │ └── PaletteIcon.tsx │ └── package.json └── v2 │ ├── schemas │ ├── plotSummaries.ts │ ├── plotSummary.ts │ ├── index.ts │ ├── castMember.ts │ ├── person.ts │ ├── crewMember.ts │ ├── screening.ts │ ├── movie.ts │ └── blockContent.ts │ ├── sanity.config.ts │ ├── themer.d.ts │ ├── tsconfig.json │ ├── package.json │ └── src │ └── Logo.tsx ├── pnpm-workspace.yaml ├── .npmrc ├── README.md ├── .prettierrc.cjs ├── .eslintrc.json ├── .prettierignore ├── package.json ├── turbo.json ├── LICENSE └── .gitignore /examples/next-static/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @sanity-io/ecosystem 2 | -------------------------------------------------------------------------------- /examples/next-dynamic/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /apps/v1/utils/ValidationError.ts: -------------------------------------------------------------------------------- 1 | export class ValidationError extends TypeError {} 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'apps/*' 3 | - 'examples/*' 4 | - 'packages/*' 5 | -------------------------------------------------------------------------------- /apps/v1/public/1x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanity-io/themer/HEAD/apps/v1/public/1x1.png -------------------------------------------------------------------------------- /apps/v1/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanity-io/themer/HEAD/apps/v1/public/favicon.ico -------------------------------------------------------------------------------- /apps/v1/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanity-io/themer/HEAD/apps/v1/public/favicon.png -------------------------------------------------------------------------------- /examples/next-dynamic/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles.css' 2 | 3 | export { default } from 'next/app' 4 | -------------------------------------------------------------------------------- /examples/next-static/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles.css' 2 | 3 | export { default } from 'next/app' 4 | -------------------------------------------------------------------------------- /examples/next-static/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | -webkit-font-smoothing: antialiased; 4 | } 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | enable-pre-post-scripts = true 3 | hoist-pattern[] = !"**/@types/**" 4 | -------------------------------------------------------------------------------- /examples/next-dynamic/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | -webkit-font-smoothing: antialiased; 4 | } 5 | -------------------------------------------------------------------------------- /examples/advanced/static/.gitkeep: -------------------------------------------------------------------------------- 1 | Files placed here will be served by the Sanity server under the `/static`-prefix 2 | -------------------------------------------------------------------------------- /examples/basic/static/.gitkeep: -------------------------------------------------------------------------------- 1 | Files placed here will be served by the Sanity server under the `/static`-prefix 2 | -------------------------------------------------------------------------------- /apps/v1/utils/validMidPoints.ts: -------------------------------------------------------------------------------- 1 | export const validMidPoints = new Set([ 2 | 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950, 3 | ]) 4 | -------------------------------------------------------------------------------- /apps/v1/hooks/usePrettier.ts: -------------------------------------------------------------------------------- 1 | console.warn('Delete useFormatted') 2 | 3 | export function useFormatted(code: string): string { 4 | return code 5 | } 6 | -------------------------------------------------------------------------------- /apps/v1/utils/stringifyColorSearchParam.ts: -------------------------------------------------------------------------------- 1 | export function stringifyColorSearchParam(color: string) { 2 | return color.toLowerCase().replace(/^#/, '') 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Themer 2 | 3 | > ⚠️ This is a very experimental project 🚨 4 | 5 | https://user-images.githubusercontent.com/81981/180262026-6b2c8243-8c47-4cac-84dd-c031df929002.mov 6 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: true, 4 | // https://github.com/pnpm/pnpm/issues/3323 5 | plugins: [require('prettier-plugin-packagejson')], 6 | } 7 | -------------------------------------------------------------------------------- /apps/v1/studios/index.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'sanity' 2 | 3 | import { config as blog } from './blog' 4 | import { config as movies } from './movies' 5 | 6 | export const config = defineConfig([movies, blog]) 7 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "prettier"], 3 | "plugins": ["simple-import-sort"], 4 | "rules": { 5 | "simple-import-sort/imports": "error", 6 | "simple-import-sort/exports": "error" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/v1/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/v1/utils/isMidPoint.ts: -------------------------------------------------------------------------------- 1 | import type { Hue } from 'utils/types' 2 | import { validMidPoints } from 'utils/validMidPoints' 3 | 4 | export const isMidPoint = (input: number): input is Hue['midPoint'] => 5 | validMidPoints.has(input) 6 | -------------------------------------------------------------------------------- /examples/next-dynamic/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/next-static/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/next-static/next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** 4 | * @type {import('next').NextConfig} 5 | **/ 6 | const nextConfig = { 7 | experimental: { urlImports: ['https://themer.sanity.build/'] }, 8 | } 9 | 10 | module.exports = nextConfig 11 | -------------------------------------------------------------------------------- /examples/basic/sanity.cli.ts: -------------------------------------------------------------------------------- 1 | import { createCliConfig } from 'sanity/cli' 2 | 3 | const projectId = process.env.SANITY_STUDIO_API_PROJECT_ID 4 | const dataset = process.env.SANITY_STUDIO_API_DATASET 5 | 6 | export default createCliConfig({ api: { projectId, dataset } }) 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | .next 3 | edge-utils 4 | public/favicon.svg 5 | .cache 6 | .vercel 7 | .turbo 8 | .sanity 9 | apps/*/.next 10 | apps/*/build 11 | apps/*/dist 12 | examples/*/.next 13 | examples/*/build 14 | examples/*/dist 15 | packages/*/dist 16 | pnpm-lock.yaml -------------------------------------------------------------------------------- /examples/advanced/sanity.cli.ts: -------------------------------------------------------------------------------- 1 | import { createCliConfig } from 'sanity/cli' 2 | 3 | const projectId = process.env.SANITY_STUDIO_API_PROJECT_ID 4 | const dataset = process.env.SANITY_STUDIO_API_DATASET 5 | 6 | export default createCliConfig({ api: { projectId, dataset } }) 7 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>sanity-io/renovate-config", 5 | "github>sanity-io/renovate-config:studio-v3", 6 | ":reviewer(team:ecosystem)" 7 | ], 8 | "ignorePresets": [":ignoreModulesAndTests"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/next-static/next.lock/lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "https://themer.sanity.build/api/hues?preset=pink-synth": { 3 | "integrity": "sha512-/olYsciNM5Al9F5aL14D0nF6IWdpDMx9AULhJfzO73Hmyw1EiRnNsWg91Z+4hJeTZvPBGTLPfIGkd0jy5p6NyQ==", 4 | "contentType": "application/javascript; charset=utf-8" 5 | }, 6 | "version": 1 7 | } 8 | -------------------------------------------------------------------------------- /apps/v1/utils/roundMidPoint.ts: -------------------------------------------------------------------------------- 1 | import type { Hue } from 'utils/types' 2 | 3 | export function roundMidPoint(value: number): Hue['midPoint'] { 4 | if (value < 75) { 5 | return 50 6 | } 7 | if (value > 925) { 8 | return 950 9 | } 10 | 11 | return (Math.round(value / 100) * 100) as Hue['midPoint'] 12 | } 13 | -------------------------------------------------------------------------------- /apps/v1/next.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** 4 | * @type {import('next').NextConfig} 5 | **/ 6 | const nextConfig = { 7 | reactStrictMode: true, 8 | images: { 9 | domains: ['cdn.sanity.io', 'source.unsplash.com'], 10 | formats: ['image/avif', 'image/webp'], 11 | }, 12 | } 13 | 14 | module.exports = nextConfig 15 | -------------------------------------------------------------------------------- /apps/v1/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import CounterSnippet from 'components/CounterSnippet' 2 | import type { AppProps } from 'next/app' 3 | 4 | export default function App({ Component, pageProps }: AppProps) { 5 | return ( 6 | <> 7 | 8 | {process.env.NEXT_PUBLIC_COUNTER_DEV && } 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /apps/v1/utils/getMidPointFromLuminance.ts: -------------------------------------------------------------------------------- 1 | import { getLuminance } from 'polished' 2 | 3 | export function getMidPointFromLuminance(color: string): number { 4 | const luminance = getLuminance(color) 5 | const corrected = 1 - luminance 6 | const tint = corrected * 650 7 | 8 | // TODO should round by 100, except for 50 and 950 9 | 10 | return Math.round(tint / 100) * 100 11 | } 12 | -------------------------------------------------------------------------------- /apps/v2/schemas/plotSummaries.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Plot summaries', 3 | name: 'plotSummaries', 4 | type: 'object', 5 | fields: [ 6 | { 7 | name: 'caption', 8 | title: 'Caption', 9 | type: 'string', 10 | }, 11 | { 12 | name: 'summaries', 13 | title: 'Summaries', 14 | type: 'array', 15 | of: [{ type: 'plotSummary' }], 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /examples/advanced/schemas/plotSummaries.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Plot summaries', 3 | name: 'plotSummaries', 4 | type: 'object', 5 | fields: [ 6 | { 7 | name: 'caption', 8 | title: 'Caption', 9 | type: 'string', 10 | }, 11 | { 12 | name: 'summaries', 13 | title: 'Summaries', 14 | type: 'array', 15 | of: [{ type: 'plotSummary' }], 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /examples/basic/schemas/plotSummaries.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Plot summaries', 3 | name: 'plotSummaries', 4 | type: 'object', 5 | fields: [ 6 | { 7 | name: 'caption', 8 | title: 'Caption', 9 | type: 'string', 10 | }, 11 | { 12 | name: 'summaries', 13 | title: 'Summaries', 14 | type: 'array', 15 | of: [{ type: 'plotSummary' }], 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /examples/next-dynamic/schemas/plotSummaries.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Plot summaries', 3 | name: 'plotSummaries', 4 | type: 'object', 5 | fields: [ 6 | { 7 | name: 'caption', 8 | title: 'Caption', 9 | type: 'string', 10 | }, 11 | { 12 | name: 'summaries', 13 | title: 'Summaries', 14 | type: 'array', 15 | of: [{ type: 'plotSummary' }], 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /examples/next-static/schemas/plotSummaries.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Plot summaries', 3 | name: 'plotSummaries', 4 | type: 'object', 5 | fields: [ 6 | { 7 | name: 'caption', 8 | title: 'Caption', 9 | type: 'string', 10 | }, 11 | { 12 | name: 'summaries', 13 | title: 'Summaries', 14 | type: 'array', 15 | of: [{ type: 'plotSummary' }], 16 | }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "turbo run build", 5 | "dev": "turbo run dev", 6 | "format": "prettier --cache --write .", 7 | "lint": "turbo run lint", 8 | "test": "turbo run test" 9 | }, 10 | "devDependencies": { 11 | "prettier": "3.2.5", 12 | "prettier-plugin-packagejson": "2.4.14", 13 | "turbo": "1.13.2" 14 | }, 15 | "packageManager": "pnpm@8.15.7" 16 | } 17 | -------------------------------------------------------------------------------- /examples/next-static/pages/[[...index]].tsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import { NextStudio } from 'next-sanity/studio' 3 | import { NextStudioHead } from 'next-sanity/studio/head' 4 | 5 | import config from '../sanity.config' 6 | 7 | export default function IndexPage() { 8 | return ( 9 | <> 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalDependencies": ["**/.env.*local"], 4 | "pipeline": { 5 | "build": { 6 | "outputs": ["dist/**", ".next/**", "build/**"], 7 | "dependsOn": ["^build"] 8 | }, 9 | "test": { 10 | "cache": false 11 | }, 12 | "lint": {}, 13 | "dev": { 14 | "dependsOn": ["^build"], 15 | "cache": false, 16 | "persistent": true 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/v2/schemas/plotSummary.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'plotSummary', 3 | title: 'Plot Summary', 4 | type: 'object', 5 | fields: [ 6 | { 7 | title: 'Summary', 8 | name: 'summary', 9 | type: 'text', 10 | }, 11 | { 12 | title: 'Author', 13 | name: 'author', 14 | type: 'string', 15 | }, 16 | { 17 | title: 'Link to author', 18 | name: 'url', 19 | type: 'url', 20 | }, 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /examples/basic/schemas/plotSummary.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'plotSummary', 3 | title: 'Plot Summary', 4 | type: 'object', 5 | fields: [ 6 | { 7 | title: 'Summary', 8 | name: 'summary', 9 | type: 'text', 10 | }, 11 | { 12 | title: 'Author', 13 | name: 'author', 14 | type: 'string', 15 | }, 16 | { 17 | title: 'Link to author', 18 | name: 'url', 19 | type: 'url', 20 | }, 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /examples/advanced/schemas/plotSummary.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'plotSummary', 3 | title: 'Plot Summary', 4 | type: 'object', 5 | fields: [ 6 | { 7 | title: 'Summary', 8 | name: 'summary', 9 | type: 'text', 10 | }, 11 | { 12 | title: 'Author', 13 | name: 'author', 14 | type: 'string', 15 | }, 16 | { 17 | title: 'Link to author', 18 | name: 'url', 19 | type: 'url', 20 | }, 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /examples/next-static/schemas/plotSummary.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'plotSummary', 3 | title: 'Plot Summary', 4 | type: 'object', 5 | fields: [ 6 | { 7 | title: 'Summary', 8 | name: 'summary', 9 | type: 'text', 10 | }, 11 | { 12 | title: 'Author', 13 | name: 'author', 14 | type: 'string', 15 | }, 16 | { 17 | title: 'Link to author', 18 | name: 'url', 19 | type: 'url', 20 | }, 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /examples/next-dynamic/schemas/plotSummary.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'plotSummary', 3 | title: 'Plot Summary', 4 | type: 'object', 5 | fields: [ 6 | { 7 | title: 'Summary', 8 | name: 'summary', 9 | type: 'text', 10 | }, 11 | { 12 | title: 'Author', 13 | name: 'author', 14 | type: 'string', 15 | }, 16 | { 17 | title: 'Link to author', 18 | name: 'url', 19 | type: 'url', 20 | }, 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /apps/v1/utils/applyHuesFromPreset.ts: -------------------------------------------------------------------------------- 1 | import { applyHues } from 'utils/applyHues' 2 | import { parseHuesFromSearchParams } from 'utils/parseHuesFromSearchParams' 3 | import type { Hues } from 'utils/types' 4 | 5 | export function applyHuesFromPreset( 6 | presetParams: URLSearchParams, 7 | searchParams: URLSearchParams, 8 | ): Hues { 9 | const presetHues = applyHues(parseHuesFromSearchParams(presetParams)) 10 | 11 | return applyHues(parseHuesFromSearchParams(searchParams), presetHues) 12 | } 13 | -------------------------------------------------------------------------------- /apps/v1/studios/movies/schemas/plotSummaries.ts: -------------------------------------------------------------------------------- 1 | import { defineType } from 'sanity' 2 | 3 | export const plotSummaries = defineType({ 4 | title: 'Plot summaries', 5 | name: 'plotSummaries', 6 | type: 'object', 7 | fields: [ 8 | { 9 | name: 'caption', 10 | title: 'Caption', 11 | type: 'string', 12 | }, 13 | { 14 | name: 'summaries', 15 | title: 'Summaries', 16 | type: 'array', 17 | of: [{ type: 'plotSummary' }], 18 | }, 19 | ], 20 | }) 21 | -------------------------------------------------------------------------------- /apps/v1/utils/__tests__/snippets.test.ts: -------------------------------------------------------------------------------- 1 | import JSON5 from 'json5' 2 | import { snippet, snippets } from 'utils/snippets' 3 | 4 | // @TODO generate tests with inline snapshots instead, to workaround CI failing 5 | describe.skip('snippets', () => { 6 | for (const id of snippets) { 7 | test(id, () => { 8 | expect( 9 | JSON.stringify( 10 | (snippet(id as any) as any)(JSON5.stringify('${first}}')), 11 | ), 12 | ).toMatchSnapshot(id) 13 | }) 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /apps/v1/utils/colors.ts: -------------------------------------------------------------------------------- 1 | import { black, hues, white } from '@sanity/color' 2 | 3 | export const lightest = white.hex.toLowerCase() 4 | export const darkest = black.hex.toLowerCase() 5 | 6 | export const NEUTRAL_TONES = ['default', 'transparent'] 7 | 8 | export const TONES = [ 9 | 'default', 10 | 'primary', 11 | 'transparent', 12 | 'positive', 13 | 'caution', 14 | 'critical', 15 | ] as const 16 | 17 | export const { blue, cyan, gray, green, magenta, orange, purple, red, yellow } = 18 | hues 19 | -------------------------------------------------------------------------------- /apps/v2/sanity.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'sanity' 2 | import { muxInput } from 'sanity-plugin-mux-input' 3 | import { structureTool } from 'sanity/structure' 4 | 5 | import { schemaTypes } from './schemas' 6 | import { themerTool } from './src' 7 | 8 | const projectId = 'c8jibo38' 9 | const dataset = 'themer-movies' 10 | 11 | export default defineConfig({ 12 | projectId, 13 | dataset, 14 | plugins: [structureTool(), muxInput(), themerTool()], 15 | schema: { types: schemaTypes }, 16 | }) 17 | -------------------------------------------------------------------------------- /examples/basic/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # Dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # Compiled Sanity Studio 9 | /dist 10 | 11 | # Temporary Sanity runtime, generated by the CLI on every dev server start 12 | /.sanity 13 | 14 | # Logs 15 | /logs 16 | *.log 17 | 18 | # Coverage directory used by testing tools 19 | /coverage 20 | 21 | # Misc 22 | .DS_Store 23 | *.pem 24 | 25 | # Typescript 26 | *.tsbuildinfo 27 | .vercel 28 | -------------------------------------------------------------------------------- /examples/advanced/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # Dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # Compiled Sanity Studio 9 | /dist 10 | 11 | # Temporary Sanity runtime, generated by the CLI on every dev server start 12 | /.sanity 13 | 14 | # Logs 15 | /logs 16 | *.log 17 | 18 | # Coverage directory used by testing tools 19 | /coverage 20 | 21 | # Misc 22 | .DS_Store 23 | *.pem 24 | 25 | # Typescript 26 | *.tsbuildinfo 27 | .vercel 28 | -------------------------------------------------------------------------------- /apps/v1/studios/movies/schemas/plotSummary.ts: -------------------------------------------------------------------------------- 1 | import { defineType } from 'sanity' 2 | 3 | export const plotSummary = defineType({ 4 | name: 'plotSummary', 5 | title: 'Plot Summary', 6 | type: 'object', 7 | fields: [ 8 | { 9 | title: 'Summary', 10 | name: 'summary', 11 | type: 'text', 12 | }, 13 | { 14 | title: 'Author', 15 | name: 'author', 16 | type: 'string', 17 | }, 18 | { 19 | title: 'Link to author', 20 | name: 'url', 21 | type: 'url', 22 | }, 23 | ], 24 | }) 25 | -------------------------------------------------------------------------------- /apps/v1/jest.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const nextJest = require('next/jest') 4 | 5 | // @ts-expect-error: next/jest is not callable 6 | const createJestConfig = nextJest({ dir: './' }) 7 | 8 | /** 9 | * @type {import('jest').Config} 10 | **/ 11 | const customJestConfig = { 12 | testEnvironment: '@edge-runtime/jest-environment', 13 | moduleDirectories: ['node_modules', '/'], 14 | } 15 | 16 | // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async 17 | module.exports = createJestConfig(customJestConfig) 18 | -------------------------------------------------------------------------------- /apps/v2/schemas/index.ts: -------------------------------------------------------------------------------- 1 | import blockContent from './blockContent' 2 | import castMember from './castMember' 3 | import crewMember from './crewMember' 4 | import movie from './movie' 5 | import person from './person' 6 | import plotSummaries from './plotSummaries' 7 | import plotSummary from './plotSummary' 8 | import screening from './screening' 9 | 10 | export const schemaTypes = [ 11 | // Document types 12 | movie, 13 | person, 14 | screening, 15 | 16 | // Other types 17 | blockContent, 18 | plotSummary, 19 | plotSummaries, 20 | castMember, 21 | crewMember, 22 | ] 23 | -------------------------------------------------------------------------------- /examples/basic/schemas/index.ts: -------------------------------------------------------------------------------- 1 | import blockContent from './blockContent' 2 | import castMember from './castMember' 3 | import crewMember from './crewMember' 4 | import movie from './movie' 5 | import person from './person' 6 | import plotSummaries from './plotSummaries' 7 | import plotSummary from './plotSummary' 8 | import screening from './screening' 9 | 10 | export const schemaTypes = [ 11 | // Document types 12 | movie, 13 | person, 14 | screening, 15 | 16 | // Other types 17 | blockContent, 18 | plotSummary, 19 | plotSummaries, 20 | castMember, 21 | crewMember, 22 | ] 23 | -------------------------------------------------------------------------------- /examples/advanced/schemas/index.ts: -------------------------------------------------------------------------------- 1 | import blockContent from './blockContent' 2 | import castMember from './castMember' 3 | import crewMember from './crewMember' 4 | import movie from './movie' 5 | import person from './person' 6 | import plotSummaries from './plotSummaries' 7 | import plotSummary from './plotSummary' 8 | import screening from './screening' 9 | 10 | export const schemaTypes = [ 11 | // Document types 12 | movie, 13 | person, 14 | screening, 15 | 16 | // Other types 17 | blockContent, 18 | plotSummary, 19 | plotSummaries, 20 | castMember, 21 | crewMember, 22 | ] 23 | -------------------------------------------------------------------------------- /examples/next-dynamic/schemas/index.ts: -------------------------------------------------------------------------------- 1 | import blockContent from './blockContent' 2 | import castMember from './castMember' 3 | import crewMember from './crewMember' 4 | import movie from './movie' 5 | import person from './person' 6 | import plotSummaries from './plotSummaries' 7 | import plotSummary from './plotSummary' 8 | import screening from './screening' 9 | 10 | export const schemaTypes = [ 11 | // Document types 12 | movie, 13 | person, 14 | screening, 15 | 16 | // Other types 17 | blockContent, 18 | plotSummary, 19 | plotSummaries, 20 | castMember, 21 | crewMember, 22 | ] 23 | -------------------------------------------------------------------------------- /examples/next-static/schemas/index.ts: -------------------------------------------------------------------------------- 1 | import blockContent from './blockContent' 2 | import castMember from './castMember' 3 | import crewMember from './crewMember' 4 | import movie from './movie' 5 | import person from './person' 6 | import plotSummaries from './plotSummaries' 7 | import plotSummary from './plotSummary' 8 | import screening from './screening' 9 | 10 | export const schemaTypes = [ 11 | // Document types 12 | movie, 13 | person, 14 | screening, 15 | 16 | // Other types 17 | blockContent, 18 | plotSummary, 19 | plotSummaries, 20 | castMember, 21 | crewMember, 22 | ] 23 | -------------------------------------------------------------------------------- /apps/v2/themer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'https://themer.sanity.build/api/hues?*' { 2 | interface Hue 3 | extends Omit { 4 | midPoint: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 5 | } 6 | interface Hues { 7 | default: Hue 8 | transparent: Hue 9 | primary: Hue 10 | positive: Hue 11 | caution: Hue 12 | critical: Hue 13 | } 14 | export const hues: Hues 15 | type Theme = import('sanity').StudioTheme 16 | export function createTheme(_hues: Hues): Theme 17 | export const theme: Theme 18 | } 19 | -------------------------------------------------------------------------------- /examples/basic/themer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'https://themer.sanity.build/api/hues?*' { 2 | interface Hue 3 | extends Omit { 4 | midPoint: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 5 | } 6 | interface Hues { 7 | default: Hue 8 | transparent: Hue 9 | primary: Hue 10 | positive: Hue 11 | caution: Hue 12 | critical: Hue 13 | } 14 | export const hues: Hues 15 | type Theme = import('sanity').StudioTheme 16 | export function createTheme(_hues: Hues): Theme 17 | export const theme: Theme 18 | } 19 | -------------------------------------------------------------------------------- /examples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "sanity build", 7 | "dev": "sanity dev --port 3003" 8 | }, 9 | "dependencies": { 10 | "react": "18.2.0", 11 | "react-dom": "18.2.0", 12 | "react-icons": "5.0.1", 13 | "react-is": "18.2.0", 14 | "sanity": "3.37.1", 15 | "sanity-plugin-mux-input": "2.3.4", 16 | "styled-components": "6.1.8" 17 | }, 18 | "devDependencies": { 19 | "@sanity/eslint-config-studio": "4.0.0", 20 | "eslint": "8.57.0", 21 | "typescript": "5.4.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/next-dynamic/themer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'https://themer.sanity.build/api/hues?*' { 2 | interface Hue 3 | extends Omit { 4 | midPoint: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 5 | } 6 | interface Hues { 7 | default: Hue 8 | transparent: Hue 9 | primary: Hue 10 | positive: Hue 11 | caution: Hue 12 | critical: Hue 13 | } 14 | export const hues: Hues 15 | type Theme = import('sanity').BaseTheme 16 | export function createTheme(_hues: Hues): Theme 17 | export const theme: Theme 18 | } 19 | -------------------------------------------------------------------------------- /examples/next-static/themer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'https://themer.sanity.build/api/hues?*' { 2 | interface Hue 3 | extends Omit { 4 | midPoint: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 5 | } 6 | interface Hues { 7 | default: Hue 8 | transparent: Hue 9 | primary: Hue 10 | positive: Hue 11 | caution: Hue 12 | critical: Hue 13 | } 14 | export const hues: Hues 15 | type Theme = import('sanity').StudioTheme 16 | export function createTheme(_hues: Hues): Theme 17 | export const theme: Theme 18 | } 19 | -------------------------------------------------------------------------------- /examples/advanced/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "advanced", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "sanity build", 7 | "dev": "sanity dev --port 3002" 8 | }, 9 | "dependencies": { 10 | "react": "18.2.0", 11 | "react-dom": "18.2.0", 12 | "react-icons": "5.0.1", 13 | "react-is": "18.2.0", 14 | "sanity": "3.37.1", 15 | "sanity-plugin-mux-input": "2.3.4", 16 | "styled-components": "6.1.8" 17 | }, 18 | "devDependencies": { 19 | "@sanity/eslint-config-studio": "4.0.0", 20 | "eslint": "8.57.0", 21 | "typescript": "5.4.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/next-dynamic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": false, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve" 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/next-static/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": false, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "incremental": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve" 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /apps/v2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "types": ["vite/client"], 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true 18 | }, 19 | "include": ["**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /examples/basic/sanity.config.ts: -------------------------------------------------------------------------------- 1 | import { theme } from 'https://themer.sanity.build/api/hues?preset=verdant' 2 | import { createConfig } from 'sanity' 3 | import { deskTool } from 'sanity/desk' 4 | import { muxInput } from 'sanity-plugin-mux-input' 5 | 6 | import { schemaTypes } from './schemas' 7 | 8 | const projectId = import.meta.env.SANITY_STUDIO_API_PROJECT_ID 9 | const dataset = import.meta.env.SANITY_STUDIO_API_DATASET 10 | 11 | export default createConfig({ 12 | projectId, 13 | dataset, 14 | theme, 15 | title: 'Basic Example', 16 | plugins: [deskTool(), muxInput()], 17 | schema: { types: schemaTypes }, 18 | }) 19 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lock Threads 3 | 4 | on: 5 | issues: 6 | types: [closed] 7 | pull_request: 8 | types: [closed] 9 | schedule: 10 | - cron: '0 0 * * *' 11 | workflow_dispatch: 12 | 13 | permissions: 14 | issues: write 15 | pull-requests: write 16 | 17 | concurrency: 18 | group: ${{ github.workflow }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | action: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5 26 | with: 27 | issue-inactive-days: 0 28 | pr-inactive-days: 7 29 | -------------------------------------------------------------------------------- /apps/v1/studios/movies/schemas/index.ts: -------------------------------------------------------------------------------- 1 | import { blockContent } from './blockContent' 2 | import { castMember } from './castMember' 3 | import { crewMember } from './crewMember' 4 | import { movie } from './movie' 5 | import { person } from './person' 6 | import { plotSummaries } from './plotSummaries' 7 | import { plotSummary } from './plotSummary' 8 | import { screening } from './screening' 9 | 10 | export const schemaTypes = [ 11 | // Document types 12 | movie, 13 | person, 14 | screening, 15 | 16 | // Other types 17 | blockContent, 18 | plotSummary, 19 | plotSummaries, 20 | castMember, 21 | crewMember, 22 | ] 23 | -------------------------------------------------------------------------------- /examples/advanced/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "types": ["vite/client"], 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true 18 | }, 19 | "include": ["**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /examples/basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "types": ["vite/client"], 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true 18 | }, 19 | "include": ["**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /examples/advanced/themer.d.ts: -------------------------------------------------------------------------------- 1 | module 'https://themer.sanity.build/api/hues?*' { 2 | interface Hue 3 | extends Omit { 4 | midPoint: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 5 | } 6 | interface Hues { 7 | default: Hue 8 | transparent: Hue 9 | primary: Hue 10 | positive: Hue 11 | caution: Hue 12 | critical: Hue 13 | } 14 | const hues: Hues 15 | type Theme = import('sanity').StudioTheme 16 | const createTheme = (hues: Hues): Theme => theme 17 | const theme: Theme 18 | 19 | export { createTheme, hues, theme } 20 | } 21 | -------------------------------------------------------------------------------- /apps/v1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "incremental": true, 12 | "esModuleInterop": true, 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve" 18 | }, 19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules", "examples"] 21 | } 22 | -------------------------------------------------------------------------------- /apps/v1/utils/types.ts: -------------------------------------------------------------------------------- 1 | import type { ColorHueConfig } from '@sanity/color' 2 | import React from 'react' 3 | 4 | export interface Hue extends Omit { 5 | // @TODO convert ColorTintKey from @sanity/color into numbers and reuse 6 | midPoint: 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950 7 | } 8 | 9 | export interface Hues { 10 | default: Hue 11 | transparent: Hue 12 | primary: Hue 13 | positive: Hue 14 | caution: Hue 15 | critical: Hue 16 | } 17 | 18 | export interface ThemePreset { 19 | title: string 20 | slug: string 21 | icon?: React.ReactNode 22 | url: string 23 | } 24 | -------------------------------------------------------------------------------- /apps/v1/components/SyncColorScheme.tsx: -------------------------------------------------------------------------------- 1 | import { type ThemeColorSchemeKey } from '@sanity/ui' 2 | import { memo, useLayoutEffect } from 'react' 3 | import { useColorScheme } from 'sanity' 4 | 5 | interface Props { 6 | forceScheme: ThemeColorSchemeKey 7 | } 8 | const SyncColorScheme = ({ forceScheme }: Props) => { 9 | const { scheme, setScheme } = useColorScheme() 10 | 11 | useLayoutEffect(() => { 12 | if (scheme !== forceScheme && setScheme) { 13 | console.count('Force syncing scheme') 14 | setScheme(forceScheme) 15 | } 16 | }, [scheme, forceScheme, setScheme]) 17 | 18 | return null 19 | } 20 | 21 | export default memo(SyncColorScheme) 22 | -------------------------------------------------------------------------------- /examples/next-dynamic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-dynamic", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "next build", 7 | "dev": "PORT=3004 next dev" 8 | }, 9 | "dependencies": { 10 | "next": "14.1.4", 11 | "next-sanity": "6.1.4", 12 | "react": "18.2.0", 13 | "react-dom": "18.2.0", 14 | "react-icons": "5.0.1", 15 | "react-is": "18.2.0", 16 | "sanity": "3.37.1", 17 | "sanity-plugin-mux-input": "2.3.4", 18 | "styled-components": "6.1.8" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "18.17.11", 22 | "@types/react": "18.2.75", 23 | "typescript": "5.4.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/next-static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-static", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "next build", 7 | "dev": "PORT=3005 next dev" 8 | }, 9 | "dependencies": { 10 | "next": "14.1.4", 11 | "next-sanity": "6.1.4", 12 | "react": "18.2.0", 13 | "react-dom": "18.2.0", 14 | "react-icons": "5.0.1", 15 | "react-is": "18.2.0", 16 | "sanity": "3.37.1", 17 | "sanity-plugin-mux-input": "2.3.4", 18 | "styled-components": "6.1.8" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "18.17.11", 22 | "@types/react": "18.2.75", 23 | "typescript": "5.4.2" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/next-dynamic/sanity.config.ts: -------------------------------------------------------------------------------- 1 | import { createConfig, defaultTheme } from 'sanity' 2 | import { deskTool } from 'sanity/desk' 3 | import { muxInput } from 'sanity-plugin-mux-input' 4 | 5 | import { schemaTypes } from './schemas' 6 | 7 | const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID 8 | const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET 9 | 10 | export default createConfig({ 11 | projectId, 12 | dataset, 13 | // allows reading the default theme variables while the custom theme is loading 14 | theme: defaultTheme, 15 | title: 'Next Dynamic Import Example', 16 | plugins: [deskTool(), muxInput({ mp4_support: 'standard' })], 17 | schema: { types: schemaTypes }, 18 | }) 19 | -------------------------------------------------------------------------------- /apps/v1/utils/expandPresetSearchParams.ts: -------------------------------------------------------------------------------- 1 | import { TONES } from 'utils/colors' 2 | import { roundMidPoint } from 'utils/roundMidPoint' 3 | import { stringifyColorSearchParam } from 'utils/stringifyColorSearchParam' 4 | import type { Hues } from 'utils/types' 5 | 6 | export function expandPresetSearchParams( 7 | searchParams: URLSearchParams, 8 | hues: Hues, 9 | ) { 10 | for (const tone of TONES) { 11 | const hue = hues[tone] 12 | searchParams.set( 13 | tone, 14 | `${stringifyColorSearchParam(hue.mid)};${roundMidPoint( 15 | hue.midPoint, 16 | )};lightest:${stringifyColorSearchParam( 17 | hue.lightest, 18 | )};darkest:${stringifyColorSearchParam(hue.darkest)}`, 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "v2", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "sanity build", 7 | "dev": "sanity dev --port 3000" 8 | }, 9 | "dependencies": { 10 | "@sanity/icons": "2.11.7", 11 | "@sanity/ui": "2.1.1", 12 | "motion": "10.17.0", 13 | "react": "18.2.0", 14 | "react-dom": "18.2.0", 15 | "react-icons": "5.0.1", 16 | "react-is": "18.2.0", 17 | "sanity": "3.37.1", 18 | "sanity-plugin-mux-input": "2.3.4", 19 | "styled-components": "6.1.8" 20 | }, 21 | "devDependencies": { 22 | "@sanity/eslint-config-studio": "4.0.0", 23 | "@types/react": "18.2.75", 24 | "eslint": "8.57.0", 25 | "typescript": "5.4.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/basic/README.md: -------------------------------------------------------------------------------- 1 | # Basic example 2 | 3 | Shows the minimum required setup for a Studio v3 install to be able to use `await import()` calls in `sanity.config.ts` to use Themer themes. 4 | 5 | It loads the Verdant theme: 6 | Screenshot 2022-07-22 at 14 15 10 7 | 8 | ## Live example 9 | 10 | https://themer-example-basic.sanity.build 11 | 12 | It only lets you see the login screen in the theme, to see the full studio: 13 | 14 | 1. Create a new project: https://www.sanity.io/get-started 15 | 2. Fork this repo/path. 16 | 3. Modify the `projectId` and `dataset` in `sanity.config.ts`. 17 | 4. `npm start` and open `http://localhost:3333`. 18 | -------------------------------------------------------------------------------- /examples/advanced/README.md: -------------------------------------------------------------------------------- 1 | # Advanced example 2 | 3 | Shows how to use `createTheme` and using `_document.tsx` to add a `` to speed up the load times for the custom theme. 4 | 5 | It loads a custom theme: 6 | Screenshot 2022-07-22 at 14 05 01 7 | 8 | ## Live example 9 | 10 | https://themer-example-advanced.sanity.build 11 | 12 | It only lets you see the login screen in the theme, to see the full studio: 13 | 14 | 1. Create a new project: https://www.sanity.io/get-started 15 | 2. Fork this repo/path. 16 | 3. Modify the `projectId` and `dataset` in `sanity.config.ts`. 17 | 4. `npm start` and open `http://localhost:3333`. 18 | -------------------------------------------------------------------------------- /examples/next-dynamic/README.md: -------------------------------------------------------------------------------- 1 | # Next dynamic import example 2 | 3 | Shows how to make Next.js load custom themes on-demand, opening up a new world of possibilities in Studio customization. 4 | 5 | It loads the Dew preset: 6 | Screenshot 2022-07-22 at 14 11 35 7 | 8 | ## Live example 9 | 10 | https://themer-example-next-dynamic.sanity.build 11 | 12 | It only lets you see the login screen in the theme, to see the full studio: 13 | 14 | 1. Create a new project: https://www.sanity.io/get-started 15 | 2. Fork this repo/path. 16 | 3. Modify the `projectId` and `dataset` in `sanity.config.ts`. 17 | 4. `npm run dev` and open `http://localhost:3000`. 18 | -------------------------------------------------------------------------------- /examples/next-static/sanity.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createTheme, 3 | hues, 4 | } from 'https://themer.sanity.build/api/hues?preset=pink-synth' 5 | import { createConfig } from 'sanity' 6 | import { deskTool } from 'sanity/desk' 7 | import { muxInput } from 'sanity-plugin-mux-input' 8 | 9 | import { schemaTypes } from './schemas' 10 | 11 | const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID 12 | const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET 13 | 14 | export default createConfig({ 15 | projectId, 16 | dataset, 17 | theme: createTheme({ 18 | ...hues, 19 | default: { ...hues.default, lightest: '#ece3e9' }, 20 | }), 21 | title: 'Next Static Import Example', 22 | plugins: [deskTool(), muxInput()], 23 | schema: { types: schemaTypes }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/v1/components/CopySnippetButton.tsx: -------------------------------------------------------------------------------- 1 | import { ClipboardIcon } from '@sanity/icons' 2 | import { useToast } from '@sanity/ui' 3 | import { Button } from 'components/Sidebar.styles' 4 | import { type ReactNode } from 'react' 5 | 6 | interface Props { 7 | text: ReactNode 8 | code: string 9 | toastTitle: string 10 | } 11 | export default function CopySnippetButton({ text, code, toastTitle }: Props) { 12 | const { push: pushToast } = useToast() 13 | 14 | return ( 15 |