@dominikcz/greg 0.9.27
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/README.md +397 -0
- package/bin/greg.js +241 -0
- package/bin/init.js +351 -0
- package/bin/templates/docs/getting-started.md +47 -0
- package/bin/templates/docs/index.md +11 -0
- package/bin/templates/greg.config.js +39 -0
- package/bin/templates/greg.config.ts +38 -0
- package/bin/templates/index.html +16 -0
- package/bin/templates/src/App.svelte +5 -0
- package/bin/templates/src/app.css +20 -0
- package/bin/templates/src/main.js +9 -0
- package/bin/templates/svelte.config.js +1 -0
- package/bin/templates/tsconfig.json +21 -0
- package/bin/templates/vite.config.js +23 -0
- package/docs/__partials/markdown/examples/basic.md +4 -0
- package/docs/__partials/markdown/examples/diff.md +10 -0
- package/docs/__partials/markdown/examples/focus.md +5 -0
- package/docs/__partials/markdown/examples/language-title.md +3 -0
- package/docs/__partials/markdown/examples/line-highlighting.md +5 -0
- package/docs/__partials/markdown/examples/line-numbers.md +5 -0
- package/docs/__partials/note.md +4 -0
- package/docs/guide/__shared-warning.md +4 -0
- package/docs/guide/asset-handling.md +88 -0
- package/docs/guide/deploying.md +162 -0
- package/docs/guide/getting-started.md +334 -0
- package/docs/guide/index.md +23 -0
- package/docs/guide/localization.md +290 -0
- package/docs/guide/markdown/code.md +95 -0
- package/docs/guide/markdown/components-and-mermaid.md +43 -0
- package/docs/guide/markdown/containers.md +110 -0
- package/docs/guide/markdown/header-anchors.md +34 -0
- package/docs/guide/markdown/includes.md +84 -0
- package/docs/guide/markdown/index.md +20 -0
- package/docs/guide/markdown/inline-attributes.md +21 -0
- package/docs/guide/markdown/links-and-toc.md +64 -0
- package/docs/guide/markdown/math.md +54 -0
- package/docs/guide/markdown/syntax-highlighting.md +75 -0
- package/docs/guide/routing.md +150 -0
- package/docs/guide/using-svelte.md +88 -0
- package/docs/guide/versioning.md +281 -0
- package/docs/incompatibilities.md +48 -0
- package/docs/index.md +43 -0
- package/docs/reference/badge.md +100 -0
- package/docs/reference/carbon-ads.md +46 -0
- package/docs/reference/code-group.md +126 -0
- package/docs/reference/home-page.md +232 -0
- package/docs/reference/index.md +18 -0
- package/docs/reference/markdowndocs.md +275 -0
- package/docs/reference/outline.md +79 -0
- package/docs/reference/search.md +263 -0
- package/docs/reference/steps.md +200 -0
- package/docs/reference/team-page.md +189 -0
- package/docs/reference/theme.md +150 -0
- package/fakeDocsGenerator/generate_docs.js +310 -0
- package/package.json +92 -0
- package/scripts/build-versions.js +609 -0
- package/scripts/generate-static.js +79 -0
- package/scripts/render-markdown.js +420 -0
- package/src/lib/MarkdownDocs/AiChat.svelte +936 -0
- package/src/lib/MarkdownDocs/BackToTop.svelte +68 -0
- package/src/lib/MarkdownDocs/Breadcrumb.svelte +68 -0
- package/src/lib/MarkdownDocs/DocsNavigation.svelte +149 -0
- package/src/lib/MarkdownDocs/DocsSiteHeader.svelte +758 -0
- package/src/lib/MarkdownDocs/DocsVersionSwitcher.svelte +103 -0
- package/src/lib/MarkdownDocs/MarkdownDocs.svelte +2115 -0
- package/src/lib/MarkdownDocs/MarkdownRenderer.svelte +487 -0
- package/src/lib/MarkdownDocs/Outline.svelte +238 -0
- package/src/lib/MarkdownDocs/PrevNext.svelte +115 -0
- package/src/lib/MarkdownDocs/SearchModal.svelte +1241 -0
- package/src/lib/MarkdownDocs/TreeView.svelte +32 -0
- package/src/lib/MarkdownDocs/TreeViewItem.svelte +219 -0
- package/src/lib/MarkdownDocs/VersionOutdatedNotice.svelte +72 -0
- package/src/lib/MarkdownDocs/__tests__/codeDirectives.test.js +54 -0
- package/src/lib/MarkdownDocs/__tests__/common.test.js +41 -0
- package/src/lib/MarkdownDocs/__tests__/docsExamplesLint.test.js +77 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/__partial-basic.md +3 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/docs/markdown/snippet.js +9 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/includes/part.md +11 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/includes/wrapper.md +5 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.js +8 -0
- package/src/lib/MarkdownDocs/__tests__/fixtures/snippets/sample.md +5 -0
- package/src/lib/MarkdownDocs/__tests__/helpers.js +67 -0
- package/src/lib/MarkdownDocs/__tests__/localeUtils.test.js +204 -0
- package/src/lib/MarkdownDocs/__tests__/markdown.test.js +704 -0
- package/src/lib/MarkdownDocs/__tests__/markdownRendererRuntime.test.js +65 -0
- package/src/lib/MarkdownDocs/__tests__/searchIndexBuilder.test.js +117 -0
- package/src/lib/MarkdownDocs/__tests__/sqliteStore.test.js +202 -0
- package/src/lib/MarkdownDocs/__tests__/useRouter.test.js +16 -0
- package/src/lib/MarkdownDocs/ai/adapters/customAdapter.js +14 -0
- package/src/lib/MarkdownDocs/ai/adapters/customAdapter.ts +43 -0
- package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.js +81 -0
- package/src/lib/MarkdownDocs/ai/adapters/ollamaAdapter.ts +116 -0
- package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.js +92 -0
- package/src/lib/MarkdownDocs/ai/adapters/openaiAdapter.ts +137 -0
- package/src/lib/MarkdownDocs/ai/aiProvider.ts +31 -0
- package/src/lib/MarkdownDocs/ai/characters.js +52 -0
- package/src/lib/MarkdownDocs/ai/characters.ts +69 -0
- package/src/lib/MarkdownDocs/ai/chunkStore.ts +25 -0
- package/src/lib/MarkdownDocs/ai/chunker.js +85 -0
- package/src/lib/MarkdownDocs/ai/chunker.ts +135 -0
- package/src/lib/MarkdownDocs/ai/docLinker.js +26 -0
- package/src/lib/MarkdownDocs/ai/docLinker.ts +36 -0
- package/src/lib/MarkdownDocs/ai/promptBuilder.js +33 -0
- package/src/lib/MarkdownDocs/ai/promptBuilder.ts +53 -0
- package/src/lib/MarkdownDocs/ai/ragPipeline.js +54 -0
- package/src/lib/MarkdownDocs/ai/ragPipeline.ts +106 -0
- package/src/lib/MarkdownDocs/ai/stores/memoryStore.js +88 -0
- package/src/lib/MarkdownDocs/ai/stores/memoryStore.ts +112 -0
- package/src/lib/MarkdownDocs/ai/stores/sqliteStore.ts +372 -0
- package/src/lib/MarkdownDocs/ai/types.ts +71 -0
- package/src/lib/MarkdownDocs/aiServer.js +288 -0
- package/src/lib/MarkdownDocs/codeDirectives.js +191 -0
- package/src/lib/MarkdownDocs/codeFenceInfo.js +45 -0
- package/src/lib/MarkdownDocs/codeGroup.ts +46 -0
- package/src/lib/MarkdownDocs/common.ts +47 -0
- package/src/lib/MarkdownDocs/docsUtils.js +281 -0
- package/src/lib/MarkdownDocs/index.plugins.js +22 -0
- package/src/lib/MarkdownDocs/layouts/LayoutDoc.svelte +8 -0
- package/src/lib/MarkdownDocs/layouts/LayoutHome.svelte +58 -0
- package/src/lib/MarkdownDocs/layouts/LayoutPage.svelte +9 -0
- package/src/lib/MarkdownDocs/loadGregConfig.js +82 -0
- package/src/lib/MarkdownDocs/localeUtils.ts +682 -0
- package/src/lib/MarkdownDocs/markdownRendererRuntime.ts +314 -0
- package/src/lib/MarkdownDocs/mermaidThemes.js +319 -0
- package/src/lib/MarkdownDocs/navigationUtils.js +22 -0
- package/src/lib/MarkdownDocs/rehypeCodeGroup.js +326 -0
- package/src/lib/MarkdownDocs/rehypeCodeTitle.js +96 -0
- package/src/lib/MarkdownDocs/rehypeToc.js +170 -0
- package/src/lib/MarkdownDocs/remarkCodeMeta.js +22 -0
- package/src/lib/MarkdownDocs/remarkContainers.js +329 -0
- package/src/lib/MarkdownDocs/remarkCustomAnchors.js +42 -0
- package/src/lib/MarkdownDocs/remarkEscapeSvelte.js +33 -0
- package/src/lib/MarkdownDocs/remarkGlobalComponents.js +65 -0
- package/src/lib/MarkdownDocs/remarkImports.js +461 -0
- package/src/lib/MarkdownDocs/remarkImportsBrowser.js +349 -0
- package/src/lib/MarkdownDocs/remarkInlineAttrs.js +95 -0
- package/src/lib/MarkdownDocs/remarkMathToHtml.js +138 -0
- package/src/lib/MarkdownDocs/searchIndexBuilder.js +497 -0
- package/src/lib/MarkdownDocs/searchServer.js +263 -0
- package/src/lib/MarkdownDocs/treeViewTypes.ts +11 -0
- package/src/lib/MarkdownDocs/useRouter.svelte.ts +114 -0
- package/src/lib/MarkdownDocs/useSplitter.svelte.ts +33 -0
- package/src/lib/MarkdownDocs/versioningDefaults.js +20 -0
- package/src/lib/MarkdownDocs/vitePluginAiServer.js +204 -0
- package/src/lib/MarkdownDocs/vitePluginCopyDocs.js +153 -0
- package/src/lib/MarkdownDocs/vitePluginFrontmatter.js +109 -0
- package/src/lib/MarkdownDocs/vitePluginGregConfig.js +108 -0
- package/src/lib/MarkdownDocs/vitePluginSearchIndex.js +57 -0
- package/src/lib/MarkdownDocs/vitePluginSearchServer.js +190 -0
- package/src/lib/components/Badge.svelte +59 -0
- package/src/lib/components/Button.svelte +138 -0
- package/src/lib/components/CarbonAds.svelte +99 -0
- package/src/lib/components/CodeGroup.svelte +102 -0
- package/src/lib/components/Feature.svelte +209 -0
- package/src/lib/components/Features.svelte +123 -0
- package/src/lib/components/Hero.svelte +399 -0
- package/src/lib/components/Image.svelte +128 -0
- package/src/lib/components/Link.svelte +105 -0
- package/src/lib/components/SocialLink.svelte +84 -0
- package/src/lib/components/SocialLinks.svelte +33 -0
- package/src/lib/components/Steps.svelte +143 -0
- package/src/lib/components/TeamMember.svelte +273 -0
- package/src/lib/components/TeamMembers.svelte +81 -0
- package/src/lib/components/TeamPage.svelte +65 -0
- package/src/lib/components/TeamPageSection.svelte +108 -0
- package/src/lib/components/TeamPageTitle.svelte +89 -0
- package/src/lib/components/index.js +24 -0
- package/src/lib/portal/context.js +12 -0
- package/src/lib/portal/index.js +3 -0
- package/src/lib/portal/portal.svelte +14 -0
- package/src/lib/portal/slot.svelte +8 -0
- package/src/lib/scss/__code.scss +128 -0
- package/src/lib/scss/__containers.scss +99 -0
- package/src/lib/scss/__markdown.scss +447 -0
- package/src/lib/scss/__scrollbar.scss +60 -0
- package/src/lib/scss/__steps.scss +100 -0
- package/src/lib/scss/__theme.scss +238 -0
- package/src/lib/scss/__toc.scss +55 -0
- package/src/lib/scss/__utilities.scss +7 -0
- package/src/lib/scss/greg.scss +9 -0
- package/src/lib/spinner/spinner.svelte +42 -0
- package/svelte.config.js +146 -0
- package/types/index.d.ts +456 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
function capitalize(str) {
|
|
2
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function joinDocPath(base, ...parts) {
|
|
6
|
+
const baseNorm = String(base || '/').replace(/\/+$/g, '') || '/';
|
|
7
|
+
const suffix = parts
|
|
8
|
+
.filter((p) => p != null && String(p).length > 0)
|
|
9
|
+
.map((p) => String(p).replace(/^\/+|\/+$/g, ''))
|
|
10
|
+
.filter(Boolean)
|
|
11
|
+
.join('/');
|
|
12
|
+
const joined = suffix ? `${baseNorm}/${suffix}` : baseNorm;
|
|
13
|
+
return joined.replace(/\/+/g, '/');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Normalise a raw frontmatter badge value to `{ text, type }` or `undefined`.
|
|
18
|
+
* Accepts a plain string (type defaults to 'tip') or an object.
|
|
19
|
+
*/
|
|
20
|
+
function normalizeBadge(raw) {
|
|
21
|
+
if (!raw) return undefined;
|
|
22
|
+
if (typeof raw === 'string') return { text: raw, type: 'tip' };
|
|
23
|
+
if (raw.text) return { text: raw.text, type: raw.type ?? 'tip' };
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Sort items by optional `order` field first (lower = earlier), then
|
|
29
|
+
* folder/file preference and alphabetically by label as tie-breakers.
|
|
30
|
+
* Items without `order` sort after items that have one.
|
|
31
|
+
*/
|
|
32
|
+
function sortItems(items) {
|
|
33
|
+
items.sort((a, b) => {
|
|
34
|
+
const aHasOrder = Number.isFinite(a._order);
|
|
35
|
+
const bHasOrder = Number.isFinite(b._order);
|
|
36
|
+
|
|
37
|
+
// Ordered items come before unordered items.
|
|
38
|
+
if (aHasOrder !== bHasOrder) return aHasOrder ? -1 : 1;
|
|
39
|
+
|
|
40
|
+
if (aHasOrder && bHasOrder && a._order !== b._order) {
|
|
41
|
+
return a._order - b._order;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const aIsFolder = Boolean(a._isSection) || (Array.isArray(a.children) && a.children.length > 0);
|
|
45
|
+
const bIsFolder = Boolean(b._isSection) || (Array.isArray(b.children) && b.children.length > 0);
|
|
46
|
+
|
|
47
|
+
// For equal order weight, keep sections ahead of leaf pages.
|
|
48
|
+
if (aIsFolder !== bIsFolder) return aIsFolder ? -1 : 1;
|
|
49
|
+
|
|
50
|
+
return a.label.localeCompare(b.label);
|
|
51
|
+
});
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
if (item.children?.length) sortItems(item.children);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build the navigation tree from the modules map.
|
|
59
|
+
*
|
|
60
|
+
* @param {Record<string, unknown>} modules - known paths map (values ignored, only keys used)
|
|
61
|
+
* @param {string} base - root path prefix, e.g. '/docs'
|
|
62
|
+
* @param {Record<string, { title?: string; order?: number; [k: string]: unknown }>} [frontmatters]
|
|
63
|
+
* - eager-loaded frontmatter keyed by the same file paths as `modules`.
|
|
64
|
+
* - `title` overrides the label derived from the file/folder name.
|
|
65
|
+
* - `order` controls the sort position within a level (lower = earlier).
|
|
66
|
+
*/
|
|
67
|
+
export function prepareMenu(modules, base, frontmatters = {}) {
|
|
68
|
+
const paths = Object.keys(modules);
|
|
69
|
+
const root = [];
|
|
70
|
+
|
|
71
|
+
// Sort so index.md files are processed first — they define the folder nodes
|
|
72
|
+
const sorted = [...paths].sort((a, b) => {
|
|
73
|
+
const aIdx = a.endsWith('index.md') ? 0 : 1;
|
|
74
|
+
const bIdx = b.endsWith('index.md') ? 0 : 1;
|
|
75
|
+
return aIdx - bIdx;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
for (const filePath of sorted) {
|
|
79
|
+
const fm = frontmatters[filePath] ?? {};
|
|
80
|
+
// filePath like: /docs/folder1/test.md (base = '/docs')
|
|
81
|
+
const relativePath = filePath.startsWith(base) ? filePath.slice(base.length) : filePath;
|
|
82
|
+
// parts: ['folder1', 'test.md'] or ['index.md'] or ['folder1', 'index.md']
|
|
83
|
+
const parts = relativePath.replace(/^\//, '').split('/').filter(Boolean);
|
|
84
|
+
|
|
85
|
+
// Skip hidden files/dirs (starting with __)
|
|
86
|
+
if (parts.some(p => p.replace(/\.md$/, '').startsWith('__'))) continue;
|
|
87
|
+
|
|
88
|
+
let currentLevel = root;
|
|
89
|
+
let lastFolderNode = null; // tracks the most recently entered folder node
|
|
90
|
+
|
|
91
|
+
for (let idx = 0; idx < parts.length; idx++) {
|
|
92
|
+
const part = parts[idx];
|
|
93
|
+
const isLast = idx === parts.length - 1;
|
|
94
|
+
|
|
95
|
+
if (isLast) {
|
|
96
|
+
if (part === 'index.md') {
|
|
97
|
+
// index.md -> represents the parent folder (or root if idx === 0)
|
|
98
|
+
// We must NOT look in currentLevel (folder's own children) — we need
|
|
99
|
+
// the folder node itself, which is lastFolderNode (or root for top-level).
|
|
100
|
+
const folderParts = parts.slice(0, idx);
|
|
101
|
+
const link = folderParts.length ? joinDocPath(base, folderParts.join('/')) : joinDocPath(base);
|
|
102
|
+
const rawLabel = folderParts.length
|
|
103
|
+
? folderParts[folderParts.length - 1]
|
|
104
|
+
: base.split('/').filter(Boolean).pop() ?? 'Home';
|
|
105
|
+
|
|
106
|
+
let node = lastFolderNode ?? root.find(c => c.link === link);
|
|
107
|
+
if (!node) {
|
|
108
|
+
node = {
|
|
109
|
+
label: fm.title ?? capitalize(rawLabel),
|
|
110
|
+
link,
|
|
111
|
+
children: [],
|
|
112
|
+
type: 'md',
|
|
113
|
+
_isSection: true,
|
|
114
|
+
_order: fm.order,
|
|
115
|
+
badge: normalizeBadge(fm.badge),
|
|
116
|
+
};
|
|
117
|
+
currentLevel.push(node);
|
|
118
|
+
} else {
|
|
119
|
+
// Upgrade type if the node was pre-created as a folder
|
|
120
|
+
node.type = 'md';
|
|
121
|
+
node._isSection = true;
|
|
122
|
+
// Apply frontmatter overrides (index.md processed first, so this wins)
|
|
123
|
+
if (fm.title != null) node.label = fm.title;
|
|
124
|
+
if (fm.order != null) node._order = fm.order;
|
|
125
|
+
if (fm.badge != null) node.badge = normalizeBadge(fm.badge);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
// Regular .md file — leaf node
|
|
129
|
+
const fileName = part.replace(/\.md$/, '');
|
|
130
|
+
const link = joinDocPath(base, parts.slice(0, idx).concat(fileName).join('/'));
|
|
131
|
+
if (!currentLevel.find(c => c.link === link)) {
|
|
132
|
+
currentLevel.push({
|
|
133
|
+
label: fm.title ?? capitalize(fileName),
|
|
134
|
+
link,
|
|
135
|
+
children: [],
|
|
136
|
+
type: 'md',
|
|
137
|
+
_order: fm.order,
|
|
138
|
+
badge: normalizeBadge(fm.badge),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
// Folder segment — find or create the node and descend
|
|
144
|
+
const folderLink = joinDocPath(base, parts.slice(0, idx + 1).join('/'));
|
|
145
|
+
let child = currentLevel.find(c => c.link === folderLink);
|
|
146
|
+
if (!child) {
|
|
147
|
+
child = { label: capitalize(part), link: folderLink, children: [], type: 'folder', _isSection: true };
|
|
148
|
+
currentLevel.push(child);
|
|
149
|
+
}
|
|
150
|
+
lastFolderNode = child;
|
|
151
|
+
currentLevel = child.children;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
sortItems(root);
|
|
157
|
+
return root;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function flattenMenu(menu) {
|
|
161
|
+
const result = [];
|
|
162
|
+
|
|
163
|
+
function flattenItem(item) {
|
|
164
|
+
result.push({ label: item.label, link: item.link, type: item.type, badge: item.badge });
|
|
165
|
+
for (const child of item.children ?? []) flattenItem(child);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const item of menu) flattenItem(item);
|
|
169
|
+
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Returns the `{ prev, next }` neighbours of `active` within the flattened
|
|
175
|
+
* navigation list (md pages only, in sidebar order).
|
|
176
|
+
*
|
|
177
|
+
* @param {string} active
|
|
178
|
+
* @param {Array<{ label: string; link: string; type?: string }>} flat
|
|
179
|
+
* @returns {{ prev: { label: string; link: string } | null, next: { label: string; link: string } | null }}
|
|
180
|
+
*/
|
|
181
|
+
export function getPrevNext(active, flat) {
|
|
182
|
+
const pages = flat.filter(x => x.type === 'md');
|
|
183
|
+
const idx = pages.findIndex(x => x.link === active);
|
|
184
|
+
if (idx === -1) return { prev: null, next: null };
|
|
185
|
+
return {
|
|
186
|
+
prev: idx > 0 ? { label: pages[idx - 1].label, link: pages[idx - 1].link } : null,
|
|
187
|
+
next: idx < pages.length - 1 ? { label: pages[idx + 1].label, link: pages[idx + 1].link } : null,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Returns the ancestor chain from the root of `menu` down to the node
|
|
193
|
+
* whose link matches `active`, inclusive.
|
|
194
|
+
*
|
|
195
|
+
* @param {string} active
|
|
196
|
+
* @param {Array<{ label: string; link: string; children?: Array<any> }>} menu
|
|
197
|
+
* @returns {Array<{ label: string; link: string }>}
|
|
198
|
+
*/
|
|
199
|
+
export function getBreadcrumbItems(active, menu) {
|
|
200
|
+
function findPath(items, target, path) {
|
|
201
|
+
for (const item of items) {
|
|
202
|
+
const next = [...path, { label: item.label, link: item.link }];
|
|
203
|
+
if (item.link === target) return next;
|
|
204
|
+
if (item.children?.length) {
|
|
205
|
+
const found = findPath(item.children, target, next);
|
|
206
|
+
if (found) return found;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
return findPath(menu, active, []) ?? [];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Converts a declarative sidebar config array to a `TreeViewItem[]` tree.
|
|
216
|
+
* Items with an `auto` path have their children generated from `frontmatters`.
|
|
217
|
+
*
|
|
218
|
+
* @param {Array<{ text?: string; link?: string; items?: Array<any>; auto?: string; badge?: any }>} items
|
|
219
|
+
* @param {Record<string, { title?: string; order?: number; [k: string]: unknown }>} frontmatters
|
|
220
|
+
* @param {string} base - docs root prefix (e.g. '/docs')
|
|
221
|
+
* @returns {import('./treeViewTypes').TreeViewItem[] | null}
|
|
222
|
+
*/
|
|
223
|
+
export function parseSidebarConfig(items, frontmatters, base) {
|
|
224
|
+
if (!Array.isArray(items)) return null;
|
|
225
|
+
|
|
226
|
+
function convert(item) {
|
|
227
|
+
if (item.auto) {
|
|
228
|
+
// `auto` is relative to `base` (srcDir), e.g. '/guide' -> '/docs/guide'
|
|
229
|
+
const autoBase = joinDocPath(base, item.auto);
|
|
230
|
+
// Only pass paths that belong to this sub-section so stray paths
|
|
231
|
+
// don't become extra folder segments inside prepareMenu.
|
|
232
|
+
const autoFrontmatters = Object.fromEntries(
|
|
233
|
+
Object.entries(frontmatters).filter(([k]) => k.startsWith(autoBase + '/') || k === autoBase)
|
|
234
|
+
);
|
|
235
|
+
const autoMenu = prepareMenu(autoFrontmatters, autoBase, autoFrontmatters);
|
|
236
|
+
const autoRoot = autoMenu.find(node => node.link === autoBase);
|
|
237
|
+
// prepareMenu(autoBase) returns pages under autoBase on the top level,
|
|
238
|
+
// while autoRoot (index page) is also a top-level node when present.
|
|
239
|
+
// For sidebar sections we need all descendants except the section root itself.
|
|
240
|
+
const children = autoMenu.filter(node => node.link !== autoBase);
|
|
241
|
+
const fallbackLabel = autoRoot?.label
|
|
242
|
+
?? capitalize(item.auto.split('/').filter(Boolean).pop() ?? 'Section');
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
// Label precedence: sidebar text -> frontmatter title -> folder name.
|
|
246
|
+
label: item.text ?? fallbackLabel,
|
|
247
|
+
link: item.link ?? autoBase,
|
|
248
|
+
...(item.target ? { target: item.target } : {}),
|
|
249
|
+
...(item.rel ? { rel: item.rel } : {}),
|
|
250
|
+
badge: normalizeBadge(item.badge),
|
|
251
|
+
children,
|
|
252
|
+
type: (item.link ? 'md' : (autoRoot?.type ?? 'folder')),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const children = Array.isArray(item.items) ? item.items.map(convert) : [];
|
|
256
|
+
return {
|
|
257
|
+
label: item.text,
|
|
258
|
+
link: item.link ?? '',
|
|
259
|
+
...(item.target ? { target: item.target } : {}),
|
|
260
|
+
...(item.rel ? { rel: item.rel } : {}),
|
|
261
|
+
badge: normalizeBadge(item.badge),
|
|
262
|
+
children,
|
|
263
|
+
type: item.link ? 'md' : 'folder',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return items.map(convert);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function getBreadcrumb(active, flat) {
|
|
271
|
+
// exact match first
|
|
272
|
+
const exact = flat.find(x => x.link === active);
|
|
273
|
+
if (exact) return exact.label;
|
|
274
|
+
|
|
275
|
+
// fallback: reconstruct from path segments
|
|
276
|
+
const parts = active.replace(/^\//, '').split('/').filter(Boolean);
|
|
277
|
+
if (!parts.length) return '';
|
|
278
|
+
const last = parts[parts.length - 1];
|
|
279
|
+
return last.charAt(0).toUpperCase() + last.slice(1);
|
|
280
|
+
}
|
|
281
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Greg — Vite plugin entry point.
|
|
3
|
+
*
|
|
4
|
+
* Import these in your vite.config.js to wire up Greg's build-time plugins:
|
|
5
|
+
*
|
|
6
|
+
* import {
|
|
7
|
+
* vitePluginGregConfig,
|
|
8
|
+
* vitePluginSearchIndex,
|
|
9
|
+
* vitePluginSearchServer,
|
|
10
|
+
* vitePluginAiServer,
|
|
11
|
+
* vitePluginFrontmatter,
|
|
12
|
+
* vitePluginCopyDocs,
|
|
13
|
+
* aiCharacters,
|
|
14
|
+
* } from '@dominikcz/greg/plugins';
|
|
15
|
+
*/
|
|
16
|
+
export { vitePluginGregConfig } from './vitePluginGregConfig.js';
|
|
17
|
+
export { vitePluginSearchIndex } from './vitePluginSearchIndex.js';
|
|
18
|
+
export { vitePluginSearchServer } from './vitePluginSearchServer.js';
|
|
19
|
+
export { vitePluginAiServer } from './vitePluginAiServer.js';
|
|
20
|
+
export { vitePluginFrontmatter } from './vitePluginFrontmatter.js';
|
|
21
|
+
export { vitePluginCopyDocs } from './vitePluginCopyDocs.js';
|
|
22
|
+
export { aiCharacters } from './ai/characters.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
let { children }: { children?: Snippet } = $props();
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<!-- Default doc layout – transparent pass-through wrapper.
|
|
7
|
+
Receives all frontmatter fields as props (passed by mdsvex). -->
|
|
8
|
+
{@render children?.()}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import Hero from "$components/Hero.svelte";
|
|
4
|
+
import Features from "$components/Features.svelte";
|
|
5
|
+
import { withBase } from "../common";
|
|
6
|
+
|
|
7
|
+
type HeroAction = {
|
|
8
|
+
theme?: "brand" | "alt";
|
|
9
|
+
text: string;
|
|
10
|
+
link: string;
|
|
11
|
+
target?: string;
|
|
12
|
+
rel?: string;
|
|
13
|
+
};
|
|
14
|
+
type FeatureItem = {
|
|
15
|
+
icon?: string;
|
|
16
|
+
title: string;
|
|
17
|
+
details?: string;
|
|
18
|
+
link?: string;
|
|
19
|
+
linkText?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type Props = {
|
|
23
|
+
hero?: {
|
|
24
|
+
name?: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
tagline?: string;
|
|
27
|
+
image?: unknown;
|
|
28
|
+
actions?: HeroAction[];
|
|
29
|
+
};
|
|
30
|
+
features?: FeatureItem[];
|
|
31
|
+
children?: Snippet;
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
let { hero, features, children }: Props = $props();
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<div class="home-layout">
|
|
39
|
+
{#if hero}
|
|
40
|
+
<Hero
|
|
41
|
+
name={hero.name}
|
|
42
|
+
text={hero.text}
|
|
43
|
+
tagline={hero.tagline}
|
|
44
|
+
image={hero.image as any}
|
|
45
|
+
actions={hero.actions?.map((a) => ({ ...a, link: withBase(a.link) }))}
|
|
46
|
+
/>
|
|
47
|
+
{/if}
|
|
48
|
+
{#if features?.length}
|
|
49
|
+
<Features features={features.map((f) => ({ ...f, link: f.link ? withBase(f.link) : f.link }))} />
|
|
50
|
+
{/if}
|
|
51
|
+
{@render children?.()}
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
.home-layout {
|
|
56
|
+
width: 100%;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
let { children }: { children?: Snippet } = $props();
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<!-- Page layout – full-width without sidebar, markdown-body styling retained.
|
|
7
|
+
The parent MarkdownDocs component reads `layout: page` from frontmatter
|
|
8
|
+
to hide the sidebar/outline. -->
|
|
9
|
+
{@render children?.()}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function deepMerge(baseValue, overrideValue) {
|
|
10
|
+
if (!isPlainObject(baseValue) || !isPlainObject(overrideValue)) {
|
|
11
|
+
return overrideValue;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const result = { ...baseValue };
|
|
15
|
+
for (const [key, overrideEntry] of Object.entries(overrideValue)) {
|
|
16
|
+
const baseEntry = result[key];
|
|
17
|
+
if (isPlainObject(baseEntry) && isPlainObject(overrideEntry)) {
|
|
18
|
+
result[key] = deepMerge(baseEntry, overrideEntry);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
result[key] = overrideEntry;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeConfig(value) {
|
|
28
|
+
return isPlainObject(value) ? value : {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveMainGregConfigPath(rootDir = process.cwd()) {
|
|
32
|
+
const tsPath = path.join(rootDir, 'greg.config.ts');
|
|
33
|
+
const jsPath = path.join(rootDir, 'greg.config.js');
|
|
34
|
+
|
|
35
|
+
if (fs.existsSync(tsPath)) return tsPath;
|
|
36
|
+
if (fs.existsSync(jsPath)) return jsPath;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolvePrvGregConfigPath(rootDir = process.cwd()) {
|
|
41
|
+
const prvPath = path.join(rootDir, 'prv', 'greg.config.js');
|
|
42
|
+
return fs.existsSync(prvPath) ? prvPath : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveGregConfigPaths(rootDir = process.cwd()) {
|
|
46
|
+
const mainConfigPath = resolveMainGregConfigPath(rootDir);
|
|
47
|
+
const prvConfigPath = resolvePrvGregConfigPath(rootDir);
|
|
48
|
+
return { mainConfigPath, prvConfigPath };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function loadGregConfigFile(configPath) {
|
|
52
|
+
if (!configPath || !fs.existsSync(configPath)) return {};
|
|
53
|
+
|
|
54
|
+
if (configPath.endsWith('.ts')) {
|
|
55
|
+
const { transform } = await import('esbuild');
|
|
56
|
+
const source = fs.readFileSync(configPath, 'utf8');
|
|
57
|
+
const { code } = await transform(source, {
|
|
58
|
+
format: 'esm',
|
|
59
|
+
loader: 'ts',
|
|
60
|
+
target: 'node18',
|
|
61
|
+
});
|
|
62
|
+
const dataUrl = 'data:text/javascript,' + encodeURIComponent(code);
|
|
63
|
+
const mod = await import(dataUrl);
|
|
64
|
+
return normalizeConfig(mod.default);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const fileUrl = pathToFileURL(configPath).href + '?t=' + Date.now();
|
|
68
|
+
const mod = await import(fileUrl);
|
|
69
|
+
return normalizeConfig(mod.default);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function loadGregConfig(rootDir = process.cwd()) {
|
|
73
|
+
const { mainConfigPath, prvConfigPath } = resolveGregConfigPaths(rootDir);
|
|
74
|
+
const mainConfig = await loadGregConfigFile(mainConfigPath);
|
|
75
|
+
|
|
76
|
+
if (!prvConfigPath) {
|
|
77
|
+
return mainConfig;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const prvConfig = await loadGregConfigFile(prvConfigPath);
|
|
81
|
+
return deepMerge(mainConfig, prvConfig);
|
|
82
|
+
}
|