Merge remote-tracking branch 'origin/main' into pr58-sync
# Conflicts: # packages/server/src/shared/messages.ts
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { AttemptCancelledError, AttemptGuard } from "@/utils/attempt-guard";
|
||||
import { getTauri } from "@/utils/tauri";
|
||||
|
||||
export interface AudioCaptureConfig {
|
||||
sampleRate?: number;
|
||||
@@ -156,16 +157,33 @@ export function useAudioRecorder(config?: AudioCaptureConfig) {
|
||||
: true;
|
||||
const currentOrigin =
|
||||
typeof window !== "undefined" && window.location ? window.location.origin : "unknown";
|
||||
const isTauri = getTauri() !== null;
|
||||
|
||||
if (missingNavigator) {
|
||||
throw new Error("Microphone capture is not supported in this environment");
|
||||
}
|
||||
|
||||
if (!secureContext) {
|
||||
console.log("[AudioRecorder][Web] Microphone preflight", {
|
||||
secureContext,
|
||||
currentOrigin,
|
||||
isTauri,
|
||||
hasMediaDevices:
|
||||
typeof navigator !== "undefined" &&
|
||||
!!navigator.mediaDevices &&
|
||||
typeof navigator.mediaDevices.getUserMedia === "function",
|
||||
});
|
||||
|
||||
if (!secureContext && !isTauri) {
|
||||
throw new Error(
|
||||
`Microphone access requires HTTPS or localhost. Current origin: ${currentOrigin}`
|
||||
);
|
||||
}
|
||||
if (!secureContext && isTauri) {
|
||||
console.warn(
|
||||
"[AudioRecorder][Web] Insecure context reported under Tauri; attempting getUserMedia anyway",
|
||||
{ currentOrigin }
|
||||
);
|
||||
}
|
||||
|
||||
const options = configRef.current;
|
||||
const constraints: MediaStreamConstraints = {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { getTauri } from "@/utils/tauri";
|
||||
|
||||
import type { DictationAudioSource, DictationAudioSourceConfig } from "./use-dictation-audio-source.types";
|
||||
|
||||
@@ -148,13 +149,29 @@ export function useDictationAudioSource(config: DictationAudioSourceConfig): Dic
|
||||
: true;
|
||||
const currentOrigin =
|
||||
typeof window !== "undefined" && window.location ? window.location.origin : "unknown";
|
||||
const isTauri = getTauri() !== null;
|
||||
|
||||
if (missingNavigator) {
|
||||
throw new Error("Microphone capture is not supported in this environment");
|
||||
}
|
||||
if (!secureContext) {
|
||||
console.log("[DictationAudio][Web] Microphone preflight", {
|
||||
secureContext,
|
||||
currentOrigin,
|
||||
isTauri,
|
||||
hasMediaDevices:
|
||||
typeof navigator !== "undefined" &&
|
||||
!!navigator.mediaDevices &&
|
||||
typeof navigator.mediaDevices.getUserMedia === "function",
|
||||
});
|
||||
if (!secureContext && !isTauri) {
|
||||
throw new Error(`Microphone access requires HTTPS or localhost. Current origin: ${currentOrigin}`);
|
||||
}
|
||||
if (!secureContext && isTauri) {
|
||||
console.warn(
|
||||
"[DictationAudio][Web] Insecure context reported under Tauri; attempting getUserMedia anyway",
|
||||
{ currentOrigin }
|
||||
);
|
||||
}
|
||||
|
||||
const AudioContextCtor = getAudioContextCtor();
|
||||
if (!AudioContextCtor) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { getIsTauriMac } from "@/constants/layout";
|
||||
import { useAggregatedAgents } from "./use-aggregated-agents";
|
||||
import { getCurrentTauriWindow } from "@/utils/tauri";
|
||||
|
||||
type FaviconStatus = "none" | "running" | "attention";
|
||||
type ColorScheme = "dark" | "light";
|
||||
@@ -90,9 +91,9 @@ function getSystemColorScheme(): ColorScheme {
|
||||
}
|
||||
|
||||
async function updateMacDockBadge(count?: number) {
|
||||
if (Platform.OS !== "web" || typeof window === "undefined" || !getIsTauriMac()) return;
|
||||
if (Platform.OS !== "web" || !getIsTauriMac()) return;
|
||||
|
||||
const tauriWindow = (window as any).__TAURI__?.window?.getCurrentWindow?.();
|
||||
const tauriWindow = getCurrentTauriWindow();
|
||||
if (!tauriWindow || typeof tauriWindow.setBadgeCount !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import type { ImageAttachment } from "@/components/message-input";
|
||||
import { getCurrentTauriWindow, getTauri } from "@/utils/tauri";
|
||||
|
||||
interface UseFileDropZoneOptions {
|
||||
onFilesDropped: (files: ImageAttachment[]) => void;
|
||||
@@ -52,10 +53,6 @@ function isImageFile(file: File): boolean {
|
||||
return file.type.startsWith("image/");
|
||||
}
|
||||
|
||||
function isTauriEnvironment(): boolean {
|
||||
return typeof window !== "undefined" && (window as any).__TAURI__ !== undefined;
|
||||
}
|
||||
|
||||
function getFileExtension(path: string): string {
|
||||
const normalizedPath = path.split("#", 1)[0]?.split("?", 1)[0] ?? path;
|
||||
const extensionIndex = normalizedPath.lastIndexOf(".");
|
||||
@@ -72,7 +69,7 @@ function isImagePath(path: string): boolean {
|
||||
function filePathToImageAttachment(path: string): ImageAttachment {
|
||||
const extension = getFileExtension(path);
|
||||
const mimeType = IMAGE_MIME_BY_EXTENSION[extension] ?? "image/jpeg";
|
||||
const convertFileSrc = (window as any).__TAURI__?.core?.convertFileSrc;
|
||||
const convertFileSrc = getTauri()?.core?.convertFileSrc;
|
||||
const uri =
|
||||
typeof convertFileSrc === "function" ? convertFileSrc(path) : path;
|
||||
|
||||
@@ -145,11 +142,11 @@ export function useFileDropZone({
|
||||
}
|
||||
|
||||
async function setupTauriDragDrop(): Promise<boolean> {
|
||||
if (!isTauriEnvironment()) {
|
||||
if (getTauri() === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tauriWindow = (window as any).__TAURI__?.window?.getCurrentWindow?.();
|
||||
const tauriWindow = getCurrentTauriWindow();
|
||||
if (!tauriWindow || typeof tauriWindow.onDragDropEvent !== "function") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { SpeechSegmenter } from "@/voice/speech-segmenter";
|
||||
import { getTauri } from "@/utils/tauri";
|
||||
|
||||
export interface SpeechmaticsAudioConfig {
|
||||
onAudioSegment?: (segment: { audioData: string; isLast: boolean }) => void;
|
||||
@@ -245,14 +246,30 @@ export function useSpeechmaticsAudio(config: SpeechmaticsAudioConfig): Speechmat
|
||||
? window.isSecureContext
|
||||
: true;
|
||||
const currentOrigin = typeof window !== "undefined" && window.location ? window.location.origin : "unknown";
|
||||
const isTauri = getTauri() !== null;
|
||||
|
||||
try {
|
||||
if (missingNavigator) {
|
||||
throw new Error("Microphone capture is not supported in this environment");
|
||||
}
|
||||
if (!secureContext) {
|
||||
console.log("[Voice][Web] Microphone preflight", {
|
||||
secureContext,
|
||||
currentOrigin,
|
||||
isTauri,
|
||||
hasMediaDevices:
|
||||
typeof navigator !== "undefined" &&
|
||||
!!navigator.mediaDevices &&
|
||||
typeof navigator.mediaDevices.getUserMedia === "function",
|
||||
});
|
||||
if (!secureContext && !isTauri) {
|
||||
throw new Error(`Microphone access requires HTTPS or localhost. Current origin: ${currentOrigin}`);
|
||||
}
|
||||
if (!secureContext && isTauri) {
|
||||
console.warn(
|
||||
"[Voice][Web] Insecure context reported under Tauri; attempting getUserMedia anyway",
|
||||
{ currentOrigin }
|
||||
);
|
||||
}
|
||||
|
||||
const AudioContextCtor = getAudioContextCtor();
|
||||
if (!AudioContextCtor) {
|
||||
|
||||
Reference in New Issue
Block a user