Skip to content

Commit 6ab0eed

Browse files
authored
Minor fixes (#1249)
* copy button, gemini 2.5, event/date change tab * ran tests * ran tests * saved * changed chat icon
1 parent c3cde4b commit 6ab0eed

File tree

6 files changed

+167
-99
lines changed

6 files changed

+167
-99
lines changed

apps/desktop/src/components/editor-area/note-header/chips/event-chip.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
44
import { openUrl } from "@tauri-apps/plugin-opener";
55
import { format, isSameDay, subDays } from "date-fns";
66
import { CalendarIcon, SearchIcon, SpeechIcon, VideoIcon, XIcon } from "lucide-react";
7-
import { useState } from "react";
7+
import { useEffect, useState } from "react";
88

99
import { useHypr } from "@/contexts";
1010
import { commands as appleCalendarCommands } from "@hypr/plugin-apple-calendar";
@@ -37,6 +37,12 @@ export function EventChip({ sessionId }: EventChipProps) {
3737
const [activeTab, setActiveTab] = useState<"event" | "date">("event");
3838
const queryClient = useQueryClient();
3939

40+
useEffect(() => {
41+
if (isPopoverOpen) {
42+
setActiveTab("event");
43+
}
44+
}, [isPopoverOpen]);
45+
4046
const {
4147
sessionCreatedAt,
4248
updateTitle,

apps/desktop/src/components/settings/components/ai/llm-custom-view.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const openaiModels = [
2525
];
2626

2727
const geminiModels = [
28+
"gemini-2.5-pro",
2829
"gemini-1.5-pro",
2930
"gemini-1.5-flash",
3031
];

apps/desktop/src/components/toolbar/buttons/chat-panel-button.tsx

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Trans } from "@lingui/react/macro";
2-
import { memo, useEffect, useState } from "react";
2+
import { MessageCircleMore } from "lucide-react";
3+
import { memo, useEffect } from "react";
34

45
import { useRightPanel } from "@/contexts";
56
import { Button } from "@hypr/ui/components/ui/button";
@@ -9,22 +10,9 @@ import Shortcut from "../../shortcut";
910

1011
function ChatPanelButtonBase() {
1112
const { isExpanded, currentView, togglePanel } = useRightPanel();
12-
const [isAnimating, setIsAnimating] = useState(false);
1313

1414
const isActive = isExpanded && currentView === "chat";
1515

16-
useEffect(() => {
17-
const animationInterval = setInterval(() => {
18-
setIsAnimating(true);
19-
const timeout = setTimeout(() => {
20-
setIsAnimating(false);
21-
}, 1625);
22-
return () => clearTimeout(timeout);
23-
}, 4625);
24-
25-
return () => clearInterval(animationInterval);
26-
}, []);
27-
2816
useEffect(() => {
2917
const handleKeyDown = (event: KeyboardEvent) => {
3018
if (event.key === "j" && (event.metaKey || event.ctrlKey)) {
@@ -53,13 +41,7 @@ function ChatPanelButtonBase() {
5341
onClick={handleClick}
5442
className={cn("hover:bg-neutral-200 text-xs size-7 p-0", isActive && "bg-neutral-200")}
5543
>
56-
<div className="relative w-6 aspect-square flex items-center justify-center">
57-
<img
58-
src={isAnimating ? "/assets/dynamic.gif" : "/assets/static.png"}
59-
alt="Chat Assistant"
60-
className="w-full h-full"
61-
/>
62-
</div>
44+
<MessageCircleMore className="w-4 h-4" />
6345
</Button>
6446
</TooltipTrigger>
6547
<TooltipContent>

apps/desktop/src/components/toolbar/buttons/share-button.tsx

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { join } from "@tauri-apps/api/path";
44
import { message } from "@tauri-apps/plugin-dialog";
55
import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
66
import { openPath, openUrl } from "@tauri-apps/plugin-opener";
7-
import { BookText, ChevronDown, ChevronUp, FileText, HelpCircle, Mail, Share2Icon } from "lucide-react";
7+
import { BookText, Check, ChevronDown, ChevronUp, Copy, FileText, HelpCircle, Mail, Share2Icon } from "lucide-react";
88
import { useState } from "react";
99

1010
import { useHypr } from "@/contexts";
@@ -38,6 +38,7 @@ function ShareButtonInNote() {
3838
const [open, setOpen] = useState(false);
3939
const [expandedId, setExpandedId] = useState<string | null>(null);
4040
const [selectedObsidianFolder, setSelectedObsidianFolder] = useState<string>("default");
41+
const [copySuccess, setCopySuccess] = useState(false);
4142
const hasEnhancedNote = !!session?.enhanced_memo_html;
4243

4344
const isObsidianConfigured = useQuery({
@@ -72,6 +73,15 @@ function ShareButtonInNote() {
7273
staleTime: 5 * 60 * 1000,
7374
});
7475

76+
const directActions: DirectAction[] = [
77+
{
78+
id: "copy",
79+
title: "Copy Note",
80+
icon: <Copy size={20} />,
81+
description: "",
82+
},
83+
];
84+
7585
const exportOptions: ExportCard[] = [
7686
{
7787
id: "pdf",
@@ -124,6 +134,10 @@ function ShareButtonInNote() {
124134
setOpen(newOpen);
125135
setExpandedId(null);
126136

137+
if (!newOpen) {
138+
setCopySuccess(false);
139+
}
140+
127141
if (newOpen) {
128142
isObsidianConfigured.refetch().then((configResult) => {
129143
if (configResult.data) {
@@ -143,7 +157,9 @@ function ShareButtonInNote() {
143157
const start = performance.now();
144158
let result: ExportResult | null = null;
145159

146-
if (optionId === "pdf") {
160+
if (optionId === "copy") {
161+
result = await exportHandlers.copy(session);
162+
} else if (optionId === "pdf") {
147163
result = await exportHandlers.pdf(session);
148164
} else if (optionId === "email") {
149165
result = await exportHandlers.email(session);
@@ -187,16 +203,22 @@ function ShareButtonInNote() {
187203
});
188204
},
189205
onSuccess: (result) => {
190-
if (result?.type === "pdf" && result.path) {
206+
if (result?.type === "copy" && result.success) {
207+
setCopySuccess(true);
208+
// Reset after 2 seconds
209+
setTimeout(() => setCopySuccess(false), 2000);
210+
} else if (result?.type === "pdf" && result.path) {
191211
openPath(result.path);
192212
} else if (result?.type === "email" && result.url) {
193213
openUrl(result.url);
194214
} else if (result?.type === "obsidian" && result.url) {
195215
openUrl(result.url);
196216
}
197217
},
198-
onSettled: () => {
199-
setOpen(false);
218+
onSettled: (result) => {
219+
if (result?.type !== "copy") {
220+
setOpen(false);
221+
}
200222
},
201223
onError: (error) => {
202224
console.error(error);
@@ -238,6 +260,35 @@ function ShareButtonInNote() {
238260
</p>
239261
</div>
240262
<div className="space-y-2">
263+
{/* Direct action buttons */}
264+
{directActions.map((action) => {
265+
const isLoading = exportMutation.isPending && exportMutation.variables?.optionId === action.id;
266+
const isSuccess = action.id === "copy" && copySuccess;
267+
268+
return (
269+
<div key={action.id} className="border rounded-lg overflow-hidden">
270+
<button
271+
onClick={() => handleExport(action.id)}
272+
disabled={exportMutation.isPending}
273+
className="w-full flex items-center justify-between p-3 hover:bg-gray-50 transition-colors disabled:opacity-50"
274+
>
275+
<div className="flex items-center space-x-3">
276+
<div className={`text-gray-700 transition-colors ${isSuccess ? "text-green-600" : ""}`}>
277+
{isSuccess ? <Check size={20} /> : action.icon}
278+
</div>
279+
<div className="text-left">
280+
<span className="font-medium text-sm block">{action.title}</span>
281+
<span className="text-xs text-gray-600">{action.description}</span>
282+
</div>
283+
</div>
284+
{isLoading && <span className="text-xs text-gray-500">Copying...</span>}
285+
{isSuccess && <span className="text-xs text-green-600">Copied!</span>}
286+
</button>
287+
</div>
288+
);
289+
})}
290+
291+
{/* Expandable export options */}
241292
{exportOptions.map((option) => {
242293
const expanded = expandedId === option.id;
243294

@@ -315,6 +366,13 @@ function ShareButtonInNote() {
315366
);
316367
}
317368

369+
interface DirectAction {
370+
id: "copy";
371+
title: string;
372+
icon: React.ReactNode;
373+
description: string;
374+
}
375+
318376
interface ExportCard {
319377
id: "pdf" | "email" | "obsidian";
320378
title: string;
@@ -324,9 +382,10 @@ interface ExportCard {
324382
}
325383

326384
interface ExportResult {
327-
type: "pdf" | "email" | "obsidian";
385+
type: "copy" | "pdf" | "email" | "obsidian";
328386
path?: string;
329387
url?: string;
388+
success?: boolean;
330389
}
331390

332391
interface ObsidianFolder {
@@ -335,6 +394,26 @@ interface ObsidianFolder {
335394
}
336395

337396
const exportHandlers = {
397+
copy: async (session: Session): Promise<ExportResult> => {
398+
try {
399+
let textToCopy = "";
400+
401+
if (session.enhanced_memo_html) {
402+
textToCopy = html2md(session.enhanced_memo_html);
403+
} else if (session.raw_memo_html) {
404+
textToCopy = html2md(session.raw_memo_html);
405+
} else {
406+
textToCopy = session.title || "No content available";
407+
}
408+
409+
await navigator.clipboard.writeText(textToCopy);
410+
return { type: "copy", success: true };
411+
} catch (error) {
412+
console.error("Failed to copy to clipboard:", error);
413+
throw new Error("Failed to copy note to clipboard");
414+
}
415+
},
416+
338417
pdf: async (session: Session): Promise<ExportResult> => {
339418
const path = await exportToPDF(session);
340419
return { type: "pdf", path };

0 commit comments

Comments
 (0)