secureweb/sweb/src/components/MarkdownContent.svelte
Mahmoud Emad f22e9faae2 chore: Update Tailwind CSS and dependencies
- Downgraded Tailwind CSS to version 3.4.17 for compatibility.
- Updated various dependencies to resolve version conflicts and
  ensure smooth operation.
- Added missing types for Node.js to enhance type safety.
- Improved the theming system using CSS custom properties (variables)
  for better customization and dark mode support.
- Refactored styles in `app.css` to improve maintainability and
  readability.  Updated the color palette to enhance the user
  experience.
- Updated the PostCSS configuration to use the new Tailwind CSS
  version.
- Updated component styles to utilize the new theming system.
2025-05-12 13:47:47 +03:00

270 lines
6.3 KiB
Svelte

<script lang="ts">
import { onMount } from "svelte";
import { marked } from "marked";
export let path: string = "";
// Base path for images
const imagePath = "/images";
let content = "";
let loading = true;
let error: string | null = null;
$: if (path) {
loadMarkdownContent(path);
}
async function loadMarkdownContent(mdPath: string) {
if (!mdPath) return;
loading = true;
error = null;
content = "";
try {
// Remove leading slash if present
const cleanPath = mdPath.startsWith("/")
? mdPath.substring(1)
: mdPath;
// If path is just a section like "introduction", append "/introduction" to it
const finalPath = cleanPath.includes("/")
? cleanPath
: `${cleanPath}/${cleanPath}`;
console.log(`Loading markdown from: /src/docs/${finalPath}.md`);
const response = await fetch(`/src/docs/${finalPath}.md`);
if (!response.ok) {
throw new Error(
`Failed to load content: ${response.status} ${response.statusText}`,
);
}
// Get the directory path for relative image references
const docDir = finalPath.substring(0, finalPath.lastIndexOf("/"));
let markdown = await response.text();
// Process markdown to fix image paths
// Replace relative image paths with absolute paths
markdown = markdown.replace(
/!\[(.*?)\]\((?!http|\/)(.*?)\)/g,
(_match, alt, imgPath) => {
return `![${alt}](/images/${docDir}/${imgPath})`;
},
);
const parsedContent = marked.parse(markdown);
content =
typeof parsedContent === "string"
? parsedContent
: await parsedContent;
} catch (err: any) {
console.error("Error loading markdown content:", err);
error = err.message || "Failed to load content";
} finally {
loading = false;
}
}
onMount(() => {
if (path) {
loadMarkdownContent(path);
}
});
</script>
<div class="markdown-content">
{#if loading}
<div class="loading">
<p>Loading content...</p>
</div>
{:else if error}
<div class="error">
<p>Error: {error}</p>
</div>
{:else}
<div class="content">
{@html content}
</div>
{/if}
</div>
<style>
.markdown-content {
/* padding: 0.5rem; */
max-width: 100%;
}
@media (min-width: 640px) {
.markdown-content {
/* padding: 1rem; */
}
}
.loading,
.error {
padding: 1rem;
text-align: center;
}
@media (min-width: 640px) {
.loading,
.error {
padding: 2rem;
}
}
.error {
color: rgb(229 62 62);
}
.content :global(h1) {
font-size: 1.75rem;
font-weight: 700;
margin-bottom: 1rem;
color: rgb(var(--color-primary-700));
word-break: break-word;
}
.content :global(h2) {
font-size: 1.5rem;
font-weight: 600;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
color: rgb(var(--color-primary-800));
word-break: break-word;
}
.content :global(h3) {
font-size: 1.25rem;
font-weight: 600;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
color: rgb(var(--color-primary-800));
word-break: break-word;
}
.content :global(p) {
margin-bottom: 1rem;
line-height: 1.6;
}
.content :global(ul),
.content :global(ol) {
margin-bottom: 1rem;
margin-left: 1.5rem;
}
.content :global(li) {
margin-bottom: 0.5rem;
}
.content :global(a) {
color: rgb(var(--color-primary-600));
text-decoration: none;
word-break: break-word;
}
.content :global(a:hover) {
text-decoration: underline;
}
.content :global(blockquote) {
border-left: 4px solid rgb(var(--color-border));
padding-left: 1rem;
margin-left: 0;
margin-right: 0;
font-style: italic;
color: rgb(var(--color-text-secondary));
}
.content :global(code) {
background-color: rgb(var(--color-background-secondary));
padding: 0.2rem 0.4rem;
border-radius: 0.25rem;
font-family: monospace;
font-size: 0.875em;
word-break: break-word;
white-space: pre-wrap;
}
.content :global(pre) {
background-color: rgb(var(--color-background-secondary));
padding: 0.75rem;
border-radius: 0.5rem;
overflow-x: auto;
margin-bottom: 1rem;
}
.content :global(pre code) {
background-color: transparent;
padding: 0;
white-space: pre;
word-break: normal;
}
.content :global(img) {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
margin: 1rem 0;
display: block;
}
.content :global(hr) {
border: 0;
border-top: 1px solid rgb(var(--color-border));
margin: 1.5rem 0;
}
.content :global(table) {
width: 100%;
border-collapse: collapse;
margin-bottom: 1rem;
display: block;
overflow-x: auto;
}
.content :global(th),
.content :global(td) {
border: 1px solid rgb(var(--color-border));
padding: 0.5rem;
min-width: 100px;
}
.content :global(th) {
background-color: rgb(var(--color-background-secondary));
}
@media (min-width: 640px) {
.content :global(h1) {
font-size: 2.25rem;
margin-bottom: 1.5rem;
}
.content :global(h2) {
font-size: 1.75rem;
margin-top: 2rem;
margin-bottom: 1rem;
}
.content :global(h3) {
font-size: 1.5rem;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
}
.content :global(ul),
.content :global(ol) {
margin-left: 2rem;
}
.content :global(pre) {
padding: 1rem;
}
}
</style>