@rimelight/cms 0.0.1 → 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.
- package/LICENSE +21 -0
- package/package.json +16 -14
- package/src/admin/api/media-file.ts +39 -0
- package/src/admin/api/media.ts +199 -0
- package/src/admin/layouts/CMSDashboardLayout.astro +178 -0
- package/src/admin/layouts/DefaultParentLayout.astro +50 -0
- package/src/admin/pages/assets.astro +424 -0
- package/src/admin/pages/byline-schema.astro +62 -0
- package/src/admin/pages/bylines.astro +16 -0
- package/src/admin/pages/categories.astro +21 -0
- package/src/admin/pages/content-types.astro +55 -0
- package/src/admin/pages/dashboard.astro +61 -0
- package/src/admin/pages/pages-edit.astro +595 -0
- package/src/admin/pages/pages-index.astro +146 -0
- package/src/admin/pages/pages-new.astro +130 -0
- package/src/admin/pages/pages-preview.astro +111 -0
- package/src/admin/pages/pages-review.astro +236 -0
- package/src/admin/pages/settings.astro +203 -0
- package/src/admin/pages/tags.astro +21 -0
- package/src/admin/pages/templates-edit.astro +234 -0
- package/src/admin/pages/templates-index.astro +95 -0
- package/src/admin/pages/templates-new.astro +193 -0
- package/src/admin/pages/users.astro +74 -0
- package/src/admin/pages/versions.astro +74 -0
- package/src/astro/BlockRenderer.astro +44 -96
- package/src/astro/PageRenderer.astro +9 -2
- package/src/astro/blocks/CalloutBlock.astro +7 -7
- package/src/astro/blocks/CardBlock.astro +5 -5
- package/src/astro/blocks/CodeBlock.astro +2 -2
- package/src/astro/blocks/FileTreeBlock.astro +7 -7
- package/src/astro/blocks/ImageBlock.astro +2 -2
- package/src/astro/blocks/OrderedListBlock.astro +2 -2
- package/src/astro/blocks/ParagraphBlock.astro +3 -3
- package/src/astro/blocks/SectionBlock.astro +6 -6
- package/src/astro/blocks/StepItemBlock.astro +3 -3
- package/src/astro/blocks/StepsBlock.astro +1 -1
- package/src/astro/blocks/TableBlock.astro +6 -6
- package/src/astro/blocks/TabsBlock.astro +9 -9
- package/src/astro/blocks/UnorderedListBlock.astro +2 -2
- package/src/astro/blocks/index.ts +35 -0
- package/src/client/components/RimelightBlock.tsx +19 -19
- package/src/client/components/RimelightBlockPicker.tsx +9 -13
- package/src/client/components/RimelightBylinesManager.tsx +438 -0
- package/src/client/components/RimelightEditor.tsx +3 -3
- package/src/client/components/RimelightPreview.tsx +37 -45
- package/src/client/components/RimelightPropertiesPanel.tsx +368 -26
- package/src/client/components/RimelightTaxonomyManager.tsx +369 -0
- package/src/client/components/RimelightTaxonomyMasterDetail.tsx +412 -0
- package/src/client/components/RimelightTemplateEditor.tsx +6 -8
- package/src/client/components/editors/CalloutEditor.tsx +2 -4
- package/src/client/components/editors/CardsEditor.tsx +2 -4
- package/src/client/components/editors/CodeEditor.tsx +1 -3
- package/src/client/components/editors/FileTreeEditor.tsx +2 -4
- package/src/client/components/editors/ImageEditor.tsx +3 -3
- package/src/client/components/editors/ParagraphEditor.tsx +2 -2
- package/src/client/components/editors/SceneEditor.tsx +6 -12
- package/src/client/components/editors/ScriptEditor.tsx +8 -16
- package/src/client/components/editors/SectionEditor.tsx +4 -4
- package/src/client/components/editors/StepItemEditor.tsx +2 -2
- package/src/client/components/editors/StepsEditor.tsx +2 -4
- package/src/client/components/editors/TabItemEditor.tsx +3 -3
- package/src/client/components/editors/TableEditor.tsx +5 -5
- package/src/client/components/editors/TabsEditor.tsx +2 -4
- package/src/client/components/index.ts +3 -0
- package/src/client/loader.ts +3 -0
- package/src/core/diff.ts +145 -0
- package/src/core/excerpt.ts +159 -0
- package/src/core/preview.ts +87 -0
- package/src/core/registry.ts +0 -39
- package/src/core/types/blocks.ts +83 -6
- package/src/core/types/bylines.ts +14 -0
- package/src/core/types/media.ts +13 -0
- package/src/core/types/taxonomies.ts +20 -0
- package/src/cron/scheduled.ts +57 -0
- package/src/docs/agent.ts +116 -0
- package/src/docs/config.ts +25 -0
- package/src/docs/index.ts +5 -0
- package/src/docs/routing.ts +104 -0
- package/src/docs/sidebar.ts +251 -0
- package/src/docs/toc.ts +47 -0
- package/src/env.d.ts +13 -0
- package/src/index.ts +40 -6
- package/src/integration.ts +196 -0
- package/src/loader/live.ts +254 -0
- package/src/markdown/index.ts +0 -1
- package/src/mcp/index.ts +228 -0
- package/src/schema/bylines.ts +23 -0
- package/src/schema/index.ts +3 -0
- package/src/schema/page_versions.ts +2 -0
- package/src/schema/pages.ts +2 -0
- package/src/schema/site_settings.ts +41 -0
- package/src/schema/taxonomies.ts +49 -0
- package/src/services/bylines.ts +74 -0
- package/src/services/search-indexer.ts +61 -0
- package/src/services/site-settings.ts +108 -0
- package/src/services/taxonomies.ts +276 -0
- package/src/storage/index.ts +253 -0
- package/src/astro/blocks/DialogueBlock.astro +0 -23
- package/src/astro/blocks/SceneBlock.astro +0 -65
- package/src/astro/blocks/ScriptBlock.astro +0 -59
- package/src/core/template-sync.ts +0 -75
- package/src/markdown/parser.ts +0 -347
- package/src/migration.ts +0 -155
- package/src/search/sync.ts +0 -32
package/src/core/registry.ts
CHANGED
|
@@ -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,40 +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
|
-
})
|
package/src/core/types/blocks.ts
CHANGED
|
@@ -17,6 +17,9 @@ export type BlockType =
|
|
|
17
17
|
| "CardsBlock"
|
|
18
18
|
| "CardBlock"
|
|
19
19
|
| "FileTreeBlock"
|
|
20
|
+
| "ComponentShowcaseBlock"
|
|
21
|
+
| "ApiDocumentationBlock"
|
|
22
|
+
| "LivePreviewBlock"
|
|
20
23
|
| (string & {})
|
|
21
24
|
|
|
22
25
|
export type TimeOfDay = "MORNING" | "NOON" | "AFTERNOON" | "EVENING" | "NIGHT" | "OTHER"
|
|
@@ -117,10 +120,15 @@ export interface CalloutBlockProps {
|
|
|
117
120
|
children: BaseBlock[]
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
import type { MediaValue } from "./media"
|
|
124
|
+
|
|
120
125
|
export interface ImageBlockProps {
|
|
121
|
-
src: string
|
|
126
|
+
src: string | MediaValue
|
|
122
127
|
alt: string
|
|
123
128
|
caption?: string
|
|
129
|
+
width?: number
|
|
130
|
+
height?: number
|
|
131
|
+
aspectRatio?: string
|
|
124
132
|
}
|
|
125
133
|
|
|
126
134
|
export interface CodeBlockProps {
|
|
@@ -191,6 +199,42 @@ export interface FileTreeBlockProps {
|
|
|
191
199
|
tree: FileTreeNode[]
|
|
192
200
|
}
|
|
193
201
|
|
|
202
|
+
export interface ComponentShowcaseBlockProps {
|
|
203
|
+
componentName: string
|
|
204
|
+
componentPath: string
|
|
205
|
+
framework: "astro" | "vue" | "solid"
|
|
206
|
+
defaultProps: Record<string, any>
|
|
207
|
+
propsConfig: Record<string, string[]>
|
|
208
|
+
togglingPattern: "icon" | "select-option" | "tab-segmented" | "pill-toggle"
|
|
209
|
+
codeExamples: {
|
|
210
|
+
astro?: string
|
|
211
|
+
vue?: string
|
|
212
|
+
solid?: string
|
|
213
|
+
}
|
|
214
|
+
playgroundUrl: string | undefined
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface ApiDocumentationBlockProps {
|
|
218
|
+
componentName: string
|
|
219
|
+
sections: {
|
|
220
|
+
props?: boolean
|
|
221
|
+
slots?: boolean
|
|
222
|
+
emits?: boolean
|
|
223
|
+
theme?: boolean
|
|
224
|
+
changelog?: boolean
|
|
225
|
+
}
|
|
226
|
+
hide?: string[]
|
|
227
|
+
show?: string[]
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface LivePreviewBlockProps {
|
|
231
|
+
componentCode: string
|
|
232
|
+
framework: "astro" | "vue" | "solid"
|
|
233
|
+
height?: string
|
|
234
|
+
width?: string
|
|
235
|
+
editable?: boolean
|
|
236
|
+
}
|
|
237
|
+
|
|
194
238
|
export interface TypedScriptBlockProps extends ScriptBlockProps {
|
|
195
239
|
children: BaseBlock[]
|
|
196
240
|
}
|
|
@@ -281,6 +325,21 @@ export interface FileTreeBlock extends BaseBlock {
|
|
|
281
325
|
props: FileTreeBlockProps
|
|
282
326
|
}
|
|
283
327
|
|
|
328
|
+
export interface ComponentShowcaseBlock extends BaseBlock {
|
|
329
|
+
type: "ComponentShowcaseBlock"
|
|
330
|
+
props: ComponentShowcaseBlockProps
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export interface ApiDocumentationBlock extends BaseBlock {
|
|
334
|
+
type: "ApiDocumentationBlock"
|
|
335
|
+
props: ApiDocumentationBlockProps
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface LivePreviewBlock extends BaseBlock {
|
|
339
|
+
type: "LivePreviewBlock"
|
|
340
|
+
props: LivePreviewBlockProps
|
|
341
|
+
}
|
|
342
|
+
|
|
284
343
|
export type TypedBlock =
|
|
285
344
|
| SectionBlock
|
|
286
345
|
| ParagraphBlock
|
|
@@ -298,6 +357,9 @@ export type TypedBlock =
|
|
|
298
357
|
| CardsBlock
|
|
299
358
|
| CardBlock
|
|
300
359
|
| FileTreeBlock
|
|
360
|
+
| ComponentShowcaseBlock
|
|
361
|
+
| ApiDocumentationBlock
|
|
362
|
+
| LivePreviewBlock
|
|
301
363
|
|
|
302
364
|
export const ALLOWED_CHILDREN_MAP: Record<string, BlockType[]> = {
|
|
303
365
|
Root: [
|
|
@@ -313,7 +375,10 @@ export const ALLOWED_CHILDREN_MAP: Record<string, BlockType[]> = {
|
|
|
313
375
|
"TabsBlock",
|
|
314
376
|
"StepsBlock",
|
|
315
377
|
"CardsBlock",
|
|
316
|
-
"FileTreeBlock"
|
|
378
|
+
"FileTreeBlock",
|
|
379
|
+
"ComponentShowcaseBlock",
|
|
380
|
+
"ApiDocumentationBlock",
|
|
381
|
+
"LivePreviewBlock"
|
|
317
382
|
],
|
|
318
383
|
SectionBlock: [
|
|
319
384
|
"SectionBlock",
|
|
@@ -326,7 +391,10 @@ export const ALLOWED_CHILDREN_MAP: Record<string, BlockType[]> = {
|
|
|
326
391
|
"TabsBlock",
|
|
327
392
|
"StepsBlock",
|
|
328
393
|
"CardsBlock",
|
|
329
|
-
"FileTreeBlock"
|
|
394
|
+
"FileTreeBlock",
|
|
395
|
+
"ComponentShowcaseBlock",
|
|
396
|
+
"ApiDocumentationBlock",
|
|
397
|
+
"LivePreviewBlock"
|
|
330
398
|
],
|
|
331
399
|
CalloutBlock: [
|
|
332
400
|
"ParagraphBlock",
|
|
@@ -337,7 +405,10 @@ export const ALLOWED_CHILDREN_MAP: Record<string, BlockType[]> = {
|
|
|
337
405
|
"TabsBlock",
|
|
338
406
|
"StepsBlock",
|
|
339
407
|
"CardsBlock",
|
|
340
|
-
"FileTreeBlock"
|
|
408
|
+
"FileTreeBlock",
|
|
409
|
+
"ComponentShowcaseBlock",
|
|
410
|
+
"ApiDocumentationBlock",
|
|
411
|
+
"LivePreviewBlock"
|
|
341
412
|
],
|
|
342
413
|
TabsBlock: ["TabItemBlock"],
|
|
343
414
|
TabItemBlock: [
|
|
@@ -348,7 +419,10 @@ export const ALLOWED_CHILDREN_MAP: Record<string, BlockType[]> = {
|
|
|
348
419
|
"TableBlock",
|
|
349
420
|
"StepsBlock",
|
|
350
421
|
"CardsBlock",
|
|
351
|
-
"FileTreeBlock"
|
|
422
|
+
"FileTreeBlock",
|
|
423
|
+
"ComponentShowcaseBlock",
|
|
424
|
+
"ApiDocumentationBlock",
|
|
425
|
+
"LivePreviewBlock"
|
|
352
426
|
],
|
|
353
427
|
StepsBlock: ["StepItemBlock"],
|
|
354
428
|
StepItemBlock: [
|
|
@@ -359,7 +433,10 @@ export const ALLOWED_CHILDREN_MAP: Record<string, BlockType[]> = {
|
|
|
359
433
|
"TableBlock",
|
|
360
434
|
"TabsBlock",
|
|
361
435
|
"CardsBlock",
|
|
362
|
-
"FileTreeBlock"
|
|
436
|
+
"FileTreeBlock",
|
|
437
|
+
"ComponentShowcaseBlock",
|
|
438
|
+
"ApiDocumentationBlock",
|
|
439
|
+
"LivePreviewBlock"
|
|
363
440
|
],
|
|
364
441
|
CardsBlock: ["CardBlock"],
|
|
365
442
|
ScriptBlock: ["SceneBlock"],
|
|
@@ -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,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
|
+
}
|