@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,193 @@
1
+ ---
2
+ import CMSDashboardLayout from "../layouts/CMSDashboardLayout.astro";
3
+ import RLAButton from "@rimelight/ui/components/button/RLAButton.astro";
4
+ import RLAInput from "@rimelight/ui/components/input/RLAInput.astro";
5
+ import RLATextarea from "@rimelight/ui/components/textarea/RLATextarea.astro";
6
+ import RLASelect from "@rimelight/ui/components/select/RLASelect.astro";
7
+ import { getRelativeLocaleUrl } from "@rimelight/i18n";
8
+
9
+ export const prerender = false;
10
+
11
+ const currentLocale = Astro.currentLocale ?? "en";
12
+
13
+ const defaultRolesList = [
14
+ { name: "admin", displayName: "Administrator", description: "Full system & CMS access" },
15
+ { name: "editor", displayName: "Editor", description: "Can create and publish pages" },
16
+ { name: "author", displayName: "Author", description: "Can create and edit drafts" },
17
+ { name: "reviewer", displayName: "Reviewer", description: "Can review & approve versions" },
18
+ { name: "viewer", displayName: "Viewer", description: "Read-only access" }
19
+ ];
20
+
21
+ const roleOptions = defaultRolesList.map((r) => ({
22
+ label: r.displayName || r.name,
23
+ value: r.name,
24
+ description: r.description || ""
25
+ }));
26
+ ---
27
+
28
+ <CMSDashboardLayout title="Create Template" description="Create a new CMS template" activeSection="templates">
29
+ <div class="max-w-3xl mx-auto space-y-6">
30
+ <form id="template-form" class="bg-elevated/40 border border-default p-6 rounded-2xl shadow space-y-6">
31
+ <div>
32
+ <label class="block text-sm font-medium text-highlighted mb-2">Slug</label>
33
+ <RLAInput name="slug" required />
34
+ </div>
35
+
36
+ <div>
37
+ <label class="block text-sm font-medium text-highlighted mb-2">Title</label>
38
+ <RLAInput name="title" required />
39
+ </div>
40
+
41
+ <div>
42
+ <label class="block text-sm font-medium text-highlighted mb-2">Description</label>
43
+ <RLATextarea name="description" rows="3" />
44
+ </div>
45
+
46
+ <div>
47
+ <label class="block text-sm font-medium text-highlighted mb-2">Page Type</label>
48
+ <RLASelect
49
+ name="pageType"
50
+ placeholder="Select page type..."
51
+ items={[
52
+ { label: "Blog Post", value: "blog" },
53
+ { label: "Wiki Page", value: "wiki" },
54
+ { label: "Documentation", value: "doc" },
55
+ { label: "Announcement", value: "announcement" }
56
+ ]}
57
+ required
58
+ />
59
+ </div>
60
+
61
+ <div class="border-t border-default pt-6 space-y-4">
62
+ <h3 class="text-md font-semibold text-highlighted">Role Permissions</h3>
63
+ <p class="text-xs text-muted mb-4">Define granular user role access for pages instantiated from this template.</p>
64
+
65
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
66
+ <div>
67
+ <label class="block text-sm font-medium text-highlighted mb-1">Who Can Create</label>
68
+ <RLASelect
69
+ name="whoCanCreate"
70
+ placeholder="Select creator roles..."
71
+ multiple={true}
72
+ items={roleOptions}
73
+ />
74
+ </div>
75
+
76
+ <div>
77
+ <label class="block text-sm font-medium text-highlighted mb-1">Who Can Edit</label>
78
+ <RLASelect
79
+ name="whoCanEdit"
80
+ placeholder="Select editor roles..."
81
+ multiple={true}
82
+ items={roleOptions}
83
+ />
84
+ </div>
85
+
86
+ <div>
87
+ <label class="block text-sm font-medium text-highlighted mb-1">Who Can Review</label>
88
+ <RLASelect
89
+ name="whoCanReview"
90
+ placeholder="Select reviewer roles..."
91
+ multiple={true}
92
+ items={roleOptions}
93
+ />
94
+ </div>
95
+
96
+ <div>
97
+ <label class="block text-sm font-medium text-highlighted mb-1">Who Can View</label>
98
+ <RLASelect
99
+ name="whoCanView"
100
+ placeholder="Select viewer roles..."
101
+ multiple={true}
102
+ items={roleOptions}
103
+ />
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <div class="border-t border-default pt-6">
109
+ <rimelight-template-editor id="template-block-editor"></rimelight-template-editor>
110
+ </div>
111
+
112
+ <div class="border-t border-default pt-6 flex items-center justify-end gap-3">
113
+ <RLAButton href={getRelativeLocaleUrl(currentLocale, "/cms/templates")} variant="outline" color="neutral" size="sm">
114
+ Cancel
115
+ </RLAButton>
116
+ <RLAButton variant="solid" color="primary" size="sm" type="submit">
117
+ Create Template
118
+ </RLAButton>
119
+ </div>
120
+ </form>
121
+ </div>
122
+
123
+ <script is:inline define:vars={{ currentLocale }}>
124
+ import('@rimelight/cms/client/loader').then(({ defineCustomElements }) => {
125
+ return defineCustomElements();
126
+ });
127
+
128
+ function parseRoles(rawVal) {
129
+ if (!rawVal) return [];
130
+ try {
131
+ const parsed = JSON.parse(rawVal);
132
+ return Array.isArray(parsed) ? parsed : [rawVal];
133
+ } catch {
134
+ return rawVal.split(',').map(r => r.trim()).filter(Boolean);
135
+ }
136
+ }
137
+
138
+ document.getElementById('template-form')?.addEventListener('submit', async (e) => {
139
+ e.preventDefault();
140
+
141
+ const formData = new FormData(e.target);
142
+ const whoCanCreate = parseRoles(formData.get('whoCanCreate'));
143
+ const whoCanEdit = parseRoles(formData.get('whoCanEdit'));
144
+ const whoCanReview = parseRoles(formData.get('whoCanReview'));
145
+ const whoCanView = parseRoles(formData.get('whoCanView'));
146
+
147
+ const templateEditor = document.getElementById('template-block-editor');
148
+ const initialBlocks = templateEditor && typeof templateEditor.getBlocks === 'function'
149
+ ? templateEditor.getBlocks()
150
+ : [];
151
+
152
+ const rolePermissions = {
153
+ whoCanCreate,
154
+ whoCanEdit,
155
+ whoCanReview,
156
+ whoCanView
157
+ };
158
+
159
+ const data = {
160
+ slug: formData.get('slug'),
161
+ title: formData.get('title'),
162
+ description: formData.get('description'),
163
+ pageType: formData.get('pageType'),
164
+ allowedRoles: whoCanCreate,
165
+ rolePermissions,
166
+ initialBlocks,
167
+ approvalRules: {
168
+ allowSelfApproval: true,
169
+ minApprovals: 1,
170
+ requiredRolesForApproval: whoCanReview
171
+ },
172
+ defaultProperties: {}
173
+ };
174
+
175
+ try {
176
+ const response = await fetch('/api/cms/templates', {
177
+ method: 'POST',
178
+ headers: { 'Content-Type': 'application/json' },
179
+ body: JSON.stringify(data)
180
+ });
181
+
182
+ if (response.ok) {
183
+ window.location.href = `/${currentLocale}/cms/templates`;
184
+ } else {
185
+ const errorData = (await response.json().catch(() => ({})));
186
+ alert(`Failed to create template: ${errorData.error || 'Unknown error'}`);
187
+ }
188
+ } catch {
189
+ alert('Error creating template: Network error');
190
+ }
191
+ });
192
+ </script>
193
+ </CMSDashboardLayout>
@@ -0,0 +1,74 @@
1
+ ---
2
+ import CMSDashboardLayout from "../layouts/CMSDashboardLayout.astro";
3
+ import RLAButton from "@rimelight/ui/components/button/RLAButton.astro";
4
+ import RLABadge from "@rimelight/ui/components/badge/RLABadge.astro";
5
+ import RLAAvatar from "@rimelight/ui/components/avatar/RLAAvatar.astro";
6
+ import RLATable from "@rimelight/ui/components/table/RLATable.astro";
7
+ import type { TableColumn } from "@rimelight/ui/components/table/table.ts";
8
+
9
+ export const prerender = false;
10
+
11
+ const users = [
12
+ { id: "usr_1", name: "Idan T.", email: "idan@rimelight.com", role: "Super Admin", status: "active", lastActive: "Just now" },
13
+ { id: "usr_2", name: "Sarah Connor", email: "sarah@rimelight.com", role: "Editor", status: "active", lastActive: "2 hours ago" },
14
+ { id: "usr_3", name: "Alex Rivers", email: "alex@rimelight.com", role: "Author", status: "active", lastActive: "Yesterday" },
15
+ { id: "usr_4", name: "Elena Vance", email: "elena@rimelight.com", role: "Reviewer", status: "invited", lastActive: "Never" },
16
+ ];
17
+
18
+ const columns: TableColumn[] = [
19
+ { id: "user", accessorKey: "name", header: "User", enableSorting: true },
20
+ { id: "role", accessorKey: "role", header: "Role", enableSorting: true },
21
+ { id: "status", accessorKey: "status", header: "Status", enableSorting: true },
22
+ { id: "lastActive", accessorKey: "lastActive", header: "Last Active", enableSorting: true },
23
+ { id: "actions", header: "Actions", enableSorting: false, meta: { class: { th: "text-right", td: "text-right" } } }
24
+ ];
25
+ ---
26
+
27
+ <CMSDashboardLayout title="CMS Users" description="Manage team permissions, editor roles, and workspace access" activeSection="users">
28
+ <Fragment slot="toolbar-right">
29
+ <RLAButton variant="solid" color="success" leadingIcon="i-lucide-user-plus">
30
+ Invite Member
31
+ </RLAButton>
32
+ </Fragment>
33
+
34
+ <RLATable
35
+ columns={columns}
36
+ data={users}
37
+ searchable
38
+ searchPlaceholder="Search users..."
39
+ class="w-full"
40
+ >
41
+ {users.map((user) => (
42
+ <tr class="hover:bg-muted/20 transition-colors" data-slot="tr" data-row-id={user.id}>
43
+ <td class="p-3.5 text-sm font-medium text-highlighted flex items-center gap-3" data-col-id="user" data-slot="td">
44
+ <RLAAvatar name={user.name} size="sm" />
45
+ <div class="flex flex-col">
46
+ <span>{user.name}</span>
47
+ <span class="text-xs text-muted font-normal">{user.email}</span>
48
+ </div>
49
+ </td>
50
+ <td class="p-3.5 text-sm text-toned" data-col-id="role" data-slot="td">
51
+ <RLABadge variant="soft" color={user.role === "Super Admin" ? "primary" : user.role === "Editor" ? "blue" : "neutral"} size="xs">
52
+ {user.role}
53
+ </RLABadge>
54
+ </td>
55
+ <td class="p-3.5" data-col-id="status" data-slot="td">
56
+ {user.status === "active" ? (
57
+ <RLABadge variant="soft" color="green" size="xs">Active</RLABadge>
58
+ ) : (
59
+ <RLABadge variant="soft" color="yellow" size="xs">Invited</RLABadge>
60
+ )}
61
+ </td>
62
+ <td class="p-3.5 text-sm text-muted text-xs" data-col-id="lastActive" data-slot="td">
63
+ {user.lastActive}
64
+ </td>
65
+ <td class="p-3.5 text-right whitespace-nowrap" data-col-id="actions" data-slot="td">
66
+ <div class="flex items-center justify-end gap-2">
67
+ <RLAButton variant="ghost" color="neutral" size="xs">Change Role</RLAButton>
68
+ <RLAButton variant="ghost" color="error" size="xs" leadingIcon="i-lucide-trash-2" aria-label="Remove user" />
69
+ </div>
70
+ </td>
71
+ </tr>
72
+ ))}
73
+ </RLATable>
74
+ </CMSDashboardLayout>
@@ -0,0 +1,74 @@
1
+ ---
2
+ import CMSDashboardLayout from "../layouts/CMSDashboardLayout.astro";
3
+ import RLABadge from "@rimelight/ui/components/badge/RLABadge.astro";
4
+ import { db } from "virtual:rimelight-cms/db";
5
+ import { pageVersions, pages } from "../../schema";
6
+ import { desc, eq } from "drizzle-orm";
7
+
8
+ export const prerender = false;
9
+
10
+ const currentLocale = Astro.currentLocale ?? "en";
11
+
12
+ let versions: any[] = [];
13
+ if (db) {
14
+ try {
15
+ const versionsList = await db
16
+ .select()
17
+ .from(pageVersions)
18
+ .orderBy(desc(pageVersions.createdAt))
19
+ .limit(50);
20
+
21
+ versions = await Promise.all(
22
+ versionsList.map(async (version: any) => {
23
+ const page = await db.select({ title: pages.title, slug: pages.slug }).from(pages).where(eq(pages.id, version.pageId)).limit(1);
24
+ return Object.assign({}, version, {
25
+ pageTitle: typeof page[0]?.title === "object" && page[0]?.title !== null ? (page[0].title as any).en || "" : page[0]?.title || "Unknown Page",
26
+ slug: page[0]?.slug || ""
27
+ });
28
+ })
29
+ );
30
+ } catch (err) {
31
+ console.warn("Failed to fetch versions:", err);
32
+ }
33
+ }
34
+ ---
35
+
36
+ <CMSDashboardLayout title="Versions" description="Manage CMS page versions" activeSection="versions">
37
+ <div class="bg-elevated/40 border border-default rounded-xl overflow-hidden">
38
+ <table class="w-full text-left text-sm">
39
+ <thead class="bg-elevated/70 border-b border-default text-xs font-semibold text-muted uppercase tracking-wider">
40
+ <tr>
41
+ <th class="px-6 py-3">Page</th>
42
+ <th class="px-6 py-3">Version</th>
43
+ <th class="px-6 py-3">Status</th>
44
+ <th class="px-6 py-3">Created</th>
45
+ </tr>
46
+ </thead>
47
+ <tbody class="divide-y divide-default/50">
48
+ {versions.map((version: any) => (
49
+ <tr class="hover:bg-elevated/50 transition">
50
+ <td class="px-6 py-4 whitespace-nowrap">
51
+ <div class="text-sm font-medium text-highlighted">{version.pageTitle}</div>
52
+ <div class="text-xs font-mono text-muted">{version.slug}</div>
53
+ </td>
54
+ <td class="px-6 py-4 whitespace-nowrap text-xs font-mono text-muted">
55
+ v{version.versionNumber}
56
+ </td>
57
+ <td class="px-6 py-4 whitespace-nowrap">
58
+ {version.status === 'published' ? (
59
+ <RLABadge variant="soft" color="primary" size="xs">Published</RLABadge>
60
+ ) : version.status === 'pending' ? (
61
+ <RLABadge variant="soft" color="warning" size="xs">Pending Review</RLABadge>
62
+ ) : (
63
+ <RLABadge variant="soft" color="neutral" size="xs">Draft</RLABadge>
64
+ )}
65
+ </td>
66
+ <td class="px-6 py-4 whitespace-nowrap text-xs text-muted">
67
+ {new Date(version.createdAt).toLocaleDateString(currentLocale)}
68
+ </td>
69
+ </tr>
70
+ ))}
71
+ </tbody>
72
+ </table>
73
+ </div>
74
+ </CMSDashboardLayout>
@@ -1,117 +1,59 @@
1
1
  ---
2
- import type { BaseBlock } from "@rimelight/cms"
3
- import ApiDocumentationBlock from "./blocks/ApiDocumentationBlock.astro"
4
- import LivePreviewBlock from "./blocks/LivePreviewBlock.astro"
5
- import ParagraphBlock from "./blocks/ParagraphBlock.astro"
6
- import CalloutBlock from "./blocks/CalloutBlock.astro"
7
- import ImageBlock from "./blocks/ImageBlock.astro"
8
- import CodeBlock from "./blocks/CodeBlock.astro"
9
- import ScriptBlock from "./blocks/ScriptBlock.astro"
10
- import SceneBlock from "./blocks/SceneBlock.astro"
11
- import DialogueBlock from "./blocks/DialogueBlock.astro"
12
- import SectionBlock from "./blocks/SectionBlock.astro"
13
- import TableBlock from "./blocks/TableBlock.astro"
14
- import TabsBlock from "./blocks/TabsBlock.astro"
15
- import TabItemBlock from "./blocks/TabItemBlock.astro"
16
- import StepsBlock from "./blocks/StepsBlock.astro"
17
- import StepItemBlock from "./blocks/StepItemBlock.astro"
18
- import CardsBlock from "./blocks/CardsBlock.astro"
19
- import CardBlock from "./blocks/CardBlock.astro"
20
- import FileTreeBlock from "./blocks/FileTreeBlock.astro"
21
- import UnorderedListBlock from "./blocks/UnorderedListBlock.astro"
22
- import OrderedListBlock from "./blocks/OrderedListBlock.astro"
2
+ import type { BaseBlock } from "../core/types/blocks"
3
+ import { defaultBlockComponents } from "./blocks/index.ts"
23
4
 
24
5
  interface Props {
25
6
  block: BaseBlock
26
7
  locale?: string
27
8
  sectionDepth?: number
28
9
  index?: number
10
+ components?: Record<string, any>
29
11
  }
30
12
 
31
- const { block, locale = "en", sectionDepth = 0, index = 0 } = Astro.props
13
+ const {
14
+ block,
15
+ locale = "en",
16
+ sectionDepth = 0,
17
+ index = 0,
18
+ components = {}
19
+ } = Astro.props
20
+
21
+ const Component = components[block.type] ?? defaultBlockComponents[block.type]
22
+ const rawChildren = block.children || block.props?.children || []
23
+ const children = (Array.isArray(rawChildren) ? rawChildren : []) as BaseBlock[]
24
+ const nextDepth = block.type === "SectionBlock" ? Math.min(6, (sectionDepth || 2) + 1) : sectionDepth
32
25
  ---
33
26
 
34
27
  {
35
- block.type === "ApiDocumentationBlock" ? (
36
- <ApiDocumentationBlock block={block} locale={locale} />
37
- ) : block.type === "LivePreviewBlock" ? (
38
- <LivePreviewBlock block={block} locale={locale} />
39
- ) : block.type === "ParagraphBlock" ? (
40
- <ParagraphBlock block={block} locale={locale} />
41
- ) : block.type === "CalloutBlock" ? (
42
- <CalloutBlock block={block} locale={locale}>
43
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
44
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
45
- ))}
46
- </CalloutBlock>
47
- ) : block.type === "ImageBlock" ? (
48
- <ImageBlock block={block} locale={locale} />
49
- ) : block.type === "CodeBlock" ? (
50
- <CodeBlock block={block} locale={locale} />
51
- ) : block.type === "TableBlock" ? (
52
- <TableBlock block={block} locale={locale} />
53
- ) : block.type === "TabsBlock" ? (
54
- <TabsBlock block={block} locale={locale}>
55
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
56
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
57
- ))}
58
- </TabsBlock>
59
- ) : block.type === "TabItemBlock" ? (
60
- <TabItemBlock block={block} locale={locale} index={index} isFirst={index === 0}>
61
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
62
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
63
- ))}
64
- </TabItemBlock>
65
- ) : block.type === "StepsBlock" ? (
66
- <StepsBlock block={block} locale={locale}>
67
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
68
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
69
- ))}
70
- </StepsBlock>
71
- ) : block.type === "StepItemBlock" ? (
72
- <StepItemBlock block={block} locale={locale} index={index}>
73
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
74
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
28
+ Component ? (
29
+ <Component
30
+ block={block}
31
+ locale={locale}
32
+ sectionDepth={sectionDepth}
33
+ index={index}
34
+ components={components}
35
+ >
36
+ {children.map((child: BaseBlock, idx: number) => (
37
+ <Astro.self
38
+ block={child}
39
+ locale={locale}
40
+ sectionDepth={nextDepth}
41
+ index={idx}
42
+ components={components}
43
+ />
44
+ ))}
45
+ </Component>
46
+ ) : children.length > 0 ? (
47
+ <div>
48
+ {children.map((child: BaseBlock, idx: number) => (
49
+ <Astro.self
50
+ block={child}
51
+ locale={locale}
52
+ sectionDepth={sectionDepth}
53
+ index={idx}
54
+ components={components}
55
+ />
75
56
  ))}
76
- </StepItemBlock>
77
- ) : block.type === "CardsBlock" ? (
78
- <CardsBlock block={block} locale={locale}>
79
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
80
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
81
- ))}
82
- </CardsBlock>
83
- ) : block.type === "CardBlock" ? (
84
- <CardBlock block={block} locale={locale} />
85
- ) : block.type === "FileTreeBlock" ? (
86
- <FileTreeBlock block={block} locale={locale} />
87
- ) : block.type === "UnorderedListBlock" ? (
88
- <UnorderedListBlock block={block} locale={locale} />
89
- ) : block.type === "OrderedListBlock" ? (
90
- <OrderedListBlock block={block} locale={locale} />
91
- ) : block.type === "ScriptBlock" ? (
92
- <ScriptBlock block={block} locale={locale}>
93
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
94
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
95
- ))}
96
- </ScriptBlock>
97
- ) : block.type === "SceneBlock" ? (
98
- <SceneBlock block={block} locale={locale}>
99
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
100
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth} index={idx} />
101
- ))}
102
- </SceneBlock>
103
- ) : block.type === "DialogueBlock" ? (
104
- <DialogueBlock block={block} locale={locale} />
105
- ) : block.type === "SectionBlock" ? (
106
- <SectionBlock block={block} locale={locale} sectionDepth={sectionDepth}>
107
- {(block.children || block.props?.children)?.map((child: BaseBlock, idx: number) => (
108
- <Astro.self block={child} locale={locale} sectionDepth={sectionDepth + 1} index={idx} />
109
- ))}
110
- </SectionBlock>
111
- ) : (
112
- <div class="cms-block-fallback p-4 border border-dashed border-gray-700 rounded my-2 text-sm text-gray-400">
113
- Unknown Block Type: {block.type}
114
57
  </div>
115
- )
58
+ ) : null
116
59
  }
117
-
@@ -9,15 +9,22 @@ interface Props {
9
9
  session?: UserSessionContext
10
10
  locale?: string
11
11
  class?: string
12
+ components?: Record<string, any>
12
13
  }
13
14
 
14
- const { blocks, session, locale = "en", class: className = "" } = Astro.props
15
+ const {
16
+ blocks,
17
+ session,
18
+ locale = "en",
19
+ class: className = "",
20
+ components = {}
21
+ } = Astro.props
15
22
 
16
23
  const sanitizedBlocks = filterBlocksForReader(blocks, session)
17
24
  ---
18
25
 
19
26
  <article class={`rimelight-cms-page ${className}`}>
20
27
  {sanitizedBlocks.map((block) => (
21
- <BlockRenderer block={block} locale={locale} />
28
+ <BlockRenderer block={block} locale={locale} components={components} />
22
29
  ))}
23
30
  </article>
@@ -20,43 +20,43 @@ const variantConfigs: Record<
20
20
  info: {
21
21
  icon: "i-lucide-info",
22
22
  classes:
23
- "bg-blue-50/50 dark:bg-blue-950/10 border-blue-200 dark:border-blue-900/50 text-blue-800 dark:text-blue-200",
23
+ "bg-info-50/50 border-info-200 text-info-800",
24
24
  label: "Info"
25
25
  },
26
26
  success: {
27
27
  icon: "i-lucide-check-circle",
28
28
  classes:
29
- "bg-green-50/50 dark:bg-green-950/10 border-green-200 dark:border-green-900/50 text-green-800 dark:text-green-200",
29
+ "bg-success-50/50 border-success-200 text-success-800",
30
30
  label: "Success"
31
31
  },
32
32
  warning: {
33
33
  icon: "i-lucide-alert-triangle",
34
34
  classes:
35
- "bg-yellow-50/50 dark:bg-yellow-950/10 border-yellow-200 dark:border-yellow-900/50 text-yellow-800 dark:text-yellow-200",
35
+ "bg-warning-50/50 border-warning-200 text-warning-800",
36
36
  label: "Warning"
37
37
  },
38
38
  error: {
39
39
  icon: "i-lucide-alert-octagon",
40
40
  classes:
41
- "bg-red-50/50 dark:bg-red-950/10 border-red-200 dark:border-red-900/50 text-red-800 dark:text-red-200",
41
+ "bg-error-50/50 border-error-200 text-error-800",
42
42
  label: "Error"
43
43
  },
44
44
  commentary: {
45
45
  icon: "i-lucide-message-square",
46
46
  classes:
47
- "bg-purple-50/50 dark:bg-purple-950/10 border-purple-200 dark:border-purple-900/50 text-purple-800 dark:text-purple-200",
47
+ "bg-ideation-50/50 border-ideation-200 text-ideation-800",
48
48
  label: "Commentary"
49
49
  },
50
50
  ideation: {
51
51
  icon: "i-lucide-lightbulb",
52
52
  classes:
53
- "bg-amber-50/50 dark:bg-amber-950/10 border-amber-200 dark:border-amber-900/50 text-amber-800 dark:text-amber-200",
53
+ "bg-warning-50/50 border-warning-200 text-warning-800",
54
54
  label: "Ideation"
55
55
  },
56
56
  source: {
57
57
  icon: "i-lucide-code",
58
58
  classes:
59
- "bg-indigo-50/50 dark:bg-indigo-950/10 border-indigo-200 dark:border-indigo-900/50 text-indigo-800 dark:text-indigo-200",
59
+ "bg-info-50/50 border-info-200 text-info-800",
60
60
  label: "Source"
61
61
  }
62
62
  }
@@ -13,30 +13,30 @@ const Tag = href ? "a" : "div"
13
13
 
14
14
  <Tag
15
15
  href={href}
16
- class="cms-card group relative block p-5 rounded-xl border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900/50 hover:border-primary-500/50 hover:shadow-md transition-all text-decoration-none no-underline"
16
+ class="cms-card group relative block p-5 rounded-xl border border-neutral-200 bg-elevated hover:border-primary-500/50 hover:shadow-md transition-all text-decoration-none no-underline"
17
17
  data-block-id={block.id}
18
18
  >
19
19
  <div class="flex items-start justify-between gap-2">
20
20
  <div class="flex items-center gap-3">
21
21
  {icon && (
22
- <div class="flex size-9 shrink-0 items-center justify-center rounded-lg bg-neutral-100 dark:bg-neutral-800 text-neutral-800 dark:text-neutral-200 group-hover:bg-primary-50 dark:group-hover:bg-primary-950/50 group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
22
+ <div class="flex size-9 shrink-0 items-center justify-center rounded-lg bg-neutral-100 text-neutral-800 group-hover:bg-primary-50 group-hover:text-primary-600 transition-colors">
23
23
  <span class={`${icon} size-5`} aria-hidden="true" />
24
24
  </div>
25
25
  )}
26
26
  {title && (
27
- <h4 class="text-base font-semibold text-neutral-900 dark:text-white m-0 group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors">
27
+ <h4 class="text-base font-semibold text-neutral-900 m-0 group-hover:text-primary-600 transition-colors">
28
28
  {title}
29
29
  </h4>
30
30
  )}
31
31
  </div>
32
32
  {badge && (
33
- <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-neutral-100 dark:bg-neutral-800 text-neutral-700 dark:text-neutral-300">
33
+ <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-neutral-100 text-neutral-700">
34
34
  {badge}
35
35
  </span>
36
36
  )}
37
37
  </div>
38
38
  {description && (
39
- <p class="mt-3 text-sm text-neutral-600 dark:text-neutral-400 m-0">
39
+ <p class="mt-3 text-sm text-neutral-600 m-0">
40
40
  {description}
41
41
  </p>
42
42
  )}
@@ -10,7 +10,7 @@ const { block } = Astro.props
10
10
  const { code = "", language = "text" } = block.props
11
11
  ---
12
12
 
13
- <div class="cms-code my-4 rounded-lg bg-gray-950 p-4 font-mono text-sm border border-gray-800 overflow-x-auto" data-block-id={block.id}>
14
- <div class="text-xs text-gray-500 uppercase mb-2 select-none">{language}</div>
13
+ <div class="cms-code my-4 rounded-lg bg-neutral-950 p-4 font-mono text-sm border border-neutral-800 overflow-x-auto" data-block-id={block.id}>
14
+ <div class="text-xs text-neutral-500 uppercase mb-2 select-none">{language}</div>
15
15
  <pre class="m-0"><code>{code}</code></pre>
16
16
  </div>