@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,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import TreeViewItem from "./TreeViewItem.svelte";
|
|
3
|
+
import type { TreeViewItem as TreeViewItemType } from "./treeViewTypes";
|
|
4
|
+
|
|
5
|
+
interface TreeViewProps {
|
|
6
|
+
tree: TreeViewItemType[];
|
|
7
|
+
active: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
navigate: (path: string) => void;
|
|
10
|
+
}
|
|
11
|
+
let {
|
|
12
|
+
tree,
|
|
13
|
+
active = "",
|
|
14
|
+
class: className = "",
|
|
15
|
+
navigate,
|
|
16
|
+
}: TreeViewProps = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<ul class="tree-view {className}">
|
|
20
|
+
{#each tree as item (item.link || item.label)}
|
|
21
|
+
<TreeViewItem {item} depth={0} {active} {navigate} />
|
|
22
|
+
{/each}
|
|
23
|
+
</ul>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
.tree-view {
|
|
27
|
+
margin: 0;
|
|
28
|
+
list-style: none;
|
|
29
|
+
padding-inline-start: 0;
|
|
30
|
+
user-select: none;
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
// based on https://svelte.dev/playground/82b00644720a4ca2bdb89c6a653ec987
|
|
3
|
+
// retain module scoped expansion state for each tree node
|
|
4
|
+
const _expansionState = new Map<string, boolean>();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import Self from "./TreeViewItem.svelte";
|
|
9
|
+
import type { TreeViewItem } from "./treeViewTypes";
|
|
10
|
+
import { ChevronRight } from "@lucide/svelte";
|
|
11
|
+
import { withBase } from "./common";
|
|
12
|
+
|
|
13
|
+
interface TreeViewItemProps {
|
|
14
|
+
item: TreeViewItem;
|
|
15
|
+
active: string;
|
|
16
|
+
depth: number;
|
|
17
|
+
navigate: (path: string) => void;
|
|
18
|
+
}
|
|
19
|
+
let {
|
|
20
|
+
item,
|
|
21
|
+
active = "",
|
|
22
|
+
depth = 0,
|
|
23
|
+
navigate,
|
|
24
|
+
}: TreeViewItemProps = $props();
|
|
25
|
+
|
|
26
|
+
const EXTERNAL_RE = /^(?:[a-z][a-z\d+\-.]*:|\/\/)/i;
|
|
27
|
+
|
|
28
|
+
// Inicjalizacja stanu rozwinięcia
|
|
29
|
+
let expanded = $state(false);
|
|
30
|
+
// Automatycznie rozwiń jeśli aktywna ścieżka jest w tej gałęzi
|
|
31
|
+
let shouldExpand = $derived(
|
|
32
|
+
active.startsWith(item.link) && item.link !== active,
|
|
33
|
+
);
|
|
34
|
+
$effect(() => {
|
|
35
|
+
if (shouldExpand && !expanded) expanded = true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function toggle() {
|
|
39
|
+
_expansionState.set(item.link, !expanded);
|
|
40
|
+
expanded = !expanded;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function shouldHandleWithRouter() {
|
|
44
|
+
if (!item.link) return false;
|
|
45
|
+
if (EXTERNAL_RE.test(item.link)) return false;
|
|
46
|
+
if (item.target && item.target !== "_self") return false;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function handleClick(e: MouseEvent) {
|
|
51
|
+
if (item.target === "_blank") {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
window.open(item.link, "_blank", "noopener,noreferrer");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!shouldHandleWithRouter()) return;
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
navigate(item.link);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function handleToggleClick(e: MouseEvent) {
|
|
63
|
+
if (item.target === "_blank") {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
window.open(item.link, "_blank", "noopener,noreferrer");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!shouldHandleWithRouter()) return;
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
toggle();
|
|
72
|
+
navigate(item.link);
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<li>
|
|
77
|
+
{#if item.children.length}
|
|
78
|
+
<a
|
|
79
|
+
href={withBase(item.link)}
|
|
80
|
+
target={item.target}
|
|
81
|
+
rel={item.rel}
|
|
82
|
+
onclick={handleToggleClick}
|
|
83
|
+
class="arrow"
|
|
84
|
+
class:active={active == item.link}
|
|
85
|
+
>
|
|
86
|
+
<span class="chevron" class:expanded>
|
|
87
|
+
<ChevronRight />
|
|
88
|
+
</span>
|
|
89
|
+
{#if item.status}<span class={item.status}
|
|
90
|
+
></span>{/if}{item.label}{#if item.badge}<span
|
|
91
|
+
class="nav-badge {item.badge.type ?? 'tip'}"
|
|
92
|
+
>{item.badge.text}</span
|
|
93
|
+
>{/if}
|
|
94
|
+
</a>
|
|
95
|
+
{#if expanded}
|
|
96
|
+
<ul>
|
|
97
|
+
{#each item.children as child}
|
|
98
|
+
<Self item={child} depth={depth + 1} {active} {navigate} />
|
|
99
|
+
{/each}
|
|
100
|
+
</ul>
|
|
101
|
+
{/if}
|
|
102
|
+
{:else}
|
|
103
|
+
<a
|
|
104
|
+
href={withBase(item.link)}
|
|
105
|
+
target={item.target}
|
|
106
|
+
rel={item.rel}
|
|
107
|
+
class="no-arrow"
|
|
108
|
+
class:active={active == item.link}
|
|
109
|
+
onclick={handleClick}
|
|
110
|
+
>{#if item.status}<span class={item.status}
|
|
111
|
+
></span>{/if}{item.label}{#if item.badge}<span
|
|
112
|
+
class="nav-badge {item.badge.type ?? 'tip'}"
|
|
113
|
+
>{item.badge.text}</span
|
|
114
|
+
>{/if}</a
|
|
115
|
+
>
|
|
116
|
+
{/if}
|
|
117
|
+
</li>
|
|
118
|
+
|
|
119
|
+
<style lang="scss">
|
|
120
|
+
li {
|
|
121
|
+
line-height: 1.5rem;
|
|
122
|
+
margin: 0.1rem 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
ul {
|
|
126
|
+
list-style: none;
|
|
127
|
+
padding-inline-start: 0.85rem;
|
|
128
|
+
margin: 0.1rem 0 0 0.6rem;
|
|
129
|
+
border-left: 1px solid var(--greg-border-color);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.no-arrow {
|
|
133
|
+
padding-left: 1.1rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.arrow {
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
display: flex;
|
|
139
|
+
|
|
140
|
+
.chevron {
|
|
141
|
+
display: flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
transition: transform 0.2s ease;
|
|
144
|
+
transform: rotate(0deg);
|
|
145
|
+
flex-shrink: 0;
|
|
146
|
+
|
|
147
|
+
&.expanded {
|
|
148
|
+
transform: rotate(90deg);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
a {
|
|
154
|
+
padding: 0.3rem 0.5rem;
|
|
155
|
+
display: block;
|
|
156
|
+
text-decoration: none;
|
|
157
|
+
color: var(--greg-menu-color);
|
|
158
|
+
border-radius: 5px;
|
|
159
|
+
font-size: 0.875rem;
|
|
160
|
+
line-height: 1.5rem;
|
|
161
|
+
|
|
162
|
+
span {
|
|
163
|
+
vertical-align: bottom;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&:hover {
|
|
167
|
+
background-color: var(--greg-menu-hover-background);
|
|
168
|
+
color: var(--greg-color);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
&.active {
|
|
172
|
+
background-color: var(--greg-accent-light);
|
|
173
|
+
color: var(--greg-accent);
|
|
174
|
+
font-weight: 600;
|
|
175
|
+
|
|
176
|
+
.arrow {
|
|
177
|
+
color: var(--greg-accent);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&:hover {
|
|
181
|
+
background-color: var(--greg-accent-light);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.nav-badge {
|
|
187
|
+
display: inline-block;
|
|
188
|
+
margin-left: 0.4rem;
|
|
189
|
+
padding: 0 7px;
|
|
190
|
+
border-radius: 10px;
|
|
191
|
+
font-size: 11px;
|
|
192
|
+
font-weight: 500;
|
|
193
|
+
line-height: 18px;
|
|
194
|
+
vertical-align: middle;
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
border: 1px solid transparent;
|
|
197
|
+
|
|
198
|
+
&.tip {
|
|
199
|
+
background-color: var(--greg-tip-bg);
|
|
200
|
+
border-color: var(--greg-tip-border);
|
|
201
|
+
color: var(--greg-tip-text);
|
|
202
|
+
}
|
|
203
|
+
&.info {
|
|
204
|
+
background-color: var(--greg-info-bg);
|
|
205
|
+
border-color: var(--greg-info-border);
|
|
206
|
+
color: var(--greg-info-text);
|
|
207
|
+
}
|
|
208
|
+
&.warning {
|
|
209
|
+
background-color: var(--greg-warning-bg);
|
|
210
|
+
border-color: var(--greg-warning-border);
|
|
211
|
+
color: var(--greg-warning-text);
|
|
212
|
+
}
|
|
213
|
+
&.danger {
|
|
214
|
+
background-color: var(--greg-danger-bg);
|
|
215
|
+
border-color: var(--greg-danger-border);
|
|
216
|
+
color: var(--greg-danger-text);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
type Props = {
|
|
3
|
+
currentTitle: string;
|
|
4
|
+
defaultTitle: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
actionLabel?: string;
|
|
7
|
+
onGoToDefault: () => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
currentTitle,
|
|
12
|
+
defaultTitle,
|
|
13
|
+
message = "",
|
|
14
|
+
actionLabel = "Go to latest",
|
|
15
|
+
onGoToDefault,
|
|
16
|
+
}: Props = $props();
|
|
17
|
+
|
|
18
|
+
const resolvedMessage = $derived(
|
|
19
|
+
String(message || "").trim() ||
|
|
20
|
+
`You are viewing an older documentation version (${currentTitle}). ${defaultTitle} is currently recommended.`,
|
|
21
|
+
);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div class="version-notice" role="status" aria-live="polite">
|
|
25
|
+
<p>{resolvedMessage}</p>
|
|
26
|
+
<button type="button" onclick={onGoToDefault}>{actionLabel}</button>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<style lang="scss">
|
|
30
|
+
.version-notice {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: space-between;
|
|
34
|
+
gap: 0.75rem;
|
|
35
|
+
padding: 0.55rem 1.25rem;
|
|
36
|
+
border-bottom: 1px solid color-mix(in srgb, var(--greg-accent) 25%, var(--greg-border-color));
|
|
37
|
+
background: color-mix(in srgb, var(--greg-accent-light) 55%, var(--greg-header-background));
|
|
38
|
+
color: var(--greg-color);
|
|
39
|
+
flex-shrink: 0;
|
|
40
|
+
|
|
41
|
+
p {
|
|
42
|
+
margin: 0;
|
|
43
|
+
font-size: 0.86rem;
|
|
44
|
+
line-height: 1.35;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
button {
|
|
48
|
+
border: 1px solid color-mix(in srgb, var(--greg-accent) 45%, var(--greg-border-color));
|
|
49
|
+
background: var(--greg-header-background);
|
|
50
|
+
color: var(--greg-accent);
|
|
51
|
+
font: inherit;
|
|
52
|
+
font-size: 0.8rem;
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
border-radius: 6px;
|
|
55
|
+
padding: 0.25rem 0.55rem;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
|
|
58
|
+
&:hover {
|
|
59
|
+
border-color: var(--greg-accent);
|
|
60
|
+
background: var(--greg-accent-light);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@media (max-width: 800px) {
|
|
66
|
+
.version-notice {
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
align-items: flex-start;
|
|
69
|
+
padding: 0.55rem 0.75rem;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parseCodeDirectives, decorateHighlightedCodeHtml } from '../codeDirectives.js';
|
|
3
|
+
import { normalizeCodeFenceInfo } from '../codeFenceInfo.js';
|
|
4
|
+
|
|
5
|
+
describe('codeDirectives', () => {
|
|
6
|
+
it('parses meta line ranges and strips inline code markers', () => {
|
|
7
|
+
const parsed = parseCodeDirectives('const a = 1; // [!code ++]\nconst token = a;', '{2} [demo.ts]');
|
|
8
|
+
expect(parsed.cleanedCode).toContain('const a = 1;');
|
|
9
|
+
expect(parsed.cleanedCode).not.toContain('[!code');
|
|
10
|
+
expect(parsed.lineInfo[0].diffAdd).toBe(true);
|
|
11
|
+
expect(parsed.lineInfo[1].highlight).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('decorates line wrappers with classes', () => {
|
|
15
|
+
const directives = parseCodeDirectives('const oldValue = 1; // [!code --]\nconst token = oldValue; // [!code focus]', '{1}');
|
|
16
|
+
const html = '<pre><code><span class="line">const oldValue = 1;</span>\n<span class="line">const token = oldValue;</span></code></pre>';
|
|
17
|
+
const out = decorateHighlightedCodeHtml(html, directives);
|
|
18
|
+
|
|
19
|
+
expect(out).toContain('line highlighted diff remove');
|
|
20
|
+
expect(out).toContain('line focused');
|
|
21
|
+
expect(out).toContain('has-focused-lines');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('does not strip markers inside markdown source fences', () => {
|
|
25
|
+
const parsed = parseCodeDirectives('const oldValue = 1; // [!code --]\n', '', 'md');
|
|
26
|
+
expect(parsed.cleanedCode).toContain('[!code --]');
|
|
27
|
+
expect(parsed.cleanedCode.endsWith('\n')).toBe(false);
|
|
28
|
+
expect(parsed.lineInfo).toEqual([]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('supports line numbers metadata with custom start index', () => {
|
|
32
|
+
const parsed = parseCodeDirectives('const a = 1;\nconst b = 2;', 'line-numbers=10', 'ts');
|
|
33
|
+
expect(parsed.lineNumbers).toEqual({ enabled: true, start: 10 });
|
|
34
|
+
|
|
35
|
+
const html = '<pre><code><span class="line">const a = 1;</span>\n<span class="line">const b = 2;</span></code></pre>';
|
|
36
|
+
const out = decorateHighlightedCodeHtml(html, parsed);
|
|
37
|
+
expect(out).toContain('data-line-numbers="true"');
|
|
38
|
+
expect(out).toContain('data-line="10"');
|
|
39
|
+
expect(out).toContain('data-line="11"');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('enables line numbers with default start when no index is provided', () => {
|
|
43
|
+
const parsed = parseCodeDirectives('const a = 1;', 'line-numbers', 'ts');
|
|
44
|
+
expect(parsed.lineNumbers).toEqual({ enabled: true, start: 1 });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('normalizes vitepress-style lang/meta forms', () => {
|
|
48
|
+
expect(normalizeCodeFenceInfo('ts{2}', '')).toEqual({ lang: 'ts', meta: '{2}' });
|
|
49
|
+
expect(normalizeCodeFenceInfo('ts:line-numbers=10', '{1}')).toEqual({
|
|
50
|
+
lang: 'ts',
|
|
51
|
+
meta: '{1} line-numbers=10',
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { pathConfig, withoutBase } from '../common';
|
|
4
|
+
|
|
5
|
+
function setConfig(config) {
|
|
6
|
+
Object.assign(pathConfig, config);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
setConfig({ base: '/greg/', srcDir: 'my_docs', docsBase: 'documentation' });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('common.withoutBase', () => {
|
|
14
|
+
it('maps {base}/{docsBase}/... to {srcDir}/... when all fields are set', () => {
|
|
15
|
+
setConfig({ base: '/greg/', srcDir: 'my_docs', docsBase: 'documentation' });
|
|
16
|
+
|
|
17
|
+
expect(withoutBase('/greg/documentation/guide/getting-started')).toBe('/documentation/guide/getting-started');
|
|
18
|
+
expect(withoutBase('/greg/documentation')).toBe('/documentation');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('keeps old base-strip behavior for paths outside /{base}/{docsBase}', () => {
|
|
22
|
+
setConfig({ base: '/greg/', srcDir: 'my_docs', docsBase: 'documentation' });
|
|
23
|
+
|
|
24
|
+
expect(withoutBase('/greg/guide/getting-started')).toBe('/guide/getting-started');
|
|
25
|
+
expect(withoutBase('/something-else/guide')).toBe('/something-else/guide');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('supports base="/" and still maps /{docsBase} to {srcDir}', () => {
|
|
29
|
+
setConfig({ base: '/', srcDir: 'content_docs', docsBase: 'documentation' });
|
|
30
|
+
|
|
31
|
+
expect(withoutBase('/documentation/guide/intro')).toBe('/documentation/guide/intro');
|
|
32
|
+
expect(withoutBase('/content_docs/guide/intro')).toBe('/content_docs/guide/intro');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('keeps external URLs and relative paths unchanged', () => {
|
|
36
|
+
setConfig({ base: '/greg/', srcDir: 'my_docs', docsBase: 'documentation' });
|
|
37
|
+
|
|
38
|
+
expect(withoutBase('https://example.com/docs')).toBe('https://example.com/docs');
|
|
39
|
+
expect(withoutBase('guide/getting-started')).toBe('guide/getting-started');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const DOCS_MARKDOWN_DIR = path.join(process.cwd(), 'docs', 'guide', 'markdown');
|
|
6
|
+
|
|
7
|
+
function isMdExampleFenceStart(line) {
|
|
8
|
+
return /^`{3,4}md\b/.test(line.trim());
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isFenceClose(line, tickCount) {
|
|
12
|
+
return new RegExp(`^\\s*${'`'.repeat(tickCount)}\\s*$`).test(line);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function findMdExampleViolations(content) {
|
|
16
|
+
const lines = content.split(/\r?\n/);
|
|
17
|
+
const violations = [];
|
|
18
|
+
|
|
19
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20
|
+
const startLine = lines[i].trim();
|
|
21
|
+
if (!isMdExampleFenceStart(startLine)) continue;
|
|
22
|
+
|
|
23
|
+
const tickCount = (startLine.match(/^`+/) ?? [''])[0].length;
|
|
24
|
+
let closeIdx = -1;
|
|
25
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
26
|
+
if (isFenceClose(lines[j], tickCount)) {
|
|
27
|
+
closeIdx = j;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (closeIdx === -1) {
|
|
33
|
+
violations.push(`Line ${i + 1}: unclosed markdown example fence`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let cursor = closeIdx + 1;
|
|
38
|
+
let hasOutput = false;
|
|
39
|
+
for (; cursor < lines.length; cursor++) {
|
|
40
|
+
const line = lines[cursor].trim();
|
|
41
|
+
if (/^Output\b/i.test(line)) {
|
|
42
|
+
hasOutput = true;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
if (isMdExampleFenceStart(line)) break;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!hasOutput) {
|
|
49
|
+
violations.push(`Line ${i + 1}: missing Output section after markdown example`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
i = closeIdx;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return violations;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe('docs lint - markdown examples', () => {
|
|
59
|
+
it('ensures every md source example has an Output section', async () => {
|
|
60
|
+
const entries = await fs.readdir(DOCS_MARKDOWN_DIR, { withFileTypes: true });
|
|
61
|
+
const mdFiles = entries
|
|
62
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
|
63
|
+
.map((entry) => path.join(DOCS_MARKDOWN_DIR, entry.name));
|
|
64
|
+
|
|
65
|
+
const failures = [];
|
|
66
|
+
|
|
67
|
+
for (const filePath of mdFiles) {
|
|
68
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
69
|
+
const violations = findMdExampleViolations(content);
|
|
70
|
+
for (const violation of violations) {
|
|
71
|
+
failures.push(`${path.relative(process.cwd(), filePath)}: ${violation}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
expect(failures, failures.join('\n')).toEqual([]);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers — builds a unified processor matching svelte.config.js setup,
|
|
3
|
+
* but outputs plain HTML (no Svelte compilation).
|
|
4
|
+
*/
|
|
5
|
+
import { unified } from 'unified';
|
|
6
|
+
import remarkParse from 'remark-parse';
|
|
7
|
+
import remarkRehype from 'remark-rehype';
|
|
8
|
+
import rehypeStringify from 'rehype-stringify';
|
|
9
|
+
import rehypeSlug from 'rehype-slug';
|
|
10
|
+
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
|
11
|
+
import rehypeCodeGroup from '../rehypeCodeGroup.js';
|
|
12
|
+
import rehypeCodeTitle from '../rehypeCodeTitle.js';
|
|
13
|
+
import { remarkContainers, rehypeContainers } from '../remarkContainers.js';
|
|
14
|
+
import { rehypeTocPlaceholder } from '../rehypeToc.js';
|
|
15
|
+
import { remarkCodeMeta } from '../remarkCodeMeta.js';
|
|
16
|
+
import { remarkImports } from '../remarkImports.js';
|
|
17
|
+
|
|
18
|
+
const defaultImportsOptions = { sourceRoot: globalThis.process.cwd(), docsDir: 'docs' };
|
|
19
|
+
|
|
20
|
+
// Single shared processor — created once so shiki's lazy grammar loading
|
|
21
|
+
// only happens on first call, not per-test.
|
|
22
|
+
let _processor = null;
|
|
23
|
+
|
|
24
|
+
async function getProcessor(opts = {}) {
|
|
25
|
+
// If custom options are passed, build a one-off processor
|
|
26
|
+
if (opts.containers || opts.toc || opts.imports) {
|
|
27
|
+
return unified()
|
|
28
|
+
.use(remarkParse)
|
|
29
|
+
.use(remarkImports, opts.imports ?? defaultImportsOptions)
|
|
30
|
+
.use(remarkCodeMeta)
|
|
31
|
+
.use(remarkContainers, opts.containers ?? {})
|
|
32
|
+
.use(remarkRehype, { allowDangerousHtml: true })
|
|
33
|
+
.use(rehypeSlug)
|
|
34
|
+
.use(rehypeAutolinkHeadings, { behavior: 'prepend', properties: { class: 'header-anchor', ariaHidden: 'true', tabIndex: -1 }, content: { type: 'text', value: '#' } })
|
|
35
|
+
.use(rehypeContainers, opts.containers ?? {})
|
|
36
|
+
.use(rehypeCodeGroup)
|
|
37
|
+
.use(rehypeCodeTitle)
|
|
38
|
+
.use(rehypeTocPlaceholder, opts.toc ?? {})
|
|
39
|
+
.use(rehypeStringify, { allowDangerousHtml: true });
|
|
40
|
+
}
|
|
41
|
+
if (!_processor) {
|
|
42
|
+
_processor = unified()
|
|
43
|
+
.use(remarkParse)
|
|
44
|
+
.use(remarkImports, defaultImportsOptions)
|
|
45
|
+
.use(remarkCodeMeta)
|
|
46
|
+
.use(remarkContainers)
|
|
47
|
+
.use(remarkRehype, { allowDangerousHtml: true })
|
|
48
|
+
.use(rehypeSlug)
|
|
49
|
+
.use(rehypeAutolinkHeadings, { behavior: 'prepend', properties: { class: 'header-anchor', ariaHidden: 'true', tabIndex: -1 }, content: { type: 'text', value: '#' } })
|
|
50
|
+
.use(rehypeContainers)
|
|
51
|
+
.use(rehypeCodeGroup)
|
|
52
|
+
.use(rehypeCodeTitle)
|
|
53
|
+
.use(rehypeTocPlaceholder)
|
|
54
|
+
.use(rehypeStringify, { allowDangerousHtml: true });
|
|
55
|
+
}
|
|
56
|
+
return _processor;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function process(markdown, opts = {}) {
|
|
60
|
+
const processor = await getProcessor(opts);
|
|
61
|
+
const input = opts.vfile
|
|
62
|
+
? { ...opts.vfile, value: markdown }
|
|
63
|
+
: opts.filename
|
|
64
|
+
? { value: markdown, path: opts.filename }
|
|
65
|
+
: markdown;
|
|
66
|
+
return String(await processor.process(input));
|
|
67
|
+
}
|