1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-08-12 12:13:58 +02:00

feat: xyflow upgrade (#7803)

* wip

* fix: reset the sizes

* fix: update zustand

* fix: update

* fix: add additional width

* wip

* fix: remove hacky code

* wip

* wip

* wip

* wip

* wip

* fix: try pre-commit

* fix: add check pre-commit

* fix: remove xyflow

* fix: remove unnecessary files

* fix: update packages

* Update scripts/generate-renderer.sh

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
This commit is contained in:
Arik Chakma
2025-04-05 01:33:01 +06:00
committed by GitHub
parent dc2142dde0
commit 2485b716dd
28 changed files with 1633 additions and 1771 deletions

7
.gitignore vendored
View File

@@ -28,9 +28,4 @@ pnpm-debug.log*
/playwright-report/
/playwright/.cache/
tests-examples
*.csv
/editor/*
!/editor/readonly-editor.tsx
!/editor/renderer/renderer.ts
!/editor/renderer/index.tsx
*.csv

View File

@@ -72,4 +72,9 @@ export default defineConfig({
}),
react(),
],
vite: {
ssr: {
noExternal: [/^@roadmapsh\/editor.*$/],
},
},
});

View File

@@ -1,14 +0,0 @@
export function ReadonlyEditor(props: any) {
return (
<div className="fixed bottom-0 left-0 right-0 top-0 z-[9999] border bg-white p-5 text-black">
<h2 className="mb-2 text-xl font-semibold">Private Component</h2>
<p className="mb-4">
Renderer is a private component. If you are a collaborator and have
access to it. Run the following command:
</p>
<code className="mt-5 rounded-md bg-gray-800 p-2 text-white">
npm run generate-renderer
</code>
</div>
);
}

View File

@@ -1,14 +0,0 @@
export function Renderer(props: any) {
return (
<div className="fixed bottom-0 left-0 right-0 top-0 z-[9999] border bg-white p-5 text-black">
<h2 className="mb-2 text-xl font-semibold">Private Component</h2>
<p className="mb-4">
Renderer is a private component. If you are a collaborator and have
access to it. Run the following command:
</p>
<code className="mt-5 rounded-md bg-gray-800 p-2 text-white">
npm run generate-renderer
</code>
</div>
);
}

View File

@@ -1,5 +0,0 @@
export function renderFlowJSON(data: any, options?: any) {
console.warn("renderFlowJSON is not implemented");
console.warn("run the following command to generate the renderer:");
console.warn("> npm run generate-renderer");
}

View File

@@ -26,6 +26,7 @@
"warm:urls": "sh ./scripts/warm-urls.sh https://roadmap.sh/sitemap-0.xml",
"compress:images": "tsx ./scripts/compress-images.ts",
"generate:roadmap-content-json": "tsx ./scripts/editor-roadmap-content-json.ts",
"migrate:editor-roadmaps": "tsx ./scripts/migrate-editor-roadmap.ts",
"test:e2e": "playwright test"
},
"dependencies": {
@@ -38,6 +39,7 @@
"@nanostores/react": "^0.8.0",
"@napi-rs/image": "^1.9.2",
"@resvg/resvg-js": "^2.6.2",
"@roadmapsh/editor": "npm:@roadmapsh/dummy-editor@^0.0.5",
"@tanstack/react-query": "^5.59.16",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
@@ -67,7 +69,6 @@
"react-resizable-panels": "^2.1.7",
"react-textarea-autosize": "^8.5.7",
"react-tooltip": "^5.28.0",
"reactflow": "^11.11.4",
"rehype-external-links": "^3.0.0",
"remark-parse": "^11.0.0",
"roadmap-renderer": "^1.0.6",
@@ -82,7 +83,7 @@
"tiptap-markdown": "^0.8.10",
"turndown": "^7.2.0",
"unified": "^11.0.5",
"zustand": "^4.5.5"
"zustand": "^5.0.1"
},
"devDependencies": {
"@ai-sdk/google": "^1.1.19",

3138
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +0,0 @@
export function Renderer(props: any) {
return (
<div className="fixed bottom-0 left-0 right-0 top-0 z-[9999] border bg-white p-5 text-black">
<h2 className="mb-2 text-xl font-semibold">Private Component</h2>
<p className="mb-4">
Renderer is a private component. If you are a collaborator and have
access to it. Run the following command:
</p>
<code className="mt-5 rounded-md bg-gray-800 p-2 text-white">
npm run generate-renderer
</code>
</div>
);
}

View File

@@ -1,5 +0,0 @@
export function renderFlowJSON(data: any, options?: any) {
console.warn("renderFlowJSON is not implemented");
console.warn("run the following command to generate the renderer:");
console.warn("> npm run generate-renderer");
}

View File

@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from 'reactflow';
import type { Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';

View File

@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Edge, Node } from 'reactflow';
import type { Edge, Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';

View File

@@ -1,7 +1,7 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from 'reactflow';
import type { Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
import { slugify } from '../src/lib/slugger';

View File

@@ -2,33 +2,10 @@
set -e
# ignore cloning if .temp/web-draw already exists
if [ ! -d ".temp/web-draw" ]; then
mkdir -p .temp
git clone git@github.com:roadmapsh/web-draw.git .temp/web-draw
fi
# Fetch the latest commit hash from the GitHub repo
LATEST_COMMIT_HASH=$(git ls-remote https://github.com/roadmapsh/web-draw-v2.git refs/heads/main | awk '{print $1}')
rm -rf editor
mkdir editor
echo "Latest commit hash: $LATEST_COMMIT_HASH"
# copy the files at /src/editor/* to /editor
# while replacing any existing files
cp -rf .temp/web-draw/src/editor/* editor
# Add @ts-nocheck to the top of each ts and tsx file
# so that the typescript compiler doesn't complain
# about the missing types
find editor -type f \( -name "*.ts" -o -name "*.tsx" \) -print0 | while IFS= read -r -d '' file; do
if [ -f "$file" ]; then
echo "// @ts-nocheck" > temp
cat "$file" >> temp
mv temp "$file"
echo "Added @ts-nocheck to $file"
fi
done
# ignore the worktree changes for the editor directory
git update-index --assume-unchanged editor/readonly-editor.tsx || true
git update-index --assume-unchanged editor/renderer/index.tsx || true
git update-index --assume-unchanged editor/renderer/renderer.ts || true
# Install the package using the latest commit hash
pnpm add github:roadmapsh/web-draw#"$LATEST_COMMIT_HASH"\&path:packages/editor

View File

@@ -0,0 +1,76 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Node } from '@roadmapsh/editor';
import matter from 'gray-matter';
import type { RoadmapFrontmatter } from '../src/lib/roadmap';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Directory containing the roadmaps
const ROADMAP_CONTENT_DIR = path.join(__dirname, '../src/data/roadmaps');
const allRoadmaps = await fs.readdir(ROADMAP_CONTENT_DIR);
const editorRoadmapIds = new Set<string>();
for (const roadmapId of allRoadmaps) {
const roadmapFrontmatterDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.md`,
);
const roadmapFrontmatterRaw = await fs.readFile(
roadmapFrontmatterDir,
'utf-8',
);
const { data } = matter(roadmapFrontmatterRaw);
const roadmapFrontmatter = data as RoadmapFrontmatter;
if (roadmapFrontmatter.renderer === 'editor') {
editorRoadmapIds.add(roadmapId);
}
}
for (const roadmapId of editorRoadmapIds) {
const roadmapJSONDir = path.join(
ROADMAP_CONTENT_DIR,
roadmapId,
`${roadmapId}.json`,
);
const roadmapJSONRaw = await fs.readFile(roadmapJSONDir, 'utf-8');
const roadmapJSON = JSON.parse(roadmapJSONRaw);
const roadmapNodes = roadmapJSON.nodes as Node[];
const updatedNodes = roadmapNodes.map((node) => {
const width = +(node?.width || node?.style?.width || 0);
const height = +(node?.height || node?.style?.height || 0);
const ADDITIONAL_WIDTH = 1;
// adding one `1px` in width to avoid the node to be cut in half
// this is a quick fix to avoid the issue
if (node?.style?.width) {
node.style.width = width + ADDITIONAL_WIDTH;
}
if (node?.width) {
node.width = width + ADDITIONAL_WIDTH;
}
return {
...node,
measured: {
width: width + ADDITIONAL_WIDTH,
height,
},
};
});
const updatedRoadmapJSON = {
...roadmapJSON,
nodes: updatedNodes,
};
const updatedRoadmapJSONString = JSON.stringify(updatedRoadmapJSON, null, 2);
await fs.writeFile(roadmapJSONDir, updatedRoadmapJSONString, 'utf-8');
}

View File

@@ -1,4 +1,4 @@
import { ReadonlyEditor } from '../../../editor/readonly-editor';
import { ReadonlyEditor } from '@roadmapsh/editor';
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal';
import {
refreshProgressCounters,
@@ -9,7 +9,7 @@ import {
} from '../../lib/resource-progress';
import { pageProgressMessage } from '../../stores/page';
import { useToast } from '../../hooks/use-toast';
import type { Node } from 'reactflow';
import type { Node } from '@roadmapsh/editor';
import { type MouseEvent, useCallback, useRef, useState } from 'react';
import { EmptyRoadmap } from './EmptyRoadmap';
import { cn } from '../../lib/classname';

View File

@@ -1,6 +1,5 @@
import { useStore } from '@nanostores/react';
import { useEffect, useState } from 'react';
import { cn } from '../../../editor/utils/classname';
import { useParams } from '../../hooks/use-params';
import { useToast } from '../../hooks/use-toast';
import { httpGet } from '../../lib/http';
@@ -13,6 +12,7 @@ import { TeamDashboard } from './TeamDashboard';
import type { QuestionGroupType } from '../../lib/question-group';
import type { GuideFileType } from '../../lib/guide';
import type { VideoFileType } from '../../lib/video';
import { cn } from '../../lib/classname';
type DashboardPageProps = {
builtInRoleRoadmaps?: BuiltInRoadmap[];

View File

@@ -11,8 +11,6 @@ import {
import { httpGet } from '../../lib/http';
import { ProgressNudge } from '../FrameRenderer/ProgressNudge';
import { getUrlParams } from '../../lib/browser.ts';
import { cn } from '../../lib/classname.ts';
import { getUser } from '../../lib/jwt.ts';
type EditorRoadmapProps = {
resourceId: string;

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef } from 'react';
import './EditorRoadmapRenderer.css';
import { lazy, useCallback, useEffect, useRef } from 'react';
import {
renderResourceProgress,
updateResourceProgress,
@@ -9,12 +10,17 @@ import {
} from '../../lib/resource-progress';
import { pageProgressMessage } from '../../stores/page';
import { useToast } from '../../hooks/use-toast';
import type { Edge, Node } from 'reactflow';
import { Renderer } from '../../../editor/renderer';
import type { Edge, Node } from '@roadmapsh/editor';
import { slugify } from '../../lib/slugger';
import { isLoggedIn } from '../../lib/jwt';
import { showLoginPopup } from '../../lib/popup';
const Renderer = lazy(() =>
import('@roadmapsh/editor').then((mod) => ({
default: mod.Renderer,
})),
);
export type RoadmapRendererProps = {
resourceId: string;
nodes: Node[];

View File

@@ -1,6 +1,6 @@
import '../GenerateRoadmap/GenerateRoadmap.css';
import { renderFlowJSON } from '../../../editor/renderer/renderer';
import { generateAIRoadmapFromText } from '../../../editor/utils/roadmap-generator';
import { renderFlowJSON } from '@roadmapsh/editor';
import { generateAIRoadmapFromText } from '@roadmapsh/editor';
import {
generateAICourseRoadmapStructure,
readAIRoadmapStream,

View File

@@ -1,3 +1,5 @@
import './GenerateRoadmap.css';
import {
type FormEvent,
type MouseEvent,
@@ -6,10 +8,8 @@ import {
useRef,
useState,
} from 'react';
import './GenerateRoadmap.css';
import { useToast } from '../../hooks/use-toast';
import { generateAIRoadmapFromText } from '../../../editor/utils/roadmap-generator';
import { renderFlowJSON } from '../../../editor/renderer/renderer';
import { generateAIRoadmapFromText, renderFlowJSON } from '@roadmapsh/editor';
import { replaceChildren } from '../../lib/dom';
import {
isLoggedIn,
@@ -278,6 +278,10 @@ export function GenerateRoadmap(props: GenerateRoadmapProps) {
width: undefined,
height: undefined,
},
measured: {
width: undefined,
height: undefined,
},
})),
edges,
},

View File

@@ -1,7 +1,7 @@
import { ChevronDownIcon, StarIcon, User2Icon } from 'lucide-react';
import { useState } from 'react';
import { cn } from '../../../editor/utils/classname';
import { markdownToHtml } from '../../lib/markdown';
import { cn } from '../../lib/classname';
type Review = {
name: string;

View File

@@ -1,3 +1,4 @@
import '../FrameRenderer/FrameRenderer.css';
import {
useCallback,
useEffect,
@@ -6,7 +7,6 @@ import {
useRef,
} from 'react';
import { Spinner } from '../ReactIcons/Spinner';
import '../FrameRenderer/FrameRenderer.css';
import type { TeamMember } from './TeamProgressPage';
import { httpGet } from '../../lib/http';
import {
@@ -15,13 +15,12 @@ import {
type ResourceType,
updateResourceProgress,
} from '../../lib/resource-progress';
import CloseIcon from '../../icons/close.svg';
import { useToast } from '../../hooks/use-toast';
import { useAuth } from '../../hooks/use-auth';
import { pageProgressMessage } from '../../stores/page';
import type { GetRoadmapResponse } from '../CustomRoadmap/CustomRoadmap';
import { ReadonlyEditor } from '../../../editor/readonly-editor';
import type { Node } from 'reactflow';
import { ReadonlyEditor } from '@roadmapsh/editor';
import type { Node } from '@roadmapsh/editor';
import { useKeydown } from '../../hooks/use-keydown';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { MemberProgressModalHeader } from './MemberProgressModalHeader';

View File

@@ -20,7 +20,7 @@ import { MemberProgressModalHeader } from './MemberProgressModalHeader';
import { replaceChildren } from '../../lib/dom.ts';
import { XIcon } from 'lucide-react';
import type { PageType } from '../CommandMenu/CommandMenu.tsx';
import { renderFlowJSON } from '../../../editor/renderer/renderer.ts';
import { renderFlowJSON } from '@roadmapsh/editor';
import { getResourceMeta } from '../../lib/roadmap.ts';
export type ProgressMapProps = {

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState, type RefObject } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { useKeydown } from '../../hooks/use-keydown';
import { httpGet } from '../../lib/http';
@@ -7,7 +7,7 @@ import { topicSelectorAll } from '../../lib/resource-progress';
import { deleteUrlParam, getUrlParams } from '../../lib/browser';
import { useAuth } from '../../hooks/use-auth';
import type { GetRoadmapResponse } from '../CustomRoadmap/CustomRoadmap';
import { ReadonlyEditor } from '../../../editor/readonly-editor';
import { ReadonlyEditor } from '@roadmapsh/editor';
import { ModalLoader } from './ModalLoader.tsx';
import { UserProgressModalHeader } from './UserProgressModalHeader';
import { X } from 'lucide-react';
@@ -173,7 +173,7 @@ export function UserCustomProgressModal(props: ProgressMapProps) {
variant="modal"
roadmap={roadmap!}
className="min-h-[400px]"
onRendered={(wrapperRef: RefObject<HTMLDivElement>) => {
onRendered={(wrapperRef) => {
const {
done = [],
learning = [],

View File

@@ -12,7 +12,7 @@ import { ModalLoader } from './ModalLoader.tsx';
import { UserProgressModalHeader } from './UserProgressModalHeader';
import { X } from 'lucide-react';
import type { AllowedRoadmapRenderer } from '../../lib/roadmap.ts';
import { renderFlowJSON } from '../../../editor/renderer/renderer.ts';
import { renderFlowJSON } from '@roadmapsh/editor';
export type ProgressMapProps = {
userId?: string;

View File

@@ -8,7 +8,7 @@ import {
import { useToast } from '../../hooks/use-toast';
import { replaceChildren } from '../../lib/dom.ts';
import type { GetUserProfileRoadmapResponse } from '../../api/user.ts';
import { ReadonlyEditor } from '../../../editor/readonly-editor.tsx';
import { ReadonlyEditor } from '@roadmapsh/editor';
import { cn } from '../../lib/classname.ts';
export type UserProfileRoadmapRendererProps = GetUserProfileRoadmapResponse & {
@@ -96,7 +96,7 @@ export function UserProfileRoadmapRenderer(
edges,
}}
className="min-h-[1000px]"
onRendered={(wrapperRef: RefObject<HTMLDivElement>) => {
onRendered={(wrapperRef) => {
done?.forEach((topicId: string) => {
topicSelectorAll(topicId, wrapperRef?.current!).forEach(
(el) => {

View File

@@ -1,4 +1,6 @@
---
import '../styles/global.css';
import Analytics from '../components/Analytics/Analytics.astro';
import LoginPopup from '../components/AuthenticationFlow/LoginPopup.astro';
import Authenticator from '../components/Authenticator/Authenticator.astro';
@@ -10,9 +12,9 @@ import { PageProgress } from '../components/PageProgress';
import { Toaster } from '../components/Toast';
import { PageSponsors } from '../components/PageSponsors/PageSponsors';
import { siteConfig } from '../lib/config';
import '../styles/global.css';
import { PageVisit } from '../components/PageVisit/PageVisit';
import type { ResourceType } from '../lib/resource-progress';
import ChangelogBanner from '../components/ChangelogBanner.astro';
import Clarity from '../components/Analytics/Clarity.astro';
import RedditPixel from '../components/Analytics/RedditPixel.astro';
import GoogleAd from '../components/Analytics/GoogleAd.astro';

View File

@@ -1,7 +1,28 @@
@import '@roadmapsh/editor/style.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
--color-gray-200: oklch(0.928 0.006 264.531);
border-color: var(--color-gray-200, currentColor);
}
}
@layer components {
.container {
@apply mx-auto !max-w-[830px] px-4;