nebula-cms 0.1.0

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 (241) hide show
  1. package/.claude/settings.local.json +42 -0
  2. package/.github/workflows/ci.yml +31 -0
  3. package/.mcp.json +12 -0
  4. package/.prettierignore +5 -0
  5. package/.prettierrc.cjs +22 -0
  6. package/AGENTS.md +183 -0
  7. package/LICENSE +201 -0
  8. package/README.md +128 -0
  9. package/package.json +74 -0
  10. package/playground/.claude/settings.local.json +5 -0
  11. package/playground/astro.config.mjs +7 -0
  12. package/playground/node_modules/.bin/astro +21 -0
  13. package/playground/node_modules/.bin/rollup +21 -0
  14. package/playground/node_modules/.bin/tsc +21 -0
  15. package/playground/node_modules/.bin/tsserver +21 -0
  16. package/playground/node_modules/.bin/vite +21 -0
  17. package/playground/node_modules/.vite/_svelte_metadata.json +1 -0
  18. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +80 -0
  19. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +7 -0
  20. package/playground/node_modules/.vite/deps/_metadata.json +184 -0
  21. package/playground/node_modules/.vite/deps/astro___aria-query.js +6776 -0
  22. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +7 -0
  23. package/playground/node_modules/.vite/deps/astro___axobject-query.js +3754 -0
  24. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +7 -0
  25. package/playground/node_modules/.vite/deps/astro___html-escaper.js +34 -0
  26. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +7 -0
  27. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  28. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +7 -0
  29. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js +1005 -0
  30. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js.map +7 -0
  31. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +8 -0
  32. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +7 -0
  33. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +21 -0
  34. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +7 -0
  35. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +223 -0
  36. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +7 -0
  37. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js +204 -0
  38. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js.map +7 -0
  39. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js +27 -0
  40. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js.map +7 -0
  41. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js +4376 -0
  42. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js.map +7 -0
  43. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js +688 -0
  44. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js.map +7 -0
  45. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js +5099 -0
  46. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js.map +7 -0
  47. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +23 -0
  48. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +7 -0
  49. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +148 -0
  50. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +7 -0
  51. package/playground/node_modules/.vite/deps/package.json +3 -0
  52. package/playground/node_modules/.vite/deps/smol-toml.js +843 -0
  53. package/playground/node_modules/.vite/deps/smol-toml.js.map +7 -0
  54. package/playground/node_modules/.vite/deps/svelte.js +55 -0
  55. package/playground/node_modules/.vite/deps/svelte.js.map +7 -0
  56. package/playground/node_modules/.vite/deps/svelte___clsx.js +9 -0
  57. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +7 -0
  58. package/playground/node_modules/.vite/deps/svelte_animate.js +57 -0
  59. package/playground/node_modules/.vite/deps/svelte_animate.js.map +7 -0
  60. package/playground/node_modules/.vite/deps/svelte_attachments.js +15 -0
  61. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +7 -0
  62. package/playground/node_modules/.vite/deps/svelte_easing.js +67 -0
  63. package/playground/node_modules/.vite/deps/svelte_easing.js.map +7 -0
  64. package/playground/node_modules/.vite/deps/svelte_events.js +11 -0
  65. package/playground/node_modules/.vite/deps/svelte_events.js.map +7 -0
  66. package/playground/node_modules/.vite/deps/svelte_internal.js +5 -0
  67. package/playground/node_modules/.vite/deps/svelte_internal.js.map +7 -0
  68. package/playground/node_modules/.vite/deps/svelte_internal_client.js +402 -0
  69. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +7 -0
  70. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +10 -0
  71. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +7 -0
  72. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +8 -0
  73. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +7 -0
  74. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +8 -0
  75. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +7 -0
  76. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +8 -0
  77. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +7 -0
  78. package/playground/node_modules/.vite/deps/svelte_legacy.js +35 -0
  79. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +7 -0
  80. package/playground/node_modules/.vite/deps/svelte_motion.js +545 -0
  81. package/playground/node_modules/.vite/deps/svelte_motion.js.map +7 -0
  82. package/playground/node_modules/.vite/deps/svelte_reactivity.js +29 -0
  83. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +7 -0
  84. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +127 -0
  85. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +7 -0
  86. package/playground/node_modules/.vite/deps/svelte_store.js +103 -0
  87. package/playground/node_modules/.vite/deps/svelte_store.js.map +7 -0
  88. package/playground/node_modules/.vite/deps/svelte_transition.js +208 -0
  89. package/playground/node_modules/.vite/deps/svelte_transition.js.map +7 -0
  90. package/playground/package.json +16 -0
  91. package/playground/pnpm-lock.yaml +3167 -0
  92. package/playground/src/content/authors/jane-doe.json +8 -0
  93. package/playground/src/content/config/build.toml +2 -0
  94. package/playground/src/content/courses/web-fundamentals.json +29 -0
  95. package/playground/src/content/docs/advanced.mdx +6 -0
  96. package/playground/src/content/docs/intro.md +6 -0
  97. package/playground/src/content/guides/getting-started.mdx +6 -0
  98. package/playground/src/content/posts/hello-world.md +7 -0
  99. package/playground/src/content/products/t-shirt.json +16 -0
  100. package/playground/src/content/recipes/pancakes.mdoc +8 -0
  101. package/playground/src/content/settings/site.yml +2 -0
  102. package/playground/src/content.config.ts +198 -0
  103. package/playground/src/env.d.ts +1 -0
  104. package/playground/src/pages/index.astro +11 -0
  105. package/playground/src/pages/nebula.astro +14 -0
  106. package/pnpm-workspace.yaml +2 -0
  107. package/scripts/subset-icons.mjs +178 -0
  108. package/src/astro/index.ts +295 -0
  109. package/src/client/Admin.svelte +283 -0
  110. package/src/client/components/BackendPicker.svelte +291 -0
  111. package/src/client/components/DraftChip.svelte +46 -0
  112. package/src/client/components/MetadataForm.svelte +56 -0
  113. package/src/client/components/ThemeToggle.svelte +18 -0
  114. package/src/client/components/dialogs/DeleteDraftDialog.svelte +51 -0
  115. package/src/client/components/dialogs/FilenameDialog.svelte +129 -0
  116. package/src/client/components/editor/EditorPane.svelte +227 -0
  117. package/src/client/components/editor/EditorTabs.svelte +81 -0
  118. package/src/client/components/editor/EditorToolbar.svelte +131 -0
  119. package/src/client/components/editor/FormatSelector.svelte +66 -0
  120. package/src/client/components/editor/Toolbar.svelte +17 -0
  121. package/src/client/components/fields/ArrayField.svelte +339 -0
  122. package/src/client/components/fields/ArrayItem.svelte +325 -0
  123. package/src/client/components/fields/BooleanField.svelte +114 -0
  124. package/src/client/components/fields/DateField.svelte +82 -0
  125. package/src/client/components/fields/EnumField.svelte +74 -0
  126. package/src/client/components/fields/FieldWrapper.svelte +96 -0
  127. package/src/client/components/fields/NumberField.svelte +99 -0
  128. package/src/client/components/fields/ObjectField.svelte +121 -0
  129. package/src/client/components/fields/SchemaField.svelte +107 -0
  130. package/src/client/components/fields/StringField.svelte +104 -0
  131. package/src/client/components/sidebar/AdminSidebar.svelte +339 -0
  132. package/src/client/components/sidebar/AdminSidebarSort.svelte +123 -0
  133. package/src/client/css/a11y.css +14 -0
  134. package/src/client/css/btn.css +113 -0
  135. package/src/client/css/dialog.css +29 -0
  136. package/src/client/css/field-input.css +39 -0
  137. package/src/client/css/reset.css +59 -0
  138. package/src/client/css/theme.css +77 -0
  139. package/src/client/index.ts +1 -0
  140. package/src/client/js/drafts/merge.svelte.ts +121 -0
  141. package/src/client/js/drafts/ops.svelte.ts +227 -0
  142. package/src/client/js/drafts/storage.ts +108 -0
  143. package/src/client/js/drafts/workers/diff.ts +40 -0
  144. package/src/client/js/editor/editor.svelte.ts +343 -0
  145. package/src/client/js/editor/languages.ts +98 -0
  146. package/src/client/js/editor/link-wrap.ts +45 -0
  147. package/src/client/js/editor/markdown-shortcuts.ts +261 -0
  148. package/src/client/js/handlers/admin.ts +246 -0
  149. package/src/client/js/state/dialogs.svelte.ts +35 -0
  150. package/src/client/js/state/router.svelte.ts +156 -0
  151. package/src/client/js/state/schema.svelte.ts +140 -0
  152. package/src/client/js/state/state.svelte.ts +334 -0
  153. package/src/client/js/state/theme.svelte.ts +173 -0
  154. package/src/client/js/storage/adapter.ts +102 -0
  155. package/src/client/js/storage/client.ts +150 -0
  156. package/src/client/js/storage/db.ts +36 -0
  157. package/src/client/js/storage/fsa.ts +110 -0
  158. package/src/client/js/storage/github.ts +297 -0
  159. package/src/client/js/storage/storage.ts +83 -0
  160. package/src/client/js/storage/workers/frontmatter.ts +320 -0
  161. package/src/client/js/storage/workers/storage.ts +177 -0
  162. package/src/client/js/storage/workers/toml-parser.ts +106 -0
  163. package/src/client/js/storage/workers/yaml-parser.ts +132 -0
  164. package/src/client/js/utils/file-types.ts +192 -0
  165. package/src/client/js/utils/format.ts +16 -0
  166. package/src/client/js/utils/frontmatter.ts +38 -0
  167. package/src/client/js/utils/schema-utils.ts +295 -0
  168. package/src/client/js/utils/slug.ts +18 -0
  169. package/src/client/js/utils/sort.ts +84 -0
  170. package/src/client/js/utils/stable-stringify.ts +27 -0
  171. package/src/client/js/utils/url-utils.ts +38 -0
  172. package/src/types.ts +25 -0
  173. package/src/virtual.d.ts +22 -0
  174. package/svelte.config.js +4 -0
  175. package/tests/astro/build.test.ts +63 -0
  176. package/tests/astro/index.test.ts +689 -0
  177. package/tests/client/components/Admin.test.ts +446 -0
  178. package/tests/client/components/BackendPicker.test.ts +239 -0
  179. package/tests/client/components/DraftChip.test.ts +53 -0
  180. package/tests/client/components/MetadataForm.test.ts +164 -0
  181. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +91 -0
  182. package/tests/client/components/dialogs/FilenameDialog.test.ts +209 -0
  183. package/tests/client/components/dialogs/dialog-stubs.ts +19 -0
  184. package/tests/client/components/editor/EditorPane.test.ts +100 -0
  185. package/tests/client/components/editor/EditorTabs.test.ts +253 -0
  186. package/tests/client/components/editor/EditorToolbar.test.ts +252 -0
  187. package/tests/client/components/editor/fixtures.ts +31 -0
  188. package/tests/client/components/fields/ArrayField.test.ts +197 -0
  189. package/tests/client/components/fields/BooleanField.test.ts +206 -0
  190. package/tests/client/components/fields/DateField.test.ts +210 -0
  191. package/tests/client/components/fields/EnumField.test.ts +246 -0
  192. package/tests/client/components/fields/NumberField.test.ts +240 -0
  193. package/tests/client/components/fields/ObjectField.test.ts +157 -0
  194. package/tests/client/components/fields/SchemaField.test.ts +190 -0
  195. package/tests/client/components/fields/StringField.test.ts +223 -0
  196. package/tests/client/components/sidebar/AdminSidebar.test.ts +285 -0
  197. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +135 -0
  198. package/tests/client/components/sidebar/sort-mock.ts +23 -0
  199. package/tests/client/js/drafts/fixtures.ts +22 -0
  200. package/tests/client/js/drafts/merge.test.ts +282 -0
  201. package/tests/client/js/drafts/ops.test.ts +658 -0
  202. package/tests/client/js/drafts/storage.test.ts +200 -0
  203. package/tests/client/js/drafts/workers/diff.test.ts +165 -0
  204. package/tests/client/js/editor/editor.test.ts +616 -0
  205. package/tests/client/js/editor/link-wrap.test.ts +225 -0
  206. package/tests/client/js/editor/markdown-shortcuts.test.ts +370 -0
  207. package/tests/client/js/handlers/admin.test.ts +467 -0
  208. package/tests/client/js/state/router.test.ts +619 -0
  209. package/tests/client/js/state/schema.test.ts +266 -0
  210. package/tests/client/js/state/state.test.ts +328 -0
  211. package/tests/client/js/storage/adapter.test.ts +115 -0
  212. package/tests/client/js/storage/client.test.ts +250 -0
  213. package/tests/client/js/storage/db.test.ts +59 -0
  214. package/tests/client/js/storage/fsa.test.ts +284 -0
  215. package/tests/client/js/storage/github.test.ts +349 -0
  216. package/tests/client/js/storage/mock-port.ts +95 -0
  217. package/tests/client/js/storage/storage.test.ts +77 -0
  218. package/tests/client/js/storage/workers/frontmatter.test.ts +479 -0
  219. package/tests/client/js/storage/workers/storage.test.ts +299 -0
  220. package/tests/client/js/storage/workers/toml-parser.test.ts +169 -0
  221. package/tests/client/js/storage/workers/yaml-parser.test.ts +168 -0
  222. package/tests/client/js/utils/file-types.test.ts +268 -0
  223. package/tests/client/js/utils/frontmatter.test.ts +87 -0
  224. package/tests/client/js/utils/schema-utils.test.ts +318 -0
  225. package/tests/client/js/utils/slug.test.ts +58 -0
  226. package/tests/client/js/utils/sort.test.ts +276 -0
  227. package/tests/client/js/utils/stable-stringify.test.ts +68 -0
  228. package/tests/client/js/utils/url-utils.test.ts +70 -0
  229. package/tests/e2e/backend-connection.test.ts +301 -0
  230. package/tests/e2e/draft-lifecycle.test.ts +388 -0
  231. package/tests/e2e/editing.test.ts +355 -0
  232. package/tests/e2e/github-adapter.test.ts +330 -0
  233. package/tests/e2e/helpers/mock-adapter.ts +166 -0
  234. package/tests/e2e/helpers/test-app.ts +155 -0
  235. package/tests/e2e/navigation.test.ts +358 -0
  236. package/tests/e2e/publishing.test.ts +345 -0
  237. package/tests/e2e/unsaved-changes.test.ts +317 -0
  238. package/tests/setup.ts +2 -0
  239. package/tests/stubs/codemirror.ts +197 -0
  240. package/tsconfig.json +19 -0
  241. package/vitest.config.ts +178 -0
@@ -0,0 +1,42 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__plugin_context7_context7__resolve-library-id",
5
+ "mcp__plugin_context7_context7__query-docs",
6
+ "Bash(pnpm fix:*)",
7
+ "Bash(pnpm lint:*)",
8
+ "Bash(git add:*)",
9
+ "Bash(git commit:*)",
10
+ "mcp__plugin_playwright_playwright__browser_navigate",
11
+ "mcp__plugin_playwright_playwright__browser_close",
12
+ "Bash(git worktree:*)",
13
+ "Bash(pnpm test:*)",
14
+ "Bash(pnpm sync:*)",
15
+ "Bash(pnpm build:*)",
16
+ "Bash(ag:*)",
17
+ "Bash(git ls-tree:*)",
18
+ "Bash(git stash:*)",
19
+ "Bash(pnpm vitest:*)",
20
+ "mcp__plugin_playwright_playwright__browser_console_messages",
21
+ "mcp__plugin_playwright_playwright__browser_click",
22
+ "mcp__plugin_playwright_playwright__browser_take_screenshot",
23
+ "mcp__plugin_playwright_playwright__browser_snapshot",
24
+ "mcp__plugin_playwright_playwright__browser_evaluate",
25
+ "WebSearch",
26
+ "mcp__playwright__browser_navigate",
27
+ "mcp__playwright__browser_take_screenshot",
28
+ "mcp__playwright__browser_snapshot",
29
+ "mcp__playwright__browser_click",
30
+ "mcp__playwright__browser_console_messages",
31
+ "mcp__playwright__browser_network_requests",
32
+ "Bash(/Users/avalon/.claude/plugins/cache/claude-plugins-official/superpowers/5.0.7/skills/brainstorming/scripts/start-server.sh --project-dir /Users/avalon/Development/nebula-cms)",
33
+ "WebFetch(domain:discuss.codemirror.net)",
34
+ "Bash(/Users/avalon/.claude/plugins/cache/claude-plugins-official/superpowers/5.0.7/skills/brainstorming/scripts/stop-server.sh /Users/avalon/Development/nebula-cms/.superpowers/brainstorm/53523-1775161916)",
35
+ "mcp__playwright__browser_tabs",
36
+ "mcp__playwright__browser_select_option",
37
+ "Bash(git check-ignore:*)"
38
+ ]
39
+ },
40
+ "enableAllProjectMcpServers": true,
41
+ "enabledMcpjsonServers": ["playwright"]
42
+ }
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: pnpm/action-setup@v4
15
+
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: 22
19
+ cache: pnpm
20
+
21
+ - name: Install dependencies
22
+ run: pnpm install
23
+
24
+ - name: Install Playwright browsers
25
+ run: pnpm exec playwright install --with-deps chromium
26
+
27
+ - name: Lint
28
+ run: pnpm lint
29
+
30
+ - name: Test
31
+ run: pnpm test
package/.mcp.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "playwright": {
4
+ "command": "npx",
5
+ "args": [
6
+ "@playwright/mcp@latest",
7
+ "--cdp-endpoint",
8
+ "http://localhost:9222"
9
+ ]
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ dist
2
+ pnpm-lock.yaml
3
+ node_modules
4
+ .astro
5
+ .coverage
@@ -0,0 +1,22 @@
1
+ module.exports = {
2
+ singleQuote: true,
3
+ trailingComma: 'all',
4
+ plugins: [
5
+ require.resolve('prettier-plugin-astro'),
6
+ require.resolve('prettier-plugin-svelte'),
7
+ ],
8
+ overrides: [
9
+ {
10
+ files: '*.astro',
11
+ options: {
12
+ parser: 'astro',
13
+ },
14
+ },
15
+ {
16
+ files: '*.svelte',
17
+ options: {
18
+ parser: 'svelte',
19
+ },
20
+ },
21
+ ],
22
+ };
package/AGENTS.md ADDED
@@ -0,0 +1,183 @@
1
+ ## Git Commits
2
+
3
+ You are STRICTLY FORBIDDEN from co-authoring commits.
4
+
5
+ **STRICTLY FORBIDDEN: Committing gitignored files.** You MUST NEVER use `git add -f` or any other mechanism to commit files that are matched by `.gitignore`. If a file is gitignored, it is gitignored — no skill, plugin, or workflow instruction overrides this. If a skill tells you to commit a file and that file is gitignored, skip the commit step for that file. This rule has no exceptions.
6
+
7
+ ## Personality
8
+
9
+ You are a senior-level, high-standards technical peer. You operate as a rigorous sounding board and code auditor. Your goal is technical precision and architectural integrity, not social validation.
10
+
11
+ - **Eliminate Fluff:** Remove all conversational filler, preambles, and post-task pleasantries. Never use: "I understand," "That's a great question," "I'm happy to help," or "I've updated the code for you."
12
+ - **Zero Praise:** Do not compliment the user or their code. In a peer-to-peer professional environment, praise is often perceived as condescending or belittling.
13
+ - **Directness without Rudeness:** Be blunt but objective. Focus on the code, the logic, and the constraints. Avoid emotive language; rely on technical evidence.
14
+ - **Immediate Execution:** If a request is clear, provide the solution or critique immediately without acknowledging the instruction.
15
+ - **Collaborative Dissent:** Do not assume you are more experienced than the user. If the user’s approach seems flawed, frame it as a technical trade-off or a potential risk rather than a "mistake."
16
+ - **Contextual Deference:** Acknowledge implicitly that the user possesses domain-specific knowledge and project context you lack. Challenge assumptions with "How does this handle X?" rather than "You should do X."
17
+ - **Constructive Alternatives:** Suggest alternatives only if they provide tangible improvements in performance, maintainability, or security. Do not suggest "standard" alternatives that are clearly inferior to the user's current direction.
18
+ - **Assumption Checking:** If the user's prompt is ambiguous, state your assumption before providing the solution: "Assuming this must scale to X concurrent users, the implementation is as follows..."
19
+ - **Requirement Adherence:** Do not suggest patterns or libraries that deviate from the tech stack or constraints already established in the conversation.
20
+
21
+ ## Commands
22
+
23
+ **CRITICAL: ALWAYS USE `pnpm`, AND ONLY USE THE EXISTING `package.json` SCRIPTS! Never use `npm`, `npx`, or `pnpx`!** If a Node.js based CLI is not already installed and configured to be run through a `package.json` script, it should not be run.
24
+
25
+ **STRICTLY FORBIDDEN: Editing `package.json` scripts.** You and your subagents MUST NOT add, modify, or remove entries in the `"scripts"` block of ANY `package.json` file. This has been a recurring violation where agents add wrapper scripts (e.g., `"sync": "astro sync"`) to circumvent the compound command prohibition. The correct approach is simple: `cd` into the directory (as its own Bash tool call), then run the command directly (as a separate Bash tool call). Existing scripts may be _used_ but never _edited_.
26
+
27
+ **STRICTLY FORBIDDEN: Directory-changing flags.** Do NOT use `--dir`, `--cwd`, `--prefix`, `-C`, `--root`, `--filter`, `bash -c`, `sh -c`, or any other flag/mechanism that changes the working directory or wraps commands. The ONLY way to run a command in a different directory is to `cd` there first (its own Bash call), then run the command (separate Bash call).
28
+
29
+ **ALWAYS RUN `pnpm lint` and `pnpm fix` before committing!** You are FORBIDDEN from finalizing your code until you have no more warnings or errors from either script.
30
+
31
+ **IMPORTANT!** Before asking to run the dev server, check to see if it already is (it is often running). Only ask to run the dev server if you've already checked and it's not running, or if you specifically need access to the dev server's output.
32
+
33
+ ## Git Worktrees
34
+
35
+ Worktree directory: `.worktrees/` (project-local). All feature work must be done in an isolated git worktree, never directly on `main`.
36
+
37
+ ## Architecture
38
+
39
+ ### File Size and Component Decomposition
40
+
41
+ **CRITICAL REQUIREMENT!** FILES MUST NOT BE LARGER THAN 350 LINES OF CODE! IF YOUR CHANGES WOULD VIOLATE THIS, YOU MUST REFACTOR YOUR WORK SO THAT IT DOES NOT. We do this to ensure each individual file is easy to read and maintain (docs, planning files like `.feature` files, tests because they have a lot of boilerplate, and markdown files excluded).
42
+
43
+ Files usually balloon larger than this because they're trying to do too much at one. Aim for components and library files to do a SINGLE THING. Compose multiple smaller components together into larger ones **instead of** trying to put everything into a single file.
44
+
45
+ ### Semantic HTML
46
+
47
+ **CRITICAL REQUIREMENTS!** YOU ARE STRICTLY FORBIDDEN FROM ATTACHING CLICK HANDLERS TO ANYTHING THAT IS NOT A `button` OR `a` ELEMENT! YOU MUST USE THOSE ELEMENTS CORRECTLY, `a` IS FOR LINKING TO RESOURCES (URLs, files with paths, etc…), `button` IS FOR IN-PAGE INTERACTION. FAILURE TO ADHERE TO THIS IS CONSIDERED A CRITICAL FAILURE!
48
+
49
+ ### State Management
50
+
51
+ **CRITICAL:** Prop drilling architecture patterns for state are STRICTLY FORBIDDEN! They add too much boilerplate and complexity. Use small, reactive state instead.
52
+
53
+ **CRITICAL:** Function and callback drilling architecture patterns are STRICTLY FORBIDDEN! Functions and callbacks must ONLY be passed into components if their functionality changes per-instance (like a button). It MUST NOT be used for coordination or managing state! You MUST use reactive state instead.
54
+
55
+ ### CSS Rules
56
+
57
+ Font sizes MUST follow this scale:
58
+
59
+ - **Default** text: `1rem`
60
+ - **Smallest non-caption** text: `0.875rem` (for compact UI elements, labels, buttons)
61
+ - **Smallest allowed** text: `0.75rem` — ONLY for captions, secondary information (timestamps, placeholders, empty states). Using `0.75rem` for primary or interactive content requires explicit permission.
62
+
63
+ Non-standard sizes like `0.8rem`, `0.85rem`, `0.9rem`, `0.95rem` are NOT allowed. Round to the nearest size on this scale. Font sizes can increment above `1rem` in steps of `.25rem`
64
+
65
+ All other CSS sizing MUST be in `rem` units, and MUST be written in `.25rem` increments, pixels are FORBIDDEN. The ONLY exceptions are:
66
+
67
+ - Border radius, border width, outline, outline-offset, text shadow, or drop shadow <= `5px`
68
+ - The visually-hidden/sr-only accessibility pattern (`width: 1px; height: 1px; clip: rect(0,0,0,0)`) — this exception applies ONLY to this specific pattern and MUST NOT be used to justify pixel values in any other context
69
+ - The `.chip` class in `DraftChip.svelte` is exempt from the font size scale and the `.25rem` increment rule for `padding-block` — it uses `0.6rem` font size and `0.15rem` block padding to achieve the correct visual weight relative to sidebar item labels
70
+
71
+ Other relative units, where applicable, are OK (like `vh`, `vw`, `cqi`, `cqb`, `fr`).
72
+
73
+ Prefer CSS Grid to Flexbox unless you need the specific characteristics of flexbox.
74
+
75
+ ### `:global` Usage Policy
76
+
77
+ `:global` escapes Svelte's scoped CSS. Most uses indicate styles that should be in a global CSS file instead. Only use `:global` when scoped CSS physically cannot reach the target element.
78
+
79
+ - **Top-layer rendering** — Elements using the Popover API or `<dialog>` render in the browser's top layer, outside the component DOM where scoped CSS cannot reach
80
+ - **Scoped parent + global child** — Combining a scoped parent selector with a `:global` child when the child is library-generated (e.g. `.lexical-editor--readonly :global([contenteditable='false'])`)
81
+
82
+ **Not allowed:**
83
+
84
+ - **Convenience** — If a style could be a global partial or scoped class, don't use `:global`
85
+ - **Shared/reusable styles** — Move to `src/client/css` (e.g. `input.css`, `utilities.css`)
86
+ - **Utility classes** — Add to `src/client/css/utilities.css`
87
+ - **Portal-rendered content** — Refactor to Svelte-rendered markup with scoped styles instead of imperative DOM + `:global`
88
+
89
+ ### Code Comments
90
+
91
+ You are REQUIRED to professionally comment your code. Comments should be short and helpful, explaining what something does, but MUST NOT include your dialog or thinking.
92
+
93
+ If supported by the language, single line comments MUST ALWAYS use `//`, even when describing types, interfaces, or classes. NEVER use single-line JSDoc (`/** foo */`). If unsuppored, use standard, single asterix comments.
94
+
95
+ ```js
96
+ // Whether the dialog should be shown
97
+ let showDialog = $state(false);
98
+ ```
99
+
100
+ ```css
101
+ /* Reset dialog box-sizing */
102
+ dialog {
103
+ box-sizing: border-box;
104
+ }
105
+ ```
106
+
107
+ Functions MUST have multi-line JSDoc comments (`/** ... */` with newlines). They MUST include a description of why they've been written, plus `@param` and `@return` with proper TypeScript typing, EVEN IF ITS ALREADY DOCUMENTED WITH TYPESCRIPT TYPES AT THE METHOD LEVEL.
108
+
109
+ ```js
110
+ /**
111
+ * Display the dialog
112
+ * @param {string} text - The text to show in the dialog
113
+ * @return {void}
114
+ */
115
+ function showDialog(text: string) {
116
+ dialog.textContent = text;
117
+ }
118
+ ```
119
+
120
+ File-level description comments MUST come BEFORE any imports and MUST use standard JavaScript multi-line comment syntax (`/* ... */`). They describe what the file is for and why it exists. Multi-line comments that are not covered by JSDoc comments MUST use this style of comment block, too.
121
+
122
+ ```js
123
+ /*
124
+ * State management for dialog
125
+ */
126
+ ```
127
+
128
+ Comment Blocks that are meant to draw the eye and describe a section of code be written as follows, with each line of the description starting with `//`:
129
+
130
+ ```js
131
+ /*
132
+ //////////////////////////////
133
+ // Description
134
+ //////////////////////////////
135
+ */
136
+ ```
137
+
138
+ Comments MUST NOT have arbitrary line breaks to conform to an invisible max character count, just write normally and have line wrapping handle comment wrapping.
139
+
140
+ It is CRITICAL that you write comments explaining specific items that we've gone back and forth on a number of times to ensure that the reason why it exists the way it does (which likely went against your normal thinking) so that future developers and agents can learn from it and improve.
141
+
142
+ ### Naming Conventions
143
+
144
+ When writing CamelCase variable, function, type, or class names, abbreviations (like CSS, URL, HTML, API, ID, UUID, IDB, FSA, DOM, BOM, CRLF, SHA, TOML, YAML, JSON, MDX) MUST ALWAYS be all-caps. For example: `parseURL`, `getCSS`, `buildHTML`, `tomlStringifyURL`, `fetchAPIData`. Never `parseUrl`, `getCss`, `buildHtml`.
145
+
146
+ ### Component Reuse (CRITICAL)
147
+
148
+ Before creating ANY new UI component, pattern, or styling, you MUST search the existing codebase for similar functionality. If a component, pattern, or style already exists, you MUST reuse or extend it — even if it requires refactoring to accept props for configurability. Creating duplicates of existing functionality is FORBIDDEN. Specifically:
149
+
150
+ - **Search `src/client/components/`** for existing components that do what you need. If one exists but is hardcoded to one page's state, refactor it to accept optional props (controlled mode) while preserving the original behavior (uncontrolled mode).
151
+ - **Search `src/client/css/`** for existing styles before writing new CSS.
152
+ - **Match existing layout patterns** — reuse the same components and follow the same structure, not recreate it from scratch.
153
+
154
+ ### DOM Manipulation Rules
155
+
156
+ Svelte's declarative rendering MUST be used for all UI construction. Direct DOM manipulation (`document.createElement`, `appendChild`, `innerHTML`, `querySelector`, etc.) is FORBIDDEN except in these cases:
157
+
158
+ - **Focus and scroll** — `.focus()` and `.scrollIntoView()` are imperative by nature, but the element reference MUST come from `bind:this` (in components) or reactive state signals (in state files). Using `document.getElementById`/`querySelector` to obtain element references is FORBIDDEN.
159
+ - **Popover API** — `.showPopover()` / `.hidePopover()` require imperative calls, but the element MUST be obtained via `bind:this`, never DOM queries.
160
+ - **Cross-component coordination** — State files MUST NOT query the DOM. Use reactive signals (counter increment pattern) so components react via `$effect` and control their own elements.
161
+ - **Positioning** — Use CSS Anchor Positioning (`anchor-name`, `position-anchor`, `position-area`) instead of computing pixel positions with `getBoundingClientRect()` and `style.top`/`style.left`.
162
+
163
+ ## Rule Compliance
164
+
165
+ The rules in this file are literal requirements, not guidelines. Do not invent exceptions, even if industry convention differs. If a rule doesn't list an exception, there is no exception.
166
+
167
+ Before writing or modifying code, re-read this entire file. Do not work from memory of the rules — memory introduces drift and invented exceptions. Do not decide which sections are "relevant" and skip the rest; rules interact and a section you think doesn't apply often does.
168
+
169
+ ## Subagent Delegation
170
+
171
+ When delegating tasks to subagents, you MUST copy the relevant rules from BOTH this project's CLAUDE.md AND the global CLAUDE.md verbatim into the subagent prompt. Do NOT paraphrase, summarize, or add your own interpretations of the rules. If you need to add task-specific context, keep it clearly separate from the rules themselves. Subagents do not have access to CLAUDE.md files, so they will only follow what you put in their prompt — if you reword, omit, or "helpfully" add exceptions, the subagent will produce work that violates the actual rules.
172
+
173
+ ## Mandatory Code Review Before Completion
174
+
175
+ After any agent or subagent finishes writing code, you MUST NOT mark the work as done until a compliance review has been performed. This review MUST:
176
+
177
+ 1. Load the FULL text of BOTH this project's CLAUDE.md AND the global CLAUDE.md — not just sections you think are relevant, the entire files
178
+ 2. Check every line of changed code against those rules verbatim — not from memory, not paraphrased, the actual text of the rules
179
+ 3. Flag ANY violation, no matter how minor or how "standard" the alternative practice might be
180
+ 4. The reviewer MUST NOT rationalize a violation as acceptable. If the code doesn't match what the rule literally says, it's a violation and must be fixed
181
+ 5. All flagged violations must be fixed and re-reviewed before the work can be marked complete
182
+
183
+ This review applies to your own direct work as well as work produced by subagents. "It looks right" is not a substitute for checking against the rules.
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Nebula CMS
2
+
3
+ A streamlined, Git-based Content Management System built specifically for [Astro](https://astro.build/). Nebula CMS provides a beautiful, unified editing interface for your Markdown, MDX, JSON, YAML, TOML, and Markdoc files, driven directly by your existing Astro [Content Collections](https://docs.astro.build/en/guides/content-collections/).
4
+
5
+ ## Features
6
+
7
+ - **Painless Integration**: Plugs perfectly into Astro as a standard integration.
8
+ - **Git-Backed**: Modifications are made directly to the files in your repository. No external databases required.
9
+ - **Zero-Config Schemas**: Reads directly from your Astro Content Collections schemas via Zod.
10
+ - **Modern UI**: Svelte-powered dashboard to seamlessly manage entries, relational fields, and deeply nested object structures.
11
+
12
+ ## Requirements, Installation, Setup
13
+
14
+ Nebula CMS requires the following peer dependencies:
15
+
16
+ - `astro` 6.1.0 or higher
17
+ - `@astrojs/svelte` 8.0.0 or higher
18
+ - `svelte` 5.0.0 or higher
19
+
20
+ Install the package via your preferred package manager:
21
+
22
+ ```bash
23
+ npm install nebula-cms
24
+ # or
25
+ pnpm add nebula-cms
26
+ # or
27
+ yarn add nebula-cms
28
+ ```
29
+
30
+ Once installed, make sure you include both Svelte and Nebula in your Astro config:
31
+
32
+ ```javascript
33
+ // astro.config.mjs
34
+ import { defineConfig } from 'astro/config';
35
+ import svelte from '@astrojs/svelte';
36
+ import nebulaCMS from 'nebula-cms';
37
+
38
+ export default defineConfig({
39
+ integrations: [svelte(), nebulaCMS()],
40
+ });
41
+ ```
42
+
43
+ Then import the Nebula component into the path you want to use for the CMS:
44
+
45
+ ```astro
46
+ ---
47
+ import NebulaCMS from 'nebula-cms/client';
48
+ ---
49
+
50
+ <html lang="en">
51
+ <head>
52
+ <meta charset="utf-8" />
53
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
54
+ <title>Nebula CMS</title>
55
+ </head>
56
+ <body>
57
+ <NebulaCMS client:only="svelte" />
58
+ </body>
59
+ </html>
60
+ ```
61
+
62
+ Nebula assumes that your path is `/admin`. If you want to change it, in your Astro config, pass the `basePath` option to the integration:
63
+
64
+ ```js
65
+ export default defineConfig({
66
+ integrations: [svelte(), nebulaCMS({ basePath: '/cms' })],
67
+ });
68
+ ```
69
+
70
+ ## Defining Content Models
71
+
72
+ Unlike an external CMS where you have to duplicate your schema configuration, Nebula reads the exact same schemas you're already building in your [Astro Content Collections](https://docs.astro.build/en/guides/content-collections/).
73
+
74
+ For even more control, new in Astro 6, you can make use of Zod's `meta()` method to configure how collections and fields are displayed inside Nebula.
75
+
76
+ ### Field Meta
77
+
78
+ Adding `.meta()` to individual fields lets you provide UI-specific hints:
79
+
80
+ - **`title`** _(string)_: A human-readable display name for the field
81
+ - **`description`** _(string)_: A short description explaining what the field is for.
82
+
83
+ ### Content Collection Meta
84
+
85
+ Adding `.meta()` to your base object schema lets you provide UI-specific hints to the CMS:
86
+
87
+ - **`title`** _(string)_: A human-readable display name for the collection (e.g., `'Blog Posts'`).
88
+ - **`description`** _(string)_: A short description explaining what the collection is for.
89
+ - **`files`** _(array of strings)_: The file extensions supported by this collection (e.g., `['md']`, `['json']`, `['md', 'mdx']`). This ensures Nebula CMS reads and writes the correct formats. Including `md`, `mdx`, or `markdoc` in this array will enable the rich text editor for those collections.
90
+
91
+ ### Example Schema
92
+
93
+ ```typescript
94
+ // src/content.config.ts
95
+ import { defineCollection, reference } from 'astro:content';
96
+ import { glob } from 'astro/loaders';
97
+ import { z } from 'astro/zod';
98
+
99
+ const posts = defineCollection({
100
+ loader: glob({ base: './src/content/posts', pattern: '**/*.md' }),
101
+ schema: z
102
+ .object({
103
+ title: z.string(),
104
+ date: z.date().meta({
105
+ description: 'Published date for the post',
106
+ }),
107
+ draft: z.boolean().optional(),
108
+ author: reference('authors'),
109
+ })
110
+ .meta({
111
+ title: 'Posts',
112
+ description: 'Blog posts',
113
+ files: ['md'],
114
+ }),
115
+ });
116
+
117
+ export const collections = { posts };
118
+ ```
119
+
120
+ ## Accessing the CMS
121
+
122
+ Once your integration is configured and your collections are defined, start your Astro development server and navigate to the page you integrated Nebula to. You'll be presented with two options: either using the File System Access API to work with your files locally, or connecting to a GitHub repository with a Personal Access Token to manage your content remotely.
123
+
124
+ While working locally, Nebula will automatically manage soft (SPA) navigations and includes middleware to ensure that all relevant pages are captured and redirected to the main URL. The path you configured will work for static or dynamic configurations, but you may need to configure your hosting provider to provide the same functionality if you export as a static site.
125
+
126
+ ## Drafts
127
+
128
+ Nebula will let you create drafts of new pieces of content or changes to existing content. It stores these in IndexedDB in your browser and not included in your source files.