An Open-Source Content Management System based on Git workflows with a friendly file-editing GUI.
The GitHub UI offers a lot of functionalities which can be overwhelming with users not familiar with Git. This software offers an abstraction on top of GitHub to allow easy creation of forks, PRs, and commits on behalf of the user. On top of that, it offers an automatically rendered form GUI (based on provided JSON schemas) which makes the creation of files easier.
As soon as the user starts a new "Session" (PR), a fork for the target repository is automatically created for the user if not yet present. It also creates an empty commit and a PR for the user. As soon as the user creates/changes files, these are "saved" as commits. Then, the user can mark a session as ready (turn the draft PR into an open PR), and receive feedback regarding failing pipelines or requested changes on the PR.
The configuration for git-clerk is done via the config file. In its most basic form, the config looks like this:
globalThis.ghConfig = {
githubRepo: "my-user/my-repo", // the target repository
githubAuthToken: () =>
new Promise((resolve) => resolve("ghp_myGithubAuthToken")), // an (async) function that returns the GitHub token for the authenticated user
};
With the schemaMap
, one can configure which file paths are associated with which JSON schemas to automatically render input forms.
globalThis.schemaMap = [
{
path: "/folder-a/file.json",
schema: {
title: "my-schema",
type: "object",
properties: {
foo: {
type: "string"
}
}
}
},
{
path: "/folder-a/<id>/file.json",
url: "https://my-schema-site.com/schemas/folder-a/schema.json",
},
[...]
]
In this example, editing the file /folder-a/file.json
loads the corresponding JSON schema passed via the schema
property, whereas editing a file with the pattern /folder-a/<id>/file.json
loads the corresponding JSON schema https://my-schema-site.com/schemas/folder-a/schema.json
.
Additionally to providing a JSON schema, the form can also be autofilled with initial content:
globalThis.schemaMap = [
{
path: "/folder-a/file.json",
schema: {
title: "my-schema",
type: "object",
properties: {
foo: {
type: "string"
}
}
},
content: {
foo: "bar"
}
},
[...]
]
In this example, the generated form will automatically fill the value "bar" into the foo
field.
By default, the entire JSON structure is stored (commited) as a JSON file. But one can also specify a specific property which should be stored in the file instead. This is handy when e.g. editing markdown or yaml files:
globalThis.schemaMap = [
{
path: "/folder-a/file.md",
schema: {
title: "my-schema",
type: "object",
properties: {
foo: {
type: "string",
format: "markdown"
}
}
},
output: "foo"
},
[...]
]
In this example, the markdown content of foo
is commited as file file.md
.
If a preview
is defined, then the editor GUI only takes half of the screen, and the other half renders the referenced preview HTML inside an iframe. Whenever the editing form updates, it sends a postMessage
including a CustomEvent
to the preview iframe with the event.type
of SCHEMA_DATA_EDITOR_UPDATE
and the event.detail
being the JSON output of the editor. The preview iframe can also emit a CustomEvent
to the parent with the event.type
of SCHEMA_DATA_PREVIEW_UPDATE
, which then updates the editor (in event.detail
it expects the JSON value that matches the currently loaded JSON schema).
An example for this setup can be seen in here.
Sometimes, multiple edits need to be done together (e.g. creating a file in the correct folder structure, putting initial content in that file, and referencing this file in a third file). To make users' lives easier, git-clerk offers automations:
globalThis.automation = [
{
title: "Bootstrap Product", // displayed in the UI as button
description: "Bootstrap a new file with the correct folder structure and ID.", // displayed in the UI as description for this automation
inputSchema: { // the input form when the user selects this automation
type: "object",
properties: {
id: {
type: "string",
minLength: 1
},
title: {
type: "string",
minLength: 1
}
},
required: ["id", "title"]
},
steps: [ // the steps the automation will perform
{
type: "add", // add a file
path: (input) => `/products/${input.id}/collection.json`, // references the above defined form output property "id"
content: (input) => ({ id: input.id, title: input.title }) // sets the content of the added file, referencing "id" and "title" from the form
},
{
type: "edit", // edits an existing file
path: "/products/catalog.json",
transform: (content, input) => { // transforms the content of an existing file
content.links = [
...content.links,
{
rel: "child",
href: `./${input.id}/collection.json`,
type: "application/json",
title: input.title
},
]
return content
}
},
{
type: "navigate", // navigates to the specified file and opens the editing view
path: (input) => `/products/${input.id}/collection.json`
}
]
},
[...]
]
VSCode + Volar (and disable Vetur).
See Vite Configuration Reference.
npm install
npm run dev
npm run build