@rimelight/cms 0.0.2 → 0.0.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 (112) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +16 -14
  3. package/src/admin/api/media-file.ts +39 -0
  4. package/src/admin/api/media.ts +199 -0
  5. package/src/admin/layouts/CMSDashboardLayout.astro +178 -0
  6. package/src/admin/layouts/DefaultParentLayout.astro +50 -0
  7. package/src/admin/pages/assets.astro +424 -0
  8. package/src/admin/pages/byline-schema.astro +62 -0
  9. package/src/admin/pages/bylines.astro +16 -0
  10. package/src/admin/pages/categories.astro +21 -0
  11. package/src/admin/pages/content-types.astro +55 -0
  12. package/src/admin/pages/dashboard.astro +61 -0
  13. package/src/admin/pages/pages-edit.astro +595 -0
  14. package/src/admin/pages/pages-index.astro +146 -0
  15. package/src/admin/pages/pages-new.astro +130 -0
  16. package/src/admin/pages/pages-preview.astro +111 -0
  17. package/src/admin/pages/pages-review.astro +236 -0
  18. package/src/admin/pages/settings.astro +203 -0
  19. package/src/admin/pages/tags.astro +21 -0
  20. package/src/admin/pages/templates-edit.astro +234 -0
  21. package/src/admin/pages/templates-index.astro +95 -0
  22. package/src/admin/pages/templates-new.astro +193 -0
  23. package/src/admin/pages/users.astro +74 -0
  24. package/src/admin/pages/versions.astro +74 -0
  25. package/src/astro/BlockRenderer.astro +44 -102
  26. package/src/astro/PageRenderer.astro +9 -2
  27. package/src/astro/blocks/CalloutBlock.astro +7 -7
  28. package/src/astro/blocks/CardBlock.astro +5 -5
  29. package/src/astro/blocks/CodeBlock.astro +2 -2
  30. package/src/astro/blocks/FileTreeBlock.astro +7 -7
  31. package/src/astro/blocks/ImageBlock.astro +2 -2
  32. package/src/astro/blocks/OrderedListBlock.astro +2 -2
  33. package/src/astro/blocks/ParagraphBlock.astro +3 -3
  34. package/src/astro/blocks/SectionBlock.astro +6 -6
  35. package/src/astro/blocks/StepItemBlock.astro +3 -3
  36. package/src/astro/blocks/StepsBlock.astro +1 -1
  37. package/src/astro/blocks/TableBlock.astro +6 -6
  38. package/src/astro/blocks/TabsBlock.astro +9 -9
  39. package/src/astro/blocks/UnorderedListBlock.astro +2 -2
  40. package/src/astro/blocks/index.ts +35 -0
  41. package/src/client/components/RimelightBlock.tsx +19 -19
  42. package/src/client/components/RimelightBlockPicker.tsx +9 -13
  43. package/src/client/components/RimelightBylinesManager.tsx +438 -0
  44. package/src/client/components/RimelightEditor.tsx +3 -3
  45. package/src/client/components/RimelightPreview.tsx +37 -45
  46. package/src/client/components/RimelightPropertiesPanel.tsx +368 -26
  47. package/src/client/components/RimelightTaxonomyManager.tsx +369 -0
  48. package/src/client/components/RimelightTaxonomyMasterDetail.tsx +412 -0
  49. package/src/client/components/RimelightTemplateEditor.tsx +6 -8
  50. package/src/client/components/editors/CalloutEditor.tsx +2 -4
  51. package/src/client/components/editors/CardsEditor.tsx +2 -4
  52. package/src/client/components/editors/CodeEditor.tsx +1 -3
  53. package/src/client/components/editors/FileTreeEditor.tsx +2 -4
  54. package/src/client/components/editors/ImageEditor.tsx +3 -3
  55. package/src/client/components/editors/ParagraphEditor.tsx +2 -2
  56. package/src/client/components/editors/SceneEditor.tsx +6 -12
  57. package/src/client/components/editors/ScriptEditor.tsx +8 -16
  58. package/src/client/components/editors/SectionEditor.tsx +4 -4
  59. package/src/client/components/editors/StepItemEditor.tsx +2 -2
  60. package/src/client/components/editors/StepsEditor.tsx +2 -4
  61. package/src/client/components/editors/TabItemEditor.tsx +3 -3
  62. package/src/client/components/editors/TableEditor.tsx +5 -5
  63. package/src/client/components/editors/TabsEditor.tsx +2 -4
  64. package/src/client/components/index.ts +3 -0
  65. package/src/client/loader.ts +3 -0
  66. package/src/core/diff.ts +145 -0
  67. package/src/core/excerpt.ts +159 -0
  68. package/src/core/preview.ts +87 -0
  69. package/src/core/registry.ts +0 -86
  70. package/src/core/types/blocks.ts +6 -1
  71. package/src/core/types/bylines.ts +14 -0
  72. package/src/core/types/media.ts +13 -0
  73. package/src/core/types/taxonomies.ts +20 -0
  74. package/src/cron/scheduled.ts +57 -0
  75. package/src/docs/agent.ts +116 -0
  76. package/src/docs/config.ts +25 -0
  77. package/src/docs/index.ts +5 -0
  78. package/src/docs/routing.ts +104 -0
  79. package/src/docs/sidebar.ts +251 -0
  80. package/src/docs/toc.ts +47 -0
  81. package/src/env.d.ts +13 -0
  82. package/src/index.ts +40 -8
  83. package/src/integration.ts +196 -0
  84. package/src/loader/live.ts +254 -0
  85. package/src/markdown/index.ts +0 -1
  86. package/src/mcp/index.ts +228 -0
  87. package/src/schema/bylines.ts +23 -0
  88. package/src/schema/index.ts +3 -4
  89. package/src/schema/page_versions.ts +2 -0
  90. package/src/schema/pages.ts +2 -0
  91. package/src/schema/site_settings.ts +41 -0
  92. package/src/schema/taxonomies.ts +49 -0
  93. package/src/services/bylines.ts +74 -0
  94. package/src/services/search-indexer.ts +61 -0
  95. package/src/services/site-settings.ts +108 -0
  96. package/src/services/taxonomies.ts +276 -0
  97. package/src/storage/index.ts +253 -0
  98. package/src/astro/blocks/ApiDocumentationBlock.astro +0 -350
  99. package/src/astro/blocks/DialogueBlock.astro +0 -23
  100. package/src/astro/blocks/LivePreviewBlock.astro +0 -27
  101. package/src/astro/blocks/SceneBlock.astro +0 -65
  102. package/src/astro/blocks/ScriptBlock.astro +0 -59
  103. package/src/core/template-sync.ts +0 -75
  104. package/src/markdown/parser.ts +0 -347
  105. package/src/migration-tools.ts +0 -334
  106. package/src/migration.ts +0 -155
  107. package/src/schema/component_dependencies.ts +0 -21
  108. package/src/schema/component_metadata.ts +0 -31
  109. package/src/schema/component_metadata_cache.ts +0 -15
  110. package/src/schema/component_versions.ts +0 -19
  111. package/src/search/sync.ts +0 -32
  112. package/src/services/component-metadata.ts +0 -134
@@ -0,0 +1,251 @@
1
+ import type { DocsConfig, SidebarItem, SidebarScope, BadgeProps } from "./config"
2
+
3
+ export interface ResolvedSidebarItem {
4
+ label: string
5
+ href?: string | undefined
6
+ icon?: string | undefined
7
+ badge?: string | BadgeProps | undefined
8
+ active?: boolean | undefined
9
+ children?: ResolvedSidebarItem[] | undefined
10
+ collapsed?: boolean | undefined
11
+ }
12
+
13
+ export interface ResolvedScope {
14
+ id: string
15
+ label: string
16
+ href: string
17
+ icon?: string | undefined
18
+ active?: boolean | undefined
19
+ }
20
+
21
+ function formatDocsUrl(
22
+ href: string,
23
+ locale: string,
24
+ activeVersion?: string,
25
+ includeVersionInUrl?: boolean
26
+ ): string {
27
+ let url = href.replace("{locale}", locale)
28
+ if (activeVersion && includeVersionInUrl) {
29
+ if (url.includes(`/${activeVersion}/`) || url.endsWith(`/${activeVersion}`)) {
30
+ return url
31
+ }
32
+ const docsPattern = new RegExp(`(/${locale}/docs)(/|$)`)
33
+ const companyDocsPattern = new RegExp(`(/${locale}/company/docs)(/|$)`)
34
+
35
+ if (companyDocsPattern.test(url)) {
36
+ url = url.replace(companyDocsPattern, `$1/${activeVersion}$2`)
37
+ } else if (docsPattern.test(url)) {
38
+ url = url.replace(docsPattern, `$1/${activeVersion}$2`)
39
+ }
40
+
41
+ url = url.replace(/\/{2,}/g, "/")
42
+ }
43
+ return url
44
+ }
45
+
46
+ function findFirstHref(
47
+ items: SidebarItem[],
48
+ locale: string,
49
+ activeVersion?: string,
50
+ includeVersionInUrl?: boolean
51
+ ): string | undefined {
52
+ for (const item of items) {
53
+ if (item.href) return formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl)
54
+ const childItems = item.children || item.items
55
+ if (childItems) {
56
+ const found = findFirstHref(childItems, locale, activeVersion, includeVersionInUrl)
57
+ if (found) return found
58
+ }
59
+ }
60
+ return undefined
61
+ }
62
+
63
+ function getScopePrefix(
64
+ items: SidebarItem[],
65
+ locale: string,
66
+ activeVersion?: string,
67
+ includeVersionInUrl?: boolean
68
+ ): string {
69
+ const allHrefs: string[] = []
70
+
71
+ function collect(item: SidebarItem) {
72
+ if (item.href)
73
+ allHrefs.push(formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl))
74
+ const childItems = item.children || item.items
75
+ if (childItems) childItems.forEach(collect)
76
+ }
77
+
78
+ items.forEach(collect)
79
+ if (allHrefs.length === 0) return ""
80
+
81
+ const parts = allHrefs.map((h) => h.split("/").filter(Boolean))
82
+ let commonLen = 0
83
+ const firstPart = parts[0]
84
+ if (!firstPart) return ""
85
+ for (let i = 0; i < firstPart.length; i++) {
86
+ if (parts.every((p) => p[i] === firstPart[i])) commonLen++
87
+ else break
88
+ }
89
+
90
+ return "/" + firstPart.slice(0, commonLen).join("/") + "/"
91
+ }
92
+
93
+ function hasPath(
94
+ items: SidebarItem[],
95
+ locale: string,
96
+ normPath: string,
97
+ activeVersion?: string,
98
+ includeVersionInUrl?: boolean
99
+ ): boolean {
100
+ for (const item of items) {
101
+ if (
102
+ item.href &&
103
+ formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl)
104
+ .replace(/\/$/, "")
105
+ .toLowerCase() === normPath
106
+ ) {
107
+ return true
108
+ }
109
+ const childItems = item.children || item.items
110
+ if (childItems && hasPath(childItems, locale, normPath, activeVersion, includeVersionInUrl)) {
111
+ return true
112
+ }
113
+ }
114
+ return false
115
+ }
116
+
117
+ export async function getDocsSidebar(
118
+ config: DocsConfig,
119
+ locale: string,
120
+ currentPath: string,
121
+ activeVersion?: string,
122
+ includeVersionInUrl?: boolean
123
+ ): Promise<{
124
+ items: ResolvedSidebarItem[]
125
+ scopes: ResolvedScope[]
126
+ activeScope?: ResolvedScope | undefined
127
+ }> {
128
+ const normPath = currentPath.replace(/\/$/, "").toLowerCase()
129
+
130
+ const sidebarScopes: SidebarScope[] = Array.isArray(config.sidebar)
131
+ ? config.sidebar.filter((item): item is SidebarScope => "id" in item || "scopes" in item)
132
+ : config.sidebar.scopes || []
133
+
134
+ const scopes: ResolvedScope[] = sidebarScopes.map((scope) => {
135
+ const id = scope.id || scope.label
136
+ let href = scope.href || ""
137
+ if (!href && scope.items && scope.items.length > 0) {
138
+ href = findFirstHref(scope.items, locale, activeVersion, includeVersionInUrl) || ""
139
+ }
140
+ return {
141
+ id,
142
+ label: scope.label,
143
+ href: formatDocsUrl(href, locale, activeVersion, includeVersionInUrl),
144
+ icon: scope.icon
145
+ }
146
+ })
147
+
148
+ const scopePrefixes: string[] = sidebarScopes.map((scope) =>
149
+ scope.items ? getScopePrefix(scope.items, locale, activeVersion, includeVersionInUrl) : ""
150
+ )
151
+
152
+ let activeScope = scopes.find((s) => normPath === s.href.replace(/\/$/, "").toLowerCase())
153
+ if (!activeScope) {
154
+ for (let i = 0; i < sidebarScopes.length; i++) {
155
+ const scope = sidebarScopes[i]
156
+ if (
157
+ scope &&
158
+ scope.items &&
159
+ hasPath(scope.items, locale, normPath, activeVersion, includeVersionInUrl)
160
+ ) {
161
+ activeScope = scopes[i]
162
+ break
163
+ }
164
+ }
165
+ }
166
+ if (!activeScope) {
167
+ const sorted = [...scopePrefixes.entries()]
168
+ .filter(([, p]) => p)
169
+ .toSorted(([, a], [, b]) => b.length - a.length)
170
+ for (const [i] of sorted) {
171
+ const prefix = scopePrefixes[i]
172
+ if (prefix && normPath.startsWith(prefix.toLowerCase())) {
173
+ activeScope = scopes[i]
174
+ break
175
+ }
176
+ }
177
+ }
178
+ if (!activeScope && scopes.length > 0) {
179
+ activeScope = scopes[0]
180
+ }
181
+ if (activeScope) activeScope.active = true
182
+
183
+ const sidebarConfig = sidebarScopes.find(
184
+ (item) => activeScope && (item.id === activeScope.id || item.label === activeScope.id)
185
+ )
186
+
187
+ const items = sidebarConfig?.items || []
188
+
189
+ async function resolveItem(item: SidebarItem): Promise<ResolvedSidebarItem[]> {
190
+ const resolved: ResolvedSidebarItem = {
191
+ label: item.label || "",
192
+ href: item.href
193
+ ? formatDocsUrl(item.href, locale, activeVersion, includeVersionInUrl)
194
+ : undefined,
195
+ icon: item.icon,
196
+ badge: item.badge,
197
+ collapsed: item.collapsed
198
+ }
199
+
200
+ if (resolved.href) {
201
+ resolved.active = normPath === resolved.href.replace(/\/$/, "").toLowerCase()
202
+ }
203
+
204
+ const childItems = item.children || item.items
205
+ if (childItems) {
206
+ const children: ResolvedSidebarItem[] = []
207
+ const resolvedChildren = await Promise.all(childItems.map((child) => resolveItem(child)))
208
+ for (const res of resolvedChildren) {
209
+ children.push(...res)
210
+ }
211
+ resolved.children = children
212
+ if (!resolved.active) {
213
+ resolved.active = children.some((c) => c.active)
214
+ }
215
+ }
216
+
217
+ return [resolved]
218
+ }
219
+
220
+ const resolvedItems: ResolvedSidebarItem[] = []
221
+ const resolvedAllItems = await Promise.all(items.map((item) => resolveItem(item)))
222
+ for (const res of resolvedAllItems) {
223
+ resolvedItems.push(...res)
224
+ }
225
+
226
+ return { items: resolvedItems, scopes, activeScope }
227
+ }
228
+
229
+ export function getSurroundItems(sidebar: ResolvedSidebarItem[], currentPath: string) {
230
+ const flatItems: { label: string; href: string }[] = []
231
+
232
+ function flatten(items: ResolvedSidebarItem[]) {
233
+ for (const item of items) {
234
+ if (item.href) flatItems.push({ label: item.label, href: item.href })
235
+ if (item.children) flatten(item.children)
236
+ }
237
+ }
238
+
239
+ flatten(sidebar)
240
+
241
+ const normPath = currentPath.replace(/\/$/, "").toLowerCase()
242
+ const currentIndex = flatItems.findIndex(
243
+ (item) => item.href.replace(/\/$/, "").toLowerCase() === normPath
244
+ )
245
+
246
+ return {
247
+ prev: currentIndex > 0 ? flatItems[currentIndex - 1] : null,
248
+ next:
249
+ currentIndex >= 0 && currentIndex < flatItems.length - 1 ? flatItems[currentIndex + 1] : null
250
+ }
251
+ }
@@ -0,0 +1,47 @@
1
+ export interface TableOfContentsHeading {
2
+ depth: number
3
+ slug: string
4
+ text: string
5
+ }
6
+
7
+ export function slugifyHeading(text: string): string {
8
+ return text
9
+ .toString()
10
+ .toLowerCase()
11
+ .normalize("NFD")
12
+ .replace(/[\u0300-\u036f]/g, "")
13
+ .replace(/\s+/g, "-")
14
+ .replace(/[^\w-]+/g, "")
15
+ .replace(/-{2,}/g, "-")
16
+ .replace(/^-+/, "")
17
+ .replace(/-+$/, "")
18
+ }
19
+
20
+ export function extractHeadingsFromBlocks(
21
+ blocks: any[] = [],
22
+ currentDepth = 2
23
+ ): TableOfContentsHeading[] {
24
+ if (!Array.isArray(blocks)) return []
25
+ const headings: TableOfContentsHeading[] = []
26
+
27
+ for (const block of blocks) {
28
+ if (!block) continue
29
+ if (block.type === "SectionBlock") {
30
+ const title = block.props?.title || ""
31
+ const level = Number(block.props?.level) || currentDepth
32
+ if (title) {
33
+ headings.push({
34
+ depth: level,
35
+ slug: slugifyHeading(title),
36
+ text: title
37
+ })
38
+ }
39
+ const children = block.children || block.props?.children || []
40
+ if (Array.isArray(children) && children.length > 0) {
41
+ headings.push(...extractHeadingsFromBlocks(children, Math.min(6, level + 1)))
42
+ }
43
+ }
44
+ }
45
+
46
+ return headings
47
+ }
package/src/env.d.ts CHANGED
@@ -2,3 +2,16 @@ declare module "*.astro" {
2
2
  const component: any
3
3
  export default component
4
4
  }
5
+
6
+ declare module "virtual:rimelight-cms/parent-layout" {
7
+ const layout: any
8
+ export default layout
9
+ }
10
+
11
+ declare module "virtual:rimelight-cms/db" {
12
+ export const db: any
13
+ }
14
+
15
+ declare module "virtual:rimelight-cms/storage" {
16
+ export const storageConfig: any
17
+ }
package/src/index.ts CHANGED
@@ -6,6 +6,9 @@ export * from "./core/types/templates.ts"
6
6
  export * from "./core/types/versioning.ts"
7
7
  export * from "./core/page-definitions.ts"
8
8
 
9
+ // Markdown Serialization & Parsing
10
+ export * from "./markdown/index.ts"
11
+
9
12
  // Re-export specific types for convenience
10
13
  export type {
11
14
  TimeOfDay,
@@ -19,8 +22,6 @@ export type {
19
22
  // Core Services & Registries
20
23
  export * from "./core/registry.ts"
21
24
  export * from "./core/validator.ts"
22
- export * from "./core/template-sync.ts"
23
- export * from "./services/component-metadata.ts"
24
25
 
25
26
  // Auth & Security Guards
26
27
  export * from "./auth/permissions.ts"
@@ -31,15 +32,9 @@ export * from "./auth/filter-templates.ts"
31
32
  // Edge Cache & Search
32
33
  export * from "./cache/purge.ts"
33
34
  export * from "./search/query.ts"
34
- export * from "./search/sync.ts"
35
35
 
36
36
  // Database Schemas & Migration
37
37
  export * from "./schema/index.ts"
38
- export * from "./migration.ts"
39
-
40
- // Markdown Serialization & Parsing
41
- export * from "./markdown/index.ts"
42
- export * from "./migration-tools.ts"
43
38
 
44
39
  // Astro Components
45
40
  export { default as PageRenderer } from "./astro/PageRenderer.astro"
@@ -47,3 +42,40 @@ export { default as BlockRenderer } from "./astro/BlockRenderer.astro"
47
42
 
48
43
  // Toast API
49
44
  export * from "./client/toast.ts"
45
+
46
+ // Excerpts & Summaries
47
+ export * from "./core/excerpt.ts"
48
+
49
+ // Dynamic Site Settings
50
+ export * from "./services/site-settings.ts"
51
+
52
+ // Astro Live Loader & Collections
53
+ export * from "./loader/live.ts"
54
+
55
+ // Cloudflare Worker Cron Dispatcher
56
+ export * from "./cron/scheduled.ts"
57
+
58
+ // Model Context Protocol (MCP) Tools
59
+ export * from "./mcp/index.ts"
60
+
61
+ // Media & Bylines
62
+ export * from "./core/types/media.ts"
63
+ export * from "./core/types/bylines.ts"
64
+ export * from "./services/bylines.ts"
65
+
66
+ // Preview Tokens & Block Diffs
67
+ export * from "./core/preview.ts"
68
+ export * from "./core/diff.ts"
69
+
70
+ // Search Indexer
71
+ export * from "./services/search-indexer.ts"
72
+
73
+ // Taxonomies Engine
74
+ export * from "./core/types/taxonomies.ts"
75
+ export * from "./services/taxonomies.ts"
76
+
77
+ // Documentation Engine & Navigation
78
+ export * from "./docs/index.ts"
79
+
80
+ // Storage Adapters (R2, etc.)
81
+ export * from "./storage/index.ts"
@@ -0,0 +1,196 @@
1
+ import type { AstroIntegration } from "astro"
2
+ import path from "node:path"
3
+ import fs from "node:fs"
4
+ import { fileURLToPath } from "node:url"
5
+ import type { StorageConfig } from "./storage/index"
6
+
7
+ export interface RimelightCmsOptions {
8
+ /**
9
+ * Route prefix for CMS admin pages. Defaults to "/[locale]/cms" (or "/cms" if no locale routing).
10
+ */
11
+ routePrefix?: string | undefined
12
+
13
+ /**
14
+ * Optional outer layout to wrap the CMS dashboard in (e.g. "./src/layouts/AppLayout.astro"). When
15
+ * omitted, renders as a dedicated standalone full-viewport dashboard.
16
+ */
17
+ parentLayout?: string | undefined
18
+
19
+ /**
20
+ * Path to the app database instance module exporting `db`. Defaults to "./src/db/index.ts" or
21
+ * "./src/db".
22
+ */
23
+ db?: string | undefined
24
+
25
+ /**
26
+ * Storage backend configuration (e.g. r2({ binding: "BLOB", publicUrl:
27
+ * "https://media.example.com" })).
28
+ */
29
+ storage?: StorageConfig | undefined
30
+ }
31
+
32
+ export function rimelightCms(options: RimelightCmsOptions = {}): AstroIntegration {
33
+ const routePrefix = options.routePrefix ?? "/[locale]/cms"
34
+ const storageConfig = {
35
+ provider: options.storage?.provider ?? "r2",
36
+ binding: options.storage?.binding ?? "BLOB",
37
+ publicUrl: options.storage?.publicUrl,
38
+ prefix: options.storage?.prefix ?? "media/"
39
+ }
40
+
41
+ return {
42
+ name: "@rimelight/cms",
43
+ hooks: {
44
+ "astro:config:setup": ({ injectRoute, updateConfig, config }) => {
45
+ const rootDir = fileURLToPath(config.root)
46
+
47
+ // Resolve parent layout if provided
48
+ let resolvedParentLayout: string | null = null
49
+ if (options.parentLayout) {
50
+ resolvedParentLayout = path.isAbsolute(options.parentLayout)
51
+ ? options.parentLayout
52
+ : path.resolve(rootDir, options.parentLayout)
53
+ }
54
+
55
+ // Resolve db module if exists
56
+ let resolvedDb: string | null = null
57
+ if (options.db) {
58
+ resolvedDb = path.isAbsolute(options.db) ? options.db : path.resolve(rootDir, options.db)
59
+ } else {
60
+ const defaultDbTs = path.resolve(rootDir, "src/db/index.ts")
61
+ const defaultDbJs = path.resolve(rootDir, "src/db/index.js")
62
+ if (fs.existsSync(defaultDbTs)) {
63
+ resolvedDb = defaultDbTs
64
+ } else if (fs.existsSync(defaultDbJs)) {
65
+ resolvedDb = defaultDbJs
66
+ }
67
+ }
68
+
69
+ // 1. Inject CMS admin routes
70
+ injectRoute({
71
+ pattern: `${routePrefix}`,
72
+ entrypoint: "@rimelight/cms/admin/pages/dashboard.astro"
73
+ })
74
+ injectRoute({
75
+ pattern: `${routePrefix}/pages`,
76
+ entrypoint: "@rimelight/cms/admin/pages/pages-index.astro"
77
+ })
78
+ injectRoute({
79
+ pattern: `${routePrefix}/pages/new`,
80
+ entrypoint: "@rimelight/cms/admin/pages/pages-new.astro"
81
+ })
82
+ injectRoute({
83
+ pattern: `${routePrefix}/pages/[id]/edit`,
84
+ entrypoint: "@rimelight/cms/admin/pages/pages-edit.astro"
85
+ })
86
+ injectRoute({
87
+ pattern: `${routePrefix}/pages/[id]/preview`,
88
+ entrypoint: "@rimelight/cms/admin/pages/pages-preview.astro"
89
+ })
90
+ injectRoute({
91
+ pattern: `${routePrefix}/pages/[id]/review`,
92
+ entrypoint: "@rimelight/cms/admin/pages/pages-review.astro"
93
+ })
94
+ injectRoute({
95
+ pattern: `${routePrefix}/templates`,
96
+ entrypoint: "@rimelight/cms/admin/pages/templates-index.astro"
97
+ })
98
+ injectRoute({
99
+ pattern: `${routePrefix}/templates/new`,
100
+ entrypoint: "@rimelight/cms/admin/pages/templates-new.astro"
101
+ })
102
+ injectRoute({
103
+ pattern: `${routePrefix}/templates/[id]/edit`,
104
+ entrypoint: "@rimelight/cms/admin/pages/templates-edit.astro"
105
+ })
106
+ injectRoute({
107
+ pattern: `${routePrefix}/assets`,
108
+ entrypoint: "@rimelight/cms/admin/pages/assets.astro"
109
+ })
110
+ injectRoute({
111
+ pattern: `${routePrefix}/categories`,
112
+ entrypoint: "@rimelight/cms/admin/pages/categories.astro"
113
+ })
114
+ injectRoute({
115
+ pattern: `${routePrefix}/tags`,
116
+ entrypoint: "@rimelight/cms/admin/pages/tags.astro"
117
+ })
118
+ injectRoute({
119
+ pattern: `${routePrefix}/bylines`,
120
+ entrypoint: "@rimelight/cms/admin/pages/bylines.astro"
121
+ })
122
+ injectRoute({
123
+ pattern: `${routePrefix}/versions`,
124
+ entrypoint: "@rimelight/cms/admin/pages/versions.astro"
125
+ })
126
+ injectRoute({
127
+ pattern: `${routePrefix}/content-types`,
128
+ entrypoint: "@rimelight/cms/admin/pages/content-types.astro"
129
+ })
130
+ injectRoute({
131
+ pattern: `${routePrefix}/byline-schema`,
132
+ entrypoint: "@rimelight/cms/admin/pages/byline-schema.astro"
133
+ })
134
+ injectRoute({
135
+ pattern: `${routePrefix}/users`,
136
+ entrypoint: "@rimelight/cms/admin/pages/users.astro"
137
+ })
138
+ injectRoute({
139
+ pattern: `${routePrefix}/settings`,
140
+ entrypoint: "@rimelight/cms/admin/pages/settings.astro"
141
+ })
142
+
143
+ // Media API Routes
144
+ injectRoute({
145
+ pattern: `${routePrefix}/api/media`,
146
+ entrypoint: "@rimelight/cms/admin/api/media.ts"
147
+ })
148
+ injectRoute({
149
+ pattern: `${routePrefix}/api/media/file/[...key]`,
150
+ entrypoint: "@rimelight/cms/admin/api/media-file.ts"
151
+ })
152
+
153
+ // 2. Vite plugin for virtual modules
154
+ updateConfig({
155
+ vite: {
156
+ plugins: [
157
+ {
158
+ name: "vite-plugin-rimelight-cms-virtuals",
159
+ resolveId(id) {
160
+ if (id === "virtual:rimelight-cms/parent-layout") {
161
+ return "\0virtual:rimelight-cms/parent-layout"
162
+ }
163
+ if (id === "virtual:rimelight-cms/db") {
164
+ return "\0virtual:rimelight-cms/db"
165
+ }
166
+ if (id === "virtual:rimelight-cms/storage") {
167
+ return "\0virtual:rimelight-cms/storage"
168
+ }
169
+ return undefined
170
+ },
171
+ load(id) {
172
+ if (id === "\0virtual:rimelight-cms/parent-layout") {
173
+ if (resolvedParentLayout && fs.existsSync(resolvedParentLayout)) {
174
+ return `export { default } from ${JSON.stringify(resolvedParentLayout)};`
175
+ }
176
+ return `export { default } from "@rimelight/cms/admin/layouts/DefaultParentLayout.astro";`
177
+ }
178
+ if (id === "\0virtual:rimelight-cms/db") {
179
+ if (resolvedDb && fs.existsSync(resolvedDb)) {
180
+ return `export * from ${JSON.stringify(resolvedDb)};`
181
+ }
182
+ return `export const db = null;`
183
+ }
184
+ if (id === "\0virtual:rimelight-cms/storage") {
185
+ return `export const storageConfig = ${JSON.stringify(storageConfig)};`
186
+ }
187
+ return undefined
188
+ }
189
+ }
190
+ ]
191
+ }
192
+ })
193
+ }
194
+ }
195
+ }
196
+ }