├── 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 |
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 |
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 |
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 |