@devilsdev/rag-pipeline-utils 2.1.1 → 2.1.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 (54) hide show
  1. package/.github/release-template.md +20 -0
  2. package/.github/workflows/ci.yml +12 -23
  3. package/.github/workflows/docs-ci.yml +44 -0
  4. package/.github/workflows/docs-deploy.yml +26 -16
  5. package/.github/workflows/publish-release-blog.yml +37 -17
  6. package/CHANGELOG.md +7 -0
  7. package/codecov +0 -0
  8. package/codecov.SHA256SUM +1 -0
  9. package/codecov.SHA256SUM.sig +16 -0
  10. package/docs-site/blog/2025-04-18-architecture-overview.md +8 -26
  11. package/docs-site/blog/{2025-04-17-release-v2.0.2.md → 2025-04-18-release-v2.1.1.md} +4 -4
  12. package/docs-site/blog/2025-04-19-prompt-engineering-best-practices.md +8 -23
  13. package/docs-site/blog/2025-04-20-image-showcase.md +6 -15
  14. package/docs-site/blog/2025-04-20-release-2.1.1.md +21 -0
  15. package/docs-site/blog/tags.yml +29 -0
  16. package/docs-site/docs/intro.mdx +6 -0
  17. package/docs-site/docusaurus.config.js +35 -18
  18. package/docs-site/package-lock.json +234 -719
  19. package/docs-site/package.json +4 -8
  20. package/docs-site/src/components/HomepageFeatures/index.js +34 -37
  21. package/docs-site/src/components/HomepageFeatures/index.module.css +57 -0
  22. package/docs-site/src/components/HomepageHeader/index.js +46 -0
  23. package/docs-site/src/components/HomepageHeader/index.module.css +77 -0
  24. package/docs-site/src/css/custom.css +101 -78
  25. package/docs-site/src/pages/index.js +11 -28
  26. package/docs-site/static/img/logo.png +0 -0
  27. package/docs-site/static/img/logo.svg +5 -1538
  28. package/jest.config.js +4 -1
  29. package/package.json +12 -6
  30. package/scripts/fix-mdx-blog-imports.js +50 -0
  31. package/scripts/fix-mdx-image-imports.js +27 -0
  32. package/scripts/generate-release-note.js +40 -0
  33. package/.github/workflows/storybook-deploy.yml +0 -33
  34. package/.storybook/main.js +0 -20
  35. package/.storybook/preview.js +0 -13
  36. package/docs-site/.plaiceholder-cache/img_hero.jpg.json +0 -1
  37. package/docs-site/.plaiceholder-cache/img_logo.png.json +0 -1
  38. package/docs-site/.storybook/preview.js +0 -8
  39. package/docs-site/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  40. package/docs-site/blog/2021-08-26-welcome/index.md +0 -28
  41. package/docs-site/blog/2025-04-17-release-2.0.md +0 -67
  42. package/docs-site/blog/2025-04-18-release-2.0.0.md +0 -21
  43. package/docs-site/blog/2025-04-18-release-v2.1.0.md +0 -13
  44. package/docs-site/docs/components/BlogCardExample.mdx +0 -7
  45. package/docs-site/scripts/generate-placeholders.js +0 -17
  46. package/docs-site/scripts/watch-images.js +0 -40
  47. package/docs-site/sidebar-snippet.txt +0 -5
  48. package/docs-site/src/components/BlogCard.jsx +0 -31
  49. package/docs-site/src/components/BlogCard.stories.jsx +0 -34
  50. package/docs-site/src/components/BlogImage.jsx +0 -58
  51. package/docs-site/src/components/BlogImage.stories.jsx +0 -20
  52. package/docs-site/src/components/HomepageFeatures/styles.module.css +0 -58
  53. package/docs-site/static/img/favicon.ico +0 -0
  54. package/docs-site/utils/image.js +0 -19
package/jest.config.js CHANGED
@@ -8,9 +8,12 @@
8
8
  // jest.config.js
9
9
 
10
10
  export default {
11
+ transform: {
12
+ '^.+\\.[t|j]sx?$': 'babel-jest', // Existing transform for JS files
13
+ '\\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2)$': 'jest-transform-stub', // this for static files
14
+ },
11
15
  verbose: true,
12
16
  testEnvironment: 'node',
13
- transform: {},
14
17
  collectCoverage: true,
15
18
  coverageDirectory: 'coverage',
16
19
  moduleFileExtensions: ['js', 'json'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devilsdev/rag-pipeline-utils",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "A modular toolkit for building RAG (Retrieval-Augmented Generation) pipelines in Node.js",
5
5
  "main": "src/core/create-pipeline.js",
6
6
  "bin": {
@@ -20,12 +20,13 @@
20
20
  "build:docs": "npx docusaurus build docs-site",
21
21
  "ci": "node scripts/ci-runner.js",
22
22
  "docs:dev": "npx docusaurus start docs-site",
23
- "docs:build": "npx docusaurus build docs-site",
24
- "docs:deploy": "npx docusaurus deploy docs-site",
25
23
  "docs:version": "npx docusaurus docs:version",
26
- "docs:serve": "npx docusaurus serve docs-site",
27
- "storybook": "storybook dev -p 6006",
28
- "build-storybook": "storybook build"
24
+ "watch:lqip": "node docs-site/scripts/watch-images.js",
25
+ "docs:clear": "docusaurus clear --site-dir docs-site",
26
+ "docs:start": "docusaurus start docs-site",
27
+ "docs:build": "docusaurus build docs-site",
28
+ "docs:serve": "docusaurus serve docs-site",
29
+ "docs:deploy": "docusaurus deploy docs-site"
29
30
  },
30
31
  "lint-staged": {
31
32
  "**/*.{js,ts}": [
@@ -56,6 +57,8 @@
56
57
  "access": "public"
57
58
  },
58
59
  "devDependencies": {
60
+ "@babel/core": "^7.26.10",
61
+ "@babel/preset-env": "^7.26.9",
59
62
  "@docusaurus/core": "^3.7.0",
60
63
  "@docusaurus/preset-classic": "^3.7.0",
61
64
  "@semantic-release/changelog": "^6.0.3",
@@ -73,6 +76,7 @@
73
76
  "@storybook/react-webpack5": "^8.6.12",
74
77
  "@storybook/test": "^8.6.12",
75
78
  "@types/jest": "^29.5.14",
79
+ "babel-jest": "^29.7.0",
76
80
  "codecov": "^3.8.2",
77
81
  "cross-env": "^7.0.3",
78
82
  "docusaurus": "^1.14.7",
@@ -81,6 +85,7 @@
81
85
  "eslint-plugin-storybook": "^0.12.0",
82
86
  "husky": "^8.0.3",
83
87
  "jest": "^29.7.0",
88
+ "jest-transform-stub": "^2.0.0",
84
89
  "lint-staged": "^15.5.1",
85
90
  "prettier": "^3.5.3",
86
91
  "prop-types": "^15.8.1",
@@ -97,6 +102,7 @@
97
102
  "csv-parse": "^5.6.0",
98
103
  "dotenv": "^16.5.0",
99
104
  "fast-glob": "^3.3.3",
105
+ "framer-motion": "^12.7.4",
100
106
  "jsdom": "^26.1.0",
101
107
  "openai": "^4.93.0",
102
108
  "pino": "^9.6.0",
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Rewrites blog .mdx files to use SSR-safe BlogImage.jsx import
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+
10
+ const BLOG_DIR = path.resolve('docs-site', 'blog');
11
+ const TARGET_IMPORT = 'import BlogImage from \'../src/components/BlogImage\'';
12
+ const OLD_IMPORT_REGEX = /import\s+BlogImage\s+from\s+['"].+BlogImage[^'"]*['"];/g;
13
+
14
+ function fixImportInFile(filePath) {
15
+ let content = fs.readFileSync(filePath, 'utf-8');
16
+
17
+ // If already correct, skip
18
+ if (content.includes(TARGET_IMPORT)) {
19
+ console.log(`✔️ ${path.basename(filePath)} already uses safe import.`);
20
+ return;
21
+ }
22
+
23
+ const replaced = content.replace(OLD_IMPORT_REGEX, TARGET_IMPORT);
24
+
25
+ if (replaced !== content) {
26
+ fs.writeFileSync(filePath, replaced);
27
+ console.log(`✅ Updated: ${path.basename(filePath)}`);
28
+ } else {
29
+ // No previous import, insert at top
30
+ fs.writeFileSync(filePath, `${TARGET_IMPORT}\n\n${content}`);
31
+ console.log(`➕ Injected import: ${path.basename(filePath)}`);
32
+ }
33
+ }
34
+
35
+ function walkDir(dir) {
36
+ const files = fs.readdirSync(dir);
37
+ for (const f of files) {
38
+ const fullPath = path.join(dir, f);
39
+ const stat = fs.statSync(fullPath);
40
+ if (stat.isDirectory()) {
41
+ walkDir(fullPath);
42
+ } else if (f.endsWith('.mdx')) {
43
+ fixImportInFile(fullPath);
44
+ }
45
+ }
46
+ }
47
+
48
+ console.log('🔍 Scanning MDX blog files...');
49
+ walkDir(BLOG_DIR);
50
+ console.log('🎉 Import rewrite complete.');
@@ -0,0 +1,27 @@
1
+ // fix-mdx-image-imports.js
2
+ // Version: 1.0.0
3
+ // Description: Auto-wrap all BlogImage imports with SafeImageWrapper to avoid SSR issues.
4
+
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import glob from 'glob';
8
+
9
+ const blogDir = path.resolve('docs-site', 'blog');
10
+
11
+ glob(`${blogDir}/**/*.mdx`, (err, files) => {
12
+ if (err) throw err;
13
+
14
+ files.forEach((file) => {
15
+ let content = fs.readFileSync(file, 'utf8');
16
+
17
+ if (content.includes('from \'@site/src/components/BlogImage\'')) {
18
+ content = content
19
+ .replace(/from '@site\/src\/components\/BlogImage'/g, 'from \'@site/src/components/SafeImageWrapper\'')
20
+ .replace(/<BlogImage/g, '<SafeImageWrapper')
21
+ .replace(/<\/BlogImage>/g, '</SafeImageWrapper>');
22
+
23
+ fs.writeFileSync(file, content, 'utf8');
24
+ console.log(`✔️ Updated: ${file}`);
25
+ }
26
+ });
27
+ });
@@ -0,0 +1,40 @@
1
+ // File: scripts/generate-release-note.js
2
+ /**
3
+ * Version: 1.2.0
4
+ * Description: Injects release metadata into markdown template
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { execSync } = require('child_process');
11
+
12
+ const [,, version, previousTag] = process.argv;
13
+
14
+ if (!version || !previousTag) {
15
+ console.error('Usage: node scripts/generate-release-note.js <version> <previousTag>');
16
+ process.exit(1);
17
+ }
18
+
19
+ const templatePath = path.join(__dirname, '../.github/release-template.md');
20
+ const outputPath = path.join(__dirname, `../docs-site/blog/${new Date().toISOString().split('T')[0]}-release-${version}.md`);
21
+
22
+ const template = fs.readFileSync(templatePath, 'utf-8');
23
+
24
+ // Count contributors
25
+ const contributors = execSync(`git shortlog -sne ${previousTag}..HEAD | wc -l`).toString().trim();
26
+ // Count commits
27
+ const commits = execSync(`git rev-list --count ${previousTag}..HEAD`).toString().trim();
28
+ // ISO date
29
+ const date = new Date().toISOString().split('T')[0];
30
+
31
+ // Inject into template
32
+ const filled = template
33
+ .replace(/{{VERSION}}/g, version)
34
+ .replace(/{{PREVIOUS_TAG}}/g, previousTag)
35
+ .replace(/{{DATE}}/g, date)
36
+ .replace(/{{CONTRIBUTORS}}/g, contributors)
37
+ .replace(/{{COMMITS}}/g, commits);
38
+
39
+ fs.writeFileSync(outputPath, filled);
40
+ console.log(`✅ Generated release blog: ${outputPath}`);
@@ -1,33 +0,0 @@
1
- # .github/workflows/storybook-deploy.yml
2
- name: 📘 Deploy Storybook
3
-
4
- on:
5
- push:
6
- branches: [main]
7
- paths:
8
- - 'docs-site/src/components/**'
9
- - '.storybook/**'
10
-
11
- jobs:
12
- deploy-storybook:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - name: Checkout
16
- uses: actions/checkout@v4
17
-
18
- - name: Setup Node
19
- uses: actions/setup-node@v4
20
- with:
21
- node-version: 20
22
-
23
- - name: Install dependencies
24
- run: npm ci
25
-
26
- - name: Build Storybook
27
- run: npm run build-storybook
28
-
29
- - name: Deploy to GitHub Pages
30
- uses: peaceiris/actions-gh-pages@v4
31
- with:
32
- github_token: ${{ secrets.GITHUB_TOKEN }}
33
- publish_dir: ./storybook-static
@@ -1,20 +0,0 @@
1
-
2
-
3
- /** @type { import('@storybook/react-webpack5').StorybookConfig } */
4
- const config = {
5
- "stories": [
6
- "../src/**/*.mdx",
7
- "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
8
- ],
9
- "addons": [
10
- "@storybook/addon-webpack5-compiler-swc",
11
- "@storybook/addon-essentials",
12
- "@storybook/addon-onboarding",
13
- "@storybook/addon-interactions"
14
- ],
15
- "framework": {
16
- "name": "@storybook/react-webpack5",
17
- "options": {}
18
- }
19
- };
20
- export default config;
@@ -1,13 +0,0 @@
1
- /** @type { import('@storybook/react').Preview } */
2
- const preview = {
3
- parameters: {
4
- controls: {
5
- matchers: {
6
- color: /(background|color)$/i,
7
- date: /Date$/i,
8
- },
9
- },
10
- },
11
- };
12
-
13
- export default preview;
@@ -1 +0,0 @@
1
- {"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAGklEQVR4nGPILapfuW5vblF9blE9A4QD4QMApeANEwX0WNkAAAAASUVORK5CYII="}
@@ -1 +0,0 @@
1
- {"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAT0lEQVR4nAFEALv/AIXNrxKW5b44TKykITGQkA0Atv/dK37FpoIVaGhbSqqbIQAPMjMPAAcXJAAHGTkXJjUQABw3QRQOJT0bFCA3HBUiNRZrJhOUbaRuqQAAAABJRU5ErkJggg=="}
@@ -1,8 +0,0 @@
1
- // .storybook/preview.js
2
- export const parameters = {
3
- layout: 'centered',
4
- docs: {
5
- title: '🧩 RAG Pipeline UI Components',
6
- },
7
- };
8
-
@@ -1,28 +0,0 @@
1
- ---
2
- slug: welcome
3
- title: Welcome
4
- tags: [facebook, hello, docusaurus]
5
- ---
6
-
7
- [Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
8
-
9
- Here are a few tips you might find useful.
10
-
11
- <!-- truncate -->
12
-
13
- Simply add Markdown files (or folders) to the `blog` directory.
14
-
15
- Regular blog authors can be added to `authors.yml`.
16
-
17
- The blog post date can be extracted from filenames, such as:
18
-
19
- - `2019-05-30-welcome.md`
20
- - `2019-05-30-welcome/index.md`
21
-
22
- A blog post folder can be convenient to co-locate blog post images:
23
-
24
- ![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg)
25
-
26
- The blog supports tags as well!
27
-
28
- **And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
@@ -1,67 +0,0 @@
1
- ---
2
- slug: release-2-0
3
- title: Release 2.0.0 – Plugin Contracts, CI Automation, Docs
4
- authors: [ali]
5
- tags: [release, changelog]
6
- description: RAG Pipeline Utils v2.0.0 introduces plugin contract validation, full CI coverage, fixture repairs, and professional Docusaurus docs.
7
- ---
8
-
9
- We’re thrilled to announce the release of **RAG Pipeline Utils v2.0.0** — a fully modular and developer-focused toolkit for constructing, validating, and managing RAG (Retrieval-Augmented Generation) pipelines.
10
-
11
- ---
12
-
13
- ## 🚀 Highlights in v2.0.0
14
-
15
- ### 🔌 Contract-Based Plugin Validation
16
-
17
- - Enforces strict interfaces for all core plugin types:
18
- - `loader.load()`
19
- - `embedder.embed()` and `embedQuery()`
20
- - `retriever.store()` and `search()`
21
- - `llm.ask()`
22
- - Includes auto-verification and CLI validation via `scripts/verify-fixture-mocks.js`
23
-
24
- ### 🧪 Enhanced CI & Git Hooks
25
-
26
- - Full GitHub Actions integration:
27
- - Lint, test, mock validation, release automation
28
- - Local enforcement using **Husky** + **validate-changelog-version.js**
29
-
30
- ### 🧰 Fixture Repair & Auto-Mock Utilities
31
-
32
- - Self-healing script: `scripts/repair-fixtures.js`
33
- - Support for regenerating mocks that align with runtime requirements and CLI tests
34
-
35
- ### 📘 Developer Documentation
36
-
37
- - Professionally published Docusaurus site:
38
- - 📚 [Visit Docs](https://devilsdev.github.io/rag-pipeline-utils)
39
- - 🧭 Usage, architecture, CLI, plugin extension, and release notes
40
- - Blog support enabled for continuous release announcements
41
-
42
- ---
43
-
44
- ## ✨ Why This Matters
45
-
46
- This release marks a shift from traditional monolithic RAG stacks to a **fully composable, testable, and CI-validated** toolkit. It was built with long-term developer experience (DX) in mind, emphasizing:
47
-
48
- - 📦 **Modularity**: Swap or add plugins without rewriting pipeline logic
49
- - 🧪 **Test Confidence**: Never publish broken mocks or CI regressions again
50
- - 🧠 **Scalability**: Suitable for both small prototypes and production pipelines
51
- - 🛠 **Tooling First**: Includes everything from config loading to evaluation dashboards
52
-
53
- ---
54
-
55
- ## 📦 Install Now
56
-
57
- ```bash
58
- npm install @devilsdev/rag-pipeline-utils
59
- ```
60
-
61
- ---
62
-
63
- For upgrade guides, contract schemas, and real-world examples, check out the [official docs](https://devilsdev.github.io/rag-pipeline-utils/docs/Introduction).
64
-
65
- 🧠 Built for engineers who want **control, clarity, and composability** in their LLM workflows.
66
-
67
- — *Ali Kahwaji & RAG Pipeline Utils Contributors*
@@ -1,21 +0,0 @@
1
- ---
2
- slug: release-2-0-0
3
- title: Release 2.0.0 — Modular RAG Toolkit
4
- authors: ali
5
- tags: [release, changelog, RAG, developer-tools]
6
- ---
7
-
8
- 🚀 **Version 2.0.0** is now live on [npm](https://www.npmjs.com/package/@devilsdev/rag-pipeline-utils)!
9
-
10
- This release introduces:
11
-
12
- - ✅ Plugin contracts for loaders, embedders, retrievers, LLMs
13
- - 🧪 Fixture mock verification + CLI validation
14
- - 🧰 Auto-repair tooling for broken test mocks
15
- - 📦 Seamless integration with `.ragrc.json` config fallback
16
- - 📄 Docusaurus documentation & blog support
17
- - 💡 Modern DX with versioned docs, coverage, CI hooks, changelogs
18
-
19
- Explore docs → [devilsdev.github.io/rag-pipeline-utils](https://devilsdev.github.io/rag-pipeline-utils)
20
-
21
- 📌 Run `npm install @devilsdev/rag-pipeline-utils` to get started.
@@ -1,13 +0,0 @@
1
- ---
2
- slug: release-v2.1.0
3
- title: ✨ Version v2.1.0 Released
4
- authors: [ali]
5
- tags: [release, changelog]
6
- ---
7
-
8
- RAG Pipeline Utils vv2.1.0 is now available on NPM!
9
-
10
- #### Highlights
11
- - 🚀 Auto-published blog from GitHub release
12
- - 📦 Version bump: v2.1.0
13
- - 📘 See CHANGELOG.md for full details
@@ -1,7 +0,0 @@
1
- import BlogCard from '@site/src/components/BlogCard';
2
-
3
- # 📸 BlogCard Demo
4
-
5
- Here's how a typical BlogCard component appears when used with a cached LQIP-enabled image:
6
-
7
- <BlogCard title="Composable Pipelines in RAG" image="img_hero.jpg" />
@@ -1,17 +0,0 @@
1
- // docs-site/scripts/generate-placeholders.js
2
- import { getPlaiceholder } from 'plaiceholder';
3
- import fs from 'fs/promises';
4
- import path from 'path';
5
-
6
- const images = ['img/hero.jpg', 'img/logo.png'];
7
-
8
- for (const relPath of images) {
9
- const fullPath = path.join(process.cwd(), 'docs-site', 'static', relPath);
10
- const buffer = await fs.readFile(fullPath);
11
- const { base64 } = await getPlaiceholder(buffer);
12
- const outPath = relPath.replace(/\//g, '_') + '.json';
13
- await fs.writeFile(
14
- path.join(process.cwd(), 'docs-site/.plaiceholder-cache', outPath),
15
- JSON.stringify({ base64 })
16
- );
17
- }
@@ -1,40 +0,0 @@
1
- // docs-site/scripts/watch-images.js
2
- // Version: 1.0.0
3
- // Description: Watches /static/img for changes and regenerates Plaiceholder LQIP cache
4
- // Author: Ali Kahwaji
5
-
6
- import { generate } from 'plaiceholder';
7
- import { readdir, readFile, writeFile } from 'fs/promises';
8
- import chokidar from 'chokidar';
9
- import path from 'path';
10
-
11
- const IMG_DIR = path.resolve('docs-site/static/img');
12
- const CACHE_DIR = path.resolve('docs-site/.plaiceholder-cache');
13
-
14
- async function processImage(file) {
15
- const buffer = await readFile(path.join(IMG_DIR, file));
16
- const { base64, img } = await generate(buffer);
17
-
18
- const output = {
19
- base64,
20
- img,
21
- };
22
-
23
- const name = file.replace(/\.(jpg|jpeg|png|webp)/i, '');
24
- await writeFile(path.join(CACHE_DIR, `img_${name}.json`), JSON.stringify(output, null, 2));
25
- console.log(`✅ LQIP generated: img_${name}.json`);
26
- }
27
-
28
- async function run() {
29
- const files = await readdir(IMG_DIR);
30
- for (const file of files) {
31
- if (/\.(jpg|jpeg|png|webp)$/i.test(file)) {
32
- await processImage(file);
33
- }
34
- }
35
-
36
- // Watch for future changes
37
- chokidar.watch(IMG_DIR).on('change', processImage).on('add', processImage);
38
- }
39
-
40
- run();
@@ -1,5 +0,0 @@
1
- {
2
- type: 'link',
3
- label: '📰 Blog',
4
- href: '/blog',
5
- },
@@ -1,31 +0,0 @@
1
- // Version: 1.0.0
2
- // Description: Blog card with image placeholder using Plaiceholder LQIP
3
- // Author: Ali Kahwaji
4
-
5
- import React from 'react';
6
- import styles from './BlogCard.module.css';
7
- import placeholder from '../../.plaiceholder-cache/img_hero.jpg.json'; // ✅ Cached placeholder
8
-
9
- export default function BlogCard({ title }) {
10
- const { base64, img } = placeholder;
11
-
12
- return (
13
- <div className={styles.card}>
14
- <img
15
- src={img.src}
16
- width={img.width}
17
- height={img.height}
18
- alt={title}
19
- loading="lazy"
20
- style={{
21
- objectFit: 'cover',
22
- borderRadius: 12,
23
- background: `url(${base64})`,
24
- backgroundSize: 'cover',
25
- backgroundPosition: 'center',
26
- }}
27
- />
28
- <h3>{title}</h3>
29
- </div>
30
- );
31
- }
@@ -1,34 +0,0 @@
1
- /**
2
- * Version: 1.0.0
3
- * File: BlogCard.stories.jsx
4
- * Description: Storybook entry for BlogCard with image placeholder integration
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- import React from 'react';
9
- import BlogCard from './BlogCard';
10
-
11
- export default {
12
- title: 'Components/BlogCard',
13
- component: BlogCard,
14
- tags: ['autodocs'],
15
- args: {
16
- title: 'Composable Pipelines',
17
- image: 'img/hero.jpg', // Path relative to /static
18
- },
19
- };
20
-
21
- const Template = (args) => <BlogCard {...args} />;
22
-
23
- export const Default = Template.bind({});
24
-
25
- export const CustomTitle = Template.bind({});
26
- CustomTitle.args = {
27
- title: 'Flexible RAG Components',
28
- };
29
-
30
- export const WithAnotherImage = Template.bind({});
31
- WithAnotherImage.args = {
32
- title: 'LLM Plugin Architecture',
33
- image: 'img/social-card.jpg',
34
- };
@@ -1,58 +0,0 @@
1
- // docs-site/src/components/BlogImage.jsx
2
- // Version: 2.0.0
3
- // Description: Resilient image renderer with LQIP blur fallback from Plaiceholder cache
4
- // Author: Ali Kahwaji
5
-
6
- import React from 'react';
7
- import PropTypes from 'prop-types';
8
- import defaultPlaceholder from '../../.plaiceholder-cache/img_hero.jpg.json'; // Default fallback image
9
-
10
- /**
11
- * BlogImage
12
- * Renders an image using precomputed LQIP placeholders
13
- *
14
- * @param {string} srcKey - Cache key (e.g., 'img_logo') without .json
15
- * @param {string} alt - Image alt text
16
- * @param {string} [className] - Optional className for custom styling
17
- * @param {object} [style] - Optional inline styles
18
- * @returns {JSX.Element}
19
- */
20
- export default function BlogImage({ srcKey, alt, className = '', style = {} }) {
21
- let imageData;
22
-
23
- try {
24
- imageData = require(`../../.plaiceholder-cache/${srcKey}.json`);
25
- } catch (e) {
26
- console.warn(`⚠️ LQIP not found for ${srcKey}, using fallback.`);
27
- imageData = defaultPlaceholder;
28
- }
29
-
30
- const { base64, img } = imageData;
31
-
32
- return (
33
- <img
34
- src={img.src}
35
- width={img.width}
36
- height={img.height}
37
- alt={alt}
38
- loading="lazy"
39
- className={className}
40
- style={{
41
- objectFit: 'cover',
42
- background: `url(${base64})`,
43
- backgroundSize: 'cover',
44
- backgroundPosition: 'center',
45
- borderRadius: '12px',
46
- transition: 'opacity 0.3s ease',
47
- ...style,
48
- }}
49
- />
50
- );
51
- }
52
-
53
- BlogImage.propTypes = {
54
- srcKey: PropTypes.string.isRequired,
55
- alt: PropTypes.string.isRequired,
56
- className: PropTypes.string,
57
- style: PropTypes.object,
58
- };
@@ -1,20 +0,0 @@
1
- // Version: 1.0.0
2
- // Description: Storybook story for BlogImage component
3
- // Author: Ali Kahwaji
4
-
5
- import React from 'react';
6
- import BlogImage from '../BlogImage';
7
-
8
- export default {
9
- title: 'Components/BlogImage',
10
- component: BlogImage,
11
- };
12
-
13
- const Template = (args) => <BlogImage {...args} />;
14
-
15
- export const Default = Template.bind({});
16
- Default.args = {
17
- srcKey: 'img_hero',
18
- alt: 'Default placeholder image',
19
- className: '',
20
- };