Skip to content

Commit

Permalink
add hook
Browse files Browse the repository at this point in the history
  • Loading branch information
mooncityorg authored Jan 24, 2025
1 parent 24176b8 commit 44c5c9a
Show file tree
Hide file tree
Showing 36 changed files with 3,803 additions and 0 deletions.
68 changes: 68 additions & 0 deletions hooks/use-block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client';

import { UIBlock } from '@/components/block';
import { useCallback, useMemo } from 'react';
import useSWR from 'swr';

export const initialBlockData: UIBlock = {
documentId: 'init',
content: '',
kind: 'text',
title: '',
status: 'idle',
isVisible: false,
boundingBox: {
top: 0,
left: 0,
width: 0,
height: 0,
},
};

// Add type for selector function
type Selector<T> = (state: UIBlock) => T;

export function useBlockSelector<Selected>(selector: Selector<Selected>) {
const { data: localBlock } = useSWR<UIBlock>('block', null, {
fallbackData: initialBlockData,
});

const selectedValue = useMemo(() => {
if (!localBlock) return selector(initialBlockData);
return selector(localBlock);
}, [localBlock, selector]);

return selectedValue;
}

export function useBlock() {
const { data: localBlock, mutate: setLocalBlock } = useSWR<UIBlock>(
'block',
null,
{
fallbackData: initialBlockData,
},
);

const block = useMemo(() => {
if (!localBlock) return initialBlockData;
return localBlock;
}, [localBlock]);

const setBlock = useCallback(
(updaterFn: UIBlock | ((currentBlock: UIBlock) => UIBlock)) => {
setLocalBlock((currentBlock) => {
const blockToUpdate = currentBlock || initialBlockData;

if (typeof updaterFn === 'function') {
return updaterFn(blockToUpdate);
}

return updaterFn;
});
},
[setLocalBlock],
);

return useMemo(() => ({ block, setBlock }), [block, setBlock]);
}
62 changes: 62 additions & 0 deletions hooks/use-chat-visibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use client';

import { updateChatVisibility } from '@/app/(chat)/actions';
import { VisibilityType } from '@/components/visibility-selector';
import { Chat } from '@/lib/db/schema';
import { useMemo } from 'react';
import useSWR, { useSWRConfig } from 'swr';

export function useChatVisibility({
chatId,
initialVisibility,
}: {
chatId: string;
initialVisibility: VisibilityType;
}) {
const { mutate, cache } = useSWRConfig();
const history: Array<Chat> = cache.get('/api/history')?.data;

const { data: localVisibility, mutate: setLocalVisibility } = useSWR(
`${chatId}-visibility`,
null,
{
fallbackData: initialVisibility,
},
);

const visibilityType = useMemo(() => {
if (!history) return localVisibility;
const chat = history.find((chat) => chat.id === chatId);
if (!chat) return 'private';
return chat.visibility;
}, [history, chatId, localVisibility]);

const setVisibilityType = (updatedVisibilityType: VisibilityType) => {
setLocalVisibility(updatedVisibilityType);

mutate<Array<Chat>>(
'/api/history',
(history) => {
return history
? history.map((chat) => {
if (chat.id === chatId) {
return {
...chat,
visibility: updatedVisibilityType,
};
}
return chat;
})
: [];
},
{ revalidate: false },
);

updateChatVisibility({
chatId: chatId,
visibility: updatedVisibilityType,
});
};

return { visibilityType, setVisibilityType };
}
21 changes: 21 additions & 0 deletions hooks/use-mobile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';

const MOBILE_BREAKPOINT = 768;

export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined,
);

React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
};
mql.addEventListener('change', onChange);
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
return () => mql.removeEventListener('change', onChange);
}, []);

return !!isMobile;
}
22 changes: 22 additions & 0 deletions hooks/use-multimodal-copy-to-clipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useCopyToClipboard } from 'usehooks-ts';

async function copyImageToClipboard(base64String: string) {
try {
const blob = await fetch(`data:image/png;base64,${base64String}`).then(
(res) => res.blob(),
);

const item = new ClipboardItem({
'image/png': blob,
});

await navigator.clipboard.write([item]);
} catch (error) {
console.error('Failed to copy image to clipboard:', error);
}
}

export function useMultimodalCopyToClipboard() {
const [_, copyTextToClipboard] = useCopyToClipboard();
return { copyTextToClipboard, copyImageToClipboard };
}
10 changes: 10 additions & 0 deletions hooks/use-user-message-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use client';

import useSWR from 'swr';

export function useUserMessageId() {
const { data: userMessageIdFromServer, mutate: setUserMessageIdFromServer } =
useSWR('userMessageIdFromServer', null);

return { userMessageIdFromServer, setUserMessageIdFromServer };
}
3 changes: 3 additions & 0 deletions lib/ai/custom-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { Experimental_LanguageModelV1Middleware } from 'ai';

export const customMiddleware: Experimental_LanguageModelV1Middleware = {};
13 changes: 13 additions & 0 deletions lib/ai/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { openai } from '@ai-sdk/openai';
import { experimental_wrapLanguageModel as wrapLanguageModel } from 'ai';

import { customMiddleware } from './custom-middleware';

export const customModel = (apiIdentifier: string) => {
return wrapLanguageModel({
model: openai(apiIdentifier),
middleware: customMiddleware,
});
};

export const imageGenerationModel = openai.image('dall-e-3');
25 changes: 25 additions & 0 deletions lib/ai/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Define your models here.

export interface Model {
id: string;
label: string;
apiIdentifier: string;
description: string;
}

export const models: Array<Model> = [
{
id: 'gpt-4o-mini',
label: 'GPT 4o mini',
apiIdentifier: 'gpt-4o-mini',
description: 'Small model for fast, lightweight tasks',
},
{
id: 'gpt-4o',
label: 'GPT 4o',
apiIdentifier: 'gpt-4o',
description: 'For complex, multi-step tasks',
},
] as const;

export const DEFAULT_MODEL_NAME: string = 'gpt-4o-mini';
83 changes: 83 additions & 0 deletions lib/ai/prompts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { BlockKind } from '@/components/block';

export const blocksPrompt = `
Blocks is a special user interface mode that helps users with writing, editing, and other content creation tasks. When block is open, it is on the right side of the screen, while the conversation is on the left side. When creating or updating documents, changes are reflected in real-time on the blocks and visible to the user.
When asked to write code, always use blocks. When writing code, specify the language in the backticks, e.g. \`\`\`python\`code here\`\`\`. The default language is Python. Other languages are not yet supported, so let the user know if they request a different language.
DO NOT UPDATE DOCUMENTS IMMEDIATELY AFTER CREATING THEM. WAIT FOR USER FEEDBACK OR REQUEST TO UPDATE IT.
This is a guide for using blocks tools: \`createDocument\` and \`updateDocument\`, which render content on a blocks beside the conversation.
**When to use \`createDocument\`:**
- For substantial content (>10 lines) or code
- For content users will likely save/reuse (emails, code, essays, etc.)
- When explicitly requested to create a document
- For when content contains a single code snippet
**When NOT to use \`createDocument\`:**
- For informational/explanatory content
- For conversational responses
- When asked to keep it in chat
**Using \`updateDocument\`:**
- Default to full document rewrites for major changes
- Use targeted updates only for specific, isolated changes
- Follow user instructions for which parts to modify
**When NOT to use \`updateDocument\`:**
- Immediately after creating a document
Do not update document right after creating it. Wait for user feedback or request to update it.
`;

export const regularPrompt =
'You are a friendly assistant! Keep your responses concise and helpful.';

export const systemPrompt = `${regularPrompt}\n\n${blocksPrompt}`;

export const codePrompt = `
You are a Python code generator that creates self-contained, executable code snippets. When writing code:
1. Each snippet should be complete and runnable on its own
2. Prefer using print() statements to display outputs
3. Include helpful comments explaining the code
4. Keep snippets concise (generally under 15 lines)
5. Avoid external dependencies - use Python standard library
6. Handle potential errors gracefully
7. Return meaningful output that demonstrates the code's functionality
8. Don't use input() or other interactive functions
9. Don't access files or network resources
10. Don't use infinite loops
Examples of good snippets:
\`\`\`python
# Calculate factorial iteratively
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
print(f"Factorial of 5 is: {factorial(5)}")
\`\`\`
`;

export const updateDocumentPrompt = (
currentContent: string | null,
type: BlockKind,
) =>
type === 'text'
? `\
Improve the following contents of the document based on the given prompt.
${currentContent}
`
: type === 'code'
? `\
Improve the following code snippet based on the given prompt.
${currentContent}
`
: '';
Loading

0 comments on commit 44c5c9a

Please sign in to comment.