Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions apps/desktop2/src/components/interactive-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface InteractiveButtonProps {
children: ReactNode;
onClick?: () => void;
onCmdClick?: () => void;
onMouseDown?: (e: MouseEvent<HTMLElement>) => void;
contextMenu?: ReactNode;
className?: string;
disabled?: boolean;
Expand All @@ -15,6 +16,7 @@ export function InteractiveButton({
children,
onClick,
onCmdClick,
onMouseDown,
contextMenu,
className,
disabled,
Expand All @@ -40,7 +42,12 @@ export function InteractiveButton({

if (!contextMenu) {
return (
<Element onClick={handleClick} className={className} disabled={!asChild ? disabled : undefined}>
<Element
onClick={handleClick}
onMouseDown={onMouseDown}
className={className}
disabled={!asChild ? disabled : undefined}
>
{children}
</Element>
);
Expand All @@ -49,7 +56,12 @@ export function InteractiveButton({
return (
<ContextMenu>
<ContextMenuTrigger asChild={asChild}>
<Element onClick={handleClick} className={className} disabled={!asChild ? disabled : undefined}>
<Element
onClick={handleClick}
onMouseDown={onMouseDown}
className={className}
disabled={!asChild ? disabled : undefined}
>
{children}
</Element>
</ContextMenuTrigger>
Expand Down
127 changes: 66 additions & 61 deletions apps/desktop2/src/components/main/body/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Button } from "@hypr/ui/components/ui/button";
import { cn } from "@hypr/ui/lib/utils";

import { useRouteContext } from "@tanstack/react-router";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { ArrowLeftIcon, ArrowRightIcon, PanelLeftOpenIcon, PlusIcon } from "lucide-react";
import { Reorder } from "motion/react";
import { useCallback, useEffect, useRef } from "react";
import { useHotkeys } from "react-hotkeys-hook";

import { cn } from "@hypr/ui/lib/utils";
import { useShell } from "../../../contexts/shell";
import { type Tab, uniqueIdfromTab, useTabs } from "../../../store/zustand/tabs";
import { id } from "../../../utils";
Expand All @@ -23,6 +25,7 @@ export function Body() {

useTabCloseHotkey();
useTabSelectHotkeys();
useNewTabHotkeys();

if (!currentTab) {
return null;
Expand Down Expand Up @@ -62,70 +65,43 @@ function Header({ tabs }: { tabs: Tab[] }) {
return (
<div
className={cn([
"w-full h-9 flex items-end",
"w-full h-9 flex items-center",
!leftsidebar.expanded && "pl-[72px]",
])}
>
{!leftsidebar.expanded && (
<div className="flex items-center justify-center h-full px-3 shrink-0 bg-white z-20">
<Button size="icon" variant="ghost" onClick={() => leftsidebar.setExpanded(true)}>
<PanelLeftOpenIcon
className="h-5 w-5 cursor-pointer"
onClick={() => leftsidebar.setExpanded(true)}
size={16}
/>
</div>
</Button>
)}

<div className="flex items-center h-full shrink-0">
<button
<Button
onClick={goBack}
disabled={!canGoBack}
className={cn([
"flex items-center justify-center",
"h-full",
"px-1.5",
"rounded-lg",
"transition-colors",
canGoBack && ["hover:bg-gray-50", "group"],
!canGoBack && "cursor-not-allowed",
])}
variant="ghost"
size="icon"
>
<ArrowLeftIcon
className={cn([
"h-4 w-4",
canGoBack && ["text-black/70", "cursor-pointer", "group-hover:text-black"],
!canGoBack && ["text-black/30", "cursor-not-allowed"],
])}
/>
</button>
<button
<ArrowLeftIcon size={16} />
</Button>
<Button
onClick={goNext}
disabled={!canGoNext}
className={cn([
"flex items-center justify-center",
"h-full",
"px-1.5",
"rounded-lg",
"transition-colors",
canGoNext && ["hover:bg-gray-50", "group"],
!canGoNext && "cursor-not-allowed",
])}
variant="ghost"
size="icon"
>
<ArrowRightIcon
className={cn([
"h-4 w-4",
canGoNext && ["text-black/70", "cursor-pointer", "group-hover:text-black"],
!canGoNext && ["text-black/30", "cursor-not-allowed"],
])}
/>
</button>
<ArrowRightIcon size={16} />
</Button>
</div>

<div
ref={tabsScrollContainerRef}
data-tauri-drag-region
className={cn([
"[&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]",
"flex-1 min-w-0 overflow-x-auto overflow-y-hidden h-full",
"w-fit overflow-x-auto overflow-y-hidden h-full",
])}
>
<Reorder.Group
Expand Down Expand Up @@ -158,22 +134,21 @@ function Header({ tabs }: { tabs: Tab[] }) {
</Reorder.Group>
</div>

<button
onClick={handleNewNote}
className={cn([
"flex items-center justify-center",
"h-full",
"px-1.5",
"rounded-lg",
"bg-white hover:bg-gray-50",
"transition-colors",
"shrink-0",
])}
<div
data-tauri-drag-region
className="flex-1 flex h-full items-center justify-between"
>
<PlusIcon className="h-4 w-4 text-color3 cursor-pointer" />
</button>
<Button
onClick={handleNewNote}
variant="ghost"
size="icon"
className="text-color3"
>
<PlusIcon size={16} />
</Button>

<Search />
<Search />
</div>
</div>
);
}
Expand Down Expand Up @@ -303,8 +278,8 @@ export function StandardTabWrapper(
{ children, afterBorder }: { children: React.ReactNode; afterBorder?: React.ReactNode },
) {
return (
<div className="flex flex-col h-full gap-1">
<div className="flex flex-col px-4 py-1 rounded-lg border flex-1 overflow-hidden relative">
<div className="flex flex-col h-full">
<div className="flex flex-col p-2 rounded-lg border flex-1 overflow-hidden relative">
{children}
<TabChatButton />
</div>
Expand All @@ -328,7 +303,7 @@ const useTabCloseHotkey = () => {
await appWindow.close();
}
},
{ enableOnFormTags: true },
{ enableOnFormTags: true, enableOnContentEditable: true },
[tabs, currentTab, close],
);
};
Expand All @@ -353,11 +328,41 @@ const useTabSelectHotkeys = () => {
event.preventDefault();
select(target);
},
{ enableOnFormTags: true },
{ enableOnFormTags: true, enableOnContentEditable: true },
[tabs, select],
);
};

const useNewTabHotkeys = () => {
const { persistedStore, internalStore } = useRouteContext({ from: "__root__" });
const { currentTab, close, openNew } = useTabs();

useHotkeys(
["mod+n", "mod+t"],
(e) => {
e.preventDefault();

const sessionId = id();
const user_id = internalStore?.getValue("user_id");

persistedStore?.setRow("sessions", sessionId, { user_id, created_at: new Date().toISOString() });

if (e.key === "n" && currentTab) {
close(currentTab);
}

openNew({
type: "sessions",
id: sessionId,
active: true,
state: { editor: "raw" },
});
},
{ enableOnFormTags: true, enableOnContentEditable: true },
[persistedStore, internalStore, currentTab, close, openNew],
);
};
Comment on lines +336 to +364
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix inconsistent session initialization.

Line 348 creates a session row without the title field, but handleNewNote (line 56) includes title: "". This inconsistency could cause issues if the schema expects the title field.

Apply this diff to align with handleNewNote:

-      persistedStore?.setRow("sessions", sessionId, { user_id, created_at: new Date().toISOString() });
+      persistedStore?.setRow("sessions", sessionId, { user_id, created_at: new Date().toISOString(), title: "" });
🤖 Prompt for AI Agents
In apps/desktop2/src/components/main/body/index.tsx around lines 336 to 364, the
session row written in the hotkey handler is missing the title field (created at
line ~348) while handleNewNote (line 56) sets title: ""; update the
persistedStore?.setRow call to include title: "" alongside user_id and
created_at so the session initialization matches handleNewNote and the schema
expectation.


function useScrollActiveTabIntoView(tabs: Tab[]) {
const tabRefsMap = useRef<Map<string, HTMLDivElement>>(new Map());

Expand Down
12 changes: 7 additions & 5 deletions apps/desktop2/src/components/main/body/sessions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ export function TabContentNote({ tab }: { tab: Extract<Tab, { type: "sessions" }
return (
<AudioPlayer.Provider url="/assets/audio.wav">
<StandardTabWrapper afterBorder={tab.state.editor === "transcript" && <AudioPlayer.Timeline />}>
<div className="mt-0.5 mb-2">
<OuterHeader sessionId={tab.id} />
<OuterHeader sessionId={tab.id} />
<div className="mt-3 px-2">
<TitleInput tab={tab} />
<div className="mt-2">
<NoteInput tab={tab} />
</div>
<FloatingActionButton tab={tab} />
</div>
<TitleInput tab={tab} />
<NoteInput tab={tab} />
<FloatingActionButton tab={tab} />
</StandardTabWrapper>
</AudioPlayer.Provider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,21 @@ function Header(
}

return (
<div className="border-b border-neutral-100">
<div className="flex">
{editorTabs.map((view) => (
<button
key={view}
onClick={() => handleTabChange(view)}
className={cn([
"relative px-3 py-2 text-xs font-medium transition-all duration-200 border-b-2 -mb-px flex items-center gap-1.5",
currentTab === view
? ["text-neutral-900", "border-neutral-900"]
: ["text-neutral-600", "border-transparent", "hover:text-neutral-800"],
])}
>
{labelForEditorView(view)}
</button>
))}
</div>
<div className="flex gap-4">
{editorTabs.map((view) => (
<button
key={view}
onClick={() => handleTabChange(view)}
className={cn([
"relative py-2 text-xs font-medium transition-all duration-200 border-b-2 -mb-px",
currentTab === view
? ["text-neutral-900", "border-neutral-900"]
: ["text-neutral-600", "border-transparent", "hover:text-neutral-800"],
])}
>
{labelForEditorView(view)}
</button>
))}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ const PlaceHolderInner = () => {

return (
<div className="flex flex-col gap-1">
<span className="text-gray-400">
<span className="text-[#e5e5e5]">
Take notes or press <kbd>/</kbd> for commands.
</span>
<div className={cn("flex flex-row items-center gap-1", isNarrow && "hidden")}>
<span className="text-gray-400">You can also upload/drop an</span>
<span className="text-[#e5e5e5]">You can also upload/drop an</span>
<button
type="button"
className="text-gray-400 hover:text-gray-600 transition-colors underline"
className="text-gray-400 hover:text-gray-600 transition-colors underline decoration-dotted hover:decoration-solid"
onClick={handleFileSelect}
>
audio file or transcript file.
Expand Down
Loading
Loading