Add new podcast episodes and update content structure

- Created new podcast episode files for "Freelance Client Stories", "Productivity Without Burnout", "Conversations with Builders", "Stable Frontend Systems", "Writing Cleaner Service Layers", and "Foundations Over Tools" with relevant metadata and episode notes.
- Introduced a new TypeScript type definition for PodcastKind and PodcastEpisode to enhance type safety.
- Implemented a content reading function to fetch and sort podcast episodes by date.
- Updated package.json to include new dependencies: gray-matter for markdown parsing and pdfjs-dist for PDF handling.
- Added PDF files for Atomic Design and personal resume to the public directory.
This commit is contained in:
poyrazavsever
2026-03-10 22:21:40 +03:00
parent 3bf8b362e4
commit c253bad8bf
14 changed files with 786 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
import { ContentContent } from "@/components/content-content";
import { YOUTUBE_VIDEO_LINKS } from "@/data/youtube-videos";
import { getPdfNotes, getPodcastCollections } from "@/lib/content-page";
export default async function ContentPage() {
const [{ yazilim, masaBasi }, pdfFiles] = await Promise.all([
getPodcastCollections(),
getPdfNotes(),
]);
return (
<ContentContent
yazilimEpisodes={yazilim}
masaBasiEpisodes={masaBasi}
youtubeLinks={YOUTUBE_VIDEO_LINKS}
pdfFiles={pdfFiles}
/>
);
}
+371
View File
@@ -0,0 +1,371 @@
"use client";
import { useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Icon } from "@iconify/react";
import { Badge, Button, Card, Typography } from "poyraz-ui/atoms";
import { Modal, ModalContent, ModalTitle, Sheet, SheetContent, SheetTitle } from "poyraz-ui/molecules";
import type { PodcastEpisode } from "@/data/content-types";
type ContentContentProps = {
yazilimEpisodes: PodcastEpisode[];
masaBasiEpisodes: PodcastEpisode[];
youtubeLinks: readonly string[];
pdfFiles: string[];
};
function PdfFirstPagePreview({ src, title }: { src: string; title: string }) {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const [failed, setFailed] = useState(false);
useEffect(() => {
let cancelled = false;
let loadingTask: { promise: Promise<unknown>; destroy?: () => void } | null = null;
const render = async () => {
try {
const pdfjs = await import("pdfjs-dist");
const lib = pdfjs as unknown as {
version: string;
getDocument: (src: string) => { promise: Promise<unknown>; destroy?: () => void };
GlobalWorkerOptions: { workerSrc: string };
};
lib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${lib.version}/build/pdf.worker.min.mjs`;
loadingTask = lib.getDocument(src);
const pdf = (await loadingTask.promise) as {
getPage: (page: number) => Promise<{
getViewport: (opts: { scale: number }) => { width: number; height: number };
render: (opts: {
canvasContext: CanvasRenderingContext2D;
viewport: { width: number; height: number };
}) => { promise: Promise<void> };
}>;
};
const page = await pdf.getPage(1);
const viewport = page.getViewport({ scale: 1.2 });
const canvas = canvasRef.current;
if (!canvas || cancelled) return;
const context = canvas.getContext("2d");
if (!context) return;
const ratio = window.devicePixelRatio || 1;
canvas.width = Math.floor(viewport.width * ratio);
canvas.height = Math.floor(viewport.height * ratio);
canvas.style.width = `${viewport.width}px`;
canvas.style.height = `${viewport.height}px`;
context.setTransform(ratio, 0, 0, ratio, 0, 0);
await page.render({ canvasContext: context, viewport }).promise;
} catch {
if (!cancelled) {
setFailed(true);
}
}
};
void render();
return () => {
cancelled = true;
if (loadingTask?.destroy) {
loadingTask.destroy();
}
};
}, [src]);
if (failed) {
return (
<div className="flex h-full w-full items-center justify-center bg-muted/20 p-2">
<Typography variant="small" className="text-muted-foreground">
Preview unavailable
</Typography>
</div>
);
}
return (
<div className="flex h-full w-full items-center justify-center bg-white p-2">
<canvas ref={canvasRef} aria-label={title} className="h-auto max-h-full w-auto max-w-full" />
</div>
);
}
function getYoutubeEmbedUrl(link: string) {
try {
const url = new URL(link);
const videoId = url.searchParams.get("v");
return videoId ? `https://www.youtube.com/embed/${videoId}` : null;
} catch {
return null;
}
}
function PodcastColumn({
title,
subtitle,
episodes,
onOpenEpisode,
}: {
title: string;
subtitle: string;
episodes: PodcastEpisode[];
onOpenEpisode: (episode: PodcastEpisode) => void;
}) {
return (
<Card className="rounded-sm border-border p-4">
<Typography variant="large" className="text-base">
{title}
</Typography>
<Typography variant="small" className="mt-1 text-muted-foreground">
{subtitle}
</Typography>
<div className="mt-3 grid gap-2">
{episodes.slice(0, 3).map((episode) => (
<button
key={`${episode.podcast}-${episode.slug}`}
type="button"
onClick={() => onOpenEpisode(episode)}
className="cursor-pointer text-left"
>
<Card className="rounded-sm border-border p-3 transition-colors hover:border-zinc-700">
<Typography variant="small" className="font-semibold text-foreground">
{episode.title}
</Typography>
<Typography variant="small" className="mt-0.5 text-muted-foreground">
{episode.date}
</Typography>
</Card>
</button>
))}
</div>
</Card>
);
}
export function ContentContent({
yazilimEpisodes,
masaBasiEpisodes,
youtubeLinks,
pdfFiles,
}: ContentContentProps) {
const [selectedEpisode, setSelectedEpisode] = useState<PodcastEpisode | null>(null);
const [sheetOpen, setSheetOpen] = useState(false);
const [pdfModalOpen, setPdfModalOpen] = useState(false);
const [activePdfIndex, setActivePdfIndex] = useState(0);
const activePdf = pdfFiles[activePdfIndex] ?? null;
const canGoPrev = activePdfIndex > 0;
const canGoNext = activePdfIndex < pdfFiles.length - 1;
const embeddedVideos = useMemo(
() =>
youtubeLinks
.map((link) => ({ link, embedUrl: getYoutubeEmbedUrl(link) }))
.slice(0, 3),
[youtubeLinks],
);
const openEpisode = (episode: PodcastEpisode) => {
setSelectedEpisode(episode);
setSheetOpen(true);
};
const openPdfModal = (index: number) => {
setActivePdfIndex(index);
setPdfModalOpen(true);
};
return (
<section className="flex h-full flex-col gap-3 overflow-y-auto">
<section className="grid gap-3 md:grid-cols-2">
<PodcastColumn
title="Poyraz ile Yazilim"
subtitle="Regular schedule, every Sunday."
episodes={yazilimEpisodes}
onOpenEpisode={openEpisode}
/>
<PodcastColumn
title="Poyraz ile Masa Basi"
subtitle="Irregular schedule, guest-focused."
episodes={masaBasiEpisodes}
onOpenEpisode={openEpisode}
/>
</section>
<section className="space-y-2">
<Typography variant="large" className="text-base">
Latest YouTube Videos
</Typography>
<div className="grid gap-2 md:grid-cols-3">
{embeddedVideos.map((item) => (
<Card key={item.link} className="overflow-hidden rounded-sm border-border p-0">
{item.embedUrl ? (
<div className="aspect-video w-full">
<iframe
src={item.embedUrl}
title="YouTube video player"
className="h-full w-full"
loading="lazy"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerPolicy="strict-origin-when-cross-origin"
allowFullScreen
/>
</div>
) : (
<div className="p-3">
<Typography variant="small" className="text-muted-foreground">
Invalid video link.
</Typography>
</div>
)}
</Card>
))}
</div>
</section>
<section className="space-y-2">
<Typography variant="large" className="text-base">
LinkedIn PDF Notes
</Typography>
<div className="grid gap-2 grid-cols-2 md:grid-cols-3">
{pdfFiles.map((pdf, index) => (
<button
key={pdf}
type="button"
onClick={() => openPdfModal(index)}
className="cursor-pointer text-left"
>
<Card className="overflow-hidden rounded-sm border-border p-0 transition-colors hover:border-zinc-700">
<div className="aspect-square w-full">
<PdfFirstPagePreview src={`/pdf/${pdf}`} title={`${pdf} preview`} />
</div>
</Card>
</button>
))}
</div>
</section>
<Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
<SheetContent side="right" className="w-full max-w-xl p-0">
<div className="border-b border-border px-4 py-3">
<SheetTitle>{selectedEpisode?.title ?? "Episode"}</SheetTitle>
{selectedEpisode ? (
<Typography variant="small" className="mt-1 text-muted-foreground">
{selectedEpisode.date}
</Typography>
) : null}
</div>
<div className="flex max-h-[calc(100dvh-64px)] flex-col gap-3 overflow-y-auto p-4">
{selectedEpisode ? (
<>
<div className="flex flex-wrap gap-2">
<Badge className="rounded-sm">{selectedEpisode.podcast}</Badge>
</div>
<div className="flex flex-wrap gap-2">
<Link
href={selectedEpisode.youtubeUrl}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1.5 rounded-sm border border-border px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground"
>
<Icon icon="mdi:youtube" width={16} height={16} className="text-red-600" />
YouTube
</Link>
<Link
href={selectedEpisode.spotifyUrl}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1.5 rounded-sm border border-border px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground"
>
<Icon icon="mdi:spotify" width={16} height={16} className="text-green-600" />
Spotify
</Link>
</div>
<Card className="rounded-sm border-border p-3">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
h2: ({ children }) => (
<Typography variant="large" className="mt-3 text-foreground first:mt-0">
{children}
</Typography>
),
p: ({ children }) => (
<Typography variant="small" className="mt-1 text-muted-foreground first:mt-0">
{children}
</Typography>
),
li: ({ children }) => (
<li className="ml-5 list-disc text-sm text-muted-foreground">{children}</li>
),
}}
>
{selectedEpisode.markdown}
</ReactMarkdown>
</Card>
</>
) : null}
</div>
</SheetContent>
</Sheet>
<Modal open={pdfModalOpen} onOpenChange={setPdfModalOpen}>
<ModalContent size="xl" className="rounded-sm p-4">
<ModalTitle>{activePdf ? activePdf.replace(/\.pdf$/i, "") : "PDF Note"}</ModalTitle>
<div className="mt-3 flex flex-wrap items-center justify-between gap-2">
<Typography variant="small" className="text-muted-foreground">
{pdfFiles.length === 0 ? "0 / 0" : `${activePdfIndex + 1} / ${pdfFiles.length}`}
</Typography>
<div className="flex items-center gap-2">
<Button
type="button"
variant="outline"
className="rounded-sm"
disabled={!canGoPrev}
onClick={() => setActivePdfIndex((prev) => Math.max(0, prev - 1))}
>
Prev
</Button>
<Button
type="button"
variant="outline"
className="rounded-sm"
disabled={!canGoNext}
onClick={() => setActivePdfIndex((prev) => Math.min(pdfFiles.length - 1, prev + 1))}
>
Next
</Button>
</div>
</div>
{activePdf ? (
<div className="mt-3 h-[70dvh] overflow-hidden rounded-sm border border-border">
<iframe
src={`/pdf/${activePdf}`}
title={activePdf}
className="h-full w-full"
/>
</div>
) : (
<Card className="mt-3 rounded-sm border-border p-3">
<Typography variant="small" className="text-muted-foreground">
No PDF found in /public/pdf.
</Typography>
</Card>
)}
</ModalContent>
</Modal>
</section>
);
}
@@ -0,0 +1,16 @@
---
title: "Freelance Client Stories"
date: "2026-01-30"
youtubeUrl: "https://www.youtube.com/watch?v=XWUeVzf0t6Y&t=2s"
spotifyUrl: "https://open.spotify.com/show/placeholder-masabasi"
podcast: "masa-basi"
---
## Episode Notes
Real stories from client communication, scope alignment, and delivery expectations.
## Sources
- Proposal templates
- Client handoff checklist
@@ -0,0 +1,16 @@
---
title: "Productivity Without Burnout"
date: "2026-02-20"
youtubeUrl: "https://www.youtube.com/watch?v=H8sP8HejI7A"
spotifyUrl: "https://open.spotify.com/show/placeholder-masabasi"
podcast: "masa-basi"
---
## Episode Notes
An informal talk on balancing shipping speed with sustainable routines.
## Sources
- Weekly planning template
- Timeboxing references
@@ -0,0 +1,16 @@
---
title: "Conversations with Builders"
date: "2026-03-07"
youtubeUrl: "https://www.youtube.com/watch?v=9JEOGCX5aSQ&t=22s"
spotifyUrl: "https://open.spotify.com/show/placeholder-masabasi"
podcast: "masa-basi"
---
## Episode Notes
Guest conversation about discipline, career direction, and making room for deep work.
## Sources
- Guest reading list
- Workflow snapshots
@@ -0,0 +1,16 @@
---
title: "Stable Frontend Systems"
date: "2026-02-23"
youtubeUrl: "https://www.youtube.com/watch?v=XWUeVzf0t6Y&t=2s"
spotifyUrl: "https://open.spotify.com/show/placeholder-yazilim"
podcast: "yazilim"
---
## Episode Notes
This one is about component contracts, UI consistency, and reducing regressions in fast-moving projects.
## Sources
- Design token architecture references
- Next.js App Router docs
@@ -0,0 +1,16 @@
---
title: "Writing Cleaner Service Layers"
date: "2026-03-02"
youtubeUrl: "https://www.youtube.com/watch?v=H8sP8HejI7A"
spotifyUrl: "https://open.spotify.com/show/placeholder-yazilim"
podcast: "yazilim"
---
## Episode Notes
We discuss service-layer boundaries, validation ownership, and error mapping strategy.
## Sources
- Practical clean architecture examples
- Team code review checklist
@@ -0,0 +1,17 @@
---
title: "Foundations Over Tools"
date: "2026-03-09"
youtubeUrl: "https://www.youtube.com/watch?v=9JEOGCX5aSQ&t=22s"
spotifyUrl: "https://open.spotify.com/show/placeholder-yazilim"
podcast: "yazilim"
---
## Episode Notes
This episode focuses on software fundamentals and why architecture basics matter more than tool hype.
## Sources
- Domain modeling notes
- Node.js internals docs
- System design interview references
+11
View File
@@ -0,0 +1,11 @@
export type PodcastKind = "yazilim" | "masa-basi";
export type PodcastEpisode = {
slug: string;
title: string;
date: string;
youtubeUrl: string;
spotifyUrl: string;
podcast: PodcastKind;
markdown: string;
};
+73
View File
@@ -0,0 +1,73 @@
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
import matter from "gray-matter";
import type { PodcastEpisode, PodcastKind } from "@/data/content-types";
type EpisodeFrontmatter = {
title: string;
date: string;
youtubeUrl: string;
spotifyUrl: string;
podcast: PodcastKind;
};
const PODCAST_DIR = path.join(process.cwd(), "content", "podcasts");
const PDF_DIR = path.join(process.cwd(), "public", "pdf");
function sortEpisodesByDateDesc(episodes: PodcastEpisode[]) {
return [...episodes].sort((a, b) => {
const aTime = Number(new Date(a.date));
const bTime = Number(new Date(b.date));
if (!Number.isNaN(aTime) && !Number.isNaN(bTime)) {
return bTime - aTime;
}
return a.title.localeCompare(b.title);
});
}
async function readPodcastEpisodes(kind: PodcastKind): Promise<PodcastEpisode[]> {
const folder = path.join(PODCAST_DIR, kind);
const files = await readdir(folder);
const mdFiles = files.filter((file) => file.endsWith(".md"));
const episodes = await Promise.all(
mdFiles.map(async (file) => {
const fullPath = path.join(folder, file);
const raw = await readFile(fullPath, "utf8");
const parsed = matter(raw);
const data = parsed.data as Partial<EpisodeFrontmatter>;
return {
slug: file.replace(/\.md$/, ""),
title: data.title ?? "Untitled Episode",
date: data.date ?? "1970-01-01",
youtubeUrl: data.youtubeUrl ?? "",
spotifyUrl: data.spotifyUrl ?? "",
podcast: data.podcast ?? kind,
markdown: parsed.content.trim(),
} satisfies PodcastEpisode;
}),
);
return sortEpisodesByDateDesc(episodes);
}
export async function getPodcastCollections() {
const [yazilim, masaBasi] = await Promise.all([
readPodcastEpisodes("yazilim"),
readPodcastEpisodes("masa-basi"),
]);
return {
yazilim,
masaBasi,
};
}
export async function getPdfNotes() {
const files = await readdir(PDF_DIR);
const pdfFiles = files.filter((file) => file.toLowerCase().endsWith(".pdf"));
return pdfFiles.sort((a, b) => a.localeCompare(b));
}
+2
View File
@@ -10,8 +10,10 @@
},
"dependencies": {
"@iconify/react": "^6.0.2",
"gray-matter": "^4.0.3",
"mermaid": "^11.13.0",
"next": "16.1.6",
"pdfjs-dist": "^5.5.207",
"poyraz-ui": "^2.0.1",
"react": "19.2.3",
"react-dom": "19.2.3",
+213
View File
@@ -11,12 +11,18 @@ importers:
'@iconify/react':
specifier: ^6.0.2
version: 6.0.2(react@19.2.3)
gray-matter:
specifier: ^4.0.3
version: 4.0.3
mermaid:
specifier: ^11.13.0
version: 11.13.0
next:
specifier: 16.1.6
version: 16.1.6(@babel/core@7.29.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
pdfjs-dist:
specifier: ^5.5.207
version: 5.5.207
poyraz-ui:
specifier: ^2.0.1
version: 2.0.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react-hook-form@7.71.2(react@19.2.3))(react@19.2.3)(tailwindcss@4.2.1)(zod@4.3.6)
@@ -407,6 +413,76 @@ packages:
'@mermaid-js/parser@1.0.1':
resolution: {integrity: sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==}
'@napi-rs/canvas-android-arm64@0.1.96':
resolution: {integrity: sha512-ew1sPrN3dGdZ3L4FoohPfnjq0f9/Jk7o+wP7HkQZokcXgIUD6FIyICEWGhMYzv53j63wUcPvZeAwgewX58/egg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@napi-rs/canvas-darwin-arm64@0.1.96':
resolution: {integrity: sha512-Q/wOXZ5PzTqpdmA5eUOcegCf4Go/zz3aZ5DlzSeDpOjFmfwMKh8EzLAoweQ+mJVagcHQyzoJhaTEnrO68TNyNg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@napi-rs/canvas-darwin-x64@0.1.96':
resolution: {integrity: sha512-UrXiQz28tQEvGM1qvyptewOAfmUrrd5+wvi6Rzjj2VprZI8iZ2KIvBD2lTTG1bVF95AbeDeG7PJA0D9sLKaOFA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.96':
resolution: {integrity: sha512-I90ODxweD8aEP6XKU/NU+biso95MwCtQ2F46dUvhec1HesFi0tq/tAJkYic/1aBSiO/1kGKmSeD1B0duOHhEHQ==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@napi-rs/canvas-linux-arm64-gnu@0.1.96':
resolution: {integrity: sha512-Dx/0+RFV++w3PcRy+4xNXkghhXjA5d0Mw1bs95emn5Llinp1vihMaA6WJt3oYv2LAHc36+gnrhIBsPhUyI2SGw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@napi-rs/canvas-linux-arm64-musl@0.1.96':
resolution: {integrity: sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@napi-rs/canvas-linux-riscv64-gnu@0.1.96':
resolution: {integrity: sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
'@napi-rs/canvas-linux-x64-gnu@0.1.96':
resolution: {integrity: sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@napi-rs/canvas-linux-x64-musl@0.1.96':
resolution: {integrity: sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@napi-rs/canvas-win32-arm64-msvc@0.1.96':
resolution: {integrity: sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@napi-rs/canvas-win32-x64-msvc@0.1.96':
resolution: {integrity: sha512-UYGdTltVd+Z8mcIuoqGmAXXUvwH5CLf2M6mIB5B0/JmX5J041jETjqtSYl7gN+aj3k1by/SG6sS0hAwCqyK7zw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@napi-rs/canvas@0.1.96':
resolution: {integrity: sha512-6NNmNxvoJKeucVjxaaRUt3La2i5jShgiAbaY3G/72s1Vp3U06XPrAIxkAjBxpDcamEn/t+WJ4OOlGmvILo4/Ew==}
engines: {node: '>= 10'}
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
@@ -1436,6 +1512,9 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
argparse@1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@@ -2017,6 +2096,11 @@ packages:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
esquery@1.7.0:
resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
engines: {node: '>=0.10'}
@@ -2036,6 +2120,10 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
extend-shallow@2.0.1:
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
engines: {node: '>=0.10.0'}
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
@@ -2158,6 +2246,10 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
gray-matter@4.0.3:
resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
engines: {node: '>=6.0'}
hachure-fill@0.5.2:
resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
@@ -2293,6 +2385,10 @@ packages:
is-decimal@2.0.1:
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
is-extendable@0.1.1:
resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
engines: {node: '>=0.10.0'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@@ -2385,6 +2481,10 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@3.14.2:
resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==}
hasBin: true
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
@@ -2426,6 +2526,10 @@ packages:
khroma@2.1.0:
resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
kind-of@6.0.3:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
langium@4.2.1:
resolution: {integrity: sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==}
engines: {node: '>=20.10.0', npm: '>=10.2.3'}
@@ -2754,6 +2858,9 @@ packages:
resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==}
engines: {node: '>= 0.4'}
node-readable-to-web-readable-stream@0.4.2:
resolution: {integrity: sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==}
node-releases@2.0.36:
resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==}
@@ -2832,6 +2939,10 @@ packages:
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
pdfjs-dist@5.5.207:
resolution: {integrity: sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==}
engines: {node: '>=20.19.0 || >=22.13.0 || >=24'}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -3040,6 +3151,10 @@ packages:
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
section-matter@1.0.0:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -3102,6 +3217,9 @@ packages:
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
stable-hash@0.0.5:
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
@@ -3135,6 +3253,10 @@ packages:
stringify-entities@4.0.4:
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
strip-bom-string@1.0.0:
resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
engines: {node: '>=0.10.0'}
strip-bom@3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
@@ -3747,6 +3869,54 @@ snapshots:
dependencies:
langium: 4.2.1
'@napi-rs/canvas-android-arm64@0.1.96':
optional: true
'@napi-rs/canvas-darwin-arm64@0.1.96':
optional: true
'@napi-rs/canvas-darwin-x64@0.1.96':
optional: true
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.96':
optional: true
'@napi-rs/canvas-linux-arm64-gnu@0.1.96':
optional: true
'@napi-rs/canvas-linux-arm64-musl@0.1.96':
optional: true
'@napi-rs/canvas-linux-riscv64-gnu@0.1.96':
optional: true
'@napi-rs/canvas-linux-x64-gnu@0.1.96':
optional: true
'@napi-rs/canvas-linux-x64-musl@0.1.96':
optional: true
'@napi-rs/canvas-win32-arm64-msvc@0.1.96':
optional: true
'@napi-rs/canvas-win32-x64-msvc@0.1.96':
optional: true
'@napi-rs/canvas@0.1.96':
optionalDependencies:
'@napi-rs/canvas-android-arm64': 0.1.96
'@napi-rs/canvas-darwin-arm64': 0.1.96
'@napi-rs/canvas-darwin-x64': 0.1.96
'@napi-rs/canvas-linux-arm-gnueabihf': 0.1.96
'@napi-rs/canvas-linux-arm64-gnu': 0.1.96
'@napi-rs/canvas-linux-arm64-musl': 0.1.96
'@napi-rs/canvas-linux-riscv64-gnu': 0.1.96
'@napi-rs/canvas-linux-x64-gnu': 0.1.96
'@napi-rs/canvas-linux-x64-musl': 0.1.96
'@napi-rs/canvas-win32-arm64-msvc': 0.1.96
'@napi-rs/canvas-win32-x64-msvc': 0.1.96
optional: true
'@napi-rs/wasm-runtime@0.2.12':
dependencies:
'@emnapi/core': 1.8.1
@@ -4773,6 +4943,10 @@ snapshots:
dependencies:
color-convert: 2.0.1
argparse@1.0.10:
dependencies:
sprintf-js: 1.0.3
argparse@2.0.1: {}
aria-hidden@1.2.6:
@@ -5550,6 +5724,8 @@ snapshots:
acorn-jsx: 5.3.2(acorn@8.16.0)
eslint-visitor-keys: 4.2.1
esprima@4.0.1: {}
esquery@1.7.0:
dependencies:
estraverse: 5.3.0
@@ -5564,6 +5740,10 @@ snapshots:
esutils@2.0.3: {}
extend-shallow@2.0.1:
dependencies:
is-extendable: 0.1.1
extend@3.0.2: {}
fast-deep-equal@3.1.3: {}
@@ -5686,6 +5866,13 @@ snapshots:
graceful-fs@4.2.11: {}
gray-matter@4.0.3:
dependencies:
js-yaml: 3.14.2
kind-of: 6.0.3
section-matter: 1.0.0
strip-bom-string: 1.0.0
hachure-fill@0.5.2: {}
has-bigints@1.1.0: {}
@@ -5838,6 +6025,8 @@ snapshots:
is-decimal@2.0.1: {}
is-extendable@0.1.1: {}
is-extglob@2.1.1: {}
is-finalizationregistry@1.1.1:
@@ -5927,6 +6116,11 @@ snapshots:
js-tokens@4.0.0: {}
js-yaml@3.14.2:
dependencies:
argparse: 1.0.10
esprima: 4.0.1
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
@@ -5962,6 +6156,8 @@ snapshots:
khroma@2.1.0: {}
kind-of@6.0.3: {}
langium@4.2.1:
dependencies:
chevrotain: 11.1.2
@@ -6502,6 +6698,9 @@ snapshots:
object.entries: 1.1.9
semver: 6.3.1
node-readable-to-web-readable-stream@0.4.2:
optional: true
node-releases@2.0.36: {}
object-assign@4.1.1: {}
@@ -6595,6 +6794,11 @@ snapshots:
pathe@2.0.3: {}
pdfjs-dist@5.5.207:
optionalDependencies:
'@napi-rs/canvas': 0.1.96
node-readable-to-web-readable-stream: 0.4.2
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -6867,6 +7071,11 @@ snapshots:
scheduler@0.27.0: {}
section-matter@1.0.0:
dependencies:
extend-shallow: 2.0.1
kind-of: 6.0.3
semver@6.3.1: {}
semver@7.7.4: {}
@@ -6968,6 +7177,8 @@ snapshots:
space-separated-tokens@2.0.2: {}
sprintf-js@1.0.3: {}
stable-hash@0.0.5: {}
stop-iteration-iterator@1.1.0:
@@ -7030,6 +7241,8 @@ snapshots:
character-entities-html4: 2.1.0
character-entities-legacy: 3.0.0
strip-bom-string@1.0.0: {}
strip-bom@3.0.0: {}
strip-json-comments@3.1.1: {}
Binary file not shown.
BIN
View File
Binary file not shown.