create-atsdc-stack 1.1.0 → 1.2.0
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/.claude/settings.local.json +3 -1
- package/CLAUDE.md +236 -215
- package/CONTRIBUTING.md +342 -342
- package/INSTALLATION.md +359 -359
- package/LICENSE +201 -201
- package/README.md +405 -405
- package/app/.env.example +17 -17
- package/app/.github/labeler.yml +61 -0
- package/app/.github/workflows/browser-tests.yml +101 -0
- package/app/.github/workflows/check.yml +24 -0
- package/app/.github/workflows/greetings.yml +16 -0
- package/app/.github/workflows/label.yml +22 -0
- package/app/.github/workflows/stale.yml +27 -0
- package/app/.github/workflows/summary.yml +34 -0
- package/app/.stylelintrc.json +8 -0
- package/app/README.md +251 -251
- package/app/astro.config.mjs +83 -83
- package/app/drizzle.config.ts +16 -16
- package/app/package.json +66 -52
- package/app/playwright.config.ts +27 -0
- package/app/public/manifest.webmanifest +36 -36
- package/app/pwa-assets.config.ts +8 -0
- package/app/src/components/Card.astro +36 -36
- package/app/src/db/initialize.ts +107 -107
- package/app/src/db/schema.ts +72 -72
- package/app/src/db/validations.ts +158 -158
- package/app/src/layouts/Layout.astro +63 -63
- package/app/src/lib/config.ts +36 -36
- package/app/src/lib/content-converter.ts +141 -141
- package/app/src/lib/dom-utils.ts +230 -230
- package/app/src/lib/exa-search.ts +269 -269
- package/app/src/pages/api/chat.ts +91 -91
- package/app/src/pages/api/posts.ts +350 -350
- package/app/src/pages/index.astro +87 -87
- package/app/src/styles/components/button.scss +152 -152
- package/app/src/styles/components/card.scss +180 -180
- package/app/src/styles/components/form.scss +240 -240
- package/app/src/styles/global.scss +141 -141
- package/app/src/styles/pages/index.scss +80 -80
- package/app/src/styles/reset.scss +83 -83
- package/app/src/styles/variables/globals.scss +96 -96
- package/app/src/styles/variables/mixins.scss +238 -238
- package/app/tests/browser.test.nopause.ts +10 -0
- package/app/tests/browser.test.ts +13 -0
- package/bin/cli.js +1151 -1151
- package/package.json +8 -6
- package/app/.astro/settings.json +0 -5
- package/app/.astro/types.d.ts +0 -1
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Content Converter Utilities
|
|
3
|
-
* Provides functions for converting between HTML and Markdown using marked and turndown
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { marked } from 'marked';
|
|
7
|
-
import TurndownService from 'turndown';
|
|
8
|
-
|
|
9
|
-
// Configure marked for markdown to HTML conversion
|
|
10
|
-
marked.setOptions({
|
|
11
|
-
gfm: true, // GitHub Flavored Markdown
|
|
12
|
-
breaks: true, // Convert \n to <br>
|
|
13
|
-
headerIds: true, // Add IDs to headings
|
|
14
|
-
mangle: false, // Don't escape email addresses
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// Configure turndown for HTML to markdown conversion
|
|
18
|
-
const turndownService = new TurndownService({
|
|
19
|
-
headingStyle: 'atx', // Use # for headings
|
|
20
|
-
hr: '---', // Use --- for horizontal rules
|
|
21
|
-
bulletListMarker: '-', // Use - for bullet lists
|
|
22
|
-
codeBlockStyle: 'fenced', // Use ``` for code blocks
|
|
23
|
-
fence: '```', // Code fence marker
|
|
24
|
-
emDelimiter: '*', // Use * for emphasis
|
|
25
|
-
strongDelimiter: '**', // Use ** for strong
|
|
26
|
-
linkStyle: 'inlined', // Use [text](url) for links
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Convert Markdown to HTML
|
|
31
|
-
* @param markdown - The markdown string to convert
|
|
32
|
-
* @returns HTML string
|
|
33
|
-
*/
|
|
34
|
-
export async function markdownToHtml(markdown: string): Promise<string> {
|
|
35
|
-
try {
|
|
36
|
-
const html = await marked.parse(markdown);
|
|
37
|
-
return html;
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error('Error converting markdown to HTML:', error);
|
|
40
|
-
throw new Error('Failed to convert markdown to HTML');
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Convert Markdown to HTML synchronously
|
|
46
|
-
* @param markdown - The markdown string to convert
|
|
47
|
-
* @returns HTML string
|
|
48
|
-
*/
|
|
49
|
-
export function markdownToHtmlSync(markdown: string): string {
|
|
50
|
-
try {
|
|
51
|
-
const html = marked.parse(markdown) as string;
|
|
52
|
-
return html;
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.error('Error converting markdown to HTML:', error);
|
|
55
|
-
throw new Error('Failed to convert markdown to HTML');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Convert HTML to Markdown
|
|
61
|
-
* @param html - The HTML string to convert
|
|
62
|
-
* @returns Markdown string
|
|
63
|
-
*/
|
|
64
|
-
export function htmlToMarkdown(html: string): string {
|
|
65
|
-
try {
|
|
66
|
-
const markdown = turndownService.turndown(html);
|
|
67
|
-
return markdown;
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error('Error converting HTML to markdown:', error);
|
|
70
|
-
throw new Error('Failed to convert HTML to markdown');
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Sanitize and convert Markdown to HTML
|
|
76
|
-
* Useful for user-generated content
|
|
77
|
-
* @param markdown - The markdown string to convert
|
|
78
|
-
* @returns Sanitized HTML string
|
|
79
|
-
*/
|
|
80
|
-
export async function sanitizeMarkdown(markdown: string): Promise<string> {
|
|
81
|
-
try {
|
|
82
|
-
// Basic sanitization - remove script tags and dangerous attributes
|
|
83
|
-
const sanitized = markdown
|
|
84
|
-
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
|
85
|
-
.replace(/on\w+="[^"]*"/g, '')
|
|
86
|
-
.replace(/on\w+='[^']*'/g, '');
|
|
87
|
-
|
|
88
|
-
const html = await marked.parse(sanitized);
|
|
89
|
-
return html;
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.error('Error sanitizing markdown:', error);
|
|
92
|
-
throw new Error('Failed to sanitize markdown');
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Extract plain text from markdown
|
|
98
|
-
* @param markdown - The markdown string
|
|
99
|
-
* @returns Plain text string
|
|
100
|
-
*/
|
|
101
|
-
export function markdownToPlainText(markdown: string): string {
|
|
102
|
-
try {
|
|
103
|
-
const html = marked.parse(markdown) as string;
|
|
104
|
-
const text = html
|
|
105
|
-
.replace(/<[^>]*>/g, '') // Remove HTML tags
|
|
106
|
-
.replace(/ /g, ' ') // Replace
|
|
107
|
-
.replace(/&/g, '&') // Replace &
|
|
108
|
-
.replace(/</g, '<') // Replace <
|
|
109
|
-
.replace(/>/g, '>') // Replace >
|
|
110
|
-
.replace(/"/g, '"') // Replace "
|
|
111
|
-
.trim();
|
|
112
|
-
return text;
|
|
113
|
-
} catch (error) {
|
|
114
|
-
console.error('Error extracting plain text:', error);
|
|
115
|
-
throw new Error('Failed to extract plain text');
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Generate excerpt from markdown
|
|
121
|
-
* @param markdown - The markdown string
|
|
122
|
-
* @param maxLength - Maximum length of the excerpt (default: 200)
|
|
123
|
-
* @returns Excerpt string
|
|
124
|
-
*/
|
|
125
|
-
export function generateExcerpt(markdown: string, maxLength: number = 200): string {
|
|
126
|
-
const plainText = markdownToPlainText(markdown);
|
|
127
|
-
|
|
128
|
-
if (plainText.length <= maxLength) {
|
|
129
|
-
return plainText;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Truncate at word boundary
|
|
133
|
-
const truncated = plainText.slice(0, maxLength);
|
|
134
|
-
const lastSpace = truncated.lastIndexOf(' ');
|
|
135
|
-
|
|
136
|
-
if (lastSpace > 0) {
|
|
137
|
-
return truncated.slice(0, lastSpace) + '...';
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return truncated + '...';
|
|
141
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Content Converter Utilities
|
|
3
|
+
* Provides functions for converting between HTML and Markdown using marked and turndown
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { marked } from 'marked';
|
|
7
|
+
import TurndownService from 'turndown';
|
|
8
|
+
|
|
9
|
+
// Configure marked for markdown to HTML conversion
|
|
10
|
+
marked.setOptions({
|
|
11
|
+
gfm: true, // GitHub Flavored Markdown
|
|
12
|
+
breaks: true, // Convert \n to <br>
|
|
13
|
+
headerIds: true, // Add IDs to headings
|
|
14
|
+
mangle: false, // Don't escape email addresses
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Configure turndown for HTML to markdown conversion
|
|
18
|
+
const turndownService = new TurndownService({
|
|
19
|
+
headingStyle: 'atx', // Use # for headings
|
|
20
|
+
hr: '---', // Use --- for horizontal rules
|
|
21
|
+
bulletListMarker: '-', // Use - for bullet lists
|
|
22
|
+
codeBlockStyle: 'fenced', // Use ``` for code blocks
|
|
23
|
+
fence: '```', // Code fence marker
|
|
24
|
+
emDelimiter: '*', // Use * for emphasis
|
|
25
|
+
strongDelimiter: '**', // Use ** for strong
|
|
26
|
+
linkStyle: 'inlined', // Use [text](url) for links
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Convert Markdown to HTML
|
|
31
|
+
* @param markdown - The markdown string to convert
|
|
32
|
+
* @returns HTML string
|
|
33
|
+
*/
|
|
34
|
+
export async function markdownToHtml(markdown: string): Promise<string> {
|
|
35
|
+
try {
|
|
36
|
+
const html = await marked.parse(markdown);
|
|
37
|
+
return html;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('Error converting markdown to HTML:', error);
|
|
40
|
+
throw new Error('Failed to convert markdown to HTML');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Convert Markdown to HTML synchronously
|
|
46
|
+
* @param markdown - The markdown string to convert
|
|
47
|
+
* @returns HTML string
|
|
48
|
+
*/
|
|
49
|
+
export function markdownToHtmlSync(markdown: string): string {
|
|
50
|
+
try {
|
|
51
|
+
const html = marked.parse(markdown) as string;
|
|
52
|
+
return html;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Error converting markdown to HTML:', error);
|
|
55
|
+
throw new Error('Failed to convert markdown to HTML');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Convert HTML to Markdown
|
|
61
|
+
* @param html - The HTML string to convert
|
|
62
|
+
* @returns Markdown string
|
|
63
|
+
*/
|
|
64
|
+
export function htmlToMarkdown(html: string): string {
|
|
65
|
+
try {
|
|
66
|
+
const markdown = turndownService.turndown(html);
|
|
67
|
+
return markdown;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Error converting HTML to markdown:', error);
|
|
70
|
+
throw new Error('Failed to convert HTML to markdown');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sanitize and convert Markdown to HTML
|
|
76
|
+
* Useful for user-generated content
|
|
77
|
+
* @param markdown - The markdown string to convert
|
|
78
|
+
* @returns Sanitized HTML string
|
|
79
|
+
*/
|
|
80
|
+
export async function sanitizeMarkdown(markdown: string): Promise<string> {
|
|
81
|
+
try {
|
|
82
|
+
// Basic sanitization - remove script tags and dangerous attributes
|
|
83
|
+
const sanitized = markdown
|
|
84
|
+
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
|
85
|
+
.replace(/on\w+="[^"]*"/g, '')
|
|
86
|
+
.replace(/on\w+='[^']*'/g, '');
|
|
87
|
+
|
|
88
|
+
const html = await marked.parse(sanitized);
|
|
89
|
+
return html;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error('Error sanitizing markdown:', error);
|
|
92
|
+
throw new Error('Failed to sanitize markdown');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Extract plain text from markdown
|
|
98
|
+
* @param markdown - The markdown string
|
|
99
|
+
* @returns Plain text string
|
|
100
|
+
*/
|
|
101
|
+
export function markdownToPlainText(markdown: string): string {
|
|
102
|
+
try {
|
|
103
|
+
const html = marked.parse(markdown) as string;
|
|
104
|
+
const text = html
|
|
105
|
+
.replace(/<[^>]*>/g, '') // Remove HTML tags
|
|
106
|
+
.replace(/ /g, ' ') // Replace
|
|
107
|
+
.replace(/&/g, '&') // Replace &
|
|
108
|
+
.replace(/</g, '<') // Replace <
|
|
109
|
+
.replace(/>/g, '>') // Replace >
|
|
110
|
+
.replace(/"/g, '"') // Replace "
|
|
111
|
+
.trim();
|
|
112
|
+
return text;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('Error extracting plain text:', error);
|
|
115
|
+
throw new Error('Failed to extract plain text');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Generate excerpt from markdown
|
|
121
|
+
* @param markdown - The markdown string
|
|
122
|
+
* @param maxLength - Maximum length of the excerpt (default: 200)
|
|
123
|
+
* @returns Excerpt string
|
|
124
|
+
*/
|
|
125
|
+
export function generateExcerpt(markdown: string, maxLength: number = 200): string {
|
|
126
|
+
const plainText = markdownToPlainText(markdown);
|
|
127
|
+
|
|
128
|
+
if (plainText.length <= maxLength) {
|
|
129
|
+
return plainText;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Truncate at word boundary
|
|
133
|
+
const truncated = plainText.slice(0, maxLength);
|
|
134
|
+
const lastSpace = truncated.lastIndexOf(' ');
|
|
135
|
+
|
|
136
|
+
if (lastSpace > 0) {
|
|
137
|
+
return truncated.slice(0, lastSpace) + '...';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return truncated + '...';
|
|
141
|
+
}
|