svedocs 0.1.0-beta.2

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 (192) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +38 -0
  3. package/dist/ai/openai-compatible.d.ts +13 -0
  4. package/dist/ai/openai-compatible.d.ts.map +1 -0
  5. package/dist/ai/openai-compatible.js +92 -0
  6. package/dist/ai/providers.d.ts +19 -0
  7. package/dist/ai/providers.d.ts.map +1 -0
  8. package/dist/ai/providers.js +185 -0
  9. package/dist/ai/rate-limit.d.ts +18 -0
  10. package/dist/ai/rate-limit.d.ts.map +1 -0
  11. package/dist/ai/rate-limit.js +52 -0
  12. package/dist/ai/response.d.ts +4 -0
  13. package/dist/ai/response.d.ts.map +1 -0
  14. package/dist/ai/response.js +151 -0
  15. package/dist/ai/runtime.d.ts +30 -0
  16. package/dist/ai/runtime.d.ts.map +1 -0
  17. package/dist/ai/runtime.js +105 -0
  18. package/dist/ai/types.d.ts +93 -0
  19. package/dist/ai/types.d.ts.map +1 -0
  20. package/dist/ai/types.js +1 -0
  21. package/dist/ai.d.ts +7 -0
  22. package/dist/ai.d.ts.map +1 -0
  23. package/dist/ai.js +6 -0
  24. package/dist/ambient.d.ts +5 -0
  25. package/dist/cloudflare.d.ts +27 -0
  26. package/dist/cloudflare.d.ts.map +1 -0
  27. package/dist/cloudflare.js +146 -0
  28. package/dist/config.d.ts +311 -0
  29. package/dist/config.d.ts.map +1 -0
  30. package/dist/config.js +243 -0
  31. package/dist/core/checks.d.ts +4 -0
  32. package/dist/core/checks.d.ts.map +1 -0
  33. package/dist/core/checks.js +252 -0
  34. package/dist/core/config.d.ts +5 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +182 -0
  37. package/dist/core/content.d.ts +7 -0
  38. package/dist/core/content.d.ts.map +1 -0
  39. package/dist/core/content.js +211 -0
  40. package/dist/core/links.d.ts +8 -0
  41. package/dist/core/links.d.ts.map +1 -0
  42. package/dist/core/links.js +94 -0
  43. package/dist/core/navigation.d.ts +5 -0
  44. package/dist/core/navigation.d.ts.map +1 -0
  45. package/dist/core/navigation.js +144 -0
  46. package/dist/core/search.d.ts +5 -0
  47. package/dist/core/search.d.ts.map +1 -0
  48. package/dist/core/search.js +41 -0
  49. package/dist/core/types.d.ts +269 -0
  50. package/dist/core/types.d.ts.map +1 -0
  51. package/dist/core/types.js +4 -0
  52. package/dist/core/utils.d.ts +12 -0
  53. package/dist/core/utils.d.ts.map +1 -0
  54. package/dist/core/utils.js +52 -0
  55. package/dist/core.d.ts +8 -0
  56. package/dist/core.d.ts.map +1 -0
  57. package/dist/core.js +7 -0
  58. package/dist/index.d.ts +6 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +3 -0
  61. package/dist/mdx/ast.d.ts +32 -0
  62. package/dist/mdx/ast.d.ts.map +1 -0
  63. package/dist/mdx/ast.js +148 -0
  64. package/dist/mdx/code.d.ts +14 -0
  65. package/dist/mdx/code.d.ts.map +1 -0
  66. package/dist/mdx/code.js +315 -0
  67. package/dist/mdx/compile.d.ts +26 -0
  68. package/dist/mdx/compile.d.ts.map +1 -0
  69. package/dist/mdx/compile.js +63 -0
  70. package/dist/mdx/diff.d.ts +5 -0
  71. package/dist/mdx/diff.d.ts.map +1 -0
  72. package/dist/mdx/diff.js +160 -0
  73. package/dist/mdx/utils.d.ts +10 -0
  74. package/dist/mdx/utils.d.ts.map +1 -0
  75. package/dist/mdx/utils.js +64 -0
  76. package/dist/og/image.d.ts +21 -0
  77. package/dist/og/image.d.ts.map +1 -0
  78. package/dist/og/image.js +216 -0
  79. package/dist/og/metadata.d.ts +7 -0
  80. package/dist/og/metadata.d.ts.map +1 -0
  81. package/dist/og/metadata.js +160 -0
  82. package/dist/og/types.d.ts +59 -0
  83. package/dist/og/types.d.ts.map +1 -0
  84. package/dist/og/types.js +1 -0
  85. package/dist/og.d.ts +4 -0
  86. package/dist/og.d.ts.map +1 -0
  87. package/dist/og.js +3 -0
  88. package/dist/search/algolia.d.ts +10 -0
  89. package/dist/search/algolia.d.ts.map +1 -0
  90. package/dist/search/algolia.js +82 -0
  91. package/dist/search/cloudflare-sync.d.ts +5 -0
  92. package/dist/search/cloudflare-sync.d.ts.map +1 -0
  93. package/dist/search/cloudflare-sync.js +169 -0
  94. package/dist/search/cloudflare.d.ts +9 -0
  95. package/dist/search/cloudflare.d.ts.map +1 -0
  96. package/dist/search/cloudflare.js +115 -0
  97. package/dist/search/local.d.ts +9 -0
  98. package/dist/search/local.d.ts.map +1 -0
  99. package/dist/search/local.js +122 -0
  100. package/dist/search/runtime.d.ts +41 -0
  101. package/dist/search/runtime.d.ts.map +1 -0
  102. package/dist/search/runtime.js +151 -0
  103. package/dist/search/types.d.ts +172 -0
  104. package/dist/search/types.d.ts.map +1 -0
  105. package/dist/search/types.js +1 -0
  106. package/dist/search/typesense.d.ts +11 -0
  107. package/dist/search/typesense.d.ts.map +1 -0
  108. package/dist/search/typesense.js +91 -0
  109. package/dist/search/utils.d.ts +10 -0
  110. package/dist/search/utils.d.ts.map +1 -0
  111. package/dist/search/utils.js +55 -0
  112. package/dist/search.d.ts +8 -0
  113. package/dist/search.d.ts.map +1 -0
  114. package/dist/search.js +7 -0
  115. package/dist/svelte.d.ts +24 -0
  116. package/dist/svelte.d.ts.map +1 -0
  117. package/dist/svelte.js +51 -0
  118. package/dist/testing.d.ts +3 -0
  119. package/dist/testing.d.ts.map +1 -0
  120. package/dist/testing.js +23 -0
  121. package/dist/theme/AskAiPanel.svelte +433 -0
  122. package/dist/theme/AskAiPanel.svelte.d.ts +27 -0
  123. package/dist/theme/AskAiPanel.svelte.d.ts.map +1 -0
  124. package/dist/theme/Button.svelte +34 -0
  125. package/dist/theme/Button.svelte.d.ts +41 -0
  126. package/dist/theme/Button.svelte.d.ts.map +1 -0
  127. package/dist/theme/Checkbox.svelte +63 -0
  128. package/dist/theme/Checkbox.svelte.d.ts +45 -0
  129. package/dist/theme/Checkbox.svelte.d.ts.map +1 -0
  130. package/dist/theme/CodeBlock.svelte +6 -0
  131. package/dist/theme/CodeBlock.svelte.d.ts +22 -0
  132. package/dist/theme/CodeBlock.svelte.d.ts.map +1 -0
  133. package/dist/theme/DocPage.svelte +121 -0
  134. package/dist/theme/DocPage.svelte.d.ts +33 -0
  135. package/dist/theme/DocPage.svelte.d.ts.map +1 -0
  136. package/dist/theme/DocsApp.svelte +78 -0
  137. package/dist/theme/DocsApp.svelte.d.ts +63 -0
  138. package/dist/theme/DocsApp.svelte.d.ts.map +1 -0
  139. package/dist/theme/DocsLayout.svelte +167 -0
  140. package/dist/theme/DocsLayout.svelte.d.ts +40 -0
  141. package/dist/theme/DocsLayout.svelte.d.ts.map +1 -0
  142. package/dist/theme/FloatingToolbar.svelte +73 -0
  143. package/dist/theme/FloatingToolbar.svelte.d.ts +22 -0
  144. package/dist/theme/FloatingToolbar.svelte.d.ts.map +1 -0
  145. package/dist/theme/FormField.svelte +41 -0
  146. package/dist/theme/FormField.svelte.d.ts +35 -0
  147. package/dist/theme/FormField.svelte.d.ts.map +1 -0
  148. package/dist/theme/HomePage.svelte +228 -0
  149. package/dist/theme/HomePage.svelte.d.ts +58 -0
  150. package/dist/theme/HomePage.svelte.d.ts.map +1 -0
  151. package/dist/theme/Input.svelte +36 -0
  152. package/dist/theme/Input.svelte.d.ts +34 -0
  153. package/dist/theme/Input.svelte.d.ts.map +1 -0
  154. package/dist/theme/PageLayout.svelte +36 -0
  155. package/dist/theme/PageLayout.svelte.d.ts +31 -0
  156. package/dist/theme/PageLayout.svelte.d.ts.map +1 -0
  157. package/dist/theme/RootLayout.svelte +330 -0
  158. package/dist/theme/RootLayout.svelte.d.ts +40 -0
  159. package/dist/theme/RootLayout.svelte.d.ts.map +1 -0
  160. package/dist/theme/ScopeSwitcher.svelte +103 -0
  161. package/dist/theme/ScopeSwitcher.svelte.d.ts +24 -0
  162. package/dist/theme/ScopeSwitcher.svelte.d.ts.map +1 -0
  163. package/dist/theme/SearchDialog.svelte +253 -0
  164. package/dist/theme/SearchDialog.svelte.d.ts +28 -0
  165. package/dist/theme/SearchDialog.svelte.d.ts.map +1 -0
  166. package/dist/theme/Select.svelte +37 -0
  167. package/dist/theme/Select.svelte.d.ts +42 -0
  168. package/dist/theme/Select.svelte.d.ts.map +1 -0
  169. package/dist/theme/SidebarTree.svelte +136 -0
  170. package/dist/theme/SidebarTree.svelte.d.ts +24 -0
  171. package/dist/theme/SidebarTree.svelte.d.ts.map +1 -0
  172. package/dist/theme/Textarea.svelte +33 -0
  173. package/dist/theme/Textarea.svelte.d.ts +33 -0
  174. package/dist/theme/Textarea.svelte.d.ts.map +1 -0
  175. package/dist/theme/ThemeToggle.svelte +39 -0
  176. package/dist/theme/ThemeToggle.svelte.d.ts +21 -0
  177. package/dist/theme/ThemeToggle.svelte.d.ts.map +1 -0
  178. package/dist/theme/forms.d.ts +4 -0
  179. package/dist/theme/forms.d.ts.map +1 -0
  180. package/dist/theme/forms.js +3 -0
  181. package/dist/theme/index.d.ts +21 -0
  182. package/dist/theme/index.d.ts.map +1 -0
  183. package/dist/theme/index.js +19 -0
  184. package/dist/theme/portal.d.ts +5 -0
  185. package/dist/theme/portal.d.ts.map +1 -0
  186. package/dist/theme/portal.js +19 -0
  187. package/dist/theme/styles.css +3697 -0
  188. package/dist/virtual.d.ts +59 -0
  189. package/dist/vite.d.ts +10 -0
  190. package/dist/vite.d.ts.map +1 -0
  191. package/dist/vite.js +304 -0
  192. package/package.json +118 -0
@@ -0,0 +1,160 @@
1
+ import { escapeAttribute, escapeHtml } from './utils.js';
2
+ export function createDiffRows(raw) {
3
+ let oldLine = 0;
4
+ let newLine = 0;
5
+ return raw.replace(/\r?\n$/, '').split(/\r?\n/).map((line) => {
6
+ const hunk = /^@@\s+-(\d+)(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/.exec(line);
7
+ if (hunk) {
8
+ oldLine = Number(hunk[1]) - 1;
9
+ newLine = Number(hunk[2]) - 1;
10
+ return {
11
+ kind: 'meta',
12
+ content: line
13
+ };
14
+ }
15
+ if (line.startsWith('+++') || line.startsWith('---')) {
16
+ return {
17
+ kind: 'meta',
18
+ content: line
19
+ };
20
+ }
21
+ if (line.startsWith('+')) {
22
+ newLine += 1;
23
+ return {
24
+ kind: 'add',
25
+ newLine,
26
+ content: line.slice(1)
27
+ };
28
+ }
29
+ if (line.startsWith('-')) {
30
+ oldLine += 1;
31
+ return {
32
+ kind: 'remove',
33
+ oldLine,
34
+ content: line.slice(1)
35
+ };
36
+ }
37
+ oldLine += 1;
38
+ newLine += 1;
39
+ return {
40
+ kind: 'context',
41
+ oldLine,
42
+ newLine,
43
+ content: line.startsWith(' ') ? line.slice(1) : line
44
+ };
45
+ });
46
+ }
47
+ export function createDiffSplitRows(rows) {
48
+ const splitRows = [];
49
+ let index = 0;
50
+ while (index < rows.length) {
51
+ const row = rows[index];
52
+ if (!row)
53
+ break;
54
+ if (row.kind === 'meta') {
55
+ splitRows.push({ kind: 'meta', content: row.content });
56
+ index += 1;
57
+ continue;
58
+ }
59
+ if (row.kind === 'context') {
60
+ splitRows.push({ kind: 'context', old: row, new: row });
61
+ index += 1;
62
+ continue;
63
+ }
64
+ if (row.kind === 'remove') {
65
+ const oldRows = [];
66
+ const newRows = [];
67
+ while (rows[index]?.kind === 'remove') {
68
+ oldRows.push(rows[index]);
69
+ index += 1;
70
+ }
71
+ while (rows[index]?.kind === 'add') {
72
+ newRows.push(rows[index]);
73
+ index += 1;
74
+ }
75
+ pushPairedChangeRows(splitRows, oldRows, newRows);
76
+ continue;
77
+ }
78
+ const newRows = [];
79
+ while (rows[index]?.kind === 'add') {
80
+ newRows.push(rows[index]);
81
+ index += 1;
82
+ }
83
+ pushPairedChangeRows(splitRows, [], newRows);
84
+ }
85
+ return splitRows;
86
+ }
87
+ export function renderSplitDiffHtml(block) {
88
+ const title = block.title ?? 'Diff';
89
+ const rows = block.splitRows.length > 0 ? block.splitRows : createDiffSplitRows(block.diffRows);
90
+ const counts = [
91
+ block.addedLines > 0 ? `+${block.addedLines}` : '',
92
+ block.removedLines > 0 ? `-${block.removedLines}` : ''
93
+ ].filter(Boolean).join(' ');
94
+ return `<div class="sd-diff sd-diff-split sd-code" data-language="${escapeAttribute(block.language)}"${block.title ? ` data-title="${escapeAttribute(block.title)}"` : ''} data-diff="true" data-diff-mode="split"${block.addedLines > 0 ? ` data-added-lines="${block.addedLines}"` : ''}${block.removedLines > 0 ? ` data-removed-lines="${block.removedLines}"` : ''} data-copy="${escapeAttribute(block.raw)}">
95
+ ${renderDiffHeader(title, counts)}
96
+ <div class="sd-diff-panes" role="table" aria-label="${escapeAttribute(title)} diff">
97
+ ${renderPane(rows, 'old', 'Before')}
98
+ ${renderPane(rows, 'new', 'After')}
99
+ </div>
100
+ </div>`;
101
+ }
102
+ function pushPairedChangeRows(target, oldRows, newRows) {
103
+ const length = Math.max(oldRows.length, newRows.length);
104
+ for (let index = 0; index < length; index += 1) {
105
+ target.push({
106
+ kind: 'change',
107
+ ...(oldRows[index] ? { old: oldRows[index] } : {}),
108
+ ...(newRows[index] ? { new: newRows[index] } : {})
109
+ });
110
+ }
111
+ }
112
+ function renderDiffHeader(title, counts) {
113
+ const stats = counts
114
+ ? `<span class="sd-code-stats">${counts.split(' ').map(renderDiffStat).join('')}</span>`
115
+ : '';
116
+ return `<div class="sd-code-header">
117
+ <span class="sd-code-language" data-language="diff">Diff</span>
118
+ <span class="sd-code-title">${escapeHtml(title)}</span>
119
+ ${stats}
120
+ <button type="button" class="sd-code-copy" data-sd-copy="" aria-label="Copy diff" title="Copy diff"><svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5h9v14H9zM6 8v12h10"/></svg></button>
121
+ </div>`;
122
+ }
123
+ function renderDiffStat(count) {
124
+ const className = count.startsWith('+') ? 'sd-code-stat-add' : 'sd-code-stat-remove';
125
+ return `<span class="${className}">${escapeHtml(count)}</span>`;
126
+ }
127
+ function renderPane(rows, side, label) {
128
+ return `<section class="sd-diff-pane" data-side="${side}" role="rowgroup" aria-label="${label}">
129
+ <div class="sd-diff-column-label" role="columnheader" data-side="${side}">${label}</div>
130
+ <div class="sd-diff-scroll" role="presentation" tabindex="0">
131
+ <div class="sd-diff-lines">
132
+ ${rows.map((row) => renderPaneRow(row, side)).join('')}
133
+ </div>
134
+ </div>
135
+ </section>`;
136
+ }
137
+ function renderPaneRow(row, side) {
138
+ if (row.kind === 'meta') {
139
+ return `<div class="sd-diff-row sd-diff-meta" role="row">
140
+ <div class="sd-diff-cell sd-diff-meta-cell" role="cell" data-side="${side}">
141
+ <span class="sd-diff-line-no" aria-hidden="true"></span>
142
+ <span class="sd-diff-code">${escapeHtml(row.content ?? '')}</span>
143
+ </div>
144
+ </div>`;
145
+ }
146
+ return `<div class="sd-diff-row ${row.kind === 'change' ? 'sd-diff-change' : 'sd-diff-context'}" role="row">
147
+ ${renderSideCell(side === 'old' ? row.old : row.new, side)}
148
+ </div>`;
149
+ }
150
+ function renderSideCell(row, side) {
151
+ if (!row) {
152
+ return `<div class="sd-diff-cell sd-diff-empty" role="cell" data-side="${side}" aria-hidden="true"></div>`;
153
+ }
154
+ const line = side === 'old' ? row.oldLine : row.newLine;
155
+ const kind = row.kind === 'remove' ? 'remove' : row.kind === 'add' ? 'add' : 'context';
156
+ return `<div class="sd-diff-cell sd-diff-${kind}" role="cell" data-side="${side}" data-line="${line ?? ''}">
157
+ <span class="sd-diff-line-no">${line ?? ''}</span>
158
+ <span class="sd-diff-code">${escapeHtml(row.content)}</span>
159
+ </div>`;
160
+ }
@@ -0,0 +1,10 @@
1
+ export declare function markdownToPlainText(markdown: string): string;
2
+ export declare function stripInlineMarkdown(value: string): string;
3
+ export declare function parseLineSet(value: string | undefined): number[];
4
+ export declare function readQuotedMeta(meta: string, key: string): string | undefined;
5
+ export declare function mergeHtmlClass(attrs: string, className: string): string;
6
+ export declare function mergeClassNames(value: unknown, className: string): string[];
7
+ export declare function normalizeShikiLanguage(language: string): string;
8
+ export declare function escapeHtml(value: string): string;
9
+ export declare function escapeAttribute(value: string): string;
10
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/mdx/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU5D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAahE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG5E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAG3E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD"}
@@ -0,0 +1,64 @@
1
+ export function markdownToPlainText(markdown) {
2
+ return markdown
3
+ .replace(/```[\s\S]*?```/g, ' ')
4
+ .replace(/`([^`]+)`/g, '$1')
5
+ .replace(/<[^>]+>/g, ' ')
6
+ .replace(/!\[[^\]]*\]\([^)]+\)/g, ' ')
7
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
8
+ .replace(/[#>*_\-|~]/g, ' ')
9
+ .replace(/\s+/g, ' ')
10
+ .trim();
11
+ }
12
+ export function stripInlineMarkdown(value) {
13
+ return value
14
+ .replace(/`([^`]+)`/g, '$1')
15
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
16
+ .replace(/[*_~]/g, '')
17
+ .trim();
18
+ }
19
+ export function parseLineSet(value) {
20
+ if (!value)
21
+ return [];
22
+ const lines = new Set();
23
+ for (const part of value.split(',')) {
24
+ const [startRaw, endRaw] = part.trim().split('-');
25
+ const start = Number(startRaw);
26
+ const end = Number(endRaw ?? startRaw);
27
+ if (!Number.isInteger(start) || !Number.isInteger(end))
28
+ continue;
29
+ for (let line = Math.min(start, end); line <= Math.max(start, end); line += 1) {
30
+ if (line > 0)
31
+ lines.add(line);
32
+ }
33
+ }
34
+ return [...lines].sort((a, b) => a - b);
35
+ }
36
+ export function readQuotedMeta(meta, key) {
37
+ const match = new RegExp(`${key}=("[^"]+"|'[^']+'|[^\\s]+)`).exec(meta);
38
+ return match?.[1]?.replace(/^["']|["']$/g, '');
39
+ }
40
+ export function mergeHtmlClass(attrs, className) {
41
+ const match = /class="([^"]*)"/.exec(attrs);
42
+ const classes = new Set([...(match?.[1]?.split(/\s+/).filter(Boolean) ?? []), className]);
43
+ return [...classes].join(' ');
44
+ }
45
+ export function mergeClassNames(value, className) {
46
+ const classes = Array.isArray(value) ? value.map(String) : typeof value === 'string' ? value.split(/\s+/) : [];
47
+ return [...new Set([...classes, className])];
48
+ }
49
+ export function normalizeShikiLanguage(language) {
50
+ if (language === 'txt')
51
+ return 'text';
52
+ if (language === 'sh' || language === 'shell')
53
+ return 'bash';
54
+ return language || 'text';
55
+ }
56
+ export function escapeHtml(value) {
57
+ return value
58
+ .replace(/&/g, '&amp;')
59
+ .replace(/</g, '&lt;')
60
+ .replace(/>/g, '&gt;');
61
+ }
62
+ export function escapeAttribute(value) {
63
+ return escapeHtml(value).replace(/"/g, '&quot;');
64
+ }
@@ -0,0 +1,21 @@
1
+ import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
2
+ import type { OgImageInput, OgImageOptions, OgRenderer, OgRenderOptions, OgTemplate, OgTemplateNode } from './types.js';
3
+ export declare const defaultOgTemplate: {
4
+ readonly name: "default";
5
+ readonly render: typeof createDefaultOgTemplateNode;
6
+ };
7
+ export declare function createPageOgImagePath(page: SvedocsPage, format?: 'svg' | 'png'): string;
8
+ export declare function createConfiguredOgImageFormat(config: SvedocsResolvedConfig): 'svg' | 'png';
9
+ export declare function createConfiguredOgImageRenderer(config: SvedocsResolvedConfig): OgRenderer;
10
+ export declare function createConfiguredOgImageTemplate(config: SvedocsResolvedConfig): OgTemplate | undefined;
11
+ export declare function createPageOgImageEntries(pages: SvedocsPage[], format?: 'svg' | 'png'): Array<{
12
+ path: string;
13
+ }>;
14
+ export declare function createPageOgImageResponse(config: SvedocsResolvedConfig, page: SvedocsPage, options?: OgImageOptions): Promise<Response>;
15
+ export declare function createOgImageInput(config: SvedocsResolvedConfig, page: SvedocsPage): OgImageInput;
16
+ export declare function createOgSvg(input: OgImageInput): string;
17
+ export declare function createDefaultOgTemplateNode(input: OgImageInput): OgTemplateNode;
18
+ export declare function createSatoriOgSvg(input: OgImageInput, options: OgRenderOptions): Promise<string>;
19
+ export declare function createOgImage(input: OgImageInput, options?: OgImageOptions): Promise<string | Uint8Array>;
20
+ export declare function createOgPng(input: OgImageInput, options?: OgRenderOptions): Promise<Uint8Array>;
21
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/og/image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAExH,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,GAAE,KAAK,GAAG,KAAa,GAAG,MAAM,CAG9F;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,qBAAqB,GAAG,KAAK,GAAG,KAAK,CAE1F;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,qBAAqB,GAAG,UAAU,CAEzF;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,qBAAqB,GAAG,UAAU,GAAG,SAAS,CAIrG;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,GAAE,KAAK,GAAG,KAAa,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAIrH;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAmBnB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY,CAMjG;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAYvD;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,YAAY,GAAG,cAAc,CA2D/E;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAYtG;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC,CAEnH;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,CASzG"}
@@ -0,0 +1,216 @@
1
+ export const defaultOgTemplate = {
2
+ name: 'default',
3
+ render: createDefaultOgTemplateNode
4
+ };
5
+ export function createPageOgImagePath(page, format = 'svg') {
6
+ const name = page.routePath === '/' ? 'index' : page.routePath.replace(/^\/+/, '').replace(/[^a-zA-Z0-9]+/g, '-');
7
+ return `/og/${name}.${format}`;
8
+ }
9
+ export function createConfiguredOgImageFormat(config) {
10
+ return config.seo.ogImage === false ? 'svg' : config.seo.ogImage.format;
11
+ }
12
+ export function createConfiguredOgImageRenderer(config) {
13
+ return config.seo.ogImage === false ? 'svg' : config.seo.ogImage.renderer;
14
+ }
15
+ export function createConfiguredOgImageTemplate(config) {
16
+ return config.seo.ogImage !== false && typeof config.seo.ogImage.template === 'function'
17
+ ? config.seo.ogImage.template
18
+ : undefined;
19
+ }
20
+ export function createPageOgImageEntries(pages, format = 'svg') {
21
+ return pages
22
+ .filter((page) => !page.hidden)
23
+ .map((page) => ({ path: createPageOgImagePath(page, format).replace(/^\/og\//, '') }));
24
+ }
25
+ export async function createPageOgImageResponse(config, page, options = {}) {
26
+ const format = options.format ?? createConfiguredOgImageFormat(config);
27
+ const template = options.template ?? createConfiguredOgImageTemplate(config);
28
+ const asset = await createOgImage(createOgImageInput(config, page), {
29
+ ...options,
30
+ format,
31
+ renderer: options.renderer ?? createConfiguredOgImageRenderer(config),
32
+ ...(template ? { template } : {})
33
+ });
34
+ const body = typeof asset === 'string' ? asset : new Blob([toArrayBuffer(asset)], { type: 'image/png' });
35
+ return new Response(body, {
36
+ headers: {
37
+ 'content-type': format === 'png' ? 'image/png' : 'image/svg+xml; charset=utf-8',
38
+ 'cache-control': 'public, max-age=31536000, immutable'
39
+ }
40
+ });
41
+ }
42
+ export function createOgImageInput(config, page) {
43
+ return {
44
+ title: page.seo.title,
45
+ description: page.seo.description ?? config.site.description,
46
+ siteName: config.site.name
47
+ };
48
+ }
49
+ export function createOgSvg(input) {
50
+ const title = escapeHtml(input.title);
51
+ const description = escapeHtml(input.description ?? input.siteName ?? 'svedocs');
52
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
53
+ <rect width="1200" height="630" fill="#11130f"/>
54
+ <path d="M0 84h1200M0 168h1200M0 252h1200M0 336h1200M0 420h1200M0 504h1200M120 0v630M240 0v630M360 0v630M480 0v630M600 0v630M720 0v630M840 0v630M960 0v630M1080 0v630" stroke="#2f332d" stroke-width="2"/>
55
+ <rect x="84" y="84" width="96" height="96" fill="#50d6b3"/>
56
+ <rect x="180" y="180" width="96" height="96" fill="#ff8a66"/>
57
+ <rect x="84" y="276" width="96" height="96" fill="#f4f1e8"/>
58
+ <text x="300" y="270" fill="#f4f1e8" font-family="Arial, sans-serif" font-size="78" font-weight="800">${title}</text>
59
+ <text x="306" y="352" fill="#aaa698" font-family="Arial, sans-serif" font-size="34">${description}</text>
60
+ </svg>`;
61
+ }
62
+ export function createDefaultOgTemplateNode(input) {
63
+ return {
64
+ type: 'div',
65
+ props: {
66
+ style: {
67
+ display: 'flex',
68
+ width: '1200px',
69
+ height: '630px',
70
+ background: '#11130f',
71
+ color: '#f4f1e8',
72
+ position: 'relative',
73
+ fontFamily: 'Inter',
74
+ overflow: 'hidden'
75
+ },
76
+ children: [
77
+ gridLayer(),
78
+ pixelMark(),
79
+ {
80
+ type: 'div',
81
+ props: {
82
+ style: {
83
+ display: 'flex',
84
+ position: 'absolute',
85
+ left: '300px',
86
+ top: '200px',
87
+ width: '780px',
88
+ flexDirection: 'column'
89
+ },
90
+ children: [
91
+ {
92
+ type: 'div',
93
+ props: {
94
+ style: {
95
+ fontSize: 78,
96
+ fontWeight: 800,
97
+ lineHeight: 1,
98
+ color: '#f4f1e8'
99
+ },
100
+ children: input.title
101
+ }
102
+ },
103
+ {
104
+ type: 'div',
105
+ props: {
106
+ style: {
107
+ marginTop: 28,
108
+ fontSize: 34,
109
+ lineHeight: 1.25,
110
+ color: '#aaa698'
111
+ },
112
+ children: input.description ?? input.siteName ?? 'svedocs'
113
+ }
114
+ }
115
+ ]
116
+ }
117
+ }
118
+ ]
119
+ }
120
+ };
121
+ }
122
+ export async function createSatoriOgSvg(input, options) {
123
+ if (!options.fonts?.length) {
124
+ throw new Error('Satori OG rendering requires at least one font.');
125
+ }
126
+ const { default: satori } = await importOptionalOgModule('satori');
127
+ return satori((options.template ?? createDefaultOgTemplateNode)(input), {
128
+ width: 1200,
129
+ height: 630,
130
+ fonts: options.fonts
131
+ });
132
+ }
133
+ export async function createOgImage(input, options = {}) {
134
+ return options.format === 'png' ? createOgPng(input, options) : renderOgSvg(input, options);
135
+ }
136
+ export async function createOgPng(input, options = {}) {
137
+ const { Resvg } = await importOptionalOgModule('@resvg/resvg-js');
138
+ const renderer = new Resvg(await renderOgSvg(input, options), {
139
+ fitTo: {
140
+ mode: 'width',
141
+ value: 1200
142
+ }
143
+ });
144
+ return renderer.render().asPng();
145
+ }
146
+ function gridLayer() {
147
+ return {
148
+ type: 'div',
149
+ props: {
150
+ style: {
151
+ display: 'flex',
152
+ position: 'absolute',
153
+ inset: 0,
154
+ backgroundImage: 'linear-gradient(#2f332d 2px, transparent 2px), linear-gradient(90deg, #2f332d 2px, transparent 2px)',
155
+ backgroundSize: '120px 84px'
156
+ }
157
+ }
158
+ };
159
+ }
160
+ function pixelMark() {
161
+ return {
162
+ type: 'div',
163
+ props: {
164
+ style: {
165
+ display: 'flex',
166
+ position: 'absolute',
167
+ left: '84px',
168
+ top: '84px',
169
+ width: '192px',
170
+ height: '288px',
171
+ flexWrap: 'wrap'
172
+ },
173
+ children: [
174
+ colorBlock('#50d6b3'),
175
+ colorBlock('transparent'),
176
+ colorBlock('transparent'),
177
+ colorBlock('#ff8a66'),
178
+ colorBlock('#f4f1e8'),
179
+ colorBlock('transparent')
180
+ ]
181
+ }
182
+ };
183
+ }
184
+ function colorBlock(color) {
185
+ return {
186
+ type: 'div',
187
+ props: {
188
+ style: {
189
+ width: '96px',
190
+ height: '96px',
191
+ background: color
192
+ }
193
+ }
194
+ };
195
+ }
196
+ async function renderOgSvg(input, options) {
197
+ return options.renderer === 'satori' ? createSatoriOgSvg(input, options) : createOgSvg(input);
198
+ }
199
+ function escapeHtml(value) {
200
+ return value
201
+ .replace(/&/g, '&amp;')
202
+ .replace(/</g, '&lt;')
203
+ .replace(/>/g, '&gt;')
204
+ .replace(/"/g, '&quot;');
205
+ }
206
+ function toArrayBuffer(value) {
207
+ return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
208
+ }
209
+ function importOptionalOgModule(specifier) {
210
+ if (typeof process !== 'undefined' && process.versions?.node) {
211
+ return import(specifier);
212
+ }
213
+ // Keep optional Node-only OG renderers out of edge bundles unless the caller actually uses them.
214
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
215
+ return dynamicImport(specifier);
216
+ }
@@ -0,0 +1,7 @@
1
+ import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
2
+ import type { SvedocsPageAlternate, SvedocsPageMetadata } from './types.js';
3
+ export declare function createPageMetadata(config: SvedocsResolvedConfig, page: SvedocsPage): SvedocsPageMetadata;
4
+ export declare function createSitemapXml(config: SvedocsResolvedConfig, pages: SvedocsPage[]): string;
5
+ export declare function createPageAlternates(config: SvedocsResolvedConfig, page: SvedocsPage, pages: SvedocsPage[]): SvedocsPageAlternate[];
6
+ export declare function createRobotsTxt(config: SvedocsResolvedConfig): string;
7
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/og/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,WAAW,GAAG,mBAAmB,CAwCxG;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAe5F;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,EAAE,GACnB,oBAAoB,EAAE,CA+BxB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAOrE"}
@@ -0,0 +1,160 @@
1
+ import { createConfiguredOgImageFormat, createPageOgImagePath } from './image.js';
2
+ export function createPageMetadata(config, page) {
3
+ const title = page.routePath === '/' ? page.seo.title : `${page.seo.title} | ${config.site.name}`;
4
+ const description = page.seo.description ?? config.site.description;
5
+ const canonical = page.seo.canonical ?? createAbsoluteUrl(config, page.routePath);
6
+ const generatedImage = config.seo.ogImage === false
7
+ ? undefined
8
+ : createAbsoluteUrl(config, createPageOgImagePath(page, createConfiguredOgImageFormat(config)));
9
+ const image = page.seo.image ? createAbsoluteUrl(config, page.seo.image) : generatedImage;
10
+ const type = page.seo.type ?? (page.kind === 'doc' ? 'article' : 'website');
11
+ const author = page.seo.author ?? config.seo.defaultAuthor;
12
+ const updatedTime = page.seo.updatedTime ?? page.lastUpdated;
13
+ return {
14
+ title,
15
+ description,
16
+ ...(canonical ? { canonical } : {}),
17
+ ...(image ? { image } : {}),
18
+ openGraph: {
19
+ title,
20
+ description,
21
+ type,
22
+ ...(canonical ? { url: canonical } : {}),
23
+ ...(image ? { image } : {}),
24
+ siteName: config.site.name,
25
+ ...(author ? { author } : {}),
26
+ ...(page.seo.publishedTime ? { publishedTime: page.seo.publishedTime } : {}),
27
+ ...(updatedTime ? { updatedTime } : {})
28
+ },
29
+ twitter: {
30
+ card: image ? 'summary_large_image' : 'summary',
31
+ title,
32
+ description,
33
+ ...(image ? { image } : {})
34
+ },
35
+ jsonLd: createPageJsonLd(config, page, {
36
+ title,
37
+ description,
38
+ ...(canonical ? { canonical } : {}),
39
+ ...(image ? { image } : {})
40
+ })
41
+ };
42
+ }
43
+ export function createSitemapXml(config, pages) {
44
+ const urls = pages
45
+ .filter((page) => !page.hidden)
46
+ .map((page) => {
47
+ const loc = page.seo.canonical ?? createAbsoluteUrl(config, page.routePath) ?? page.routePath;
48
+ const lastmod = page.seo.updatedTime ?? page.lastUpdated;
49
+ return [
50
+ ' <url>',
51
+ ` <loc>${escapeXml(loc)}</loc>`,
52
+ ...(lastmod ? [` <lastmod>${escapeXml(lastmod)}</lastmod>`] : []),
53
+ ' </url>'
54
+ ].join('\n');
55
+ })
56
+ .join('\n');
57
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls}\n</urlset>\n`;
58
+ }
59
+ export function createPageAlternates(config, page, pages) {
60
+ if (config.i18n.locales.length === 0)
61
+ return [];
62
+ const candidates = pages
63
+ .filter((candidate) => !candidate.hidden)
64
+ .filter((candidate) => candidate.scopePath === page.scopePath);
65
+ const alternates = [];
66
+ for (const candidate of candidates) {
67
+ if (!candidate.locale)
68
+ continue;
69
+ const href = candidate.seo.canonical ?? createAbsoluteUrl(config, candidate.routePath);
70
+ if (!href)
71
+ continue;
72
+ alternates.push({
73
+ lang: candidate.locale,
74
+ href,
75
+ locale: candidate.locale
76
+ });
77
+ }
78
+ const defaultLocale = config.i18n.defaultLocale;
79
+ const defaultPage = defaultLocale
80
+ ? candidates.find((candidate) => candidate.locale === defaultLocale)
81
+ : undefined;
82
+ const defaultHref = defaultPage?.seo.canonical ?? (defaultPage ? createAbsoluteUrl(config, defaultPage.routePath) : undefined);
83
+ const defaultAlternate = defaultHref
84
+ ? [
85
+ {
86
+ lang: 'x-default',
87
+ href: defaultHref,
88
+ ...(defaultLocale ? { locale: defaultLocale } : {})
89
+ }
90
+ ]
91
+ : [];
92
+ return uniqueAlternates([...alternates, ...defaultAlternate]);
93
+ }
94
+ export function createRobotsTxt(config) {
95
+ const sitemap = createAbsoluteUrl(config, '/sitemap.xml');
96
+ return [
97
+ 'User-agent: *',
98
+ 'Allow: /',
99
+ ...(sitemap ? [`Sitemap: ${sitemap}`] : [])
100
+ ].join('\n') + '\n';
101
+ }
102
+ function uniqueAlternates(alternates) {
103
+ const seen = new Set();
104
+ return alternates.filter((alternate) => {
105
+ const key = `${alternate.lang}:${alternate.href}`;
106
+ if (seen.has(key))
107
+ return false;
108
+ seen.add(key);
109
+ return true;
110
+ });
111
+ }
112
+ function createPageJsonLd(config, page, metadata) {
113
+ const graph = {
114
+ '@context': 'https://schema.org',
115
+ '@type': page.kind === 'doc' ? 'TechArticle' : 'WebPage',
116
+ headline: metadata.title,
117
+ description: metadata.description,
118
+ isPartOf: {
119
+ '@type': 'WebSite',
120
+ name: config.site.name,
121
+ ...(config.site.url ? { url: config.site.url } : {})
122
+ }
123
+ };
124
+ if (metadata.canonical)
125
+ graph.url = metadata.canonical;
126
+ if (metadata.image)
127
+ graph.image = metadata.image;
128
+ const author = page.seo.author ?? config.seo.defaultAuthor;
129
+ const updatedTime = page.seo.updatedTime ?? page.lastUpdated;
130
+ if (author) {
131
+ graph.author = {
132
+ '@type': 'Person',
133
+ name: author
134
+ };
135
+ }
136
+ if (page.seo.publishedTime)
137
+ graph.datePublished = page.seo.publishedTime;
138
+ if (updatedTime)
139
+ graph.dateModified = updatedTime;
140
+ if (page.kind === 'doc') {
141
+ graph.position = page.order;
142
+ graph.about = page.headings.map((heading) => heading.text);
143
+ }
144
+ return graph;
145
+ }
146
+ function createAbsoluteUrl(config, value) {
147
+ if (/^https?:\/\//.test(value))
148
+ return value;
149
+ if (!config.site.url)
150
+ return undefined;
151
+ return new URL(value, config.site.url).href;
152
+ }
153
+ function escapeXml(value) {
154
+ return value
155
+ .replace(/&/g, '&amp;')
156
+ .replace(/</g, '&lt;')
157
+ .replace(/>/g, '&gt;')
158
+ .replace(/"/g, '&quot;')
159
+ .replace(/'/g, '&apos;');
160
+ }