-
-
Notifications
You must be signed in to change notification settings - Fork 8
feat: Implement Calendar Notepad with AI integration and collaborator tagging #652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
bfa31eb
f2d047f
823d967
f4890d8
aeb89b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,5 @@ import ReactMarkdown, { Options } from 'react-markdown' | |
| export const MemoizedReactMarkdown: FC<Options> = memo( | ||
| ReactMarkdown, | ||
| (prevProps, nextProps) => | ||
| prevProps.children === nextProps.children && | ||
| prevProps.className === nextProps.className | ||
| prevProps.children === nextProps.children | ||
|
Comment on lines
6
to
+7
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify where MemoizedReactMarkdown is used and whether non-children props vary.
rg -n -C3 '\bMemoizedReactMarkdown\b'
rg -n -C3 '<MemoizedReactMarkdown[^>]*className=|<MemoizedReactMarkdown[^>]*components=|<MemoizedReactMarkdown[^>]*remarkPlugins=|<MemoizedReactMarkdown[^>]*rehypePlugins='Repository: QueueLab/QCX Length of output: 1627 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== components/ui/markdown.tsx (lines 1-120) ==="
nl -ba components/ui/markdown.tsx | sed -n '1,120p'
echo "=== components/message.tsx (around MemoizedReactMarkdown usage) ==="
nl -ba components/message.tsx | sed -n '1,120p'Repository: QueueLab/QCX Length of output: 166 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== components/ui/markdown.tsx (first 160 lines) ==="
cat -n components/ui/markdown.tsx | sed -n '1,160p'
echo "=== components/message.tsx (first 220 lines) ==="
cat -n components/message.tsx | sed -n '1,220p'Repository: QueueLab/QCX Length of output: 2287 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n "MemoizedReactMarkdown" -SRepository: QueueLab/QCX Length of output: 332 Custom
🤖 Prompt for AI Agents |
||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { tool } from 'ai' | ||
| import { calendarSchema } from '@/lib/schema/calendar' | ||
| import { saveNote, getChatNotes } from '@/lib/actions/calendar' | ||
| import { ToolProps } from '.' | ||
| import { Section } from '@/components/section' | ||
| import { BotMessage } from '@/components/message' | ||
| import { nanoid } from '@/lib/utils' | ||
|
|
||
| export const calendarTool = ({ uiStream }: ToolProps) => | ||
| tool({ | ||
| description: 'Create reminders, add notes to the calendar, or list existing notes for the current chat.', | ||
| parameters: calendarSchema, | ||
| execute: async ({ action, content, date, location }) => { | ||
| // In a real execution, we need the chatId. | ||
| // Since tools are called within the submit action context, | ||
| // we might need to find a way to pass chatId here if it's not readily available. | ||
| // For this implementation, we'll assume the AI state provides it or we handle it in app/actions. | ||
|
|
||
| if (action === 'create_note' && content) { | ||
| // We'll return a result that the caller (submit action) can use to actually save | ||
| return { | ||
| type: 'CALENDAR_ACTION', | ||
| action: 'create', | ||
| note: { | ||
| content, | ||
| date: date ? new Date(date) : new Date(), | ||
| locationTags: location ? { | ||
| type: 'Point', | ||
| coordinates: [location.longitude, location.latitude] | ||
| } : null | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (action === 'list_notes') { | ||
| return { | ||
| type: 'CALENDAR_ACTION', | ||
| action: 'list' | ||
| } | ||
| } | ||
|
|
||
| return { error: 'Invalid calendar action' } | ||
| } | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { z } from 'zod' | ||
|
|
||
| export const calendarSchema = z.object({ | ||
| action: z.string().describe('The action to perform: "create_note" or "list_notes"'), | ||
| content: z.string().optional().describe('The content of the note or reminder'), | ||
| date: z.string().optional().describe('The date for the note/reminder in ISO format'), | ||
|
Comment on lines
+4
to
+6
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the file and print the relevant section with line numbers
if [ -f "lib/schema/calendar.ts" ]; then
echo "== lib/schema/calendar.ts =="
nl -ba lib/schema/calendar.ts | sed -n '1,120p'
else
echo "File lib/schema/calendar.ts not found"
exit 1
fi
# Search for other schema patterns (enums/iso validation) in lib/schema
echo "== Search: z.enum / iso date usage in lib/schema =="
rg -n "z\.enum\(|iso\.date\(|iso\.datetime\(|zod.*iso" lib/schema || true
# Search for "calendarSchema" usage to see expected payloads
echo "== Search: calendarSchema usage =="
rg -n "calendarSchema" -S . || trueRepository: QueueLab/QCX Length of output: 146 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE="lib/schema/calendar.ts"
if [ -f "$FILE" ]; then
echo "== $FILE (first 160 lines) =="
cat -n "$FILE" | sed -n '1,160p'
else
echo "File $FILE not found"
exit 1
fi
echo
echo "== Search: z.enum / iso.date / iso.datetime patterns in lib/schema =="
rg -n --hidden --glob 'lib/schema/**/*' "z\.enum\(|iso\.date\(|iso\.datetime\(" . || true
echo
echo "== Search: calendarSchema usage =="
rg -n "calendarSchema" -S . || trueRepository: QueueLab/QCX Length of output: 1778 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== lib/agents/tools/calendar.tsx =="
if [ -f "lib/agents/tools/calendar.tsx" ]; then
cat -n "lib/agents/tools/calendar.tsx" | sed -n '1,220p'
else
echo "File lib/agents/tools/calendar.tsx not found"
fi
echo
echo "== Search: ISO date / parse / z.string().datetime patterns =="
rg -n --hidden "iso\.date|iso\.datetime|datetime\(|Date\.parse|new Date\(|/ISO/i|ISO format|regex.*date" . || true
echo
echo "== Search: action create_note/list_notes usage =="
rg -n --hidden "create_note|list_notes|calendarSchema\.shape|parameters\.action|action ===" lib || trueRepository: QueueLab/QCX Length of output: 6057 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== lib/actions/calendar.ts =="
if [ -f "lib/actions/calendar.ts" ]; then
cat -n lib/actions/calendar.ts | sed -n '1,220p'
else
echo "File lib/actions/calendar.ts not found"
fi
echo
echo "== tests/calendar.spec.ts =="
if [ -f "tests/calendar.spec.ts" ]; then
cat -n tests/calendar.spec.ts | sed -n '1,200p'
else
echo "File tests/calendar.spec.ts not found"
fiRepository: QueueLab/QCX Length of output: 12226 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Find calendarNotes schema =="
rg -n "calendarNotes" lib/db/schema -S || true
# Try to locate the file that defines calendarNotes
FILE=$(fd -a "schema.*" lib/db || true | head -n 20 | head -n 1)
echo "Sample schema files under lib/db: "
fd -a "schema*" lib/db | head -n 30 || true
# Search for the exact field name "date" within calendarNotes definition
rg -n "calendarNotes.*date|date.*calendarNotes" lib/db -S || true
echo
echo "== Find NewCalendarNote / CalendarNote types =="
rg -n "type (NewCalendarNote|CalendarNote)|export type (NewCalendarNote|CalendarNote)" lib -S || true
rg -n "NewCalendarNote" lib/types lib -S || true
echo
echo "== Inspect lib/db/schema directory for calendarNotes file =="
fd -a "calendar*.*" lib/db/schema lib/db || trueRepository: QueueLab/QCX Length of output: 994 🏁 Script executed: #!/bin/bash
set -euo pipefail
SCHEMA_FILE="lib/db/schema.ts"
TYPES_FILE="lib/types/index.ts"
echo "== Inspect $SCHEMA_FILE (calendarNotes definition) =="
if [ -f "$SCHEMA_FILE" ]; then
# show only the regions likely containing calendarNotes
rg -n "calendarNotes" "$SCHEMA_FILE" -S
echo
line=$(rg -n "calendarNotes" "$SCHEMA_FILE" -S | head -n 1 | cut -d: -f1 || true)
if [ -n "${line:-}" ]; then
start=$((line-40)); end=$((line+120))
start=$((start<1 ? 1 : start))
sed -n "${start},${end}p" "$SCHEMA_FILE" | cat -n
fi
else
echo "Missing $SCHEMA_FILE"
fi
echo
echo "== Inspect $TYPES_FILE (CalendarNote/NewCalendarNote) =="
if [ -f "$TYPES_FILE" ]; then
rg -n "export type (CalendarNote|NewCalendarNote)|calendarNotes|date:" "$TYPES_FILE" -S
echo
rg -n "export type CalendarNote" "$TYPES_FILE" -S
line=$(rg -n "export type CalendarNote" "$TYPES_FILE" -S | head -n 1 | cut -d: -f1 || true)
if [ -n "${line:-}" ]; then
start=$((line-20)); end=$((line+80))
start=$((start<1 ? 1 : start))
sed -n "${start},${end}p" "$TYPES_FILE" | cat -n
fi
else
echo "Missing $TYPES_FILE"
fiRepository: QueueLab/QCX Length of output: 7694 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n "CALENDAR_ACTION" -S . || true
rg -n "type: 'CALENDAR_ACTION'" -S . || true
echo
echo "== Inspect likely handler in app/actions.tsx (search around CALENDAR_ACTION) =="
FILE="app/actions.tsx"
if [ -f "$FILE" ]; then
line=$(rg -n "CALENDAR_ACTION" "$FILE" -S | head -n 1 | cut -d: -f1 || true)
if [ -n "${line:-}" ]; then
start=$((line-80)); end=$((line+120))
start=$((start<1 ? 1 : start))
sed -n "${start},${end}p" "$FILE" | cat -n
else
echo "No CALENDAR_ACTION in $FILE"
fi
else
echo "Missing $FILE"
fiRepository: QueueLab/QCX Length of output: 6980 Validate
Suggested fix export const calendarSchema = z.object({
- action: z.string().describe('The action to perform: "create_note" or "list_notes"'),
+ action: z
+ .enum(['create_note', 'list_notes'])
+ .describe('The action to perform: "create_note" or "list_notes"'),
content: z.string().optional().describe('The content of the note or reminder'),
- date: z.string().optional().describe('The date for the note/reminder in ISO format'),
+ date: z
+ .union([z.iso.datetime(), z.iso.date()])
+ .optional()
+ .describe('The date for the note/reminder in ISO format'),🤖 Prompt for AI Agents |
||
| location: z.object({ | ||
| latitude: z.number(), | ||
| longitude: z.number(), | ||
| place_name: z.string().optional() | ||
| }).optional().describe('Optional location to associate with the note') | ||
| }) | ||
|
|
||
| export type CalendarToolSchema = z.infer<typeof calendarSchema> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not emit
success: truewhen note persistence fails.Line 542 can return
nullfromsaveNote, but Line 549 always reports success. That creates false confirmations and inconsistent chat/calendar state.Suggested fix
if (result.action === 'create') { const saved = await saveNote({ ...result.note, chatId: aiState.get().chatId, userId: '', // set in saveNote userTags: null, mapFeatureId: null }) - output.result = { success: true, note: saved } + output.result = saved + ? { success: true, note: saved } + : { success: false, error: 'Failed to save calendar note' } } else if (result.action === 'list') {🤖 Prompt for AI Agents