Skip to content

Commit 902447f

Browse files
committed
chore: fit individual demos for mobile
1 parent f5b1a51 commit 902447f

File tree

7 files changed

+525
-91
lines changed

7 files changed

+525
-91
lines changed

typescript-sdk/apps/dojo/src/app/[integrationId]/feature/predictive_state_updates/page.tsx

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import { useEditor, EditorContent } from "@tiptap/react";
1010
import StarterKit from "@tiptap/starter-kit";
1111
import { useEffect, useState } from "react";
1212
import { CopilotKit, useCoAgent, useCopilotAction, useCopilotChat } from "@copilotkit/react-core";
13-
import { CopilotSidebar } from "@copilotkit/react-ui";
13+
import { CopilotChat, CopilotSidebar } from "@copilotkit/react-ui";
14+
import { useMobileView } from "@/utils/use-mobile-view";
15+
import { useMobileChat } from "@/utils/use-mobile-chat";
1416

1517
const extensions = [StarterKit];
1618

@@ -22,6 +24,19 @@ interface PredictiveStateUpdatesProps {
2224

2325
export default function PredictiveStateUpdates({ params }: PredictiveStateUpdatesProps) {
2426
const { integrationId } = React.use(params);
27+
const { isMobile } = useMobileView();
28+
const defaultChatHeight = 50
29+
const {
30+
isChatOpen,
31+
setChatHeight,
32+
setIsChatOpen,
33+
isDragging,
34+
chatHeight,
35+
handleDragStart
36+
} = useMobileChat(defaultChatHeight)
37+
const chatTitle = 'AI Document Editor'
38+
const chatDescription = 'Ask me to create or edit a document'
39+
const initialLabel = 'Hi 👋 How can I help with your document?'
2540

2641
return (
2742
<CopilotKit
@@ -39,16 +54,99 @@ export default function PredictiveStateUpdates({ params }: PredictiveStateUpdate
3954
} as React.CSSProperties
4055
}
4156
>
42-
<CopilotSidebar
43-
defaultOpen={true}
44-
labels={{
45-
title: "AI Document Editor",
46-
initial: "Hi 👋 How can I help with your document?",
47-
}}
48-
clickOutsideToClose={false}
49-
>
50-
<DocumentEditor />
51-
</CopilotSidebar>
57+
{isMobile ? (
58+
<>
59+
{/* Chat Toggle Button */}
60+
<div className="fixed bottom-0 left-0 right-0 z-50">
61+
<div className="bg-gradient-to-t from-white via-white to-transparent h-6"></div>
62+
<div
63+
className="bg-white border-t border-gray-200 px-4 py-3 flex items-center justify-between cursor-pointer shadow-lg"
64+
onClick={() => {
65+
if (!isChatOpen) {
66+
setChatHeight(defaultChatHeight); // Reset to good default when opening
67+
}
68+
setIsChatOpen(!isChatOpen);
69+
}}
70+
>
71+
<div className="flex items-center gap-3">
72+
<div>
73+
<div className="font-medium text-gray-900">{chatTitle}</div>
74+
<div className="text-sm text-gray-500">{chatDescription}</div>
75+
</div>
76+
</div>
77+
<div className={`transform transition-transform duration-300 ${isChatOpen ? 'rotate-180' : ''}`}>
78+
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
79+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
80+
</svg>
81+
</div>
82+
</div>
83+
</div>
84+
85+
{/* Pull-Up Chat Container */}
86+
<div
87+
className={`fixed inset-x-0 bottom-0 z-40 bg-white rounded-t-2xl shadow-[0px_0px_20px_0px_rgba(0,0,0,0.15)] transform transition-all duration-300 ease-in-out flex flex-col ${
88+
isChatOpen ? 'translate-y-0' : 'translate-y-full'
89+
} ${isDragging ? 'transition-none' : ''}`}
90+
style={{
91+
height: `${chatHeight}vh`,
92+
paddingBottom: 'env(safe-area-inset-bottom)' // Handle iPhone bottom padding
93+
}}
94+
>
95+
{/* Drag Handle Bar */}
96+
<div
97+
className="flex justify-center pt-3 pb-2 flex-shrink-0 cursor-grab active:cursor-grabbing"
98+
onMouseDown={handleDragStart}
99+
>
100+
<div className="w-12 h-1 bg-gray-400 rounded-full hover:bg-gray-500 transition-colors"></div>
101+
</div>
102+
103+
{/* Chat Header */}
104+
<div className="px-4 py-3 border-b border-gray-100 flex-shrink-0">
105+
<div className="flex items-center justify-between">
106+
<div className="flex items-center gap-3">
107+
<h3 className="font-semibold text-gray-900">{chatTitle}</h3>
108+
</div>
109+
<button
110+
onClick={() => setIsChatOpen(false)}
111+
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
112+
>
113+
<svg className="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
114+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
115+
</svg>
116+
</button>
117+
</div>
118+
</div>
119+
120+
{/* Chat Content - Flexible container for messages and input */}
121+
<div className="flex-1 flex flex-col min-h-0 overflow-hidden pb-16">
122+
<CopilotChat
123+
className="h-full flex flex-col"
124+
labels={{
125+
initial: initialLabel,
126+
}}
127+
/>
128+
</div>
129+
</div>
130+
131+
{/* Backdrop */}
132+
{isChatOpen && (
133+
<div
134+
className="fixed inset-0 z-30"
135+
onClick={() => setIsChatOpen(false)}
136+
/>
137+
)}
138+
</>
139+
) : (
140+
<CopilotSidebar
141+
defaultOpen={true}
142+
labels={{
143+
title: chatTitle,
144+
initial: initialLabel,
145+
}}
146+
clickOutsideToClose={false}
147+
/>
148+
)}
149+
<DocumentEditor />
52150
</div>
53151
</CopilotKit>
54152
);

typescript-sdk/apps/dojo/src/app/[integrationId]/feature/shared_state/page.tsx

Lines changed: 109 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"use client";
22
import { CopilotKit, useCoAgent, useCopilotChat } from "@copilotkit/react-core";
3-
import { CopilotSidebar } from "@copilotkit/react-ui";
3+
import { CopilotChat, CopilotSidebar } from "@copilotkit/react-ui";
44
import React, { useState, useEffect, useRef } from "react";
55
import { Role, TextMessage } from "@copilotkit/runtime-client-gql";
66
import "@copilotkit/react-ui/styles.css";
77
import "./style.css";
8+
import { useMobileView } from "@/utils/use-mobile-view";
9+
import { useMobileChat } from "@/utils/use-mobile-chat";
810

911
interface SharedStateProps {
1012
params: Promise<{
@@ -14,6 +16,20 @@ interface SharedStateProps {
1416

1517
export default function SharedState({ params }: SharedStateProps) {
1618
const { integrationId } = React.use(params);
19+
const { isMobile } = useMobileView();
20+
const defaultChatHeight = 50
21+
const {
22+
isChatOpen,
23+
setChatHeight,
24+
setIsChatOpen,
25+
isDragging,
26+
chatHeight,
27+
handleDragStart
28+
} = useMobileChat(defaultChatHeight)
29+
30+
const chatTitle = 'AI Recipe Assistant'
31+
const chatDescription = 'Ask me to craft recipes'
32+
const initialLabel = 'Hi 👋 How can I help with your recipe?'
1733

1834
return (
1935
<CopilotKit
@@ -34,14 +50,98 @@ export default function SharedState({ params }: SharedStateProps) {
3450
}
3551
>
3652
<Recipe />
37-
<CopilotSidebar
38-
defaultOpen={true}
39-
labels={{
40-
title: "AI Recipe Assistant",
41-
initial: "Hi 👋 How can I help with your recipe?",
42-
}}
43-
clickOutsideToClose={false}
44-
/>
53+
{isMobile ? (
54+
<>
55+
{/* Chat Toggle Button */}
56+
<div className="fixed bottom-0 left-0 right-0 z-50">
57+
<div className="bg-gradient-to-t from-white via-white to-transparent h-6"></div>
58+
<div
59+
className="bg-white border-t border-gray-200 px-4 py-3 flex items-center justify-between cursor-pointer shadow-lg"
60+
onClick={() => {
61+
if (!isChatOpen) {
62+
setChatHeight(defaultChatHeight); // Reset to good default when opening
63+
}
64+
setIsChatOpen(!isChatOpen);
65+
}}
66+
>
67+
<div className="flex items-center gap-3">
68+
<div>
69+
<div className="font-medium text-gray-900">{chatTitle}</div>
70+
<div className="text-sm text-gray-500">{chatDescription}</div>
71+
</div>
72+
</div>
73+
<div className={`transform transition-transform duration-300 ${isChatOpen ? 'rotate-180' : ''}`}>
74+
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
75+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 15l7-7 7 7" />
76+
</svg>
77+
</div>
78+
</div>
79+
</div>
80+
81+
{/* Pull-Up Chat Container */}
82+
<div
83+
className={`fixed inset-x-0 bottom-0 z-40 bg-white rounded-t-2xl shadow-[0px_0px_20px_0px_rgba(0,0,0,0.15)] transform transition-all duration-300 ease-in-out flex flex-col ${
84+
isChatOpen ? 'translate-y-0' : 'translate-y-full'
85+
} ${isDragging ? 'transition-none' : ''}`}
86+
style={{
87+
height: `${chatHeight}vh`,
88+
paddingBottom: 'env(safe-area-inset-bottom)' // Handle iPhone bottom padding
89+
}}
90+
>
91+
{/* Drag Handle Bar */}
92+
<div
93+
className="flex justify-center pt-3 pb-2 flex-shrink-0 cursor-grab active:cursor-grabbing"
94+
onMouseDown={handleDragStart}
95+
>
96+
<div className="w-12 h-1 bg-gray-400 rounded-full hover:bg-gray-500 transition-colors"></div>
97+
</div>
98+
99+
{/* Chat Header */}
100+
<div className="px-4 py-3 border-b border-gray-100 flex-shrink-0">
101+
<div className="flex items-center justify-between">
102+
<div className="flex items-center gap-3">
103+
<h3 className="font-semibold text-gray-900">{chatTitle}</h3>
104+
</div>
105+
<button
106+
onClick={() => setIsChatOpen(false)}
107+
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
108+
>
109+
<svg className="w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
110+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
111+
</svg>
112+
</button>
113+
</div>
114+
</div>
115+
116+
{/* Chat Content - Flexible container for messages and input */}
117+
<div className="flex-1 flex flex-col min-h-0 overflow-hidden pb-16">
118+
<CopilotChat
119+
className="h-full flex flex-col"
120+
labels={{
121+
initial: initialLabel,
122+
}}
123+
/>
124+
</div>
125+
</div>
126+
127+
{/* Backdrop */}
128+
{isChatOpen && (
129+
<div
130+
className="fixed inset-0 z-30"
131+
onClick={() => setIsChatOpen(false)}
132+
/>
133+
)}
134+
</>
135+
) : (
136+
<CopilotSidebar
137+
defaultOpen={true}
138+
labels={{
139+
title: chatTitle,
140+
initial: initialLabel,
141+
}}
142+
clickOutsideToClose={false}
143+
/>
144+
)}
45145
</div>
46146
</CopilotKit>
47147
);

0 commit comments

Comments
 (0)