@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
@@ -1,5 +1,3 @@
1
- import type { TimeOfDay, Setting, Transition } from "./types/blocks"
2
-
3
1
  export interface BlockDefinition {
4
2
  type: string
5
3
  label: string
@@ -60,87 +58,3 @@ BlockRegistry.register({
60
58
  description: "Code block with syntax highlighting",
61
59
  defaultProps: { code: "", language: "typescript" }
62
60
  })
63
-
64
- BlockRegistry.register({
65
- type: "ScriptBlock",
66
- label: "Script",
67
- description: "Script block with title, synopsis, characters, and plot tracking",
68
- defaultProps: {
69
- title: "",
70
- synopsis: "",
71
- characters: [],
72
- aPlot: "",
73
- bPlot: "",
74
- cPlot: "",
75
- stinger: ""
76
- },
77
- allowChildren: true
78
- })
79
-
80
- BlockRegistry.register({
81
- type: "SceneBlock",
82
- label: "Scene",
83
- description: "Scene block with location, time, setting, and transition",
84
- defaultProps: {
85
- location: "",
86
- timeOfDay: "OTHER" as TimeOfDay,
87
- setting: "OTHER" as Setting,
88
- transition: "NONE" as Transition,
89
- description: ""
90
- },
91
- allowChildren: true
92
- })
93
-
94
- BlockRegistry.register({
95
- type: "DialogueBlock",
96
- label: "Dialogue",
97
- description: "Dialogue block with character, parenthetical, and line",
98
- defaultProps: { character: "", parenthetical: "", line: "" }
99
- })
100
-
101
- BlockRegistry.register({
102
- type: "ComponentShowcaseBlock",
103
- label: "Component Showcase",
104
- description: "Live component rendering with props injection and multi-framework code examples",
105
- defaultProps: {
106
- componentName: "",
107
- componentPath: "",
108
- framework: "astro",
109
- defaultProps: {},
110
- propsConfig: {},
111
- togglingPattern: "tab-segmented",
112
- codeExamples: {}
113
- },
114
- allowChildren: true
115
- })
116
-
117
- BlockRegistry.register({
118
- type: "ApiDocumentationBlock",
119
- label: "API Documentation",
120
- description: "Automated API documentation generation for component props, slots, emits, and theme",
121
- defaultProps: {
122
- componentName: "",
123
- sections: {
124
- props: true,
125
- slots: true,
126
- emits: true,
127
- theme: true,
128
- changelog: false
129
- },
130
- hide: [],
131
- show: []
132
- }
133
- })
134
-
135
- BlockRegistry.register({
136
- type: "LivePreviewBlock",
137
- label: "Live Preview",
138
- description: "Runtime code execution with live editing and error handling",
139
- defaultProps: {
140
- componentCode: "",
141
- framework: "astro",
142
- height: "300px",
143
- width: "100%",
144
- editable: false
145
- }
146
- })
@@ -120,10 +120,15 @@ export interface CalloutBlockProps {
120
120
  children: BaseBlock[]
121
121
  }
122
122
 
123
+ import type { MediaValue } from "./media"
124
+
123
125
  export interface ImageBlockProps {
124
- src: string
126
+ src: string | MediaValue
125
127
  alt: string
126
128
  caption?: string
129
+ width?: number
130
+ height?: number
131
+ aspectRatio?: string
127
132
  }
128
133
 
129
134
  export interface CodeBlockProps {
@@ -0,0 +1,14 @@
1
+ export type BylineRole =
2
+ | "written_by"
3
+ | "reviewed_by"
4
+ | "edited_by"
5
+ | "illustrated_by"
6
+ | "translated_by"
7
+ | "curated_by"
8
+
9
+ export interface BylineCredit {
10
+ userId: string
11
+ role: BylineRole
12
+ customTitle?: string
13
+ displayOrder?: number
14
+ }
@@ -0,0 +1,13 @@
1
+ export interface MediaValue {
2
+ id: string
3
+ src: string
4
+ alt: string
5
+ width?: number
6
+ height?: number
7
+ format?: string
8
+ size?: number
9
+ blurhash?: string
10
+ previewUrl?: string
11
+ provider: "r2" | "external" | "local"
12
+ meta?: Record<string, unknown>
13
+ }
@@ -0,0 +1,20 @@
1
+ import type { Localized } from "./pages"
2
+
3
+ export interface TaxonomyTerm {
4
+ id: string
5
+ taxonomy: string
6
+ slug: string
7
+ label: Localized | string
8
+ description?: Localized | string | null
9
+ parentId?: string | null
10
+ displayOrder?: number
11
+ children?: TaxonomyTerm[]
12
+ count?: number
13
+ createdAt?: Date
14
+ updatedAt?: Date
15
+ }
16
+
17
+ export interface TaxonomyQueryOptions {
18
+ includeCounts?: boolean
19
+ locale?: string
20
+ }
@@ -0,0 +1,57 @@
1
+ import { and, isNotNull, isNull, lt, lte } from "drizzle-orm"
2
+ import { pageDraftLocks } from "../schema/page_draft_locks.ts"
3
+ import { pages } from "../schema/pages.ts"
4
+
5
+ export interface CmsCronResult {
6
+ expiredLocksCleaned: number
7
+ scheduledPagesActive: number
8
+ timestamp: string
9
+ }
10
+
11
+ /**
12
+ * Handles the periodic Cloudflare Worker cron trigger (e.g. "* * * * *").
13
+ *
14
+ * - Cleans up expired page draft locks
15
+ * - Checks for active scheduled pages
16
+ */
17
+ export async function handleCmsCron(
18
+ _event: any,
19
+ _env: any,
20
+ _ctx: any,
21
+ db: any
22
+ ): Promise<CmsCronResult> {
23
+ const result: CmsCronResult = {
24
+ expiredLocksCleaned: 0,
25
+ scheduledPagesActive: 0,
26
+ timestamp: new Date().toISOString()
27
+ }
28
+
29
+ if (!db) return result
30
+
31
+ try {
32
+ const now = new Date()
33
+
34
+ // 1. Purge expired draft edit locks
35
+ const deletedLocks = await db
36
+ .delete(pageDraftLocks)
37
+ .where(lt(pageDraftLocks.expiresAt, now))
38
+ .returning({ pageId: pageDraftLocks.pageId })
39
+
40
+ result.expiredLocksCleaned = deletedLocks.length
41
+
42
+ // 2. Query active published pages in current window
43
+ const activeScheduled = await db
44
+ .select({ id: pages.id, slug: pages.slug })
45
+ .from(pages)
46
+ .where(
47
+ and(isNull(pages.deletedAt), isNotNull(pages.publishedVersionId), lte(pages.postedAt, now))
48
+ )
49
+ .limit(10)
50
+
51
+ result.scheduledPagesActive = activeScheduled.length
52
+ } catch (error) {
53
+ console.error("[CmsCron] Error executing scheduled tasks:", error)
54
+ }
55
+
56
+ return result
57
+ }
@@ -0,0 +1,116 @@
1
+ import { blocksToMarkdown } from "../markdown/index.ts"
2
+ import { pages } from "../schema/pages.ts"
3
+ import { eq, and, isNull, isNotNull } from "drizzle-orm"
4
+
5
+ export interface AgentMarkdownOptions {
6
+ siteUrl?: string
7
+ llmsUrl?: string
8
+ sourceUrl?: string
9
+ }
10
+
11
+ function resolveLocalized(val: unknown, locale: string = "en"): string {
12
+ if (!val) return ""
13
+ if (typeof val === "string") return val
14
+ if (typeof val === "object" && val !== null) {
15
+ const rec = val as Record<string, string>
16
+ return rec[locale] || rec.en || Object.values(rec)[0] || ""
17
+ }
18
+ return typeof val === "number" ? String(val) : ""
19
+ }
20
+
21
+ /**
22
+ * Renders a CMS page record into clean, agent-ready Markdown with YAML frontmatter.
23
+ */
24
+ export function renderPageAsMarkdown(
25
+ page: any,
26
+ locale: string = "en",
27
+ options: AgentMarkdownOptions = {}
28
+ ): string {
29
+ const { siteUrl = "https://rimelight.com", llmsUrl = "/llms.txt", sourceUrl } = options
30
+
31
+ let contentObj = page.content
32
+ if (typeof contentObj === "string") {
33
+ try {
34
+ contentObj = JSON.parse(contentObj)
35
+ } catch {
36
+ contentObj = { blocks: [], properties: {} }
37
+ }
38
+ } else if (!contentObj || typeof contentObj !== "object") {
39
+ contentObj = { blocks: [], properties: {} }
40
+ }
41
+
42
+ const blocks = Array.isArray(contentObj.blocks) ? contentObj.blocks : []
43
+ const properties = contentObj.properties || {}
44
+
45
+ const title = resolveLocalized(page.title, locale) || properties.title || "Untitled"
46
+ const description = resolveLocalized(page.description, locale) || properties.description || ""
47
+ const version = properties.version || page.templateVersion
48
+ const markdownBody = blocksToMarkdown(blocks)
49
+
50
+ const frontmatter = [
51
+ "---",
52
+ `title: ${JSON.stringify(title)}`,
53
+ ...(description ? [`description: ${JSON.stringify(description)}`] : []),
54
+ ...(version ? [`version: ${JSON.stringify(version)}`] : []),
55
+ ...(page.type ? [`type: ${JSON.stringify(page.type)}`] : []),
56
+ "---",
57
+ "",
58
+ "> Documentation Index",
59
+ `> Fetch the complete documentation index at: ${new URL(llmsUrl, siteUrl).href}`,
60
+ "> Use this file to discover all available pages and sections.",
61
+ "",
62
+ `# ${title}`,
63
+ "",
64
+ markdownBody,
65
+ ""
66
+ ]
67
+
68
+ if (sourceUrl) {
69
+ frontmatter.push(`Source: ${new URL(sourceUrl, siteUrl).href}`, "")
70
+ }
71
+
72
+ return frontmatter.join("\n")
73
+ }
74
+
75
+ /**
76
+ * Collate all published pages of a given type into a single corpus markdown document.
77
+ */
78
+ export async function renderCorpusMarkdown(
79
+ db: any,
80
+ options: {
81
+ type?: string
82
+ locale?: string
83
+ siteUrl?: string
84
+ title?: string
85
+ description?: string
86
+ } = {}
87
+ ): Promise<string> {
88
+ const {
89
+ type = "doc",
90
+ locale = "en",
91
+ siteUrl = "https://rimelight.com",
92
+ title = "Rimelight Documentation Corpus",
93
+ description = "Complete single-corpus documentation for AI agents."
94
+ } = options
95
+
96
+ if (!db) return `# ${title}\n\n${description}`
97
+
98
+ const conditions = [isNull(pages.deletedAt), isNotNull(pages.publishedVersionId)]
99
+ if (type) {
100
+ conditions.push(eq(pages.type, type))
101
+ }
102
+
103
+ const rows = await db
104
+ .select()
105
+ .from(pages)
106
+ .where(and(...conditions))
107
+
108
+ const parts = [`# ${title}`, "", description, "", `Generated for site: ${siteUrl}`, "", "---", ""]
109
+
110
+ for (const page of rows) {
111
+ const pageMd = renderPageAsMarkdown(page, locale, { siteUrl })
112
+ parts.push(pageMd, "", "---", "")
113
+ }
114
+
115
+ return parts.join("\n")
116
+ }
@@ -0,0 +1,25 @@
1
+ import type { SidebarItem, SidebarScope } from "@rimelight/ui/components/sidebar/sidebar.ts"
2
+ import type { BadgeProps } from "@rimelight/ui/components/badge/badge.ts"
3
+
4
+ export interface EditLinkConfig {
5
+ baseUrl: string
6
+ }
7
+
8
+ export interface TableOfContentsConfig {
9
+ minHeadingLevel?: number
10
+ maxHeadingLevel?: number
11
+ }
12
+
13
+ export interface DocsConfig {
14
+ basePath?: string
15
+ sidebar:
16
+ | (SidebarItem | SidebarScope)[]
17
+ | {
18
+ scopes: SidebarScope[]
19
+ }
20
+ editLink?: EditLinkConfig
21
+ tableOfContents?: false | TableOfContentsConfig
22
+ }
23
+
24
+ export const defineDocsConfig = <const T extends DocsConfig>(config: T): T => config
25
+ export type { BadgeProps, SidebarItem, SidebarScope }
@@ -0,0 +1,5 @@
1
+ export * from "./config.ts"
2
+ export * from "./sidebar.ts"
3
+ export * from "./routing.ts"
4
+ export * from "./toc.ts"
5
+ export * from "./agent.ts"
@@ -0,0 +1,104 @@
1
+ export interface DocEntry {
2
+ id?: string
3
+ slug?: string
4
+ version?: string
5
+ properties?: Record<string, any>
6
+ [key: string]: any
7
+ }
8
+
9
+ export function getDocsVersions(entries: (DocEntry | string)[], locale?: string): string[] {
10
+ const versions = new Set<string>()
11
+ for (const entry of entries) {
12
+ if (typeof entry === "string") {
13
+ const parts = entry.replace(/\\/g, "/").split("/")
14
+ if (parts[0]?.startsWith("v")) {
15
+ versions.add(parts[0])
16
+ }
17
+ continue
18
+ }
19
+
20
+ if (entry.version && entry.version.startsWith("v")) {
21
+ versions.add(entry.version)
22
+ continue
23
+ }
24
+
25
+ if (entry.properties?.version && String(entry.properties.version).startsWith("v")) {
26
+ versions.add(String(entry.properties.version))
27
+ continue
28
+ }
29
+
30
+ const path = (entry.slug || entry.id || "").replace(/\\/g, "/")
31
+ const parts = path.split("/")
32
+ if (locale && parts[1] === locale && parts[0]?.startsWith("v")) {
33
+ versions.add(parts[0])
34
+ } else if (parts[0]?.startsWith("v")) {
35
+ versions.add(parts[0])
36
+ }
37
+ }
38
+ return Array.from(versions).toSorted((a, b) => {
39
+ return b.localeCompare(a, undefined, { numeric: true, sensitivity: "base" })
40
+ })
41
+ }
42
+
43
+ export function parseDocsSlug(slug: string | undefined, versions: string[]) {
44
+ if (versions.length === 0) {
45
+ return {
46
+ activeVersion: "",
47
+ contentPath: slug || "index",
48
+ includeVersionInUrl: false
49
+ }
50
+ }
51
+
52
+ const defaultVersion = versions[0]
53
+ const isMultiVersion = versions.length > 1
54
+
55
+ if (!slug) {
56
+ return {
57
+ activeVersion: defaultVersion,
58
+ contentPath: "index",
59
+ includeVersionInUrl: isMultiVersion
60
+ }
61
+ }
62
+
63
+ const parts = slug.split("/")
64
+ const firstPart = parts[0]
65
+
66
+ if (firstPart && versions.includes(firstPart)) {
67
+ return {
68
+ activeVersion: firstPart,
69
+ contentPath: parts.slice(1).join("/") || "index",
70
+ includeVersionInUrl: isMultiVersion
71
+ }
72
+ }
73
+
74
+ return {
75
+ activeVersion: defaultVersion,
76
+ contentPath: slug || "index",
77
+ includeVersionInUrl: isMultiVersion
78
+ }
79
+ }
80
+
81
+ export function formatDocsUrl(
82
+ href: string,
83
+ locale: string,
84
+ activeVersion?: string,
85
+ includeVersionInUrl?: boolean
86
+ ): string {
87
+ let url = href.replace("{locale}", locale)
88
+ if (activeVersion && includeVersionInUrl) {
89
+ if (url.includes(`/${activeVersion}/`) || url.endsWith(`/${activeVersion}`)) {
90
+ return url
91
+ }
92
+ const docsPattern = new RegExp(`(/${locale}/docs)(/|$)`)
93
+ const companyDocsPattern = new RegExp(`(/${locale}/company/docs)(/|$)`)
94
+
95
+ if (companyDocsPattern.test(url)) {
96
+ url = url.replace(companyDocsPattern, `$1/${activeVersion}$2`)
97
+ } else if (docsPattern.test(url)) {
98
+ url = url.replace(docsPattern, `$1/${activeVersion}$2`)
99
+ }
100
+
101
+ url = url.replace(/\/{2,}/g, "/")
102
+ }
103
+ return url
104
+ }