From 505186537e3d6db72f24d41ac83f6ebdde7b59a3 Mon Sep 17 00:00:00 2001 From: Joel Hooks Date: Mon, 8 Jan 2024 08:44:45 -0800 Subject: [PATCH] Merge pull request #49 * chore: single codemirror component --- .../app/{tips => }/_components/codemirror.tsx | 13 +- .../app/articles/_components/codemirror.tsx | 157 ------------------ .../_components/edit-article-form.tsx | 2 +- .../src/app/articles/page.tsx | 42 +++++ .../app/tips/_components/edit-tip-form.tsx | 2 +- .../inngest/functions/ai/feedback-markers.ts | 8 +- .../src/inngest/functions/articles/chat.ts | 9 + 7 files changed, 69 insertions(+), 164 deletions(-) rename apps/course-builder-web/src/app/{tips => }/_components/codemirror.tsx (92%) delete mode 100644 apps/course-builder-web/src/app/articles/_components/codemirror.tsx create mode 100644 apps/course-builder-web/src/app/articles/page.tsx diff --git a/apps/course-builder-web/src/app/tips/_components/codemirror.tsx b/apps/course-builder-web/src/app/_components/codemirror.tsx similarity index 92% rename from apps/course-builder-web/src/app/tips/_components/codemirror.tsx rename to apps/course-builder-web/src/app/_components/codemirror.tsx index fe9195aaf..122fd13ca 100644 --- a/apps/course-builder-web/src/app/tips/_components/codemirror.tsx +++ b/apps/course-builder-web/src/app/_components/codemirror.tsx @@ -13,7 +13,7 @@ import {useCallback, useEffect, useState} from 'react' import {markdown} from '@codemirror/lang-markdown' import YPartyKitProvider from 'y-partykit/provider' import {useSession} from 'next-auth/react' -import {SearchCursor} from '@codemirror/search' +import {SearchCursor, SearchQuery} from '@codemirror/search' import {Decoration} from '@codemirror/view' import {FeedbackMarker} from '@/lib/feedback-marker' @@ -145,14 +145,20 @@ const useCodemirror = ({ for (let marker of markers) { let cursor = new SearchCursor(view.state.doc, marker.originalText) + let query = new SearchQuery({ + search: marker.originalText, + caseSensitive: false, + }) + + console.log('-----', query.getCursor(view.state.doc).next()) + while (!cursor.done) { cursor.next() + console.log('+++++', query.getCursor(view.state.doc).next()) const highlight_decoration = Decoration.mark({ attributes: {style: 'background-color: yellow'}, }) - console.log({cursor, highlight_decoration}) - view.dispatch({ effects: highlight_effect.of([ highlight_decoration.range( @@ -198,6 +204,7 @@ const useCodemirror = ({ // Set up awareness return () => { + provider?.doc?.destroy() provider?.destroy() view?.destroy() } diff --git a/apps/course-builder-web/src/app/articles/_components/codemirror.tsx b/apps/course-builder-web/src/app/articles/_components/codemirror.tsx deleted file mode 100644 index 7e388b2df..000000000 --- a/apps/course-builder-web/src/app/articles/_components/codemirror.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import {env} from '@/env.mjs' -import * as Y from 'yjs' -import {yCollab} from 'y-codemirror.next' -import {basicSetup, EditorView} from 'codemirror' -import {EditorState, Extension} from '@codemirror/state' -import {useCallback, useEffect, useState} from 'react' -import {markdown} from '@codemirror/lang-markdown' -import YPartyKitProvider from 'y-partykit/provider' -import {useSession} from 'next-auth/react' - -export const CodemirrorEditor = ({ - roomName, - value, - onChange, - markers, -}: { - roomName: string - value: string - onChange: (data: any) => void - markers?: any[] -}) => { - const {codemirrorElementRef} = useCodemirror({ - roomName, - value, - onChange, - markers, - }) - - return ( -
-
-
- ) -} - -const CourseBuilderEditorTheme = { - '.cm-content, .cm-gutter': { - minHeight: '100%', - height: '100%', - }, - '.cm-content': { - padding: '2rem 0', - fontSize: '14px', - fontFamily: 'var(--font-mono)', - }, - '.cm-line': { - padding: '0 2rem', - }, - '.cm-gutters': { - backgroundColor: 'hsl(var(--background))', - borderRight: 'none', - opacity: 0.35, - }, - '.cm-activeLineGutter': { - backgroundColor: 'transparent', - }, - '.cm-lineNumbers': { - fontSize: '10px', - }, - '&.cm-focused': { - outline: 'none', - }, - '.cm-activeLine': { - backgroundColor: 'hsl(var(--foreground) / 3%)', - }, -} - -const styles: Extension[] = [ - EditorView.theme(CourseBuilderEditorTheme), - EditorView.lineWrapping, - EditorView.domEventHandlers({}), -] - -/** - * @see {@link https://codemirror.net/docs/ref/#codemirror.basicSetup Code Mirror Basic Setup} - * @param options - * @constructor - */ -const useCodemirror = ({ - roomName, - value, - onChange, - markers, -}: { - roomName: string - value: string - onChange: (data: any) => void - markers?: any[] -}) => { - const [element, setElement] = useState() - const [yUndoManager, setYUndoManager] = useState() - - const {data: session} = useSession() - - useEffect(() => { - let view: EditorView - - let provider = new YPartyKitProvider( - env.NEXT_PUBLIC_PARTY_KIT_URL, - roomName, - ) - - if (!element) { - return - } - - const ytext = provider.doc.getText('codemirror') - - const undoManager = new Y.UndoManager(ytext) - setYUndoManager(undoManager) - - let updateListenerExtension = EditorView.updateListener.of((update) => { - if (update.docChanged) { - onChange(update.state.doc.toString()) - } - }) - - const awareness = provider.awareness - - if (session) { - awareness.setLocalStateField('user', { - ...session.user, - color: '#ffb61e', // should be a hex color - }) - } - - // Set up CodeMirror and extensions - const state = EditorState.create({ - doc: ytext.toString(), - extensions: [ - basicSetup, - updateListenerExtension, - markdown(), - yCollab(ytext, provider.awareness, {undoManager}), - ...styles, - ], - }) - - // Attach CodeMirror to element - view = new EditorView({ - state, - parent: element, - }) - - return () => { - provider?.destroy() - view?.destroy() - } - }, [element, roomName, value, session]) - - return { - codemirrorElementRef: useCallback((node: HTMLElement | null) => { - if (!node) return - setElement(node) - }, []), - } -} diff --git a/apps/course-builder-web/src/app/articles/_components/edit-article-form.tsx b/apps/course-builder-web/src/app/articles/_components/edit-article-form.tsx index c791642fe..6ef372614 100644 --- a/apps/course-builder-web/src/app/articles/_components/edit-article-form.tsx +++ b/apps/course-builder-web/src/app/articles/_components/edit-article-form.tsx @@ -22,7 +22,6 @@ import {api} from '@/trpc/react' import {useRouter} from 'next/navigation' import {ArticleAssistant} from './article-assistant' import Link from 'next/link' -import {CodemirrorEditor} from './codemirror' import {ImagePlusIcon, ZapIcon} from 'lucide-react' import {CloudinaryUploadWidget} from './cloudinary-upload-widget' import {CloudinaryMediaBrowser} from './cloudinary-media-browser' @@ -30,6 +29,7 @@ import {cn} from '@/lib/utils' import {Article} from '@/lib/articles' import {useSocket} from '@/hooks/use-socket' import {FeedbackMarker} from '@/lib/feedback-marker' +import {CodemirrorEditor} from '@/app/_components/codemirror' const ArticleFormSchema = z.object({ title: z.string().min(2).max(90), diff --git a/apps/course-builder-web/src/app/articles/page.tsx b/apps/course-builder-web/src/app/articles/page.tsx new file mode 100644 index 000000000..cf36aac2e --- /dev/null +++ b/apps/course-builder-web/src/app/articles/page.tsx @@ -0,0 +1,42 @@ +import {CardTitle, CardHeader, CardContent, Card} from '@coursebuilder/ui' +import {getServerAuthSession} from '@/server/auth' +import {getAbility} from '@/lib/ability' +import * as React from 'react' +import Link from 'next/link' +import {sanityQuery} from '@/server/sanity.server' + +export default async function ArticlesIndexPage() { + const session = await getServerAuthSession() + const ability = getAbility({user: session?.user}) + const articles = await sanityQuery< + { + _id: string + title: string + slug: string + }[] + >(`*[_type == "article"]{ + _id, + title, + "slug": slug.current, + }`) + + return ( +
+
+

Articles

+ {articles.map((article) => ( + + + + + {article.title} + + + + + + ))} +
+
+ ) +} diff --git a/apps/course-builder-web/src/app/tips/_components/edit-tip-form.tsx b/apps/course-builder-web/src/app/tips/_components/edit-tip-form.tsx index 49abd4656..f1bbc8c65 100644 --- a/apps/course-builder-web/src/app/tips/_components/edit-tip-form.tsx +++ b/apps/course-builder-web/src/app/tips/_components/edit-tip-form.tsx @@ -24,13 +24,13 @@ import {useRouter} from 'next/navigation' import {type Tip} from '@/lib/tips' import {TipAssistant} from './tip-assistant' import Link from 'next/link' -import {CodemirrorEditor} from '@/app/tips/_components/codemirror' import {ImagePlusIcon, ZapIcon} from 'lucide-react' import {CloudinaryUploadWidget} from './cloudinary-upload-widget' import {CloudinaryMediaBrowser} from './cloudinary-media-browser' import {cn} from '@/lib/utils' import {FeedbackMarker} from '@/lib/feedback-marker' import {useSocket} from '@/hooks/use-socket' +import {CodemirrorEditor} from '@/app/_components/codemirror' const NewTipFormSchema = z.object({ title: z.string().min(2).max(90), diff --git a/apps/course-builder-web/src/inngest/functions/ai/feedback-markers.ts b/apps/course-builder-web/src/inngest/functions/ai/feedback-markers.ts index 579206675..f227ec659 100644 --- a/apps/course-builder-web/src/inngest/functions/ai/feedback-markers.ts +++ b/apps/course-builder-web/src/inngest/functions/ai/feedback-markers.ts @@ -11,7 +11,7 @@ export const generateFeedbackMarkers = inngest.createFunction( name: 'Generate Feedback Markers', debounce: { key: 'event.data.resourceId', - period: '5s', + period: '15s', }, }, {event: BODY_TEXT_UPDATED}, @@ -47,6 +47,10 @@ export const generateFeedbackMarkers = inngest.createFunction( * don't waste our time with feedback that is not actionable * harsh feedback is OK, no need to spare our feelings * act like the authority that you are + * do keep the feedback premium and useful + * do exclude feedback that is marginal or not actionable + * focus on ideas over grammar + * limit the feedback to the the top three issues ## Feedback Marker Zod Schema @@ -72,7 +76,7 @@ export const generateFeedbackMarkers = inngest.createFunction( ${JSON.stringify(event.data.currentFeedback)} - ## Template\n\n{markers:[{"originalText": "quoted from the text", "feedback": "useful feedback", "fullSuggestedChange": "rewritten text that captures the feedback meaningfully", "level": "critical", "type": "grammar"}]}`, + ## Template\n\n{markers:[{"originalText": "quoted from the text", "feedback": "useful feedback", "fullSuggestedChange": "rewritten text that captures the feedback meaningfully", "level": "how much attention is needed?", "type": "a single word that describes the type of change"}]}`, role: 'system', }, { diff --git a/apps/course-builder-web/src/inngest/functions/articles/chat.ts b/apps/course-builder-web/src/inngest/functions/articles/chat.ts index ca517ff74..fd1940769 100644 --- a/apps/course-builder-web/src/inngest/functions/articles/chat.ts +++ b/apps/course-builder-web/src/inngest/functions/articles/chat.ts @@ -60,6 +60,15 @@ export const articleChat = inngest.createFunction( }) if (messages.length === 1 && systemPrompt) { + if (event.data.currentFeedback) { + messages = [ + { + content: JSON.stringify(event.data.currentFeedback), + role: 'assistant', + }, + ...messages, + ] + } messages = [systemPrompt, ...messages] }