diff --git a/README.md b/README.md index 5927b2d1..9ab8d36b 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ LinkedIn + + Twitter + Y Combinator diff --git a/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts b/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts index 8dd95289..3b743a3a 100644 --- a/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts +++ b/apps/rowboat/app/api/copilot-stream-response/[streamId]/route.ts @@ -4,6 +4,8 @@ import { redisClient } from "@/app/lib/redis"; import { CopilotAPIRequest } from "@/src/application/lib/copilot/types"; import { streamMultiAgentResponse } from "@/src/application/lib/copilot/copilot"; +export const maxDuration = 300; + export async function GET(request: Request, props: { params: Promise<{ streamId: string }> }) { const params = await props.params; // get the payload from redis diff --git a/apps/rowboat/app/api/stream-response/[streamId]/route.ts b/apps/rowboat/app/api/stream-response/[streamId]/route.ts index 0f873ff8..4fc9de3f 100644 --- a/apps/rowboat/app/api/stream-response/[streamId]/route.ts +++ b/apps/rowboat/app/api/stream-response/[streamId]/route.ts @@ -4,6 +4,8 @@ import { requireAuth } from "@/app/lib/auth"; import { z } from "zod"; import { TurnEvent } from "@/src/entities/models/turn"; +export const maxDuration = 300; + export async function GET(request: Request, props: { params: Promise<{ streamId: string }> }) { const params = await props.params; diff --git a/apps/rowboat/app/app.tsx b/apps/rowboat/app/app.tsx index ef2458ea..ff538017 100644 --- a/apps/rowboat/app/app.tsx +++ b/apps/rowboat/app/app.tsx @@ -39,10 +39,6 @@ export function App() { {/* Footer */}
© 2025 RowBoat Labs
-
- Terms of Service - Privacy Policy -
); diff --git a/apps/rowboat/app/lib/components/input-field.tsx b/apps/rowboat/app/lib/components/input-field.tsx index 42ef48a6..1dab3020 100644 --- a/apps/rowboat/app/lib/components/input-field.tsx +++ b/apps/rowboat/app/lib/components/input-field.tsx @@ -41,6 +41,7 @@ interface TextInputFieldProps extends BaseInputFieldProps { showSaveButton?: boolean; showDiscardButton?: boolean; immediateSave?: boolean; + minHeight?: string; } // Select input specific props @@ -109,6 +110,7 @@ function TextInputField({ showSaveButton = false, showDiscardButton = false, immediateSave = false, + minHeight, }: TextInputFieldProps) { const [isEditing, setIsEditing] = useState(false); const [localValue, setLocalValue] = useState(value); @@ -248,10 +250,10 @@ function TextInputField({ {/* Input field */} {mentions ? ( -
+
!locked && !disabled && setIsEditing(true)} > {/* Content */} @@ -337,14 +340,14 @@ function TextInputField({ "whitespace-pre-wrap": multiline, "flex items-center": !multiline, })}> - {value ? ( + {(mentions ? localValue : value) ? ( <> {markdown ? (
@@ -355,7 +358,7 @@ function TextInputField({ "max-h-[420px] overflow-y-auto": multiline })}> diff --git a/apps/rowboat/app/lib/components/mentions_editor.tsx b/apps/rowboat/app/lib/components/mentions_editor.tsx index 0f0bd72e..35b0aef3 100644 --- a/apps/rowboat/app/lib/components/mentions_editor.tsx +++ b/apps/rowboat/app/lib/components/mentions_editor.tsx @@ -97,13 +97,20 @@ export default function MentionEditor({ }) { const containerRef = useRef(null); const quillRef = useRef(null); + const atValuesRef = useRef(atValues); + const onValueChangeRef = useRef(onValueChange); + const externalValueRef = useRef(value); + const isApplyingExternalRef = useRef(false); function getMarkdown(): string { if (!quillRef.current) { return ""; } // generate markdown representation of content - const markdown = quillRef.current.getContents().map((op) => { + const delta = quillRef.current.getContents() as unknown as Delta; + // Quill Delta has .ops + const ops: any[] = (delta as any).ops || []; + const markdown = ops.map((op) => { if (op.insert && typeof op.insert === 'object' && 'mention' in op.insert) { const mentionOp = op.insert as { mention: Match }; return `[@${mentionOp.mention.id}](#mention)`; @@ -120,6 +127,20 @@ export default function MentionEditor({ navigator.clipboard.writeText(getMarkdown()); } + // Keep refs up to date without re-initializing Quill + useEffect(() => { + atValuesRef.current = atValues; + }, [atValues]); + + useEffect(() => { + onValueChangeRef.current = onValueChange; + }, [onValueChange]); + + useEffect(() => { + externalValueRef.current = value; + }, [value]); + + // Initialize Quill once useEffect(() => { if (!containerRef.current) { return; @@ -140,15 +161,14 @@ export default function MentionEditor({ mentionDenotationChars: ["@"], showDenotationChar: true, source: async function (searchTerm: string, renderList: (values: Match[], searchTerm: string) => void) { + const list = atValuesRef.current || []; if (searchTerm.length === 0) { - renderList(atValues, searchTerm); + renderList(list, searchTerm); } else { - const matches = []; - for (let i = 0; i < atValues.length; i++) { - if ( - atValues[i].value.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1 - ) { - matches.push(atValues[i]); + const matches: Match[] = []; + for (let i = 0; i < list.length; i++) { + if (list[i].value.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1) { + matches.push(list[i]); } } renderList(matches, searchTerm); @@ -165,15 +185,18 @@ export default function MentionEditor({ }); // clear the quill contents - quill.setContents([]); + quill.setText('', Quill.sources.SILENT); // convert the markdown to parts - const parts = markdownToParts(value, atValues); + const parts = markdownToParts(externalValueRef.current, atValuesRef.current); insertPartsIntoQuill(quill, parts); quill.on(Quill.events.TEXT_CHANGE, (delta: Delta, oldDelta: Delta, source: string) => { - if (onValueChange) { - onValueChange(getMarkdown()); + if (isApplyingExternalRef.current) { + return; + } + if (onValueChangeRef.current) { + onValueChangeRef.current(getMarkdown()); } }); quillRef.current = quill; @@ -193,7 +216,22 @@ export default function MentionEditor({ quillRef.current.off(Quill.events.TEXT_CHANGE); } } - }, [atValues, onValueChange, placeholder, value, autoFocus]); + // Mount once + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // Sync external value into the editor without re-initializing + useEffect(() => { + if (!quillRef.current) return; + const current = getMarkdown(); + if (value === current) return; + const quill = quillRef.current; + isApplyingExternalRef.current = true; + quill.setText('', Quill.sources.SILENT); + const parts = markdownToParts(value, atValuesRef.current); + insertPartsIntoQuill(quill, parts); + isApplyingExternalRef.current = false; + }, [value]); return
) : (
+ {/* Description Section */} +
+ + { + handleUpdate({ ...agent, description: value }); + showSavedMessage(); + }} + multiline={true} + placeholder="Enter a description for this agent" + minHeight="40px" + className="w-full" + /> +
{/* Instructions Section */}
@@ -493,22 +509,7 @@ export function AgentConfig({ />
-
- -
- { - handleUpdate({ ...agent, description: value }); - showSavedMessage(); - }} - multiline={true} - placeholder="Enter a description for this agent" - className="w-full" - /> -
-
+
{/* Behavior Section Card */} diff --git a/apps/rowboat/app/projects/[projectId]/jobs/components/job-view.tsx b/apps/rowboat/app/projects/[projectId]/jobs/components/job-view.tsx index c39b941e..f36054a2 100644 --- a/apps/rowboat/app/projects/[projectId]/jobs/components/job-view.tsx +++ b/apps/rowboat/app/projects/[projectId]/jobs/components/job-view.tsx @@ -55,7 +55,7 @@ export function JobView({ projectId, jobId }: { projectId: string; jobId: string 'Deployment ID': reason.triggerDeploymentId, }, payload: reason.payload, - link: reason.triggerDeploymentId ? `/projects/${projectId}/job-rules/triggers/${reason.triggerDeploymentId}` : null + link: reason.triggerDeploymentId ? `/projects/${projectId}/manage-triggers/triggers/${reason.triggerDeploymentId}` : null }; } if (reason.type === 'scheduled_job_rule') { @@ -65,7 +65,7 @@ export function JobView({ projectId, jobId }: { projectId: string; jobId: string 'Rule ID': reason.ruleId, }, payload: null, - link: `/projects/${projectId}/job-rules/scheduled/${reason.ruleId}` + link: `/projects/${projectId}/manage-triggers/scheduled/${reason.ruleId}` }; } if (reason.type === 'recurring_job_rule') { @@ -75,7 +75,7 @@ export function JobView({ projectId, jobId }: { projectId: string; jobId: string 'Rule ID': reason.ruleId, }, payload: null, - link: `/projects/${projectId}/job-rules/recurring/${reason.ruleId}` + link: `/projects/${projectId}/manage-triggers/recurring/${reason.ruleId}` }; } return { diff --git a/apps/rowboat/app/projects/[projectId]/jobs/components/jobs-list.tsx b/apps/rowboat/app/projects/[projectId]/jobs/components/jobs-list.tsx index 36b9d06d..ceac96b1 100644 --- a/apps/rowboat/app/projects/[projectId]/jobs/components/jobs-list.tsx +++ b/apps/rowboat/app/projects/[projectId]/jobs/components/jobs-list.tsx @@ -99,21 +99,21 @@ export function JobsList({ projectId, filters, showTitle = true, customTitle }: return { type: 'Composio Trigger', display: `Composio: ${reason.triggerTypeSlug}`, - link: reason.triggerDeploymentId ? `/projects/${projectId}/job-rules/triggers/${reason.triggerDeploymentId}` : null + link: reason.triggerDeploymentId ? `/projects/${projectId}/manage-triggers/triggers/${reason.triggerDeploymentId}` : null }; } if (reason.type === 'scheduled_job_rule') { return { type: 'Scheduled Job Rule', display: `Scheduled Rule`, - link: `/projects/${projectId}/job-rules/scheduled/${reason.ruleId}` + link: `/projects/${projectId}/manage-triggers/scheduled/${reason.ruleId}` }; } if (reason.type === 'recurring_job_rule') { return { type: 'Recurring Job Rule', display: `Recurring Rule`, - link: `/projects/${projectId}/job-rules/recurring/${reason.ruleId}` + link: `/projects/${projectId}/manage-triggers/recurring/${reason.ruleId}` }; } return { diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/components/composio-trigger-deployment-view.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/composio-trigger-deployment-view.tsx similarity index 89% rename from apps/rowboat/app/projects/[projectId]/job-rules/components/composio-trigger-deployment-view.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/components/composio-trigger-deployment-view.tsx index 19d54c0c..5efe0f22 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/components/composio-trigger-deployment-view.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/composio-trigger-deployment-view.tsx @@ -47,7 +47,7 @@ export function ComposioTriggerDeploymentView({ projectId, deploymentId }: { pro setDeleting(true); try { await deleteComposioTriggerDeployment({ projectId, deploymentId: deployment.id }); - window.location.href = `/projects/${projectId}/job-rules`; + window.location.href = `/projects/${projectId}/manage-triggers?tab=triggers`; } catch (e) { console.error(e); alert('Failed to delete trigger'); @@ -62,9 +62,8 @@ export function ComposioTriggerDeploymentView({ projectId, deploymentId }: { pro - - @@ -77,9 +76,9 @@ export function ComposioTriggerDeploymentView({ projectId, deploymentId }: { pro onClick={() => setShowDeleteConfirm(true)} variant="secondary" size="sm" - className="flex items-center gap-2 bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800" + startContent={} + className="bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800 whitespace-nowrap" > - Delete
@@ -151,14 +150,15 @@ export function ComposioTriggerDeploymentView({ projectId, deploymentId }: { pro

Delete External Trigger

Are you sure you want to delete this external trigger? This will remove the linked webhook in Composio and delete this deployment.

- +
diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/components/create-recurring-job-rule-form.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/create-recurring-job-rule-form.tsx similarity index 95% rename from apps/rowboat/app/projects/[projectId]/job-rules/components/create-recurring-job-rule-form.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/components/create-recurring-job-rule-form.tsx index 465507c3..ccdd3dee 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/components/create-recurring-job-rule-form.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/create-recurring-job-rule-form.tsx @@ -89,7 +89,7 @@ export function CreateRecurringJobRuleForm({ projectId }: { projectId: string }) input: { messages: convertedMessages }, cron: cronExpression, }); - router.push(`/projects/${projectId}/job-rules`); + router.push(`/projects/${projectId}/manage-triggers?tab=recurring`); } catch (error) { console.error("Failed to create recurring job rule:", error); alert("Failed to create recurring job rule"); @@ -102,9 +102,8 @@ export function CreateRecurringJobRuleForm({ projectId }: { projectId: string }) - - @@ -181,9 +180,9 @@ export function CreateRecurringJobRuleForm({ projectId }: { projectId: string }) onClick={addMessage} variant="secondary" size="sm" - className="flex items-center gap-2" + startContent={} + className="whitespace-nowrap" > - Add Message
@@ -231,7 +230,8 @@ export function CreateRecurringJobRuleForm({ projectId }: { projectId: string }) diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/components/job-rules-tabs.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/job-rules-tabs.tsx similarity index 57% rename from apps/rowboat/app/projects/[projectId]/job-rules/components/job-rules-tabs.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/components/job-rules-tabs.tsx index 7afb463a..d1e99622 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/components/job-rules-tabs.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/job-rules-tabs.tsx @@ -1,18 +1,35 @@ 'use client'; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { Tabs, Tab } from "@/components/ui/tabs"; import { ScheduledJobRulesList } from "../scheduled/components/scheduled-job-rules-list"; import { RecurringJobRulesList } from "./recurring-job-rules-list"; import { TriggersTab } from "./triggers-tab"; export function JobRulesTabs({ projectId }: { projectId: string }) { - const [activeTab, setActiveTab] = useState("triggers"); + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + const initialTab = (searchParams.get('tab') ?? 'triggers'); + const [activeTab, setActiveTab] = useState(initialTab); const handleTabChange = (key: React.Key) => { - setActiveTab(key.toString()); + const nextTab = key.toString(); + setActiveTab(nextTab); + const params = new URLSearchParams(searchParams.toString()); + params.set('tab', nextTab); + router.replace(`${pathname}?${params.toString()}`); }; + useEffect(() => { + const current = searchParams.get('tab') ?? 'triggers'; + if (current !== activeTab) { + setActiveTab(current); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [searchParams]); + return (

The requested rule could not be found.

- + @@ -133,9 +133,8 @@ export function RecurringJobRuleView({ projectId, ruleId }: { projectId: string; - - @@ -149,31 +148,21 @@ export function RecurringJobRuleView({ projectId, ruleId }: { projectId: string;
@@ -297,6 +286,7 @@ export function RecurringJobRuleView({ projectId, ruleId }: { projectId: string; variant="secondary" onClick={() => setShowDeleteConfirm(false)} disabled={deleting} + className="whitespace-nowrap" > Cancel @@ -304,19 +294,11 @@ export function RecurringJobRuleView({ projectId, ruleId }: { projectId: string; variant="secondary" onClick={handleDelete} disabled={deleting} - className="flex items-center gap-2 bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800" + isLoading={deleting} + startContent={} + className="bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800 whitespace-nowrap" > - {deleting ? ( - <> - - Deleting... - - ) : ( - <> - - Delete - - )} + {deleting ? 'Deleting...' : 'Delete'}
diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/components/recurring-job-rules-list.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/recurring-job-rules-list.tsx similarity index 95% rename from apps/rowboat/app/projects/[projectId]/job-rules/components/recurring-job-rules-list.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/components/recurring-job-rules-list.tsx index 008ae461..d5444eaa 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/components/recurring-job-rules-list.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/recurring-job-rules-list.tsx @@ -134,7 +134,7 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) { } rightActions={
- + @@ -168,7 +168,7 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) {
@@ -220,15 +220,10 @@ export function RecurringJobRulesList({ projectId }: { projectId: string }) { disabled={loadingMore} variant="secondary" size="sm" + isLoading={loadingMore} + className="whitespace-nowrap" > - {loadingMore ? ( - <> - - Loading... - - ) : ( - 'Load More' - )} + {loadingMore ? 'Loading...' : 'Load More'}
)} diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/components/triggers-tab.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/triggers-tab.tsx similarity index 88% rename from apps/rowboat/app/projects/[projectId]/job-rules/components/triggers-tab.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/components/triggers-tab.tsx index a68594db..ede8bf28 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/components/triggers-tab.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/components/triggers-tab.tsx @@ -4,12 +4,13 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Spinner, Link } from '@heroui/react'; import { Button } from '@/components/ui/button'; import { Panel } from '@/components/common/panel-common'; -import { Plus, Trash2, ZapIcon, ChevronDown, ChevronUp } from 'lucide-react'; +import { Plus, Trash2, ZapIcon, ChevronDown, ChevronUp, ArrowLeftIcon } from 'lucide-react'; +import Image from 'next/image'; import { z } from 'zod'; import { ComposioTriggerDeployment } from '@/src/entities/models/composio-trigger-deployment'; import { ComposioTriggerType } from '@/src/entities/models/composio-trigger-type'; import { isToday, isThisWeek, isThisMonth } from '@/lib/utils/date'; -import { listComposioTriggerDeployments, deleteComposioTriggerDeployment, createComposioTriggerDeployment, listComposioTriggerTypes } from '@/app/actions/composio.actions'; +import { listComposioTriggerDeployments, deleteComposioTriggerDeployment, createComposioTriggerDeployment } from '@/app/actions/composio.actions'; import { SelectComposioToolkit } from '../../tools/components/SelectComposioToolkit'; import { ComposioTriggerTypesPanel } from '../../workflow/components/ComposioTriggerTypesPanel'; import { TriggerConfigForm } from '../../workflow/components/TriggerConfigForm'; @@ -20,6 +21,8 @@ import { fetchProject } from '@/app/actions/project.actions'; type TriggerDeployment = z.infer; +// Removed friendly name computation; backend now provides friendly trigger name + export function TriggersTab({ projectId }: { projectId: string }) { const [triggers, setTriggers] = useState([]); const [loading, setLoading] = useState(true); @@ -31,7 +34,6 @@ export function TriggersTab({ projectId }: { projectId: string }) { const [isSubmittingTrigger, setIsSubmittingTrigger] = useState(false); const [deletingTrigger, setDeletingTrigger] = useState(null); const [projectConfig, setProjectConfig] = useState | null>(null); - const [triggerTypeNames, setTriggerTypeNames] = useState>({}); const [expandedTrigger, setExpandedTrigger] = useState(null); const [cursor, setCursor] = useState(null); const [hasMore, setHasMore] = useState(false); @@ -46,31 +48,6 @@ export function TriggersTab({ projectId }: { projectId: string }) { } }, [projectId]); - const loadTriggerTypeNames = useCallback(async () => { - try { - const names: Record = {}; - - // Get unique toolkit slugs from existing triggers - const uniqueToolkits = [...new Set(triggers.map(t => t.toolkitSlug))]; - - // Fetch trigger types for each toolkit - for (const toolkitSlug of uniqueToolkits) { - try { - const response = await listComposioTriggerTypes(toolkitSlug); - response.items.forEach(triggerType => { - names[triggerType.slug] = triggerType.name; - }); - } catch (err) { - console.error(`Error fetching trigger types for ${toolkitSlug}:`, err); - } - } - - setTriggerTypeNames(names); - } catch (err: any) { - console.error('Error loading trigger type names:', err); - } - }, [triggers]); - const sections = useMemo(() => { const groups: Record = { Today: [], @@ -204,7 +181,11 @@ export function TriggersTab({ projectId }: { projectId: string }) { triggerConfig, }); - // Success! Go back to triggers list and reload + // Success! Go back to triggers list tab and reload + if (typeof window !== 'undefined') { + window.location.href = `/projects/${projectId}/manage-triggers?tab=triggers`; + return; + } handleBackToList(); } catch (err: any) { console.error('Error creating trigger:', err); @@ -225,10 +206,8 @@ export function TriggersTab({ projectId }: { projectId: string }) { }, [showCreateFlow, loadTriggers]); useEffect(() => { - if (triggers.length > 0) { - loadTriggerTypeNames(); - } - }, [triggers, loadTriggerTypeNames]); + // No-op: trigger names are now derived from slug locally + }, [triggers]); const renderTriggerList = () => { if (loading) { @@ -261,7 +240,7 @@ export function TriggersTab({ projectId }: { projectId: string }) {
} rightActions={ - } @@ -349,13 +328,29 @@ export function TriggersTab({ projectId }: { projectId: string }) { >
- -
- - Active - + +
+ {trigger.logo && ( + {`${trigger.toolkitSlug} + )} + {trigger.toolkitSlug && ( + + {trigger.toolkitSlug} + + )} +
+
+
+ Active - {triggerTypeNames[trigger.triggerTypeSlug] || trigger.triggerTypeSlug} + {trigger.triggerTypeName}
@@ -373,10 +368,9 @@ export function TriggersTab({ projectId }: { projectId: string }) { size="sm" isLoading={deletingTrigger === trigger.id} onClick={() => handleDeleteTrigger(trigger.id)} + startContent={} className="text-red-600 hover:text-red-700 hover:bg-red-50 dark:text-red-400 dark:hover:text-red-300 dark:hover:bg-red-950" - > - - + />
{/* Advanced Details Section - Collapsible */} @@ -422,15 +416,10 @@ export function TriggersTab({ projectId }: { projectId: string }) { disabled={loadingMore} variant="secondary" size="sm" + isLoading={loadingMore} + className="whitespace-nowrap" > - {loadingMore ? ( - <> - - Loading... - - ) : ( - 'Load More' - )} + {loadingMore ? 'Loading...' : 'Load More'}
)} @@ -471,8 +460,10 @@ export function TriggersTab({ projectId }: { projectId: string }) {
diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/page.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/page.tsx similarity index 100% rename from apps/rowboat/app/projects/[projectId]/job-rules/page.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/page.tsx diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/recurring/[ruleId]/page.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/recurring/[ruleId]/page.tsx similarity index 100% rename from apps/rowboat/app/projects/[projectId]/job-rules/recurring/[ruleId]/page.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/recurring/[ruleId]/page.tsx diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/recurring/new/page.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/recurring/new/page.tsx similarity index 100% rename from apps/rowboat/app/projects/[projectId]/job-rules/recurring/new/page.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/recurring/new/page.tsx diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/[ruleId]/page.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/[ruleId]/page.tsx similarity index 100% rename from apps/rowboat/app/projects/[projectId]/job-rules/scheduled/[ruleId]/page.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/[ruleId]/page.tsx diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/create-scheduled-job-rule-form.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/create-scheduled-job-rule-form.tsx similarity index 93% rename from apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/create-scheduled-job-rule-form.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/create-scheduled-job-rule-form.tsx index 1fb4605e..9fbe46b0 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/create-scheduled-job-rule-form.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/create-scheduled-job-rule-form.tsx @@ -90,7 +90,7 @@ export function CreateScheduledJobRuleForm({ projectId }: { projectId: string }) input: { messages: convertedMessages }, scheduledTime: scheduledTimeString, }); - router.push(`/projects/${projectId}/job-rules`); + router.push(`/projects/${projectId}/manage-triggers?tab=scheduled`); } catch (error) { console.error("Failed to create scheduled job rule:", error); alert("Failed to create scheduled job rule"); @@ -105,9 +105,8 @@ export function CreateScheduledJobRuleForm({ projectId }: { projectId: string }) - - @@ -147,9 +146,9 @@ export function CreateScheduledJobRuleForm({ projectId }: { projectId: string }) onClick={addMessage} variant="secondary" size="sm" - className="flex items-center gap-2" + startContent={} + className="whitespace-nowrap" > - Add Message
@@ -197,7 +196,8 @@ export function CreateScheduledJobRuleForm({ projectId }: { projectId: string }) diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/scheduled-job-rule-view.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/scheduled-job-rule-view.tsx similarity index 90% rename from apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/scheduled-job-rule-view.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/scheduled-job-rule-view.tsx index 73c07ae3..7b44fb46 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/scheduled-job-rule-view.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/scheduled-job-rule-view.tsx @@ -46,7 +46,7 @@ export function ScheduledJobRuleView({ projectId, ruleId }: { projectId: string; ruleId: rule.id, }); // Redirect back to job rules list - router.push(`/projects/${projectId}/job-rules`); + router.push(`/projects/${projectId}/manage-triggers?tab=scheduled`); } catch (error) { console.error("Failed to delete rule:", error); alert("Failed to delete rule"); @@ -80,9 +80,8 @@ export function ScheduledJobRuleView({ projectId, ruleId }: { projectId: string; - - @@ -97,9 +96,9 @@ export function ScheduledJobRuleView({ projectId, ruleId }: { projectId: string; onClick={() => setShowDeleteConfirm(true)} variant="secondary" size="sm" - className="flex items-center gap-2 bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800" + startContent={} + className="bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800 whitespace-nowrap" > - Delete
@@ -204,6 +203,7 @@ export function ScheduledJobRuleView({ projectId, ruleId }: { projectId: string; variant="secondary" onClick={() => setShowDeleteConfirm(false)} disabled={deleting} + className="whitespace-nowrap" > Cancel @@ -211,19 +211,11 @@ export function ScheduledJobRuleView({ projectId, ruleId }: { projectId: string; variant="secondary" onClick={handleDelete} disabled={deleting} - className="flex items-center gap-2 bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800" + isLoading={deleting} + startContent={} + className="bg-red-50 hover:bg-red-100 text-red-700 dark:bg-red-950 dark:hover:bg-red-900 dark:text-red-400 border border-red-200 dark:border-red-800 whitespace-nowrap" > - {deleting ? ( - <> - - Deleting... - - ) : ( - <> - - Delete - - )} + {deleting ? 'Deleting...' : 'Delete'}
diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/scheduled-job-rules-list.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/scheduled-job-rules-list.tsx similarity index 94% rename from apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/scheduled-job-rules-list.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/scheduled-job-rules-list.tsx index da980452..b8baf176 100644 --- a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/components/scheduled-job-rules-list.tsx +++ b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/components/scheduled-job-rules-list.tsx @@ -112,7 +112,7 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) { } rightActions={
- + @@ -146,7 +146,7 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) {
@@ -190,15 +190,10 @@ export function ScheduledJobRulesList({ projectId }: { projectId: string }) { disabled={loadingMore} variant="secondary" size="sm" + isLoading={loadingMore} + className="whitespace-nowrap" > - {loadingMore ? ( - <> - - Loading... - - ) : ( - 'Load More' - )} + {loadingMore ? 'Loading...' : 'Load More'}
)} diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/scheduled/new/page.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/new/page.tsx similarity index 100% rename from apps/rowboat/app/projects/[projectId]/job-rules/scheduled/new/page.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/scheduled/new/page.tsx diff --git a/apps/rowboat/app/projects/[projectId]/job-rules/triggers/[deploymentId]/page.tsx b/apps/rowboat/app/projects/[projectId]/manage-triggers/triggers/[deploymentId]/page.tsx similarity index 100% rename from apps/rowboat/app/projects/[projectId]/job-rules/triggers/[deploymentId]/page.tsx rename to apps/rowboat/app/projects/[projectId]/manage-triggers/triggers/[deploymentId]/page.tsx diff --git a/apps/rowboat/app/projects/[projectId]/workflow/components/ComposioTriggerTypesPanel.tsx b/apps/rowboat/app/projects/[projectId]/workflow/components/ComposioTriggerTypesPanel.tsx index 3e906be7..41426bf5 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/components/ComposioTriggerTypesPanel.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/components/ComposioTriggerTypesPanel.tsx @@ -1,12 +1,13 @@ 'use client'; import React, { useState, useEffect, useCallback } from 'react'; -import { Button, Card, CardBody, CardHeader, Spinner } from '@heroui/react'; +import { Button, Card, CardBody, Spinner } from '@heroui/react'; import { ChevronLeft, ChevronRight, ZapIcon, ArrowLeft } from 'lucide-react'; import { z } from 'zod'; import { ComposioTriggerType } from '@/src/entities/models/composio-trigger-type'; import { listComposioTriggerTypes } from '@/app/actions/composio.actions'; import { ZToolkit } from "@/src/application/lib/composio/types"; +import { PictureImg } from '@/components/ui/picture-img'; interface ComposioTriggerTypesPanelProps { toolkit: z.infer; @@ -151,32 +152,42 @@ export function ComposioTriggerTypesPanel({
) : (
-
+
{triggerTypes.map((triggerType) => ( - handleTriggerTypeSelect(triggerType)} > - -
- -
-
-

+

+ {toolkit.meta?.logo ? ( + + ) : ( +
+ +
+ )} +
+

{triggerType.name} +

+
+
+ +
+

+ {triggerType.description}

- - -

- {triggerType.description} -

-
-
}
- {isLive &&
+ {isLive &&
This version is locked. Changes applied will not be reflected. @@ -135,43 +133,59 @@ export function TopBar({ {/* Deploy CTA - always visible */}
- - - + {isLive ? ( + + + + + + } + onPress={() => { if (projectId) { router.push(`/projects/${projectId}/config`); } }} + > + API & SDK Settings + + } + onPress={() => { if (projectId) { router.push(`/projects/${projectId}/manage-triggers`); } }} + > + Manage Triggers + + + + ) : ( + <> - - - } - onPress={onSettingsModalOpen} - > - API & SDK settings - - } - onPress={() => { if (projectId) { router.push(`/projects/${projectId}/job-rules`); } }} - > - Manage triggers - - {!isLive ? ( - <> + + + + + } @@ -187,10 +201,10 @@ export function TopBar({ > Reset to live version - - ) : null} - - + + + + )}
{isLive &&
@@ -199,6 +213,7 @@ export function TopBar({ size="md" onPress={() => onChangeMode('draft')} className="gap-2 px-4 bg-gray-600 hover:bg-gray-700 text-white font-semibold text-sm" + startContent={} > Switch to draft diff --git a/apps/rowboat/app/projects/[projectId]/workflow/components/TriggerConfigForm.tsx b/apps/rowboat/app/projects/[projectId]/workflow/components/TriggerConfigForm.tsx index 22f7dba6..29a32528 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/components/TriggerConfigForm.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/components/TriggerConfigForm.tsx @@ -199,7 +199,9 @@ export function TriggerConfigForm({ onChange={(e) => handleFieldChange(fieldName, e.target.value)} className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 - focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400" + focus:outline-none focus:ring-0 focus:ring-transparent focus:ring-offset-0 + focus:border-blue-500 dark:focus:border-blue-400 + transition-all duration-200" required={isRequired} > @@ -234,6 +236,11 @@ export function TriggerConfigForm({ description={property.description} isInvalid={!!fieldError} errorMessage={fieldError} + classNames={{ + base: "ring-0 !ring-0 outline-none !outline-none shadow-none !shadow-none focus:ring-0 !focus:ring-0 focus:ring-transparent !focus:ring-transparent focus-visible:ring-0 !focus-visible:ring-0 focus-visible:outline-none !focus-visible:outline-none focus-within:ring-0 !focus-within:ring-0 focus-within:shadow-none !focus-within:shadow-none", + mainWrapper: "ring-0 !ring-0 outline-none !outline-none shadow-none !shadow-none focus:ring-0 !focus:ring-0 focus:ring-transparent !focus:ring-transparent focus-visible:ring-0 !focus-visible:ring-0 focus-visible:outline-none !focus-visible:outline-none focus-within:ring-0 !focus-within:ring-0 focus-within:shadow-none !focus-within:shadow-none", + inputWrapper: "ring-0 !ring-0 outline-none !outline-none shadow-none !shadow-none focus:ring-0 !focus:ring-0 focus:ring-transparent !focus:ring-transparent focus-visible:ring-0 !focus-visible:ring-0 focus-visible:outline-none !focus-visible:outline-none focus-within:ring-0 !focus-within:ring-0 focus-within:shadow-none !focus-within:shadow-none data-[focus=true]:ring-0 group-data-[focus=true]:ring-0 data-[focus=true]:shadow-none group-data-[focus=true]:shadow-none", + }} /> ); })} diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index f677f96d..9788e7a4 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -34,7 +34,7 @@ import { ModelsResponse } from "@/app/lib/types/billing_types"; import { AgentGraphVisualizer } from "../entities/AgentGraphVisualizer"; import { Panel } from "@/components/common/panel-common"; import { Button as CustomButton } from "@/components/ui/button"; -import { ConfigApp } from "../config/app"; + import { InputField } from "@/app/lib/components/input-field"; import { VoiceSection } from "../config/components/voice"; import { TopBar } from "./components/TopBar"; @@ -376,7 +376,7 @@ function reducer(state: State, action: Action): State { properties: {}, required: [] }, - mockTool: true, + mockTool: false, ...action.tool }); draft.selection = { @@ -872,9 +872,6 @@ export function WorkflowEditor({ // Modal state for revert confirmation const { isOpen: isRevertModalOpen, onOpen: onRevertModalOpen, onClose: onRevertModalClose } = useDisclosure(); - // Modal state for settings - const { isOpen: isSettingsModalOpen, onOpen: onSettingsModalOpen, onClose: onSettingsModalClose } = useDisclosure(); - // Modal state for phone/Twilio configuration const { isOpen: isPhoneModalOpen, onOpen: onPhoneModalOpen, onClose: onPhoneModalClose } = useDisclosure(); @@ -1280,7 +1277,6 @@ export function WorkflowEditor({ onChangeMode={onChangeMode} onRevertToLive={handleRevertToLive} onToggleCopilot={() => setShowCopilot(!showCopilot)} - onSettingsModalOpen={onSettingsModalOpen} /> {/* Content Area */} @@ -1498,26 +1494,7 @@ export function WorkflowEditor({ - {/* Settings Modal */} - - - - API & SDK - - - - - - + {/* Phone/Twilio Modal */} {/* Rowboat Logo */}
- + } {!collapsed && + {collapsed && - - - {!collapsed && ( - {item.label} - )} - - ); + } + {!collapsed && + + {item.label} + } + }) )} @@ -247,7 +258,7 @@ export default function Sidebar({ projectId, useAuth, collapsed = false, onToggl
{USE_PRODUCT_TOUR && !isProjectsRoute && ( - )} - {useAuth && ( - -
- - {!collapsed && Account} -
-
- )} + {useAuth && <> + {collapsed && + + } + {!collapsed && } + }
{/* Create Assistant Modal */} - diff --git a/apps/rowboat/components/common/billing-upgrade-modal.tsx b/apps/rowboat/components/common/billing-upgrade-modal.tsx index fe087517..abc0863d 100644 --- a/apps/rowboat/components/common/billing-upgrade-modal.tsx +++ b/apps/rowboat/components/common/billing-upgrade-modal.tsx @@ -84,7 +84,6 @@ export function BillingUpgradeModal({ isOpen, onClose, errorMessage }: BillingUp description: "Great for power users or teams", features: [ "20,000 credits", - "o3 and o3-pro", "Priority support", ], recommended: true diff --git a/apps/rowboat/components/common/panel-common.tsx b/apps/rowboat/components/common/panel-common.tsx index 87228150..1ceb8208 100644 --- a/apps/rowboat/components/common/panel-common.tsx +++ b/apps/rowboat/components/common/panel-common.tsx @@ -82,11 +82,47 @@ export function Panel({ data-tour-target={tourTarget} > {variant === 'copilot' && showWelcome && ( -
+
{/* Replace Sparkles icon with mascot image */} - Rowboat Mascot + Rowboat Mascot + + {/* Welcome/Intro Section */} +
+

+ 👋 Welcome to Rowboat! +

+

+ I'm your copilot for building agents and adding tools to them. +

+

+ Here's what you can do in Rowboat: +

+
+
+ + Build AI agents instantly with natural language. +
+
+ 🔌 + Connect tools with one-click integrations. +
+
+ 📂 + Power with knowledge by adding documents for RAG. +
+
+ 🔄 + Automate workflows by setting up triggers and actions. +
+
+ 🚀 + Deploy anywhere via API or SDK. +
+
+
+ {SHOW_COPILOT_MARQUEE && ( -
+
What can I help you build?