@rigel-cms/rigel 1.4.0-rc.1 → 1.4.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +4 -2
  2. package/docs/README.md +28 -0
  3. package/docs/authors.md +133 -0
  4. package/docs/content-locales.md +12 -3
  5. package/docs/content-model.md +131 -0
  6. package/docs/getting-started.md +139 -0
  7. package/docs/github.md +112 -0
  8. package/docs/media.md +75 -0
  9. package/docs/menus.md +95 -0
  10. package/docs/preview-hosts.md +78 -0
  11. package/docs/preview.md +100 -0
  12. package/docs/seo.md +87 -0
  13. package/docs/sidebar.md +97 -0
  14. package/package.json +2 -3
  15. package/src/article-render.ts +12 -5
  16. package/src/config.ts +5 -0
  17. package/src/editor/App.svelte +67 -21
  18. package/src/editor/i18n/de/fields.json +2 -0
  19. package/src/editor/i18n/de/shell.json +8 -2
  20. package/src/editor/i18n/en/fields.json +2 -0
  21. package/src/editor/i18n/en/shell.json +8 -2
  22. package/src/editor/i18n/es/fields.json +2 -0
  23. package/src/editor/i18n/es/shell.json +8 -2
  24. package/src/editor/i18n/fr/fields.json +2 -0
  25. package/src/editor/i18n/fr/shell.json +8 -2
  26. package/src/editor/i18n/it/fields.json +2 -0
  27. package/src/editor/i18n/it/shell.json +8 -2
  28. package/src/editor/i18n/ja/fields.json +2 -0
  29. package/src/editor/i18n/ja/shell.json +8 -2
  30. package/src/editor/i18n/nl/fields.json +2 -0
  31. package/src/editor/i18n/nl/shell.json +8 -2
  32. package/src/editor/i18n/ru/fields.json +2 -0
  33. package/src/editor/i18n/ru/shell.json +8 -2
  34. package/src/editor/i18n/zh/fields.json +2 -0
  35. package/src/editor/i18n/zh/shell.json +8 -2
  36. package/src/editor/lib/BlockEditor.svelte +4 -3
  37. package/src/editor/lib/CollectionPanel.svelte +10 -2
  38. package/src/editor/lib/MarkdownEditor.svelte +38 -5
  39. package/src/editor/lib/PropertiesTabs.svelte +8 -1
  40. package/src/editor/lib/SchemaField.svelte +16 -2
  41. package/src/editor/lib/SchemaPanel.svelte +9 -3
  42. package/src/editor/lib/Settings.svelte +37 -30
  43. package/src/editor/lib/TopBar.svelte +13 -49
  44. package/src/editor/lib/VisualEditor.svelte +64 -13
  45. package/src/editor/lib/markdown-blocks.ts +69 -0
  46. package/src/editor/lib/media-library.ts +2 -1
  47. package/src/editor/lib/settings.ts +2 -0
  48. package/src/editor/styles.css +17 -1
  49. package/src/index.ts +1 -1
  50. package/src/manifest/pages.ts +0 -4
  51. package/src/manifest/search.ts +41 -0
  52. package/src/manifest/types.ts +1 -0
  53. package/src/schema/types.ts +1 -0
  54. package/src/schema/validate.ts +1 -0
  55. package/src/vite/manifest-plugin.ts +2 -1
package/README.md CHANGED
@@ -18,9 +18,11 @@ Rigel edits content, not `.astro` components. Developers define the editable fie
18
18
 
19
19
  ## Get started
20
20
 
21
- Use the [Astro starter](https://github.com/rigel-cms/rigel-starter), or follow the [installation guide](https://rigelcms.dev/docs/getting-started/) to add Rigel to an existing site. Requires Node.js 24 and Astro 7.3 or later within Astro 7.
21
+ Use the [Astro starter](https://github.com/rigel-cms/rigel-starter), or follow the [installation guide](docs/getting-started.md) to add Rigel to an existing site. Requires Node.js 24 and Astro 7.3 or later within Astro 7.
22
22
 
23
- [Documentation](https://rigelcms.dev/docs/) · [Content model](https://rigelcms.dev/docs/content-model/) · [Previews](https://rigelcms.dev/docs/preview/) · [GitHub setup](https://rigelcms.dev/docs/github-publishing/)
23
+ [Documentation](docs/README.md) · [Content model](docs/content-model.md) · [Previews](docs/preview.md) · [GitHub setup](docs/github.md)
24
+
25
+ The guides live in this repository and ship with the npm package. They describe the current source; features on `main` may not yet be in npm's `latest` release. The [documentation website](https://rigelcms.dev/docs/) is also available.
24
26
 
25
27
  ## Content languages
26
28
 
package/docs/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Rigel CMS documentation
2
+
3
+ Rigel edits content stored in an Astro site's repository. Developers define the fields and templates; editors change content at `/rigel/` and press Save. The site owns routing, HTML, styles and deployment.
4
+
5
+ These guides follow the SDK source in this repository. Check your installed version before using features that have not reached npm's `latest` release.
6
+
7
+ ## Start here
8
+
9
+ - [Getting started](getting-started.md): use the starter or add an editable page to an existing Astro site.
10
+ - [Content model](content-model.md): native documents, fields, sections and shared references.
11
+ - [Preview bindings](preview.md): prepare real page templates for unsaved visual editing.
12
+
13
+ ## Configure your site
14
+
15
+ - [Content navigation](sidebar.md): sidebar entries, categories and page tables.
16
+ - [Content languages](content-locales.md): localized types, template selection and search scope.
17
+ - [Menus](menus.md): JSON menu data and your site's navigation renderer.
18
+ - [Media](media.md): uploads, metadata, providers and image pickers.
19
+ - [Authors and references](authors.md): reusable author records and article selectors.
20
+ - [Content and SEO](seo.md): editable metadata and the HTML your templates must render.
21
+ - [Extensions](extensions.md): custom article controls and saved blocks.
22
+
23
+ ## Save and publish
24
+
25
+ - [GitHub connection and publishing](github.md): authentication, draft saves and reviewed publishing.
26
+ - [Live and draft previews](preview-hosts.md): preview mappings and deployment hosts.
27
+
28
+ Content Save and website deployment are separate operations. Local Save writes files without committing them. GitHub Save writes the configured editing branch; publishing follows the configured branch workflow and hosting pipeline.
@@ -0,0 +1,133 @@
1
+ # Authors and references
2
+
3
+ Authors are shared content documents. They describe contributors to your site, independently of the account signed into Rigel or the author label attached to a media upload.
4
+
5
+ ## Declare author documents
6
+
7
+ Add an author type to the site's schema:
8
+
9
+ ```ts
10
+ import { defineDocument, defineSchema } from "@rigel-cms/rigel/content";
11
+
12
+ const authorDocument = defineDocument({
13
+ name: "author",
14
+ title: "Authors",
15
+ type: "document",
16
+ folder: "src/content/authors",
17
+ shared: true,
18
+ creatable: true,
19
+ columns: ["photo", "email"],
20
+ fields: [
21
+ { key: "name", label: "Name", type: "string", required: true },
22
+ { key: "description", label: "Description", type: "text" },
23
+ { key: "photo", label: "Photo", type: "image" },
24
+ { key: "email", label: "Email", type: "string" },
25
+ { key: "url", label: "Website", type: "url" },
26
+ ],
27
+ });
28
+
29
+ export default defineSchema({
30
+ documents: [authorDocument],
31
+ objects: [],
32
+ });
33
+ ```
34
+
35
+ Merge this declaration into your existing schema rather than replacing its document and object lists. Pass that schema to `rigel({ schema })`; see the [content model](content-model.md) and [getting started](getting-started.md).
36
+
37
+ For an initial author, create `src/content/authors/sam.json`:
38
+
39
+ ```json
40
+ {
41
+ "_id": "author:sam",
42
+ "_type": "author",
43
+ "name": "Sam",
44
+ "description": "Editor",
45
+ "photo": "/images/sam.jpg",
46
+ "email": "sam@example.com",
47
+ "url": "https://example.com"
48
+ }
49
+ ```
50
+
51
+ The filename supplies the stable slug. `_id`, when present, must match `author:<slug>`, and `_type` must match the declared document type. Only `name` is required by this schema. Use an existing image URL or omit `photo`.
52
+
53
+ Shared types appear under Shared content. A custom [sidebar](sidebar.md) can link to the author table with `{ kind: "link", target: { kind: "documents", type: "author" }, icon: "users" }`.
54
+
55
+ The table uses names, slugs and configured columns; image columns lead with square thumbnails. Thumbnails leave the original image unchanged, and missing author photos use a placeholder. Shared documents open in a form rather than a website canvas. Add new starts an in-memory draft; Save writes its JSON using the selected backend. Existing records keep explicit Save and unsaved-navigation protection.
56
+
57
+ Set `creatable: false` for a developer-maintained list. Rigel then hides Add new and the content stores reject creation. Existing records remain editable. Document deletion is developer-managed. For nonsingletons, creation is enabled by default.
58
+
59
+ ## Reference an author from native content
60
+
61
+ Native schema fields use an array of allowed document types and store reference objects. Add this field to a document's or object's `fields`:
62
+
63
+ ```ts
64
+ { key: "author", label: "Author", type: "reference", to: ["author"] }
65
+ ```
66
+
67
+ Its saved value is:
68
+
69
+ ```json
70
+ { "author": { "_ref": "author:sam" } }
71
+ ```
72
+
73
+ For several contributors, use an array of references:
74
+
75
+ ```ts
76
+ {
77
+ key: "authors",
78
+ label: "Authors",
79
+ type: "array",
80
+ items: [{ type: "reference", to: ["author"] }],
81
+ }
82
+ ```
83
+
84
+ Each array entry needs its own stable `_key`:
85
+
86
+ ```json
87
+ {
88
+ "authors": [
89
+ { "_key": "contributor1", "_ref": "author:sam" }
90
+ ]
91
+ }
92
+ ```
93
+
94
+ The editor provides the reference and array controls. These are native schema fields, not a separate author selector API. Selection changes the current draft; it does not save it. Validation checks the reference shape, declared target type and target existence. Array entries require unique stable keys. Save the author before referencing it from another document.
95
+
96
+ ## Render referenced data
97
+
98
+ Native references remain objects in saved content. Resolve them in an Astro template with `derefDocument`:
99
+
100
+ ```astro
101
+ ---
102
+ import { derefDocument } from "virtual:rigel-content";
103
+ interface Props { author: unknown }
104
+ const { author: reference } = Astro.props;
105
+ const author = derefDocument(reference);
106
+ const name = author?.type === "author" ? String(author.data.name ?? "") : "";
107
+ ---
108
+ {name && <span>{name}</span>}
109
+ ```
110
+
111
+ Pass the saved field as the component's `author` prop. Resolve each entry separately for an author array. `getDocument("author:sam")` also reads a document by its stable ID. Both helpers return `undefined` when resolution fails, so handle missing references in your templates.
112
+
113
+ The starter's `src/content/schema.ts` demonstrates native references such as its shared primary contact. Its section renderer uses `derefDocument` for shared content. Those patterns apply to authors without changing document bodies or inventing an author-specific field type. See [preview](preview.md) for binding content to an editable website view.
114
+
115
+ ## References in configured article collections
116
+
117
+ Configured `collections[].fields` use a different reference contract from native schema fields. The starter's Blog and News examples use this collection form:
118
+
119
+ ```ts
120
+ { key: "author", label: "Author", type: "reference", to: "author" }
121
+ ```
122
+
123
+ Here `to` is a string, and the saved article frontmatter contains the string ID `"author:sam"`. Set `multiple: true` to store an array of string IDs instead. This collection option does not apply to native `FieldDef`; native content uses the reference objects and arrays above.
124
+
125
+ Collection fields can instead use a fixed `options` array of `{ value, label }` entries. Supply exactly one of `to` or `options`. Optional option metadata includes `description`, `photo`, `email` and `url`, but the selector displays names only. Fixed options live in site configuration and cannot be added through Authors.
126
+
127
+ Collection validation rejects unknown or duplicate IDs and incorrect single/multiple shapes. Historical unavailable references remain visible in the selector rather than silently clearing. The starter's `src/components/PostPage.astro` resolves collection string IDs with `getDocument` and joins names for multiple authors. References are not automatically expanded in frontmatter or arbitrary templates.
128
+
129
+ ## Public data and shared content
130
+
131
+ Author email addresses, biographies and photos are site content, not credentials or private account data. Local-mode builds include author records in the CMS manifest; GitHub-mode builds omit document values and load them after authentication. Your website templates may also expose these fields. Publish only details contributors have agreed to share, and render only the fields your website needs.
132
+
133
+ Shared content without a locale remains available across content languages. See [content locales](content-locales.md), [media](media.md) for photo selection, [GitHub](github.md) for remote saves, and the [documentation index](README.md) for the other guides.
@@ -4,6 +4,7 @@ Content languages are optional. Configure them separately from `languages`, whic
4
4
 
5
5
  ```js
6
6
  rigel({
7
+ search: { scope: "all" },
7
8
  contentLocales: {
8
9
  defaultLocale: "en",
9
10
  locales: [
@@ -44,7 +45,15 @@ defineDocument({
44
45
 
45
46
  Each type can have different fields, allowed sections and initial content. You can also annotate an existing `defineCopy` page with `locale`. Rigel reads the annotation, not the folder name.
46
47
 
47
- With multiple configured locales, the editor shows a Content language dropdown. It filters navigation, page lists, collections and search. Switching away from unsaved changes offers the existing Save, Discard and Keep editing choices. New sessions start with `defaultLocale`.
48
+ With multiple configured locales, the editor shows a Content language dropdown. It filters navigation, page lists and collections. Switching away from unsaved changes offers the existing Save, Discard and Keep editing choices. New sessions start with `defaultLocale`.
49
+
50
+ ## Global search
51
+
52
+ Sidebar search (⌘/Ctrl + K) searches **all content languages** by default. Set `search: { scope: "currentLocale" }` in your Astro integration to default to the selected content language plus shared resources instead. Missing or invalid scope values fall back to `"all"`. This setting does not change the Pages table's language filter or its live search.
53
+
54
+ In **Settings → Content → Search content languages**, editors can choose **Website default**, **All languages**, or **Current content language**, alongside the article editing preferences. The override applies immediately and persists in `rigelcms.settings.userPref.searchScope` in this browser. Website default removes the override; Erase All Data resets it with the other browser preferences. It is independent of the editor interface language, which lives in **Settings → Interface** after Theme and Accessibility. Current content language is disabled when there are fewer than two configured content languages.
55
+
56
+ Results include legacy pages, native documents, collections, posts and menus. Shared resources appear once. Localized results show two-character language codes when multiple content languages exist, with the full configured label available on hover and to screen readers. Opening another language's result switches content language and opens that exact resource through the existing unsaved-changes guard; Keep editing leaves the language and draft untouched. Search still shows at most 12 results.
48
57
 
49
58
  Content without a `locale` remains visible in every language. Use this for common authors, media and other shared records. A shared document type can also have a locale when its records are language-specific. The language filter is an editing convenience, not an access-control boundary: the manifest and publishing batch still contain the site's complete content.
50
59
 
@@ -54,8 +63,8 @@ Your Astro routes and templates still own website localization, including public
54
63
 
55
64
  ## Page categories and templates
56
65
 
57
- Set `category` on a nonshared document definition to group its pages, for example `category: "Articles"`. This is schema metadata, not an editable field in stored documents. Uncategorised native documents and legacy copy pages remain in **Core** without a migration. When categories exist, the Pages table offers Core, All pages and each category visible in the selected content language. Core is the initial filter when uncategorised pages or types exist; category-only sites initially show All pages. Back returns to the originating table/filter, not a filter inferred from the opened page.
66
+ Set `category` on a nonshared document definition to group its pages, for example `category: "Articles"`. This is schema metadata, not an editable field in stored documents. Uncategorised native documents and legacy copy pages remain in **Core** without a migration. Pages initially shows **All pages**. When categories exist, the table offers Core, All pages and each category visible in the selected content language. The live search matches title/name, slug and category substrings, ignores case and surrounding whitespace, and combines with the selected category. Its clear button restores input focus. Back preserves both the query and originating table/filter, not a filter inferred from the opened page. Content-language switches retain the query and reset unavailable categories to All pages.
58
67
 
59
68
  Add new asks for a page name and a category from the locale-eligible, creatable document types. When a category contains multiple types, the editor also requires a Template choice using each definition's title. Shared, singleton and `creatable: false` types are not creation choices. A single uncategorised type keeps the name-only dialog. Creation uses the selected type's identity, initial values and section template; nothing is written until Save.
60
69
 
61
- A visual variant can be another document definition with its own category, defaults and template—for example a Resources category with a dark-theme initial value. Its native type, folder, preview mapping and host renderer must be configured normally. A category does not change the editor theme, rewrite existing document types or generate website routes.
70
+ Use a per-page theme field for light and dark variants that share the same content structure, and let your host template render that choice. Create a separate document definition when the fields or section template differ. Categories organize content; they do not change the editor theme, rewrite existing document types or generate website routes.
@@ -0,0 +1,131 @@
1
+ # Content model
2
+
3
+ Rigel stores schema documents as JSON files in your site repository. Developers declare fields and render the public pages; editors change the values in `/rigel/`. Schema documents can coexist with `defineCopy` modules, article collections and [menus](menus.md).
4
+
5
+ For installation, see [Getting started](getting-started.md). For the full guide list, see the [documentation index](README.md).
6
+
7
+ ## Declare documents and sections
8
+
9
+ Put a schema in `src/content/schema.ts`:
10
+
11
+ ```ts
12
+ import { defineDocument, defineObject, defineSchema } from "@rigel-cms/rigel/content";
13
+
14
+ export default defineSchema({
15
+ documents: [
16
+ defineDocument({
17
+ name: "page",
18
+ title: "Pages",
19
+ type: "document",
20
+ folder: "src/content/pages",
21
+
22
+ fields: [
23
+ { key: "title", label: "Page name", type: "string", required: true },
24
+ {
25
+ key: "sections", label: "Sections", type: "array", min: 1,
26
+ items: [{ type: "object", name: "intro" }],
27
+ },
28
+ ],
29
+ template: [{ type: "intro", values: { heading: "About the project" } }],
30
+ }),
31
+ defineDocument({
32
+ name: "callout",
33
+ title: "Callouts",
34
+ type: "document",
35
+ folder: "src/content/shared/callouts",
36
+ shared: true,
37
+ fields: [{ key: "heading", label: "Heading", type: "string", required: true }],
38
+ }),
39
+ ],
40
+ objects: [
41
+ defineObject({
42
+ name: "intro", title: "Introduction", type: "section",
43
+ fields: [
44
+ { key: "heading", label: "Heading", type: "string", required: true, initialValue: "Start here" },
45
+ { key: "body", label: "Body", type: "markdown", initialValue: "Write an introduction." },
46
+ { key: "callout", label: "Shared callout", type: "reference", to: ["callout"] },
47
+ ],
48
+ }),
49
+ ],
50
+ });
51
+ ```
52
+
53
+ Pass the schema to the integration in `astro.config.mjs`. This is a configuration excerpt:
54
+
55
+ ```js
56
+ import schema from "./src/content/schema";
57
+
58
+ rigel({
59
+ schema,
60
+ preview: { documents: { page: { url: "/{slug}/", index: "/" } } },
61
+ });
62
+ ```
63
+
64
+ The helpers return typed declarations; the integration validates the schema. Import helpers from `@rigel-cms/rigel/content` in site templates to avoid importing the Astro integration. Add `/// <reference types="@rigel-cms/rigel/types" />` to `src/env.d.ts` for the virtual-module types.
65
+
66
+ `virtual:rigel-content` exposes `documents`, `getDocument(id)` and `derefDocument(value)`. These are build/dev-time content helpers, not a hosted content API. Your Astro routes must enumerate and render the documents. A schema declaration or preview mapping does not create public routes.
67
+
68
+ ## Choose fields
69
+
70
+ Schema scalar types are `string`, `text`, `markdown`, `boolean`, `number`, `date`, `image`, `file`, `url`, `color`, `code` and `password`. A password control masks its display; the value remains content in the repository, so use a separate secret store for credentials.
71
+
72
+ An `object` field declares nested `fields` or uses `of: "objectName"`. An `array` declares allowed `items`; named object items use `name` to select an object from `objects`. Section objects use `type: "section"` and belong in the page's `sections` array. Object and reference array items require unique, stable `_key` values. Named object items also store `_type`.
73
+
74
+ A schema reference declares `to: ["callout"]` and stores `{ "_ref": "callout:notice" }`. That record must exist and belong to an allowed type before the reference can pass Save validation. Embedding an object stores a copy; a reference points to one shared record. See [Authors](authors.md) for article references, whose collection field declaration uses a different shape, and [Media](media.md) for image and file fields.
75
+
76
+ Schema `markdown` fields store Markdown strings. Article collection bodies store Lexical state in `{ frontmatter, body }` JSON and have a read-only Markdown view. Keep those formats separate. Custom article blocks and widgets are covered in [Extensions](extensions.md).
77
+
78
+ Markdown fields open in the article block editor when the source survives a conversion round trip. Set `sidebarOnly: true` on a `markdown` field to keep it in the properties panel even when its preview binding is available, for example a FAQ answer edited beside the question that owns it:
79
+
80
+ ```ts
81
+ const faqAnswer = { key: "answer", label: "Answer", type: "markdown", sidebarOnly: true };
82
+ ```
83
+
84
+ The flag applies to `markdown` fields only, and Rigel rejects it on any other type.
85
+
86
+ ## Seed new content
87
+
88
+ A document's `initialValue` supplies initial field values. Field-level `initialValue` fills values that the document did not supply. The entered title takes precedence for a `title` field, or for `name` when the type has no `title` field.
89
+
90
+ A document `template` seeds its `sections`. Each entry selects a section type and may supply `values`, which override that section's field defaults. Adding a section in the editor also uses its field defaults. Give required fields useful defaults where possible; placeholders are hints, not saved values. A default reference still needs an existing target, and a default image needs a real asset.
91
+
92
+ Creation and duplication clone values and regenerate existing nested `_key` values. Each new section gets its own key. Reordering keeps those keys, so preview bindings continue to point to the same items. Defaults do not exempt content from required-field validation.
93
+
94
+ ## Keep identity separate from URLs
95
+
96
+ For `src/content/pages/about.json`, the storage identity is `page:about`. `_id`, when present, must match that identity, and `_type` must match the document type. The filename remains stable when an editor changes the page name or URL slug.
97
+
98
+ A non-shared document's `slug` value is an optional URL override. Rigel normalizes edited slugs to lowercase ASCII kebab-case and checks mapped page-route collisions; absent overrides fall back to the filename. Changing `title` alone does not change the URL. Use `effectiveSlug` or `pageRoute` from `@rigel-cms/rigel/content` in host routing rather than assuming every public URL matches the filename. The `index` mapping above maps an effective slug of `home` to `/`.
99
+
100
+ Rigel checks declared document routes and fixed copy-page mappings, not every route your Astro site might emit. Developers must avoid collisions with unrelated host routes and keep public routing consistent with preview mappings.
101
+
102
+ ## Organize pages and shared records
103
+
104
+ Non-shared document types appear in All pages. Uncategorized pages belong to Core; set `category: "Legal"` or another name to create a separate group. This is editor metadata, not a route prefix. Shared types use standalone forms rather than a website canvas. Use `singleton: true` for a single settings record and `creatable: false` to disable creation. Provision that record in the repository; the declaration does not generate its file.
105
+
106
+ A shared edit affects every consuming page after Save and the site's rebuild or development refresh. A preview URL does not turn a shared record into a page editor. See [Sidebar](sidebar.md) for navigation and [Content locales](content-locales.md) for optional language-specific types.
107
+
108
+ ## Bind the page preview
109
+
110
+ The schema cannot infer which DOM elements display a field. Give rendered elements explicit `data-rigel-*` bindings and an owning document ID. This Astro excerpt assumes `doc` and an `intro` section from the schema above:
111
+
112
+ ```astro
113
+ <section data-rigel-section="intro" data-rigel-key={section._key} data-rigel-doc={doc.id}>
114
+ <h1 data-rigel-text={`sections.${section._key}.heading`} data-rigel-doc={doc.id}>
115
+ {section.heading}
116
+ </h1>
117
+ <div data-rigel-richtext={`sections.${section._key}.body`} data-rigel-doc={doc.id}>
118
+ <Fragment set:html={renderedBody} />
119
+ </div>
120
+ </section>
121
+ ```
122
+
123
+ Here `renderedBody` must be safely rendered Markdown supplied by your host code. Bindings use dot-separated field paths with stable array keys, not CSS selectors or array positions. New sections also need prepared DOM templates. See [Preview](preview.md) for templates, shared-reference bindings and the sandbox limits, and [SEO](seo.md) for native page metadata.
124
+
125
+ ## Save and storage limits
126
+
127
+ Content stays in memory until explicit Save. Unsaved-navigation guards offer a chance to save, discard or keep editing; failed saves retain the draft. Browser preferences persist independently of content.
128
+
129
+ The local document endpoint works only in development with the local backend. It validates content and reference targets, requires same-origin writes, rejects unsafe paths and prototype keys, and writes within declared folders. Document files and request bodies have a 1 MiB limit. The request checks Content-Length before reading, then checks the buffered body size; this is not a streaming memory limit.
130
+
131
+ Local writes use temporary files and atomic replacement; creation refuses an existing target. Local Save writes files without creating a Git commit. The SDK does not provide a general multi-process transaction or reserve arbitrary Astro routes. Schema document deletion remains developer-owned, so check references before removing files or types. For remote saving and publishing, see [GitHub](github.md).
@@ -0,0 +1,139 @@
1
+ # Getting started
2
+
3
+ Use Node.js 24 and Astro 7.3 or later within Astro 7. These guides describe the SDK in this repository. Features on `main` may not yet be available under npm's `latest` tag.
4
+
5
+ ## Start with a prepared site
6
+
7
+ Use the [Rigel starter](https://github.com/rigel-cms/rigel-starter) as a GitHub template, then clone your new repository. The starter includes page templates, articles, menus and media examples.
8
+
9
+ With pnpm installed, run these commands in the site directory:
10
+
11
+ ```sh
12
+ pnpm install
13
+ pnpm dev
14
+ ```
15
+
16
+ Open `/rigel/` on the local server. Change a field and press Save. Local mode writes content files in your checkout; it does not create Git commits. Run `pnpm build` to check the published routes before deploying your site.
17
+
18
+ ## Add Rigel to an existing Astro site
19
+
20
+ Install the SDK with your site's package manager:
21
+
22
+ ```sh
23
+ npm install @rigel-cms/rigel
24
+ ```
25
+
26
+ To test a prerelease, install `@rigel-cms/rigel@next` instead. Keep the resolved version in your site's lockfile.
27
+
28
+ The example below creates an editable About page. Keep your existing Astro integrations when adding Rigel.
29
+
30
+ Create `src/content/schema.ts`:
31
+
32
+ ```ts
33
+ import { defineDocument, defineSchema } from '@rigel-cms/rigel/content';
34
+
35
+ export default defineSchema({
36
+ objects: [],
37
+ documents: [
38
+ defineDocument({
39
+ name: 'page',
40
+ title: 'Pages',
41
+ type: 'document',
42
+ folder: 'src/content/pages',
43
+ fields: [
44
+ { key: 'title', label: 'Title', type: 'string', required: true },
45
+ { key: 'intro', label: 'Introduction', type: 'text' },
46
+ { key: 'draft', label: 'Draft', type: 'boolean', initialValue: true },
47
+ ],
48
+ }),
49
+ ],
50
+ });
51
+ ```
52
+
53
+ Add the integration in `astro.config.mjs`:
54
+
55
+ ```js
56
+ import { defineConfig } from 'astro/config';
57
+ import { rigel } from '@rigel-cms/rigel';
58
+ import schema from './src/content/schema';
59
+
60
+ export default defineConfig({
61
+ integrations: [
62
+ rigel({
63
+ backend: 'local',
64
+ schema,
65
+ preview: { documents: { page: { url: '/{slug}/' } } },
66
+ }),
67
+ ],
68
+ });
69
+ ```
70
+
71
+ Create `src/content/pages/about.json`:
72
+
73
+ ```json
74
+ {
75
+ "_id": "page:about",
76
+ "_type": "page",
77
+ "title": "About",
78
+ "intro": "A short introduction to the people behind this website.",
79
+ "slug": "about",
80
+ "draft": false
81
+ }
82
+ ```
83
+
84
+ The record's ID stays stable when its public slug changes. The saved example is published so that the route below also appears in a production build. Newly created pages start as drafts because of the schema's `initialValue`.
85
+
86
+ Add Rigel's virtual-module declarations to `src/env.d.ts`:
87
+
88
+ ```ts
89
+ import '@rigel-cms/rigel/types';
90
+ ```
91
+
92
+ Create `src/pages/[slug].astro`:
93
+
94
+ ```astro
95
+ ---
96
+ import { documents } from 'virtual:rigel-content';
97
+ import { effectiveSlug } from '@rigel-cms/rigel/content';
98
+
99
+ export function getStaticPaths() {
100
+ return documents
101
+ .filter(doc => doc.type === 'page' && (import.meta.env.DEV || doc.data.draft !== true))
102
+ .map(doc => ({
103
+ params: { slug: effectiveSlug(doc.data, doc.slug) },
104
+ props: { doc },
105
+ }));
106
+ }
107
+
108
+ const { doc } = Astro.props;
109
+ ---
110
+ <!doctype html>
111
+ <html lang="en">
112
+ <head>
113
+ <meta charset="utf-8" />
114
+ <title>{String(doc.data.metaTitle || doc.data.title)}</title>
115
+ </head>
116
+ <body>
117
+ <main data-rigel-doc={doc.id}>
118
+ <h1 data-rigel-text="title" data-rigel-doc={doc.id}>{String(doc.data.title)}</h1>
119
+ <p data-rigel-text="intro" data-rigel-doc={doc.id}>{String(doc.data.intro ?? '')}</p>
120
+ </main>
121
+ </body>
122
+ </html>
123
+ ```
124
+
125
+ Run `npm run dev`, open `/rigel/`, then choose Pages and About. The preview mapping loads `/about/`; the `data-rigel-*` bindings connect its text to the editable fields. Your Astro route owns the HTML and draft filtering.
126
+
127
+ ## What saves where
128
+
129
+ Content edits require Save. Leaving an unsaved editor opens the existing save, discard or keep-editing dialog. Browser preferences, such as interface language and search scope, save separately in browser storage.
130
+
131
+ Local editing is available through Astro's development server. Deploying a static build does not deploy a filesystem-write service. Configure the [GitHub backend](github.md) for editing a deployed site, then let your hosting pipeline build the saved content.
132
+
133
+ ## Continue
134
+
135
+ - [Content model](content-model.md): fields, objects, references and page templates.
136
+ - [Preview bindings](preview.md): images, Markdown and repeated sections.
137
+ - [Content languages](content-locales.md): localized pages and search preferences.
138
+ - [Live and draft hosts](preview-hosts.md): connect previews to your deployment workflow.
139
+ - [Documentation index](README.md): menus, media, authors and other guides.
package/docs/github.md ADDED
@@ -0,0 +1,112 @@
1
+ # GitHub connection and publishing
2
+
3
+ Rigel's GitHub backend reads content from a workspace branch and commits changes when an editor chooses Save. Publishing is a separate action: review the saved changes, then confirm a pull request squash merge into the publishing branch. Your hosting provider builds and deploys the result.
4
+
5
+ For installation and a first local edit, start with [Getting started](getting-started.md). For branch builds and preview URLs, see [Preview hosts](preview-hosts.md).
6
+
7
+ ## Configure the site
8
+
9
+ Keep your site's schema, collections, menus and preview settings when switching backends. A root-level Astro project can use this configuration:
10
+
11
+ ```js
12
+ import { defineConfig } from "astro/config";
13
+ import { rigel } from "@rigel-cms/rigel";
14
+
15
+ export default defineConfig({
16
+ integrations: [
17
+ rigel({
18
+ backend: "github",
19
+ repo: "example-org/website",
20
+ branch: "drafts",
21
+ publishBranch: "main",
22
+ repoRoot: "",
23
+ authOrigin: "https://auth.rigelcms.dev",
24
+ appSlug: "rigel-cms",
25
+ }),
26
+ ],
27
+ });
28
+ ```
29
+
30
+ Replace `example-org/website` with the repository containing your site's content. The example shows the default branch, path and authentication settings explicitly; only `backend` and `repo` are required to select this backend.
31
+
32
+ | Option | Meaning |
33
+ | --- | --- |
34
+ | `repo` | GitHub repository in `owner/name` form, not a URL. |
35
+ | `branch` | Editor workspace branch, default `drafts`. Use a dedicated branch; the remote backend rejects `main` and `master` as workspaces. |
36
+ | `publishBranch` | Destination for publishing, default `main`. Set it to `master` or another branch if that is what your production host builds. It must differ from `branch` and already exist. |
37
+ | `repoRoot` | Astro project folder relative to the repository root. Use `""` for a root-level project or, for example, `"sites/website"` for a subfolder. Content folders remain relative to the Astro project. |
38
+ | `authOrigin` | Authentication gateway origin. The default uses the public Rigel gateway. Custom values must be HTTPS origins without paths, credentials, queries or fragments; HTTP is allowed on loopback. |
39
+ | `appSlug` | GitHub App slug, default `rigel-cms`. Override it together with the gateway only when using your own compatible authenticator. |
40
+
41
+ The default connection uses the [public Rigel CMS GitHub App](https://github.com/apps/rigel-cms). Site developers and editors do not need to deploy a gateway or create an App. The gateway handles the OAuth exchange; the editor uses the resulting GitHub App user token to call GitHub. App secrets belong on the authenticator, never in site configuration.
42
+
43
+ ## Prepare repository access
44
+
45
+ An editor needs write access to the configured repository. The GitHub App must be installed for that repository, active, and granted Contents read/write permission. Authorizing a GitHub identity alone does not grant editing access.
46
+
47
+ Publishing also needs Pull requests read/write permission. The connection check verifies Contents write and the user's repository write access; a successful connection does not prove that publishing permissions are available. If an installation needs updated permissions, its owner must approve them, then the editor must reconnect.
48
+
49
+ For in-editor publishing, enable squash merging and disable automatic head-branch deletion in the repository settings. Rigel checks both settings during review and confirmation so that the workspace survives a publish. It does not change these settings for you.
50
+
51
+ ## Sign in and install the App
52
+
53
+ - Open `/rigel/` and choose **Sign in with GitHub**. Allow the sign-in popup and approve only an editor origin you trust.
54
+ - Complete GitHub authorization. Rigel checks the configured App's installations, selected repositories, Contents permission and your repository write access before loading the editor.
55
+ - If access is missing, choose **Install / configure GitHub access**. GitHub opens its account or organization and repository picker in a separate tab. Choose **Only select repositories** and include your content repository. An organization owner may need to approve the installation.
56
+ - Return to the editor tab and choose **Check repository access**. Installing the App does not automatically reconnect the editor tab.
57
+
58
+ For local testing with the public gateway, use `http://localhost`. Use HTTPS for a deployed editor.
59
+
60
+ Repository selection limits the App's access, not the token to one editor. A GitHub App user token can access repositories available to both the user and the App. Treat the editor origin as trusted code.
61
+
62
+ The editor stores the short-lived session in `localStorage`, scoped to the gateway, repository, workspace branch and App slug. Reloading can restore an unexpired session, after which Rigel checks access again. This storage is accessible to scripts on the same origin; deploy the editor on an origin you control and trust. The login handshake checks the popup, gateway origin and a fresh nonce before accepting a token.
63
+
64
+ Expired or revoked access blocks writes. **Reconnect GitHub** checks access again while keeping the current draft. **Sign out** uses the unsaved-changes guard and clears the editor session; it does not revoke the App's authorization in GitHub or clear the gateway's HttpOnly cookie. Manage GitHub grants in your GitHub settings.
65
+
66
+ ## Load content and save drafts
67
+
68
+ After access verification, Rigel loads content from the current workspace branch, rather than using build-time content values. If the workspace does not exist, this initial load creates it from `publishBranch`. Signing in and connecting can therefore create a branch before the first Save. An existing workspace is never reset or force-pushed.
69
+
70
+ Content edits stay in memory until you choose **Save**, including the **Save and leave** option in the unsaved-changes dialog. Each Save commits the selected copy page, schema document, article or menu to the workspace. Changing a field does not autosave, and Save does not publish.
71
+
72
+ Rigel validates content and builds each write from the loaded branch revision, then advances the workspace with a non-force update and verifies the result. If another writer advances the branch, the stale write is rejected rather than replacing their work. This also protects against two editors claiming the same URL slug in different files. Preserve your unsaved edits before reloading after a conflict.
73
+
74
+ If the connection fails after a write may have completed, retrying Save verifies the pending commit and content before attempting another change. A conflict or failed verification leaves the draft available in the editor.
75
+
76
+ Legacy copy modules must use data-only `defineCopy({ ... })` literals. Computed values, spreads, functions and template literals are rejected instead of executed. Schema documents, articles and menus keep their JSON formats and validation.
77
+
78
+ ## Review and publish
79
+
80
+ - Save your edits. If inputs are still unsaved, Publish explains that you must save first; it neither saves nor discards them.
81
+ - Choose **Publish** to open the review. This stage only reads GitHub; it does not create a PR or merge anything.
82
+ - Check the repository, both branches and the complete changed-file list. The review covers the entire repository, including files outside CMS folders and `repoRoot`. It is a file/status review, not a line-by-line diff. Inspect the diff on GitHub if you need that detail.
83
+ - Choose **Confirm publish**. Rigel finds an open PR for the exact repository and branch pair, or creates one. It stops if multiple matching PRs exist. It then attempts a normal synchronous squash merge pinned to the reviewed workspace commit.
84
+ - Wait for confirmation. Rigel reports publishing success only after reading back that exact PR as merged and closed, with the expected source, destination and a valid merge commit SHA.
85
+
86
+ Canceling a review makes no changes. Identical branch trees mean there is nothing to publish, even if Git history says the workspace is ahead. Rigel rejects truncated trees, incomplete comparisons and reviews containing 300 or more changed-file entries instead of showing a partial list. A rename can appear as one entry with its previous filename.
87
+
88
+ If a merge response is lost, Rigel checks the existing PR. If it cannot verify the outcome, it reports an unconfirmed result rather than claiming success. Retrying confirmation checks the same PR before attempting another merge. Check that PR on GitHub if the outcome remains uncertain.
89
+
90
+ ### Publish again after a squash merge
91
+
92
+ Squash merging retains the workspace but gives the destination a different commit history. When the branches have new differences and the publishing revision is no longer an ancestor of the workspace, the review shows provisional net tree differences and offers **Sync drafts first** instead of Confirm publish.
93
+
94
+ That explicit action creates or reuses the matching PR and asks GitHub to update its source branch from the publishing branch, with the reviewed workspace SHA as the expected head. GitHub merges into the workspace without resetting its history. This step does not publish.
95
+
96
+ After a verified sync, Rigel reloads draft content. Open a fresh review and separately confirm publishing. If GitHub has not finished its asynchronous update when Rigel checks, wait, reload draft content and review again. Resolve sync conflicts or protection failures on GitHub. You do not need to reset or delete the workspace between publishes.
97
+
98
+ ### Checks, approvals and concurrent changes
99
+
100
+ Required approvals, status checks and branch protections still apply. Rigel merges only an open, non-draft PR that GitHub reports as mergeable with a `clean` merge state. If approvals or checks block it, complete them on GitHub, then retry or review again if the branches changed. Merge queues or other repository policies may require completing the merge on GitHub. Rigel does not bypass those policies or schedule an automatic merge after checks finish.
101
+
102
+ Before mutation, Rigel rechecks the reviewed branch revisions and the PR's source and destination. Observed changes invalidate the review. Save, deletion and publishing also have in-process overlap guards; these are not locks across other tabs or GitHub clients.
103
+
104
+ The merge API pins the source SHA, but has no expected destination SHA or atomic PR-target lock. A destination advance, PR retarget or settings change after the last check can still race the merge. GitHub's merge/conflict/protection rules apply, and Rigel never force-overwrites the destination. The final PR readback checks the target before reporting success, but it cannot make that operation atomic. Signing out prevents subsequent publishing requests; it cannot recall a request already sent.
105
+
106
+ ## Deployment and public data
107
+
108
+ A confirmed publish means the PR merged into `publishBranch`, not that your site deployed successfully. Connect that branch to your host's production build, configure a workspace branch build for draft previews if needed, and inspect build/deployment results on the host. Rigel does not configure branch builds or verify deployment completion. See [Preview hosts](preview-hosts.md) for that setup.
109
+
110
+ In GitHub mode, the public editor manifest includes schemas and configuration rather than build-time document, article, menu or copy values. Schemas and initial values are still public configuration; keep credentials out of them. Your website templates decide which content reaches public pages.
111
+
112
+ The media library is a build-time snapshot in GitHub mode. GitHub media uploads and imports are unavailable; manage media through your repository and rebuild the site when needed.