-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add trpc and remove some callbacks * can send messages + change to relative import for types (dunno why that works) * create navigate to chat page * messages can stream in fine * stream down db chat messages * chat page ui input * grows from top expands from bottom * fix scrolling * remark markdown * uselayouteffect scroll adjustment
- Loading branch information
1 parent
623ab37
commit 0ba2265
Showing
18 changed files
with
1,039 additions
and
224 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { DBChat } from '@repo/db'; | ||
import { DateTime } from 'luxon'; | ||
import { ulid } from 'ulid'; | ||
import { publicProcedure } from '../../trpc'; | ||
|
||
export const createChat = publicProcedure.mutation(async ({ ctx }) => { | ||
const newChat: DBChat = { | ||
id: ulid(), | ||
userID: ctx.user.id, | ||
previewName: '', | ||
createdAt: DateTime.now().toJSDate(), | ||
updatedAt: DateTime.now().toJSDate(), | ||
}; | ||
return newChat; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
import { router } from '@/trpc/trpc'; | ||
import { router } from '../../trpc'; | ||
import { createChat } from './createChat'; | ||
import { sendMessage } from './sendMessage'; | ||
|
||
export const chatRouter = router({ | ||
sendMessage, | ||
createChat, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import { editorView, editorViewCtx } from '@milkdown/core'; | ||
import type { Ctx } from '@milkdown/ctx'; | ||
import { TextSelection } from '@milkdown/prose/state'; | ||
import { MilkdownProvider, useInstance } from '@milkdown/react'; | ||
import { getMarkdown } from '@milkdown/utils'; | ||
import { ArrowUp } from 'lucide-react'; | ||
import type React from 'react'; | ||
import { useCallback, useEffect, useState } from 'react'; | ||
import { MilkdownEditor } from './Milkdown/Milkdown'; | ||
import { Button } from './ui/button'; | ||
|
||
type InputBoxProps = { | ||
handleSubmit: (text: string) => void; | ||
placeholderText: string; | ||
}; | ||
|
||
const InputBox: React.FC<InputBoxProps> = (props) => { | ||
return ( | ||
<MilkdownProvider> | ||
<InputBoxInner {...props} /> | ||
</MilkdownProvider> | ||
); | ||
}; | ||
|
||
const InputBoxInner: React.FC<InputBoxProps> = ({ | ||
handleSubmit, | ||
placeholderText, | ||
}) => { | ||
const [loading, getEditor] = useInstance(); | ||
const editorAction = useCallback( | ||
(fn: (ctx: Ctx) => void) => { | ||
if (loading) return null; | ||
return getEditor().action(fn); | ||
}, | ||
[loading, getEditor], | ||
); | ||
const [inputContent, setInputContent] = useState(''); | ||
|
||
const _handleSubmit = useCallback((): void => { | ||
editorAction((ctx) => { | ||
const md = getMarkdown()(ctx); | ||
handleSubmit(md); | ||
|
||
const view = ctx.get(editorViewCtx); | ||
const { tr } = view.state; | ||
tr.delete(0, view.state.doc.content.size); | ||
view.dispatch(tr); | ||
|
||
setInputContent(''); | ||
}); | ||
}, [handleSubmit, editorAction]); | ||
|
||
useEffect(() => { | ||
let cleanup: (() => void) | undefined; | ||
|
||
const setupEditor = () => { | ||
const handleContentUpdated = () => | ||
editorAction((ctx) => { | ||
const md = getMarkdown()(ctx); | ||
setInputContent(md); | ||
}); | ||
|
||
const handleKeyUp = (e: KeyboardEvent) => { | ||
handleContentUpdated(); | ||
}; | ||
const handleKeyDown = (e: KeyboardEvent) => { | ||
if ( | ||
e.key === 'Enter' && | ||
!e.shiftKey && | ||
!e.ctrlKey && | ||
!e.altKey && | ||
!e.metaKey | ||
) { | ||
e.preventDefault(); | ||
_handleSubmit(); | ||
} | ||
}; | ||
|
||
editorAction((ctx) => { | ||
const view = ctx.get(editorViewCtx); | ||
view.dom.addEventListener('keydown', handleKeyDown); | ||
view.dom.addEventListener('keyup', handleKeyUp); | ||
cleanup = () => { | ||
view.dom.removeEventListener('keydown', handleKeyDown); | ||
view.dom.removeEventListener('keyup', handleKeyUp); | ||
}; | ||
}); | ||
|
||
handleContentUpdated(); | ||
}; | ||
|
||
if (!loading) { | ||
setupEditor(); | ||
} | ||
|
||
return () => { | ||
if (cleanup) { | ||
cleanup(); | ||
} | ||
}; | ||
}, [loading, editorAction, _handleSubmit]); | ||
|
||
const focusOnEditor = useCallback(() => { | ||
editorAction((ctx) => { | ||
const view = ctx.get(editorViewCtx); | ||
const { state } = view; | ||
if (!state.selection) { | ||
const selection = TextSelection.create(state.doc, 1); | ||
view.focus(); | ||
view.dispatch(state.tr.setSelection(selection)); | ||
} else { | ||
view.focus(); | ||
} | ||
}); | ||
}, [editorAction]); | ||
|
||
useEffect(() => { | ||
focusOnEditor(); | ||
}, [focusOnEditor]); | ||
|
||
return ( | ||
// biome-ignore lint/a11y/useKeyWithClickEvents: This is technically a text input | ||
<div | ||
className="flex items-start w-full h-full overflow-y-scroll bg-muted p-2" | ||
onClick={() => focusOnEditor()} | ||
> | ||
<MilkdownEditor placeholderText={placeholderText} /> | ||
<div className="w-8 relative"> | ||
<SubmitButton | ||
onSubmit={_handleSubmit} | ||
show={inputContent.length > 0} | ||
/> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
type SubmitButtonProps = { | ||
onSubmit: () => void; | ||
show: boolean; | ||
}; | ||
const SubmitButton: React.FC<SubmitButtonProps> = ({ onSubmit, show }) => { | ||
return ( | ||
<Button | ||
// We only want to show the submit button if there is content in the box. | ||
className={` | ||
transition-all duration-200 ease-in-out | ||
${show ? 'scale-100 opacity-100' : 'scale-0 opacity-0'} | ||
fixed w-8 h-8 p-0 rounded-xl bg-primary text-primary-foreground flex items-center justify-center | ||
`} | ||
onClick={onSubmit} | ||
> | ||
<ArrowUp className="w-4 h-4" /> | ||
</Button> | ||
); | ||
}; | ||
|
||
export default InputBox; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.