Foundation + Schemas
This commit is contained in:
@@ -2,13 +2,22 @@ import { defineConfig, fontProviders } from 'astro/config'
|
|||||||
|
|
||||||
import keystatic from '@keystatic/astro'
|
import keystatic from '@keystatic/astro'
|
||||||
|
|
||||||
import markdoc from '@astrojs/markdoc'
|
|
||||||
import node from '@astrojs/node'
|
import node from '@astrojs/node'
|
||||||
import react from '@astrojs/react'
|
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({
|
export default defineConfig({
|
||||||
site: 'https://dave-dmg.de',
|
site: 'https://dave-dmg.de',
|
||||||
integrations: [react(), markdoc(), keystatic()],
|
integrations: [react(), devOnly(keystatic())],
|
||||||
|
|
||||||
adapter: node({
|
adapter: node({
|
||||||
mode: 'standalone',
|
mode: 'standalone',
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { config } from '@keystatic/core'
|
import { config } from '@keystatic/core'
|
||||||
|
|
||||||
|
import { contentCollections } from '@/keystatic/collections/general'
|
||||||
|
|
||||||
export default config({
|
export default config({
|
||||||
storage: {
|
storage: {
|
||||||
kind: 'local',
|
kind: 'local',
|
||||||
},
|
},
|
||||||
collections: {},
|
collections: {
|
||||||
|
...contentCollections,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
"preview": "astro preview"
|
"preview": "astro preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/markdoc": "^2.0.1",
|
|
||||||
"@astrojs/node": "^11.0.0",
|
"@astrojs/node": "^11.0.0",
|
||||||
"@astrojs/react": "^6.0.0",
|
"@astrojs/react": "^6.0.0",
|
||||||
"@keystatic/astro": "^6.0.0",
|
"@keystatic/astro": "^6.0.0",
|
||||||
"@keystatic/core": "^0.6.7",
|
"@keystatic/core": "^0.6.7",
|
||||||
|
"@markdoc/markdoc": "^0.5.10",
|
||||||
"astro": "^7.0.4",
|
"astro": "^7.0.4",
|
||||||
"react": "^19.2.8",
|
"react": "^19.2.8",
|
||||||
"react-dom": "^19.2.8"
|
"react-dom": "^19.2.8"
|
||||||
|
|||||||
76
pnpm-lock.yaml
generated
76
pnpm-lock.yaml
generated
@@ -11,9 +11,6 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
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':
|
'@astrojs/node':
|
||||||
specifier: ^11.0.0
|
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))
|
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':
|
'@keystatic/core':
|
||||||
specifier: ^0.6.7
|
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)
|
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:
|
astro:
|
||||||
specifier: ^7.0.4
|
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)
|
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:
|
prettier-plugin-astro:
|
||||||
optional: true
|
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':
|
'@astrojs/markdown-satteri@0.4.1':
|
||||||
resolution: {integrity: sha512-EniHbFNa6SQHDih7JnpPos3NWXO6hdt4raBcOosfGmlfc3gP9CI/sESA3VOf1PgJZ7SFfewlZW9Livn0JugEZg==}
|
resolution: {integrity: sha512-EniHbFNa6SQHDih7JnpPos3NWXO6hdt4raBcOosfGmlfc3gP9CI/sESA3VOf1PgJZ7SFfewlZW9Livn0JugEZg==}
|
||||||
|
|
||||||
@@ -1694,32 +1688,16 @@ packages:
|
|||||||
dom-serializer@2.0.0:
|
dom-serializer@2.0.0:
|
||||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
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:
|
domelementtype@2.3.0:
|
||||||
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
|
||||||
|
|
||||||
domelementtype@3.0.0:
|
|
||||||
resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==}
|
|
||||||
engines: {node: '>=20.19.0'}
|
|
||||||
|
|
||||||
domhandler@5.0.3:
|
domhandler@5.0.3:
|
||||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
domhandler@6.0.1:
|
|
||||||
resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==}
|
|
||||||
engines: {node: '>=20.19.0'}
|
|
||||||
|
|
||||||
domutils@3.2.2:
|
domutils@3.2.2:
|
||||||
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
|
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:
|
dset@3.1.4:
|
||||||
resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
|
resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -1750,10 +1728,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||||
engines: {node: '>=0.12'}
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
entities@8.1.0:
|
|
||||||
resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==}
|
|
||||||
engines: {node: '>=20.19.0'}
|
|
||||||
|
|
||||||
env-paths@2.2.1:
|
env-paths@2.2.1:
|
||||||
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2073,10 +2047,6 @@ packages:
|
|||||||
html-void-elements@3.0.0:
|
html-void-elements@3.0.0:
|
||||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||||
|
|
||||||
htmlparser2@12.0.0:
|
|
||||||
resolution: {integrity: sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==}
|
|
||||||
engines: {node: '>=20.19.0'}
|
|
||||||
|
|
||||||
htmlparser2@9.1.0:
|
htmlparser2@9.1.0:
|
||||||
resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==}
|
resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==}
|
||||||
|
|
||||||
@@ -3658,19 +3628,6 @@ snapshots:
|
|||||||
- '@emnapi/runtime'
|
- '@emnapi/runtime'
|
||||||
- typescript
|
- 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':
|
'@astrojs/markdown-satteri@0.4.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/internal-helpers': 0.11.0
|
'@astrojs/internal-helpers': 0.11.0
|
||||||
@@ -5212,36 +5169,18 @@ snapshots:
|
|||||||
domhandler: 5.0.3
|
domhandler: 5.0.3
|
||||||
entities: 4.5.0
|
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@2.3.0: {}
|
||||||
|
|
||||||
domelementtype@3.0.0: {}
|
|
||||||
|
|
||||||
domhandler@5.0.3:
|
domhandler@5.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 2.3.0
|
domelementtype: 2.3.0
|
||||||
|
|
||||||
domhandler@6.0.1:
|
|
||||||
dependencies:
|
|
||||||
domelementtype: 3.0.0
|
|
||||||
|
|
||||||
domutils@3.2.2:
|
domutils@3.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
dom-serializer: 2.0.0
|
dom-serializer: 2.0.0
|
||||||
domelementtype: 2.3.0
|
domelementtype: 2.3.0
|
||||||
domhandler: 5.0.3
|
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: {}
|
dset@3.1.4: {}
|
||||||
|
|
||||||
ee-first@1.1.1: {}
|
ee-first@1.1.1: {}
|
||||||
@@ -5263,8 +5202,6 @@ snapshots:
|
|||||||
|
|
||||||
entities@4.5.0: {}
|
entities@4.5.0: {}
|
||||||
|
|
||||||
entities@8.1.0: {}
|
|
||||||
|
|
||||||
env-paths@2.2.1: {}
|
env-paths@2.2.1: {}
|
||||||
|
|
||||||
error-ex@1.3.4:
|
error-ex@1.3.4:
|
||||||
@@ -5613,13 +5550,6 @@ snapshots:
|
|||||||
|
|
||||||
html-void-elements@3.0.0: {}
|
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:
|
htmlparser2@9.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype: 2.3.0
|
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",
|
"jsxImportSource": "react",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"],
|
||||||
|
"~/*": ["./*"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user