Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,5 @@
"rust-analyzer.checkOnSave": true,
"rust-analyzer.check.allTargets": true,
"rust-analyzer.cargo.targetDir": "target/analyzer",
"rust-analyzer.cargo.extraEnv": { "MACOSX_DEPLOYMENT_TARGET": "14.2" },
"rust-analyzer.cargo.features": ["v1"]
"rust-analyzer.cargo.extraEnv": { "MACOSX_DEPLOYMENT_TARGET": "14.2" }
}
25 changes: 0 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ tauri-plugin-updater = "2.9"
tauri-plugin-analytics = { path = "plugins/analytics" }
tauri-plugin-apple-calendar = { path = "plugins/apple-calendar" }
tauri-plugin-auth = { path = "plugins/auth" }
tauri-plugin-connector = { path = "plugins/connector" }
tauri-plugin-db = { path = "plugins/db" }
tauri-plugin-db2 = { path = "plugins/db2" }
tauri-plugin-listener = { path = "plugins/listener" }
Expand Down
1 change: 1 addition & 0 deletions apps/desktop2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"dompurify": "^3.3.0",
"lucide-react": "^0.544.0",
"motion": "^11.18.2",
"mutative": "^1.3.0",
"re-resizable": "^6.11.2",
"react": "^19.2.0",
"react-dom": "^19.2.0",
Expand Down
8 changes: 4 additions & 4 deletions apps/desktop2/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ tauri-plugin-db2 = { workspace = true }
tauri-plugin-deep-link = { workspace = true }
tauri-plugin-dialog = { workspace = true }
tauri-plugin-fs = { workspace = true }
tauri-plugin-listener = { workspace = true, features = ["v1"] }
tauri-plugin-local-stt = { workspace = true, features = ["v1"] }
tauri-plugin-listener = { workspace = true }
tauri-plugin-local-stt = { workspace = true }
tauri-plugin-opener = { workspace = true }
tauri-plugin-os = { workspace = true }
tauri-plugin-process = { workspace = true }
Expand All @@ -36,9 +36,9 @@ tauri-plugin-single-instance = { workspace = true }
tauri-plugin-store = { workspace = true }
tauri-plugin-store2 = { workspace = true }
tauri-plugin-tracing = { workspace = true }
tauri-plugin-tray = { workspace = true, features = ["v1"] }
tauri-plugin-tray = { workspace = true }
tauri-plugin-updater = { workspace = true }
tauri-plugin-windows = { workspace = true, default-features = false, features = ["v1"] }
tauri-plugin-windows = { workspace = true, default-features = false }

specta = { workspace = true }
specta-typescript = { workspace = true }
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop2/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"store:default",
"process:default",
"local-stt:default",
"dialog:default"
"dialog:default",
"listener:default"
]
}
2 changes: 1 addition & 1 deletion apps/desktop2/src/components/chat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback } from "react";

import { commands as windowsCommands } from "@hypr/plugin-windows/v1";
import { commands as windowsCommands } from "@hypr/plugin-windows";
import { useShell } from "../../contexts/shell";
import { useAutoCloser } from "../../hooks/useAutoCloser";
import { InteractiveContainer } from "./interactive";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export function GenerateButton() {

<FloatingButton
icon={<SparklesIcon className="w-4 h-4" />}
style={{ zIndex: 10 }}
onMouseEnter={() => setShowTemplates(true)}
onMouseLeave={() => setShowTemplates(false)}
onClick={() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,74 @@
import { Icon } from "@iconify-icon/react";
import { openUrl } from "@tauri-apps/plugin-opener";

import { useListener } from "../../../../../contexts/listener";
import * as persisted from "../../../../../store/tinybase/persisted";
import { type Tab } from "../../../../../store/zustand/tabs";
import { FloatingButton } from "./shared";

type RemoteMeeting =
| { type: "zoom"; url: string }
| { type: "google-meet"; url: string };
| { type: "zoom"; url: string | null }
| { type: "google-meet"; url: string | null };

export function ListenButton({ tab }: { tab: Extract<Tab, { type: "sessions" }> }) {
const remote = useRemoteMeeting(tab.id);
const { status, loading, start, stop } = useListener((state) => ({
status: state.status,
loading: state.loading,
start: state.start,
stop: state.stop,
}));

const isActive = status === "running_active";

const handleClick = () => {
if (isActive) {
stop();
} else {
start();
if (remote?.url) {
openUrl(remote.url);
}
}
};

if (remote?.type === "zoom") {
return (
<FloatingButton
onClick={() => openUrl(remote.url)}
onClick={handleClick}
icon={<Icon icon="streamline-logos:zoom-logo-1-block" className="w-5 h-5 text-blue-300" />}
>
Join Zoom & Start listening
{loading ? "Loading..." : isActive ? "Stop listening" : "Join Zoom & Start listening"}
</FloatingButton>
);
}

if (remote?.type === "google-meet") {
return (
<FloatingButton
onClick={() => openUrl(remote.url)}
onClick={handleClick}
icon={<Icon icon="logos:google-meet" className="w-5 h-5" />}
>
Join Google Meet & Start listening
{loading ? "Loading..." : isActive ? "Stop listening" : "Join Google Meet & Start listening"}
</FloatingButton>
);
}

return (
<FloatingButton>
<FloatingButton onClick={handleClick}>
<div className="w-2 h-2 bg-red-500 rounded-full mr-2" />
<span>Start listening</span>
<span>{loading ? "Loading..." : isActive ? "Stop listening" : "Start listening"}</span>
</FloatingButton>
);
}

function useRemoteMeeting(sessionId: string): RemoteMeeting | null {
const note = persisted.UI.useCell("sessions", sessionId, "raw_md", persisted.STORE_ID);
console.log(note);

const remote = {
type: "google-meet",
url: note,
url: null,
} as RemoteMeeting | null;

return remote;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { type Word2 } from "@hypr/plugin-listener";
import TranscriptEditor, { type SpeakerViewInnerProps } from "@hypr/tiptap/transcript";

import * as persisted from "../../../../../store/tinybase/persisted";

export function TranscriptEditorWrapper({
Expand All @@ -10,38 +7,5 @@ export function TranscriptEditorWrapper({
}) {
const value = persisted.UI.useCell("sessions", sessionId, "transcript", persisted.STORE_ID);

const handleTranscriptChange = persisted.UI.useSetPartialRowCallback(
"sessions",
sessionId,
(input: Word2[]) => ({ transcript: JSON.stringify(input) }),
[],
persisted.STORE_ID,
);

const parseTranscript = (value: string): Word2[] | null => {
if (!value) {
return null;
}
try {
const parsed = JSON.parse(value);
return parsed.words ?? null;
} catch {
return null;
}
};

return (
<TranscriptEditor
key={`session-${sessionId}-transcript`}
initialWords={parseTranscript(value ?? "")}
editable={true}
onUpdate={handleTranscriptChange}
c={SpeakerSelector}
/>
);
}

function SpeakerSelector({ speakerLabel, speakerIndex }: SpeakerViewInnerProps) {
const displayLabel = speakerLabel || `Speaker ${speakerIndex ?? 0}`;
return <span className="font-medium text-neutral-700">{displayLabel}</span>;
return <pre>{JSON.stringify(value, null, 2)}</pre>;
}
14 changes: 7 additions & 7 deletions apps/desktop2/src/components/main/body/sessions/transcript.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChevronDownIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";

import { events as listenerEvents, type Word2 } from "@hypr/plugin-listener";
import { events as listenerEvents, type Word } from "@hypr/plugin-listener";
import { Button } from "@hypr/ui/components/ui/button";
import * as persisted from "../../../../store/tinybase/persisted";
import { rowIdfromTab, type Tab } from "../../../../store/zustand/tabs";
Expand All @@ -10,8 +10,8 @@ export function TranscriptView({ tab }: { tab: Tab }) {
const sessionId = rowIdfromTab(tab);
const sessionRow = persisted.UI.useRow("sessions", sessionId, persisted.STORE_ID);

const [finalWords, setFinalWords] = useState<Word2[]>([]);
const [partialWords, setPartialWords] = useState<Word2[]>([]);
const [finalWords, setFinalWords] = useState<Word[]>([]);
const [partialWords, setPartialWords] = useState<Word[]>([]);
const [isAtBottom, setIsAtBottom] = useState(true);
const scrollContainerRef = useRef<HTMLDivElement | null>(null);

Expand All @@ -28,10 +28,10 @@ export function TranscriptView({ tab }: { tab: Tab }) {

listenerEvents.sessionEvent.listen(({ payload }: { payload: any }) => {
if (payload.type === "finalWords") {
const words = Object.values(payload.words).flat().filter((v): v is Word2 => !!v);
const words = Object.values(payload.words).flat().filter((v): v is Word => !!v);
setFinalWords((existing) => [...existing, ...words]);
} else if (payload.type === "partialWords") {
const words = Object.values(payload.words).flat().filter((v): v is Word2 => !!v);
const words = Object.values(payload.words).flat().filter((v): v is Word => !!v);
setPartialWords(words);
}
}).then((fn: () => void) => {
Expand Down Expand Up @@ -93,12 +93,12 @@ export function TranscriptView({ tab }: { tab: Tab }) {
>
<div className="px-8 text-[15px] leading-relaxed break-all space-y-2">
<span className="text-gray-800">
{finalWords.map(word => word.text).join(" ")}
{finalWords.map(word => word.word).join(" ")}
</span>
{partialWords.length > 0 && (
<span className="text-gray-400">
{" "}
{partialWords.map(word => word.text).join(" ")}
{partialWords.map(word => word.word).join(" ")}
</span>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { clsx } from "clsx";
import { Calendar, ChevronUpIcon, FolderOpen, Settings, Users } from "lucide-react";
import { useCallback, useState } from "react";

import { commands as windowsCommands } from "@hypr/plugin-windows/v1";
import { commands as windowsCommands } from "@hypr/plugin-windows";
import { useAutoCloser } from "../../../../hooks/useAutoCloser";
import { useTabs } from "../../../../store/zustand/tabs";
import { Trial } from "./banner";
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop2/src/components/settings/developers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { commands as windowsCommands } from "@hypr/plugin-windows/v1";
import { commands as windowsCommands } from "@hypr/plugin-windows";

import { useAuth } from "../../auth";

Expand Down
42 changes: 42 additions & 0 deletions apps/desktop2/src/contexts/listener.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { createContext, useContext, useRef } from "react";
import { useStore } from "zustand";
import { useShallow } from "zustand/shallow";

import { createListenerStore, type ListenerStore } from "../store/zustand/listener";

const ListenerContext = createContext<ListenerStore | null>(null);

export const ListenerProvider = ({
children,
store,
}: {
children: React.ReactNode;
store: ListenerStore;
}) => {
const storeRef = useRef<ListenerStore | null>(null);
if (!storeRef.current) {
storeRef.current = store;
}

return (
<ListenerContext.Provider value={storeRef.current}>
{children}
</ListenerContext.Provider>
);
};

export const useListener = <T,>(
selector: Parameters<
typeof useStore<ReturnType<typeof createListenerStore>, T>
>[1],
) => {
const store = useContext(ListenerContext);

if (!store) {
throw new Error(
"'useListener' must be used within a 'ListenerProvider'",
);
}

return useStore(store, useShallow(selector));
};
2 changes: 1 addition & 1 deletion apps/desktop2/src/devtool/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useNavigate } from "@tanstack/react-router";
import { useCallback, useEffect, useState } from "react";
import { useStores } from "tinybase/ui-react";

import { commands as windowsCommands } from "@hypr/plugin-windows/v1";
import { commands as windowsCommands } from "@hypr/plugin-windows";
import { cn } from "@hypr/ui/lib/utils";
import { useAutoCloser } from "../hooks/useAutoCloser";
import { type Store as PersistedStore, STORE_ID as STORE_ID_PERSISTED } from "../store/tinybase/persisted";
Expand Down
6 changes: 3 additions & 3 deletions apps/desktop2/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import {
StoreComponent as StoreComponentPersisted,
} from "./store/tinybase/persisted";

import { createOngoingSessionStore2 } from "@hypr/utils/stores";
import { routeTree } from "./routeTree.gen";
import { createListenerStore } from "./store/zustand/listener";

const ongoingSessionStore = createOngoingSessionStore2();
const listenerStore = createListenerStore();

const router = createRouter({ routeTree, context: undefined });

Expand All @@ -45,7 +45,7 @@ function App() {
context={{
persistedStore,
internalStore,
ongoingSessionStore,
listenerStore,
}}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop2/src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { lazy, Suspense, useEffect } from "react";

import { events as windowsEvents } from "@hypr/plugin-windows/v1";
import { events as windowsEvents } from "@hypr/plugin-windows";
import { AuthProvider } from "../auth";
import type { Context } from "../types";

Expand Down
Loading
Loading