+
)}
diff --git a/components/suggested-actions.tsx b/components/suggested-actions.tsx
index c561475..7fbc03d 100644
--- a/components/suggested-actions.tsx
+++ b/components/suggested-actions.tsx
@@ -21,9 +21,9 @@ function PureSuggestedActions({ chatId, append }: SuggestedActionsProps) {
action: 'What is the weather in San Francisco?',
},
{
- title: 'Help me draft an essay',
- label: 'about Silicon Valley',
- action: 'Help me draft a short essay about Silicon Valley',
+ title: 'Help me write code to',
+ label: 'demonstrate the djikstra algorithm!',
+ action: 'Help me write code to demonstrate the djikstra algorithm!',
},
];
diff --git a/components/suggestion.tsx b/components/suggestion.tsx
index 4cbf66d..e0374a5 100644
--- a/components/suggestion.tsx
+++ b/components/suggestion.tsx
@@ -8,13 +8,17 @@ import type { UISuggestion } from '@/lib/editor/suggestions';
import { CrossIcon, MessageIcon } from './icons';
import { Button } from './ui/button';
+import { cn } from '@/lib/utils';
+import { BlockKind } from './block';
export const Suggestion = ({
suggestion,
onApply,
+ blockKind,
}: {
suggestion: UISuggestion;
onApply: () => void;
+ blockKind: BlockKind;
}) => {
const [isExpanded, setIsExpanded] = useState(false);
const { width: windowWidth } = useWindowSize();
@@ -23,7 +27,10 @@ export const Suggestion = ({
{!isExpanded ? (
{
setIsExpanded(true);
}}
@@ -34,7 +41,7 @@ export const Suggestion = ({
) : (
+> = {
+ text: [
+ {
+ type: 'final-polish',
+ description: 'Add final polish',
+ icon: ,
+ },
+ {
+ type: 'adjust-reading-level',
+ description: 'Adjust reading level',
+ icon: ,
+ },
+ {
+ type: 'request-suggestions',
+ description: 'Request suggestions',
+ icon: ,
+ },
+ ],
+ code: [
+ {
+ type: 'add-comments',
+ description: 'Add comments',
+ icon: ,
+ },
+ {
+ type: 'add-logs',
+ description: 'Add logs',
+ icon: ,
+ },
+ ],
+};
+
export const Tools = ({
isToolbarVisible,
selectedTool,
@@ -267,6 +336,7 @@ export const Tools = ({
append,
isAnimating,
setIsToolbarVisible,
+ blockKind,
}: {
isToolbarVisible: boolean;
selectedTool: string | null;
@@ -277,7 +347,10 @@ export const Tools = ({
) => Promise;
isAnimating: boolean;
setIsToolbarVisible: Dispatch>;
+ blockKind: 'text' | 'code';
}) => {
+ const [primaryTool, ...secondaryTools] = toolsByBlockKind[blockKind];
+
return (
- {isToolbarVisible && (
- <>
+ {isToolbarVisible &&
+ secondaryTools.map((secondaryTool) => (
}
+ key={secondaryTool.type}
+ type={secondaryTool.type}
+ description={secondaryTool.description}
+ icon={secondaryTool.icon}
selectedTool={selectedTool}
setSelectedTool={setSelectedTool}
append={append}
isAnimating={isAnimating}
/>
-
- }
- selectedTool={selectedTool}
- setSelectedTool={setSelectedTool}
- append={append}
- isAnimating={isAnimating}
- />
- >
- )}
+ ))}
}
+ type={primaryTool.type}
+ description={primaryTool.description}
+ icon={primaryTool.icon}
selectedTool={selectedTool}
setSelectedTool={setSelectedTool}
isToolbarVisible={isToolbarVisible}
@@ -333,6 +396,7 @@ const PureToolbar = ({
isLoading,
stop,
setMessages,
+ blockKind,
}: {
isToolbarVisible: boolean;
setIsToolbarVisible: Dispatch>;
@@ -343,6 +407,7 @@ const PureToolbar = ({
) => Promise;
stop: () => void;
setMessages: Dispatch>;
+ blockKind: 'text' | 'code';
}) => {
const toolbarRef = useRef(null);
const timeoutRef = useRef>();
@@ -404,7 +469,7 @@ const PureToolbar = ({
: {
opacity: 1,
y: 0,
- height: 3 * 45,
+ height: toolsByBlockKind[blockKind].length * 47,
transition: { delay: 0 },
scale: 1,
}
@@ -461,6 +526,7 @@ const PureToolbar = ({
selectedTool={selectedTool}
setIsToolbarVisible={setIsToolbarVisible}
setSelectedTool={setSelectedTool}
+ blockKind={blockKind}
/>
)}
@@ -471,6 +537,7 @@ const PureToolbar = ({
export const Toolbar = memo(PureToolbar, (prevProps, nextProps) => {
if (prevProps.isLoading !== nextProps.isLoading) return false;
if (prevProps.isToolbarVisible !== nextProps.isToolbarVisible) return false;
+ if (prevProps.blockKind !== nextProps.blockKind) return false;
return true;
});
diff --git a/components/use-block-stream.tsx b/components/use-block-stream.tsx
index 1386f4c..e9c8892 100644
--- a/components/use-block-stream.tsx
+++ b/components/use-block-stream.tsx
@@ -4,18 +4,20 @@ import { useSWRConfig } from 'swr';
import type { Suggestion } from '@/lib/db/schema';
-import type { UIBlock } from './block';
+import type { BlockKind, UIBlock } from './block';
import { useUserMessageId } from '@/hooks/use-user-message-id';
type StreamingDelta = {
type:
| 'text-delta'
+ | 'code-delta'
| 'title'
| 'id'
| 'suggestion'
| 'clear'
| 'finish'
- | 'user-message-id';
+ | 'user-message-id'
+ | 'kind';
content: string | Suggestion;
};
@@ -67,6 +69,12 @@ export function useBlockStream({
title: delta.content as string,
};
+ case 'kind':
+ return {
+ ...draftBlock,
+ kind: delta.content as BlockKind,
+ };
+
case 'text-delta':
return {
...draftBlock,
@@ -80,6 +88,19 @@ export function useBlockStream({
status: 'streaming',
};
+ case 'code-delta':
+ return {
+ ...draftBlock,
+ content: delta.content as string,
+ isVisible:
+ draftBlock.status === 'streaming' &&
+ draftBlock.content.length > 20 &&
+ draftBlock.content.length < 30
+ ? true
+ : draftBlock.isVisible,
+ status: 'streaming',
+ };
+
case 'suggestion':
setTimeout(() => {
setOptimisticSuggestions((currentSuggestions) => [
@@ -107,5 +128,5 @@ export function useBlockStream({
return draftBlock;
}
});
- }, [streamingData, setBlock]);
+ }, [streamingData, setBlock, setUserMessageIdFromServer]);
}
diff --git a/lib/ai/prompts.ts b/lib/ai/prompts.ts
index c9eba60..a0d20a2 100644
--- a/lib/ai/prompts.ts
+++ b/lib/ai/prompts.ts
@@ -1,12 +1,15 @@
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 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.
+
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)
+ - 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
@@ -25,3 +28,37 @@ 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) => `\
+Update the following contents of the document based on the given prompt.
+
+${currentContent}
+`;
diff --git a/lib/db/migrations/0004_odd_slayback.sql b/lib/db/migrations/0004_odd_slayback.sql
new file mode 100644
index 0000000..4186ce3
--- /dev/null
+++ b/lib/db/migrations/0004_odd_slayback.sql
@@ -0,0 +1 @@
+ALTER TABLE "Document" ADD COLUMN "text" varchar DEFAULT 'text' NOT NULL;
\ No newline at end of file
diff --git a/lib/db/migrations/meta/0004_snapshot.json b/lib/db/migrations/meta/0004_snapshot.json
new file mode 100644
index 0000000..64df8a0
--- /dev/null
+++ b/lib/db/migrations/meta/0004_snapshot.json
@@ -0,0 +1,391 @@
+{
+ "id": "30ad8233-1432-428b-93fc-2bb1ba867ff1",
+ "prevId": "011efa9e-42c7-4ff6-830a-02106f6638c9",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.Chat": {
+ "name": "Chat",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "visibility": {
+ "name": "visibility",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'private'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "Chat_userId_User_id_fk": {
+ "name": "Chat_userId_User_id_fk",
+ "tableFrom": "Chat",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.Document": {
+ "name": "Document",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "text": {
+ "name": "text",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'text'"
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "Document_userId_User_id_fk": {
+ "name": "Document_userId_User_id_fk",
+ "tableFrom": "Document",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "Document_id_createdAt_pk": {
+ "name": "Document_id_createdAt_pk",
+ "columns": [
+ "id",
+ "createdAt"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "public.Message": {
+ "name": "Message",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "chatId": {
+ "name": "chatId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "Message_chatId_Chat_id_fk": {
+ "name": "Message_chatId_Chat_id_fk",
+ "tableFrom": "Message",
+ "tableTo": "Chat",
+ "columnsFrom": [
+ "chatId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.Suggestion": {
+ "name": "Suggestion",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "documentId": {
+ "name": "documentId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "documentCreatedAt": {
+ "name": "documentCreatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "originalText": {
+ "name": "originalText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "suggestedText": {
+ "name": "suggestedText",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "isResolved": {
+ "name": "isResolved",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "Suggestion_userId_User_id_fk": {
+ "name": "Suggestion_userId_User_id_fk",
+ "tableFrom": "Suggestion",
+ "tableTo": "User",
+ "columnsFrom": [
+ "userId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk": {
+ "name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
+ "tableFrom": "Suggestion",
+ "tableTo": "Document",
+ "columnsFrom": [
+ "documentId",
+ "documentCreatedAt"
+ ],
+ "columnsTo": [
+ "id",
+ "createdAt"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "Suggestion_id_pk": {
+ "name": "Suggestion_id_pk",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "public.User": {
+ "name": "User",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "password": {
+ "name": "password",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.Vote": {
+ "name": "Vote",
+ "schema": "",
+ "columns": {
+ "chatId": {
+ "name": "chatId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "messageId": {
+ "name": "messageId",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "isUpvoted": {
+ "name": "isUpvoted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "Vote_chatId_Chat_id_fk": {
+ "name": "Vote_chatId_Chat_id_fk",
+ "tableFrom": "Vote",
+ "tableTo": "Chat",
+ "columnsFrom": [
+ "chatId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "Vote_messageId_Message_id_fk": {
+ "name": "Vote_messageId_Message_id_fk",
+ "tableFrom": "Vote",
+ "tableTo": "Message",
+ "columnsFrom": [
+ "messageId"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "Vote_chatId_messageId_pk": {
+ "name": "Vote_chatId_messageId_pk",
+ "columns": [
+ "chatId",
+ "messageId"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/lib/db/migrations/meta/_journal.json b/lib/db/migrations/meta/_journal.json
index 84dfebe..591f010 100644
--- a/lib/db/migrations/meta/_journal.json
+++ b/lib/db/migrations/meta/_journal.json
@@ -29,6 +29,13 @@
"when": 1733403031014,
"tag": "0003_cloudy_glorian",
"breakpoints": true
+ },
+ {
+ "idx": 4,
+ "version": "7",
+ "when": 1733945232355,
+ "tag": "0004_odd_slayback",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/lib/db/queries.ts b/lib/db/queries.ts
index 79b7037..4a85e29 100644
--- a/lib/db/queries.ts
+++ b/lib/db/queries.ts
@@ -16,6 +16,7 @@ import {
message,
vote,
} from './schema';
+import { BlockKind } from '@/components/block';
// Optionally, if not using email/pass login, you can
// use the Drizzle adapter for Auth.js / NextAuth
@@ -169,11 +170,13 @@ export async function getVotesByChatId({ id }: { id: string }) {
export async function saveDocument({
id,
title,
+ kind,
content,
userId,
}: {
id: string;
title: string;
+ kind: BlockKind;
content: string;
userId: string;
}) {
@@ -181,6 +184,7 @@ export async function saveDocument({
return await db.insert(document).values({
id,
title,
+ kind,
content,
userId,
createdAt: new Date(),
diff --git a/lib/db/schema.ts b/lib/db/schema.ts
index 23d9e82..712cc36 100644
--- a/lib/db/schema.ts
+++ b/lib/db/schema.ts
@@ -72,6 +72,9 @@ export const document = pgTable(
createdAt: timestamp('createdAt').notNull(),
title: text('title').notNull(),
content: text('content'),
+ kind: varchar('text', { enum: ['text', 'code'] })
+ .notNull()
+ .default('text'),
userId: uuid('userId')
.notNull()
.references(() => user.id),
diff --git a/lib/editor/suggestions.tsx b/lib/editor/suggestions.tsx
index 773a41f..37885e7 100644
--- a/lib/editor/suggestions.tsx
+++ b/lib/editor/suggestions.tsx
@@ -9,6 +9,7 @@ import { createRoot } from 'react-dom/client';
import { Suggestion as PreviewSuggestion } from '@/components/suggestion';
import type { Suggestion } from '@/lib/db/schema';
+import { BlockKind } from '@/components/block';
export interface UISuggestion extends Suggestion {
selectionStart: number;
@@ -69,6 +70,7 @@ export function projectWithPositions(
export function createSuggestionWidget(
suggestion: UISuggestion,
view: EditorView,
+ blockKind: BlockKind = 'text',
): { dom: HTMLElement; destroy: () => void } {
const dom = document.createElement('span');
const root = createRoot(dom);
@@ -111,7 +113,13 @@ export function createSuggestionWidget(
dispatch(textTransaction);
};
- root.render();
+ root.render(
+ ,
+ );
return {
dom,
diff --git a/package.json b/package.json
index ac1f645..2ffe24b 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,11 @@
},
"dependencies": {
"@ai-sdk/openai": "1.0.6",
+ "@codemirror/lang-javascript": "^6.2.2",
+ "@codemirror/lang-python": "^6.1.6",
+ "@codemirror/state": "^6.5.0",
+ "@codemirror/theme-one-dark": "^6.1.2",
+ "@codemirror/view": "^6.35.3",
"@radix-ui/react-alert-dialog": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2",
@@ -37,6 +42,7 @@
"class-variance-authority": "^0.7.0",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
+ "codemirror": "^6.0.1",
"date-fns": "^4.1.0",
"diff-match-patch": "^1.0.5",
"dotenv": "^16.4.5",
@@ -62,6 +68,7 @@
"react": "19.0.0-rc-45804af1-20241021",
"react-dom": "19.0.0-rc-45804af1-20241021",
"react-markdown": "^9.0.1",
+ "react-resizable-panels": "^2.1.7",
"remark-gfm": "^4.0.0",
"server-only": "^0.0.1",
"sonner": "^1.5.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0656532..edf901b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,6 +11,21 @@ importers:
'@ai-sdk/openai':
specifier: 1.0.6
version: 1.0.6(zod@3.23.8)
+ '@codemirror/lang-javascript':
+ specifier: ^6.2.2
+ version: 6.2.2
+ '@codemirror/lang-python':
+ specifier: ^6.1.6
+ version: 6.1.6(@codemirror/view@6.35.3)
+ '@codemirror/state':
+ specifier: ^6.5.0
+ version: 6.5.0
+ '@codemirror/theme-one-dark':
+ specifier: ^6.1.2
+ version: 6.1.2
+ '@codemirror/view':
+ specifier: ^6.35.3
+ version: 6.35.3
'@radix-ui/react-alert-dialog':
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
@@ -65,6 +80,9 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
+ codemirror:
+ specifier: ^6.0.1
+ version: 6.0.1(@lezer/common@1.2.3)
date-fns:
specifier: ^4.1.0
version: 4.1.0
@@ -140,6 +158,9 @@ importers:
react-markdown:
specifier: ^9.0.1
version: 9.0.1(@types/react@18.3.12)(react@19.0.0-rc-45804af1-20241021)
+ react-resizable-panels:
+ specifier: ^2.1.7
+ version: 2.1.7(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
remark-gfm:
specifier: ^4.0.0
version: 4.0.0
@@ -330,6 +351,41 @@ packages:
cpu: [x64]
os: [win32]
+ '@codemirror/autocomplete@6.18.3':
+ resolution: {integrity: sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==}
+ peerDependencies:
+ '@codemirror/language': ^6.0.0
+ '@codemirror/state': ^6.0.0
+ '@codemirror/view': ^6.0.0
+ '@lezer/common': ^1.0.0
+
+ '@codemirror/commands@6.7.1':
+ resolution: {integrity: sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==}
+
+ '@codemirror/lang-javascript@6.2.2':
+ resolution: {integrity: sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==}
+
+ '@codemirror/lang-python@6.1.6':
+ resolution: {integrity: sha512-ai+01WfZhWqM92UqjnvorkxosZ2aq2u28kHvr+N3gu012XqY2CThD67JPMHnGceRfXPDBmn1HnyqowdpF57bNg==}
+
+ '@codemirror/language@6.10.6':
+ resolution: {integrity: sha512-KrsbdCnxEztLVbB5PycWXFxas4EOyk/fPAfruSOnDDppevQgid2XZ+KbJ9u+fDikP/e7MW7HPBTvTb8JlZK9vA==}
+
+ '@codemirror/lint@6.8.4':
+ resolution: {integrity: sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==}
+
+ '@codemirror/search@6.5.8':
+ resolution: {integrity: sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==}
+
+ '@codemirror/state@6.5.0':
+ resolution: {integrity: sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==}
+
+ '@codemirror/theme-one-dark@6.1.2':
+ resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
+
+ '@codemirror/view@6.35.3':
+ resolution: {integrity: sha512-ScY7L8+EGdPl4QtoBiOzE4FELp7JmNUsBvgBcCakXWM2uiv/K89VAzU3BMDscf0DsACLvTKePbd5+cFDTcei6g==}
+
'@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
@@ -935,6 +991,24 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ '@lezer/common@1.2.3':
+ resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==}
+
+ '@lezer/highlight@1.2.1':
+ resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
+
+ '@lezer/javascript@1.4.21':
+ resolution: {integrity: sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==}
+
+ '@lezer/lr@1.4.2':
+ resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==}
+
+ '@lezer/python@1.1.15':
+ resolution: {integrity: sha512-aVQ43m2zk4FZYedCqL0KHPEUsqZOrmAvRhkhHlVPnDD1HODDyyQv5BRIuod4DadkgBEZd53vQOtXTonNbEgjrQ==}
+
+ '@marijn/find-cluster-break@1.0.2':
+ resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
+
'@neondatabase/serverless@0.9.5':
resolution: {integrity: sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug==}
@@ -1743,6 +1817,9 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
+ codemirror@6.0.1:
+ resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
+
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -3168,6 +3245,12 @@ packages:
'@types/react':
optional: true
+ react-resizable-panels@2.1.7:
+ resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==}
+ peerDependencies:
+ react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
react-style-singleton@2.2.1:
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}
@@ -3377,6 +3460,9 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
+ style-mod@4.1.2:
+ resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
+
style-to-object@1.0.8:
resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==}
@@ -3731,6 +3817,78 @@ snapshots:
'@biomejs/cli-win32-x64@1.9.4':
optional: true
+ '@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3)':
+ dependencies:
+ '@codemirror/language': 6.10.6
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ '@lezer/common': 1.2.3
+
+ '@codemirror/commands@6.7.1':
+ dependencies:
+ '@codemirror/language': 6.10.6
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ '@lezer/common': 1.2.3
+
+ '@codemirror/lang-javascript@6.2.2':
+ dependencies:
+ '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3)
+ '@codemirror/language': 6.10.6
+ '@codemirror/lint': 6.8.4
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ '@lezer/common': 1.2.3
+ '@lezer/javascript': 1.4.21
+
+ '@codemirror/lang-python@6.1.6(@codemirror/view@6.35.3)':
+ dependencies:
+ '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3)
+ '@codemirror/language': 6.10.6
+ '@codemirror/state': 6.5.0
+ '@lezer/common': 1.2.3
+ '@lezer/python': 1.1.15
+ transitivePeerDependencies:
+ - '@codemirror/view'
+
+ '@codemirror/language@6.10.6':
+ dependencies:
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ '@lezer/common': 1.2.3
+ '@lezer/highlight': 1.2.1
+ '@lezer/lr': 1.4.2
+ style-mod: 4.1.2
+
+ '@codemirror/lint@6.8.4':
+ dependencies:
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ crelt: 1.0.6
+
+ '@codemirror/search@6.5.8':
+ dependencies:
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ crelt: 1.0.6
+
+ '@codemirror/state@6.5.0':
+ dependencies:
+ '@marijn/find-cluster-break': 1.0.2
+
+ '@codemirror/theme-one-dark@6.1.2':
+ dependencies:
+ '@codemirror/language': 6.10.6
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ '@lezer/highlight': 1.2.1
+
+ '@codemirror/view@6.35.3':
+ dependencies:
+ '@codemirror/state': 6.5.0
+ style-mod: 4.1.2
+ w3c-keyname: 2.2.8
+
'@drizzle-team/brocli@0.10.2': {}
'@emnapi/runtime@1.3.1':
@@ -4110,6 +4268,30 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
+ '@lezer/common@1.2.3': {}
+
+ '@lezer/highlight@1.2.1':
+ dependencies:
+ '@lezer/common': 1.2.3
+
+ '@lezer/javascript@1.4.21':
+ dependencies:
+ '@lezer/common': 1.2.3
+ '@lezer/highlight': 1.2.1
+ '@lezer/lr': 1.4.2
+
+ '@lezer/lr@1.4.2':
+ dependencies:
+ '@lezer/common': 1.2.3
+
+ '@lezer/python@1.1.15':
+ dependencies:
+ '@lezer/common': 1.2.3
+ '@lezer/highlight': 1.2.1
+ '@lezer/lr': 1.4.2
+
+ '@marijn/find-cluster-break@1.0.2': {}
+
'@neondatabase/serverless@0.9.5':
dependencies:
'@types/pg': 8.11.6
@@ -4899,6 +5081,18 @@ snapshots:
clsx@2.1.1: {}
+ codemirror@6.0.1(@lezer/common@1.2.3):
+ dependencies:
+ '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3)
+ '@codemirror/commands': 6.7.1
+ '@codemirror/language': 6.10.6
+ '@codemirror/lint': 6.8.4
+ '@codemirror/search': 6.5.8
+ '@codemirror/state': 6.5.0
+ '@codemirror/view': 6.35.3
+ transitivePeerDependencies:
+ - '@lezer/common'
+
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@@ -6681,6 +6875,11 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.12
+ react-resizable-panels@2.1.7(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021):
+ dependencies:
+ react: 19.0.0-rc-45804af1-20241021
+ react-dom: 19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021)
+
react-style-singleton@2.2.1(@types/react@18.3.12)(react@19.0.0-rc-45804af1-20241021):
dependencies:
get-nonce: 1.0.1
@@ -6961,6 +7160,8 @@ snapshots:
strip-json-comments@3.1.1: {}
+ style-mod@4.1.2: {}
+
style-to-object@1.0.8:
dependencies:
inline-style-parser: 0.2.4