Foundation + Schemas
This commit is contained in:
@@ -2,13 +2,22 @@ import { defineConfig, fontProviders } from 'astro/config'
|
||||
|
||||
import keystatic from '@keystatic/astro'
|
||||
|
||||
import markdoc from '@astrojs/markdoc'
|
||||
import node from '@astrojs/node'
|
||||
import react from '@astrojs/react'
|
||||
|
||||
const devOnly = (integration) => ({
|
||||
name: `${integration.name}:dev-only`,
|
||||
hooks: {
|
||||
'astro:config:setup': async (params) => {
|
||||
if (params.command !== 'dev') return
|
||||
await integration.hooks['astro:config:setup']?.(params)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://dave-dmg.de',
|
||||
integrations: [react(), markdoc(), keystatic()],
|
||||
integrations: [react(), devOnly(keystatic())],
|
||||
|
||||
adapter: node({
|
||||
mode: 'standalone',
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { config } from '@keystatic/core'
|
||||
|
||||
import { contentCollections } from '@/keystatic/collections/general'
|
||||
|
||||
export default config({
|
||||
storage: {
|
||||
kind: 'local',
|
||||
},
|
||||
collections: {},
|
||||
collections: {
|
||||
...contentCollections,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
"preview": "astro preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/markdoc": "^2.0.1",
|
||||
"@astrojs/node": "^11.0.0",
|
||||
"@astrojs/react": "^6.0.0",
|
||||
"@keystatic/astro": "^6.0.0",
|
||||
"@keystatic/core": "^0.6.7",
|
||||
"@markdoc/markdoc": "^0.5.10",
|
||||
"astro": "^7.0.4",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8"
|
||||
|
||||
76
pnpm-lock.yaml
generated
76
pnpm-lock.yaml
generated
@@ -11,9 +11,6 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@astrojs/markdoc':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.9(@types/react@19.3.0)(astro@7.3.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(idb-keyval@6.3.0)(sugarss@5.0.1(postcss@8.5.28))(yaml@2.9.1))(react@19.3.0)
|
||||
'@astrojs/node':
|
||||
specifier: ^11.0.0
|
||||
version: 11.1.6(astro@7.3.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(idb-keyval@6.3.0)(sugarss@5.0.1(postcss@8.5.28))(yaml@2.9.1))
|
||||
@@ -26,6 +23,9 @@ importers:
|
||||
'@keystatic/core':
|
||||
specifier: ^0.6.7
|
||||
version: 0.6.9(@keystar/ui@0.10.0(react-aria@3.50.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react-dom@19.3.0(react@19.3.0))(react-stately@3.48.0(react@19.3.0))(react@19.3.0))(react-aria@3.50.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react-dom@19.3.0(react@19.3.0))(react-stately@3.48.0(react@19.3.0))(react@19.3.0)
|
||||
'@markdoc/markdoc':
|
||||
specifier: ^0.5.10
|
||||
version: 0.5.10(@types/react@19.3.0)(react@19.3.0)
|
||||
astro:
|
||||
specifier: ^7.0.4
|
||||
version: 7.3.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(idb-keyval@6.3.0)(sugarss@5.0.1(postcss@8.5.28))(yaml@2.9.1)
|
||||
@@ -208,12 +208,6 @@ packages:
|
||||
prettier-plugin-astro:
|
||||
optional: true
|
||||
|
||||
'@astrojs/markdoc@2.0.9':
|
||||
resolution: {integrity: sha512-1w9Zg9czdfZXjBx9xv/eUy+65cOEr5AG6nsD36ZF3Eytir5Uip/0WpALLdOg6uN35wNbws+ZaibH5aAdC/6Jyg==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
peerDependencies:
|
||||
astro: ^7.0.0
|
||||
|
||||
'@astrojs/markdown-satteri@0.4.1':
|
||||
resolution: {integrity: sha512-EniHbFNa6SQHDih7JnpPos3NWXO6hdt4raBcOosfGmlfc3gP9CI/sESA3VOf1PgJZ7SFfewlZW9Livn0JugEZg==}
|
||||
|
||||
@@ -1694,32 +1688,16 @@ packages:
|
||||
dom-serializer@2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||
|
||||
dom-serializer@3.1.1:
|
||||
resolution: {integrity: sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
domelementtype@2.3.0:
|
||||
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||
|
||||
domelementtype@3.0.0:
|
||||
resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
domhandler@5.0.3:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
domhandler@6.0.1:
|
||||
resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
domutils@3.2.2:
|
||||
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
|
||||
|
||||
domutils@4.0.2:
|
||||
resolution: {integrity: sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
dset@3.1.4:
|
||||
resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -1750,10 +1728,6 @@ packages:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
entities@8.1.0:
|
||||
resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
env-paths@2.2.1:
|
||||
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -2073,10 +2047,6 @@ packages:
|
||||
html-void-elements@3.0.0:
|
||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||
|
||||
htmlparser2@12.0.0:
|
||||
resolution: {integrity: sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
|
||||
htmlparser2@9.1.0:
|
||||
resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==}
|
||||
|
||||
@@ -3658,19 +3628,6 @@ snapshots:
|
||||
- '@emnapi/runtime'
|
||||
- typescript
|
||||
|
||||
'@astrojs/markdoc@2.0.9(@types/react@19.3.0)(astro@7.3.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(idb-keyval@6.3.0)(sugarss@5.0.1(postcss@8.5.28))(yaml@2.9.1))(react@19.3.0)':
|
||||
dependencies:
|
||||
'@astrojs/internal-helpers': 0.11.0
|
||||
'@astrojs/prism': 4.0.2
|
||||
'@markdoc/markdoc': 0.5.10(@types/react@19.3.0)(react@19.3.0)
|
||||
astro: 7.3.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(idb-keyval@6.3.0)(sugarss@5.0.1(postcss@8.5.28))(yaml@2.9.1)
|
||||
esbuild: 0.28.2
|
||||
github-slugger: 2.0.0
|
||||
htmlparser2: 12.0.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- react
|
||||
|
||||
'@astrojs/markdown-satteri@0.4.1':
|
||||
dependencies:
|
||||
'@astrojs/internal-helpers': 0.11.0
|
||||
@@ -5212,36 +5169,18 @@ snapshots:
|
||||
domhandler: 5.0.3
|
||||
entities: 4.5.0
|
||||
|
||||
dom-serializer@3.1.1:
|
||||
dependencies:
|
||||
domelementtype: 3.0.0
|
||||
domhandler: 6.0.1
|
||||
entities: 8.1.0
|
||||
|
||||
domelementtype@2.3.0: {}
|
||||
|
||||
domelementtype@3.0.0: {}
|
||||
|
||||
domhandler@5.0.3:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
domhandler@6.0.1:
|
||||
dependencies:
|
||||
domelementtype: 3.0.0
|
||||
|
||||
domutils@3.2.2:
|
||||
dependencies:
|
||||
dom-serializer: 2.0.0
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
|
||||
domutils@4.0.2:
|
||||
dependencies:
|
||||
dom-serializer: 3.1.1
|
||||
domelementtype: 3.0.0
|
||||
domhandler: 6.0.1
|
||||
|
||||
dset@3.1.4: {}
|
||||
|
||||
ee-first@1.1.1: {}
|
||||
@@ -5263,8 +5202,6 @@ snapshots:
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
entities@8.1.0: {}
|
||||
|
||||
env-paths@2.2.1: {}
|
||||
|
||||
error-ex@1.3.4:
|
||||
@@ -5613,13 +5550,6 @@ snapshots:
|
||||
|
||||
html-void-elements@3.0.0: {}
|
||||
|
||||
htmlparser2@12.0.0:
|
||||
dependencies:
|
||||
domelementtype: 3.0.0
|
||||
domhandler: 6.0.1
|
||||
domutils: 4.0.2
|
||||
entities: 8.1.0
|
||||
|
||||
htmlparser2@9.1.0:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export const collections = {}
|
||||
12
src/content/general/articles/prima-materia.mdoc
Normal file
12
src/content/general/articles/prima-materia.mdoc
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Prima Materia
|
||||
status: draft
|
||||
noindex: false
|
||||
route:
|
||||
parent: stages
|
||||
summary:
|
||||
cover: {}
|
||||
dates:
|
||||
published: 2026-09-21T12:35:00.000Z
|
||||
tags: []
|
||||
---
|
||||
12
src/content/general/articles/stages.mdoc
Normal file
12
src/content/general/articles/stages.mdoc
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Stages
|
||||
status: draft
|
||||
noindex: false
|
||||
route:
|
||||
parent: the-crucible
|
||||
summary:
|
||||
cover: {}
|
||||
dates:
|
||||
published: 2026-09-21T12:34:00.000Z
|
||||
tags: []
|
||||
---
|
||||
15
src/content/general/articles/the-crucible.mdoc
Normal file
15
src/content/general/articles/the-crucible.mdoc
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: The Crucible
|
||||
status: draft
|
||||
noindex: false
|
||||
route: {}
|
||||
summary:
|
||||
excerpt: >-
|
||||
Where we explore the crucible – a materialistic alchemist Worldbuilding
|
||||
Framework
|
||||
cover: {}
|
||||
dates:
|
||||
published: 2026-09-21T12:33:00.000Z
|
||||
tags:
|
||||
- the-crucible
|
||||
---
|
||||
7
src/content/general/pages/imprint.mdoc
Normal file
7
src/content/general/pages/imprint.mdoc
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Imprint
|
||||
status: draft
|
||||
noindex: false
|
||||
route: {}
|
||||
summary: {}
|
||||
---
|
||||
4
src/content/general/tags/the-crucible.json
Normal file
4
src/content/general/tags/the-crucible.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "The Crucible",
|
||||
"description": "I AM JUST A ITSY-BITSY TEENY-WEENIE TEST"
|
||||
}
|
||||
30
src/keystatic/collections/general/articles.ts
Normal file
30
src/keystatic/collections/general/articles.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { collection, fields } from '@keystatic/core'
|
||||
|
||||
import { base } from '@/keystatic/fields/content/base'
|
||||
import { content } from '@/keystatic/fields/content/content'
|
||||
import { dates } from '@/keystatic/fields/content/dates'
|
||||
import { articleRoute } from '@/keystatic/fields/content/route'
|
||||
import { articleSummary } from '@/keystatic/fields/content/summary'
|
||||
import { ARTICLES, TAGS } from '@/lib/constants/collections'
|
||||
|
||||
export const articles = collection({
|
||||
label: 'Articles',
|
||||
slugField: 'title',
|
||||
path: `src/content/general/${ARTICLES}/*`,
|
||||
format: {
|
||||
contentField: 'content',
|
||||
},
|
||||
entryLayout: 'content',
|
||||
columns: ['title', 'status'],
|
||||
schema: {
|
||||
...base,
|
||||
route: articleRoute,
|
||||
summary: articleSummary(ARTICLES),
|
||||
dates,
|
||||
tags: fields.multiRelationship({
|
||||
label: 'Tags',
|
||||
collection: TAGS,
|
||||
}),
|
||||
content: content(ARTICLES),
|
||||
},
|
||||
})
|
||||
11
src/keystatic/collections/general/index.ts
Normal file
11
src/keystatic/collections/general/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ARTICLES, PAGES, TAGS } from '@/lib/constants/collections'
|
||||
|
||||
import { articles } from './articles'
|
||||
import { pages } from './pages'
|
||||
import { tags } from './tags'
|
||||
|
||||
export const contentCollections = {
|
||||
[ARTICLES]: articles,
|
||||
[PAGES]: pages,
|
||||
[TAGS]: tags,
|
||||
}
|
||||
23
src/keystatic/collections/general/pages.ts
Normal file
23
src/keystatic/collections/general/pages.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { collection } from '@keystatic/core'
|
||||
|
||||
import { base } from '@/keystatic/fields/content/base'
|
||||
import { content } from '@/keystatic/fields/content/content'
|
||||
import { pageRoute } from '@/keystatic/fields/content/route'
|
||||
import { pageSummary } from '@/keystatic/fields/content/summary'
|
||||
import { PAGES } from '@/lib/constants/collections'
|
||||
|
||||
export const pages = collection({
|
||||
label: 'Pages',
|
||||
slugField: 'title',
|
||||
path: `src/content/general/${PAGES}/*`,
|
||||
format: {
|
||||
contentField: 'content',
|
||||
},
|
||||
columns: ['title', 'status'],
|
||||
schema: {
|
||||
...base,
|
||||
route: pageRoute,
|
||||
summary: pageSummary,
|
||||
content: content(PAGES),
|
||||
},
|
||||
})
|
||||
21
src/keystatic/collections/general/tags.ts
Normal file
21
src/keystatic/collections/general/tags.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { collection, fields } from '@keystatic/core'
|
||||
|
||||
import { title } from '@/keystatic/fields/content/base'
|
||||
import { TAGS } from '@/lib/constants/collections'
|
||||
|
||||
export const tags = collection({
|
||||
label: 'Tags',
|
||||
slugField: 'title',
|
||||
path: `src/content/general/${TAGS}/*`,
|
||||
format: {
|
||||
data: 'json',
|
||||
},
|
||||
columns: ['title'],
|
||||
schema: {
|
||||
title,
|
||||
description: fields.text({
|
||||
label: 'Description',
|
||||
multiline: true,
|
||||
}),
|
||||
},
|
||||
})
|
||||
40
src/keystatic/fields/content/base.ts
Normal file
40
src/keystatic/fields/content/base.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { fields } from '@keystatic/core'
|
||||
|
||||
import { STATUS_OPTIONS } from '@/lib/constants/status'
|
||||
import { SEGMENT, SEGMENT_MESSAGE, slugify } from '@/lib/content/slug'
|
||||
|
||||
export const title = fields.slug({
|
||||
name: {
|
||||
label: 'Title',
|
||||
validation: {
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
slug: {
|
||||
label: 'File ID',
|
||||
description: 'Internal and unique across the collection.',
|
||||
generate: slugify,
|
||||
validation: {
|
||||
pattern: {
|
||||
regex: SEGMENT,
|
||||
message: SEGMENT_MESSAGE,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const status = fields.select({
|
||||
label: 'Status',
|
||||
options: STATUS_OPTIONS,
|
||||
defaultValue: 'draft',
|
||||
})
|
||||
|
||||
export const noindex = fields.checkbox({
|
||||
label: 'Hide from search engines',
|
||||
})
|
||||
|
||||
export const base = {
|
||||
title,
|
||||
status,
|
||||
noindex,
|
||||
}
|
||||
12
src/keystatic/fields/content/content.ts
Normal file
12
src/keystatic/fields/content/content.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { fields } from '@keystatic/core'
|
||||
|
||||
import { assets } from '@/lib/content/assets'
|
||||
|
||||
export const content = (collection: string) =>
|
||||
fields.markdoc({
|
||||
label: 'Content',
|
||||
options: {
|
||||
heading: [2, 3, 4],
|
||||
image: assets(collection),
|
||||
},
|
||||
})
|
||||
25
src/keystatic/fields/content/cover.ts
Normal file
25
src/keystatic/fields/content/cover.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { fields } from '@keystatic/core'
|
||||
|
||||
import { assets } from '@/lib/content/assets'
|
||||
|
||||
export const cover = (collection: string) =>
|
||||
fields.object(
|
||||
{
|
||||
image: fields.image({
|
||||
label: 'Image',
|
||||
...assets(collection),
|
||||
}),
|
||||
alt: fields.text({
|
||||
label: 'Alt text',
|
||||
}),
|
||||
caption: fields.text({
|
||||
label: 'Caption',
|
||||
}),
|
||||
credit: fields.text({
|
||||
label: 'Credit',
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Cover',
|
||||
},
|
||||
)
|
||||
18
src/keystatic/fields/content/dates.ts
Normal file
18
src/keystatic/fields/content/dates.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { fields } from '@keystatic/core'
|
||||
|
||||
export const dates = fields.object(
|
||||
{
|
||||
published: fields.datetime({
|
||||
label: 'Published',
|
||||
defaultValue: {
|
||||
kind: 'now',
|
||||
},
|
||||
}),
|
||||
updated: fields.datetime({
|
||||
label: 'Updated',
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Dates',
|
||||
},
|
||||
)
|
||||
42
src/keystatic/fields/content/route.ts
Normal file
42
src/keystatic/fields/content/route.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { fields } from '@keystatic/core'
|
||||
|
||||
import { ARTICLES } from '@/lib/constants/collections'
|
||||
import { OPTIONAL_SEGMENT, SEGMENT_MESSAGE } from '@/lib/content/slug'
|
||||
|
||||
const segment = fields.text({
|
||||
label: 'URL segment',
|
||||
description: 'Optional- Leave empty to use the slugified title',
|
||||
validation: {
|
||||
pattern: {
|
||||
regex: OPTIONAL_SEGMENT,
|
||||
message: SEGMENT_MESSAGE,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const pageRoute = fields.object(
|
||||
{
|
||||
segment,
|
||||
},
|
||||
{
|
||||
label: 'Route',
|
||||
},
|
||||
)
|
||||
|
||||
export const articleRoute = fields.object(
|
||||
{
|
||||
segment,
|
||||
parent: fields.relationship({
|
||||
label: 'Parent',
|
||||
description: 'Empty makes this a root',
|
||||
collection: ARTICLES,
|
||||
}),
|
||||
index: fields.integer({
|
||||
label: 'Index',
|
||||
description: 'Sorts among siblings. Empty sorts by date, newest first',
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Route',
|
||||
},
|
||||
)
|
||||
33
src/keystatic/fields/content/summary.ts
Normal file
33
src/keystatic/fields/content/summary.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { fields } from '@keystatic/core'
|
||||
|
||||
import { cover } from './cover'
|
||||
|
||||
const excerpt = fields.text({
|
||||
label: 'Excerpt',
|
||||
multiline: true,
|
||||
validation: {
|
||||
length: {
|
||||
max: 160,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const pageSummary = fields.object(
|
||||
{
|
||||
excerpt,
|
||||
},
|
||||
{
|
||||
label: 'Summary',
|
||||
},
|
||||
)
|
||||
|
||||
export const articleSummary = (collection: string) =>
|
||||
fields.object(
|
||||
{
|
||||
excerpt,
|
||||
cover: cover(collection),
|
||||
},
|
||||
{
|
||||
label: 'Summary',
|
||||
},
|
||||
)
|
||||
5
src/lib/constants/collections.ts
Normal file
5
src/lib/constants/collections.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/* === CONTENT COLLECTIONS === */
|
||||
|
||||
export const ARTICLES = 'articles'
|
||||
export const PAGES = 'pages'
|
||||
export const TAGS = 'tags'
|
||||
20
src/lib/constants/status.ts
Normal file
20
src/lib/constants/status.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export const STATUS_OPTIONS = [
|
||||
{
|
||||
label: 'Draft',
|
||||
value: 'draft',
|
||||
},
|
||||
{
|
||||
label: 'Published',
|
||||
value: 'published',
|
||||
},
|
||||
{
|
||||
label: 'Unlisted',
|
||||
value: 'unlisted',
|
||||
},
|
||||
{
|
||||
label: 'Archived',
|
||||
value: 'archived',
|
||||
},
|
||||
] as const
|
||||
|
||||
export type Status = (typeof STATUS_OPTIONS)[number]['value']
|
||||
6
src/lib/content/assets.ts
Normal file
6
src/lib/content/assets.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const ASSET_ROOT = 'src/assets/content'
|
||||
|
||||
export const assets = (collection: string) => ({
|
||||
directory: `${ASSET_ROOT}/${collection}`,
|
||||
publicPath: `/${ASSET_ROOT}/${collection}/`,
|
||||
})
|
||||
34
src/lib/content/slug.ts
Normal file
34
src/lib/content/slug.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
export const SEGMENT = /^[a-z0-9][a-z0-9_-]*$/
|
||||
export const OPTIONAL_SEGMENT = /^([a-z0-9][a-z0-9_-]*)?$/
|
||||
export const SEGMENT_MESSAGE = 'Lowercase letters, digits, - and _'
|
||||
|
||||
const UMLAUTS: Record<string, string> = {
|
||||
ä: 'ae',
|
||||
ö: 'oe',
|
||||
ü: 'ue',
|
||||
ß: 'ss',
|
||||
Ä: 'ae',
|
||||
Ö: 'oe',
|
||||
Ü: 'ue',
|
||||
å: 'aa',
|
||||
æ: 'ae',
|
||||
ø: 'oe',
|
||||
Å: 'aa',
|
||||
Æ: 'ae',
|
||||
Ø: 'oe',
|
||||
}
|
||||
|
||||
export const slugify = (text: string): string => {
|
||||
return text
|
||||
.split('')
|
||||
.map((char) => UMLAUTS[char] || char)
|
||||
.join('')
|
||||
.normalize('NFKD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9 _-]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/^[-_]+|[-_]+$/g, '')
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
"jsxImportSource": "react",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"~/*": ["./*"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user