feat: implement edit generated roadmap

ai/roadmap
Arik Chakma 11 months ago committed by Kamran Ahmed
parent 59bb3fc2da
commit dfbb6a6dba
  1. 63
      src/components/GenerateRoadmap/GenerateRoadmap.tsx
  2. 13
      src/helper/read-stream.ts

@ -11,6 +11,8 @@ import { RoadmapSearch } from './RoadmapSearch.tsx';
import { Spinner } from '../ReactIcons/Spinner.tsx'; import { Spinner } from '../ReactIcons/Spinner.tsx';
import { Download, PenSquare, Wand } from 'lucide-react'; import { Download, PenSquare, Wand } from 'lucide-react';
import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx'; import { ShareRoadmapButton } from '../ShareRoadmapButton.tsx';
import { httpPost } from '../../lib/http.ts';
import { pageProgressMessage } from '../../stores/page.ts';
export function GenerateRoadmap() { export function GenerateRoadmap() {
const roadmapContainerRef = useRef<HTMLDivElement>(null); const roadmapContainerRef = useRef<HTMLDivElement>(null);
@ -20,6 +22,8 @@ export function GenerateRoadmap() {
const [hasSubmitted, setHasSubmitted] = useState<boolean>(false); const [hasSubmitted, setHasSubmitted] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [roadmapTopic, setRoadmapTopic] = useState(''); const [roadmapTopic, setRoadmapTopic] = useState('');
const [nodes, setNodes] = useState<any[]>([]);
const [edges, setEdges] = useState<any[]>([]);
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => { const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
@ -66,17 +70,58 @@ export function GenerateRoadmap() {
return; return;
} }
await readAIRoadmapStream(reader, async (result) => { await readAIRoadmapStream(reader, {
const { nodes, edges } = generateAIRoadmapFromText(result); onStream: async (result) => {
const svg = await renderFlowJSON({ nodes, edges }); const { nodes, edges } = generateAIRoadmapFromText(result);
if (roadmapContainerRef?.current) { const svg = await renderFlowJSON({ nodes, edges });
replaceChildren(roadmapContainerRef?.current, svg); if (roadmapContainerRef?.current) {
} replaceChildren(roadmapContainerRef?.current, svg);
}
},
onStreamEnd: async (result) => {
const { nodes, edges } = generateAIRoadmapFromText(result);
setNodes(
nodes.map((node) => ({
...node,
// To reset the width and height of the node
// so that it can be calculated based on the content in the editor
width: undefined,
height: undefined,
style: {
...node.style,
width: undefined,
height: undefined,
},
})),
);
setEdges(edges);
},
}); });
setIsLoading(false); setIsLoading(false);
}; };
const editGeneratedRoadmap = async () => {
pageProgressMessage.set('Redirecting to Editor');
const { response, error } = await httpPost<{
roadmapId: string;
}>(`${import.meta.env.PUBLIC_API_URL}/v1-edit-ai-generated-roadmap`, {
title: roadmapTopic,
nodes,
edges,
});
if (error || !response) {
toast.error(error?.message || 'Something went wrong');
setIsLoading(false);
return;
}
window.location.href = `${import.meta.env.PUBLIC_EDITOR_APP_URL}/${response.roadmapId}`;
};
if (!hasSubmitted) { if (!hasSubmitted) {
return ( return (
<RoadmapSearch <RoadmapSearch
@ -139,7 +184,11 @@ export function GenerateRoadmap() {
pageUrl={'https://roadmap.sh'} pageUrl={'https://roadmap.sh'}
/> />
</div> </div>
<button className="inline-flex items-center justify-center gap-2 rounded-md bg-gray-200 py-1.5 pl-2.5 pr-3 text-xs font-medium text-black transition-colors transition-opacity duration-300 hover:bg-gray-300 sm:text-sm"> <button
className="inline-flex items-center justify-center gap-2 rounded-md bg-gray-200 py-1.5 pl-2.5 pr-3 text-xs font-medium text-black transition-colors transition-opacity duration-300 hover:bg-gray-300 sm:text-sm"
onClick={editGeneratedRoadmap}
disabled={isLoading}
>
<PenSquare size={15} /> <PenSquare size={15} />
Edit in Editor Edit in Editor
</button> </button>

@ -2,7 +2,13 @@ const NEW_LINE = '\n'.charCodeAt(0);
export async function readAIRoadmapStream( export async function readAIRoadmapStream(
reader: ReadableStreamDefaultReader<Uint8Array>, reader: ReadableStreamDefaultReader<Uint8Array>,
renderRoadmap: (roadmap: string) => void, {
onStream,
onStreamEnd,
}: {
onStream?: (roadmap: string) => void;
onStreamEnd?: (roadmap: string) => void;
},
) { ) {
const decoder = new TextDecoder('utf-8'); const decoder = new TextDecoder('utf-8');
let result = ''; let result = '';
@ -21,7 +27,7 @@ export async function readAIRoadmapStream(
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
if (value[i] === NEW_LINE) { if (value[i] === NEW_LINE) {
result += decoder.decode(value.slice(start, i + 1)); result += decoder.decode(value.slice(start, i + 1));
renderRoadmap(result); onStream?.(result);
start = i + 1; start = i + 1;
} }
} }
@ -31,6 +37,7 @@ export async function readAIRoadmapStream(
} }
} }
renderRoadmap(result); onStream?.(result);
onStreamEnd?.(result);
reader.releaseLock(); reader.releaseLock();
} }

Loading…
Cancel
Save