Compare commits

..

1 Commits

Author SHA1 Message Date
lebaudantoine 118bc55d94 wip file picker 2026-01-12 10:05:13 +01:00
21 changed files with 105 additions and 206 deletions
+1 -3
View File
@@ -13,7 +13,6 @@ and this project adheres to
- ✨(summary) add dutch and german languages
- 🔧(agents) make Silero VAD optional
- 🚸(frontend) explain to a user they were ejected
### Changed
@@ -25,8 +24,7 @@ and this project adheres to
### Fixed
- 🐛(frontend) remove unexpected F2 tooltip when clicking video screen
- 🩹(frontend) icon font loading to avoid text/icon flickering
- 🐛(frontend) remove unexpected F2 tooltip when clicking video screen
## [1.2.0] - 2026-01-05
-16
View File
@@ -6,22 +6,6 @@
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<!-- Font URLs are resolved and replaced by Vite during the build process. Font loading failures will not break the application. -->
<link
rel="preload"
as="font"
crossorigin="anonymous"
href="/node_modules/@fontsource/material-icons-outlined/files/material-icons-outlined-latin-400-normal.woff2"
type="font/woff2"
/>
<!-- Font URLs are resolved and replaced by Vite during the build process. Font loading failures will not break the application. -->
<link
rel="preload"
as="font"
crossorigin="anonymous"
href="/node_modules/@fontsource-variable/material-symbols-outlined/files/material-symbols-outlined-latin-wght-normal.woff2"
type="font/woff2"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>%VITE_APP_TITLE%</title>
</head>
@@ -1,7 +1,7 @@
import { css } from '@/styled-system/css'
import { css, cx } from '@/styled-system/css'
import { HStack } from '@/styled-system/jsx'
import { Spinner } from '@/primitives/Spinner'
import { Button, Icon, Text } from '@/primitives'
import { Button, Text } from '@/primitives'
import { useTranslation } from 'react-i18next'
import { RecordingStatuses } from '../hooks/useRecordingStatuses'
import { ReactNode, useEffect, useRef, useState } from 'react'
@@ -141,23 +141,31 @@ export const ControlsButton = ({
})}
onPress={() => openSidePanel()}
>
<Icon
className={css({
color: 'primary.500',
marginRight: '1rem',
})}
name="info"
/>
<span
className={cx(
'material-icons',
css({
color: 'primary.500',
marginRight: '1rem',
})
)}
>
info
</span>
<Text variant={'smNote'}>
{parseLineBreaks(t('button.anotherModeStarted'))}
</Text>
<Icon
className={css({
color: 'primary.500',
marginLeft: 'auto',
})}
name="chevron_right"
/>
<span
className={cx(
'material-icons',
css({
color: 'primary.500',
marginLeft: 'auto',
})
)}
>
chevron_right
</span>
</RACButton>
)}
<Button
@@ -1,4 +1,4 @@
import { H, Text, Icon } from '@/primitives'
import { H, Text } from '@/primitives'
import { css } from '@/styled-system/css'
import { LoginButton } from '@/components/LoginButton'
import { HStack } from '@/styled-system/jsx'
@@ -24,7 +24,9 @@ export const LoginPrompt = ({ heading, body }: LoginPromptProps) => {
})}
>
<HStack justify="start" alignItems="center" marginBottom="0.5rem">
<Icon type="symbols" name="login" />
<span className="material-symbols" aria-hidden={true}>
login
</span>
<H lvl={3} margin={false} padding={false}>
{heading}
</H>
@@ -1,5 +1,4 @@
import { Spinner } from '@/primitives/Spinner'
import { Icon } from '@/primitives'
interface RecordingStatusIconProps {
isStarted: boolean
@@ -15,8 +14,8 @@ export const RecordingStatusIcon = ({
}
if (isTranscriptActive) {
return <Icon type="symbols" name="speech_to_text" />
return <span className="material-symbols">speech_to_text</span>
}
return <Icon type="symbols" name="screen_record" />
return <span className="material-symbols">screen_record</span>
}
@@ -1,4 +1,4 @@
import { Button, Icon, H, Text } from '@/primitives'
import { Button, H, Text } from '@/primitives'
import { css } from '@/styled-system/css'
import { HStack } from '@/styled-system/jsx'
import { useEffect, useRef, useState } from 'react'
@@ -59,7 +59,7 @@ export const RequestRecording = ({
})}
>
<HStack justify="start" alignItems="center" marginBottom="0.5rem">
<Icon type="symbols" name="person_raised_hand" />
<span className="material-symbols">person_raised_hand</span>
<H lvl={3} margin={false} padding={false}>
{heading}
</H>
@@ -1,6 +1,5 @@
import { css } from '@/styled-system/css'
import { ReactNode } from 'react'
import { Icon } from '@/primitives'
type RowPosition = 'first' | 'middle' | 'last' | 'single'
@@ -46,7 +45,7 @@ export const RowWrapper = ({
})}
>
{/* fixme - doesn't handle properly material-symbols */}
<Icon name={iconName} />
<span className="material-icons">{iconName}</span>
</div>
<div
className={css({
@@ -25,12 +25,12 @@ import { VideoConference } from '../livekit/prefabs/VideoConference'
import { css } from '@/styled-system/css'
import { BackgroundProcessorFactory } from '../livekit/components/blur'
import { LocalUserChoices } from '@/stores/userChoices'
import { navigateTo } from '@/navigation/navigateTo'
import { MediaDeviceErrorAlert } from './MediaDeviceErrorAlert'
import { usePostHog } from 'posthog-js/react'
import { useConfig } from '@/api/useConfig'
import { isFireFox } from '@/utils/livekit'
import { useIsMobile } from '@/utils/useIsMobile'
import { navigateTo } from '@/navigation/navigateTo'
export const Conference = ({
roomId,
@@ -228,20 +228,10 @@ export const Conference = ({
posthog.captureException(e)
}}
onDisconnected={(e) => {
switch (e) {
case DisconnectReason.CLIENT_INITIATED:
navigateTo('feedback')
return
case DisconnectReason.DUPLICATE_IDENTITY:
case DisconnectReason.PARTICIPANT_REMOVED:
navigateTo(
'feedback',
{},
{
state: { reason: e },
}
)
return
if (e == DisconnectReason.CLIENT_INITIATED) {
navigateTo('feedback', { duplicateIdentity: false })
} else if (e == DisconnectReason.DUPLICATE_IDENTITY) {
navigateTo('feedback', { duplicateIdentity: true })
}
}}
onMediaDeviceFailure={(e, kind) => {
@@ -1,4 +1,4 @@
import { A, Div, Icon, Text } from '@/primitives'
import { A, Div, Text } from '@/primitives'
import { css } from '@/styled-system/css'
import { Button as RACButton } from 'react-aria-components'
import { useTranslation } from 'react-i18next'
@@ -87,7 +87,7 @@ const ToolButton = ({
alignItems: 'center',
})}
>
<Icon type="symbols" name="chevron_forward" />
<span className="material-symbols">chevron_forward</span>
</div>
</RACButton>
)
@@ -163,7 +163,7 @@ export const Tools = () => {
</Text>
{isTranscriptEnabled && (
<ToolButton
icon={<Icon type="symbols" name="speech_to_text" />}
icon={<span className="material-symbols">speech_to_text</span>}
title={t('tools.transcript.title')}
description={t('tools.transcript.body')}
onPress={() => openTranscript()}
@@ -171,7 +171,7 @@ export const Tools = () => {
)}
{isScreenRecordingEnabled && (
<ToolButton
icon={<Icon type="symbols" name="mode_standby" />}
icon={<span className="material-symbols">mode_standby</span>}
title={t('tools.screenRecording.title')}
description={t('tools.screenRecording.body')}
onPress={() => openScreenRecording()}
@@ -8,7 +8,7 @@ import {
ProcessorType,
} from '../blur'
import { css } from '@/styled-system/css'
import { H, P, Text, ToggleButton } from '@/primitives'
import { Button, H, P, Text, ToggleButton } from '@/primitives'
import { VisualOnlyTooltip } from '@/primitives/VisualOnlyTooltip'
import { styled } from '@/styled-system/jsx'
import { BlurOn } from '@/components/icons/BlurOn'
@@ -18,6 +18,8 @@ import { Loader } from '@/primitives/Loader'
import { useSyncAfterDelay } from '@/hooks/useSyncAfterDelay'
import { FunnyEffects } from './FunnyEffects'
import { useHasFunnyEffectsAccess } from '../../hooks/useHasFunnyEffectsAccess'
import { RiImageAddFill } from '@remixicon/react'
import { FileTrigger } from 'react-aria-components'
enum BlurRadius {
NONE = 0,
@@ -405,6 +407,17 @@ export const EffectsConfiguration = ({
>
<BlurOnStrong />
</ToggleButton>
<FileTrigger
acceptedFileTypes={['image/*']}
allowsMultiple={false}
onSelect={(e) => {
console.log(e)
}}
>
<Button variant="bigSquare">
<RiImageAddFill />
</Button>
</FileTrigger>
</div>
<div aria-live="polite" className="sr-only">
{blurStatusMessage}
@@ -3,9 +3,7 @@ import { Button } from '@/primitives'
import { Screen } from '@/layout/Screen'
import { Center, HStack, styled, VStack } from '@/styled-system/jsx'
import { Rating } from '@/features/rooms/components/Rating.tsx'
import { useLocation } from 'wouter'
import { useMemo } from 'react'
import { DisconnectReason } from 'livekit-client'
import { useLocation, useSearchParams } from 'wouter'
// fixme - duplicated with home, refactor in a proper style
const Heading = styled('h1', {
@@ -22,40 +20,25 @@ const Heading = styled('h1', {
},
})
enum DisconnectReasonKey {
DuplicateIdentity = 'duplicateIdentity',
ParticipantRemoved = 'participantRemoved',
}
export const FeedbackRoute = () => {
const { t } = useTranslation('rooms')
const [, setLocation] = useLocation()
const reasonKey = useMemo(() => {
const state = window.history.state
if (!state?.reason) return
switch (state.reason) {
case DisconnectReason.DUPLICATE_IDENTITY:
return DisconnectReasonKey.DuplicateIdentity
case DisconnectReason.PARTICIPANT_REMOVED:
return DisconnectReasonKey.ParticipantRemoved
}
}, [])
const showBackButton = reasonKey !== DisconnectReasonKey.ParticipantRemoved
const [searchParams] = useSearchParams()
return (
<Screen layout="centered" footer={false}>
<Center>
<VStack>
<Heading>{t(`feedback.heading.${reasonKey || 'normal'}`)}</Heading>
<HStack>
{showBackButton && (
<Button variant="secondary" onPress={() => window.history.back()}>
{t('feedback.back')}
</Button>
<Heading>
{t(
`feedback.heading.${searchParams.get('duplicateIdentity') ? 'duplicateIdentity' : 'normal'}`
)}
</Heading>
<HStack>
<Button variant="secondary" onPress={() => window.history.back()}>
{t('feedback.back')}
</Button>
<Button variant="primary" onPress={() => setLocation('/')}>
{t('feedback.home')}
</Button>
@@ -2,7 +2,6 @@ import { Dialog, type DialogProps } from '@/primitives'
import { Tab, Tabs, TabList } from '@/primitives/Tabs.tsx'
import { css } from '@/styled-system/css'
import { text } from '@/primitives/Text.tsx'
import { Icon } from '@/primitives/Icon'
import { Heading } from 'react-aria-components'
import { useTranslation } from 'react-i18next'
import {
@@ -107,7 +106,7 @@ export const SettingsDialogExtended = (props: SettingsDialogExtended) => {
</Tab>
{isAdminOrOwner && (
<Tab icon highlight id={SettingsDialogExtendedKey.TRANSCRIPTION}>
<Icon type="symbols" name="speech_to_text" />
<span className="material-symbols">speech_to_text</span>
{isWideScreen &&
t(`tabs.${SettingsDialogExtendedKey.TRANSCRIPTION}`)}
</Tab>
+1 -2
View File
@@ -2,8 +2,7 @@
"feedback": {
"heading": {
"normal": "Sie haben das Meeting verlassen",
"duplicateIdentity": "Sie haben dem Meeting von einem anderen Gerät aus beigetreten",
"participantRemoved": "Sie wurden von einem Administrator aus dem Anruf entfernt"
"duplicateIdentity": "Sie haben dem Meeting von einem anderen Gerät aus beigetreten"
},
"home": "Zur Startseite zurückkehren",
"back": "Dem Meeting erneut beitreten"
+1 -2
View File
@@ -2,8 +2,7 @@
"feedback": {
"heading": {
"normal": "You have left the meeting",
"duplicateIdentity": "You have joined the meeting from another device",
"participantRemoved": "You have been removed from the meeting by an administrator"
"duplicateIdentity": "You have joined the meeting from another device"
},
"home": "Return to home",
"back": "Rejoin the meeting"
+1 -2
View File
@@ -2,8 +2,7 @@
"feedback": {
"heading": {
"normal": "Vous avez quitté la réunion",
"duplicateIdentity": "Vous avez rejoint la réunion depuis un autre appareil",
"participantRemoved": "Vous avez été exclu de l'appel par un administrateur"
"duplicateIdentity": "Vous avez rejoint la réunion depuis un autre appareil"
},
"home": "Retourner à l'accueil",
"back": "Réintégrer la réunion"
+1 -2
View File
@@ -2,8 +2,7 @@
"feedback": {
"heading": {
"normal": "Je hebt de vergadering verlaten",
"duplicateIdentity": "U heeft de vergadering via een ander apparaat geopend",
"participantRemoved": "U bent door een beheerder uit het gesprek verwijderd"
"duplicateIdentity": "U heeft de vergadering via een ander apparaat geopend"
},
"home": "Keer terug naar het hoofdscherm",
"back": "Sluit weer bij de vergadering aan"
-71
View File
@@ -1,71 +0,0 @@
import { cva, RecipeVariantProps } from '@/styled-system/css'
import { ComponentPropsWithoutRef } from 'react'
const iconRecipe = cva({
base: {
fontWeight: 'normal',
fontStyle: 'normal',
display: 'inline-block',
lineHeight: 1,
textTransform: 'none',
letterSpacing: 'normal',
wordWrap: 'normal',
whiteSpace: 'nowrap',
direction: 'ltr',
},
variants: {
type: {
icons: {
fontFamily: 'Material Icons Outlined',
webkitFontSmoothing: 'antialiased',
mozOsxFontSmoothing: 'grayscale',
textRendering: 'optimizeLegibility',
fontFeatureSettings: '"liga"',
},
symbols: {
fontFamily: 'Material Symbols Outlined Variable',
},
},
size: {
sm: {
fontSize: '18px',
},
md: {
fontSize: '24px',
},
lg: {
fontSize: '32px',
},
xl: {
fontSize: '40px',
},
},
},
defaultVariants: {
type: 'icons',
size: 'md',
},
})
export type IconRecipeProps = RecipeVariantProps<typeof iconRecipe>
export type IconProps = IconRecipeProps &
ComponentPropsWithoutRef<'span'> & {
name: string
}
export const Icon = ({ name, ...props }: IconProps) => {
const [variantProps, componentProps] = iconRecipe.splitVariantProps(props)
const { className, ...remainingComponentProps } = componentProps
return (
<span
translate="no"
aria-hidden="true"
className={[iconRecipe(variantProps), className].join(' ')}
{...remainingComponentProps}
>
{name}
</span>
)
}
-1
View File
@@ -30,4 +30,3 @@ export { Ul } from './Ul'
export { VerticallyOffCenter } from './VerticallyOffCenter'
export { TextArea } from './TextArea'
export { Switch } from './Switch'
export { Icon } from './Icon'
+5
View File
@@ -43,6 +43,11 @@ export const routes: Record<
feedback: {
name: 'feedback',
path: '/feedback',
to: (params: { duplicateIdentity?: false }) =>
'/feedback' +
(params.duplicateIdentity
? `?duplicateIdentity=${params?.duplicateIdentity}`
: ''),
Component: FeedbackRoute,
},
legalTerms: {
+27
View File
@@ -0,0 +1,27 @@
@import '@fontsource/material-icons-outlined';
.material-icons,
.material-symbols {
font-weight: normal;
font-style: normal;
font-size: 24px;
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'liga';
}
.material-icons {
font-family: 'Material Icons Outlined';
}
.material-symbols {
font-family: 'Material Symbols Outlined Variable';
}
+1 -33
View File
@@ -1,4 +1,5 @@
@import './livekit.css';
@import './icons.css';
@layer reset, base, tokens, recipes, utilities;
html,
body,
@@ -53,36 +54,3 @@ body:has(.lk-video-conference) #crisp-chatbox > * > div[role='button'] {
transform: translateY(0);
}
}
/* Generated using the Advanced installation guidelines from
* https://fontsource.org/fonts/material-icons-outlined/install
*/
@font-face {
font-family: 'Material Icons Outlined';
font-style: normal;
font-display: block;
font-weight: 400;
src: url(@fontsource/material-icons-outlined/files/material-icons-outlined-latin-400-normal.woff2)
format('woff2');
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* Generated using the Advanced installation guidelines from
* https://fontsource.org/fonts/material-symbols-outlined/install
*/
/* material-symbols-outlined-latin-wght-normal */
@font-face {
font-family: 'Material Symbols Outlined Variable';
font-style: normal;
font-display: block;
font-weight: 100 700;
src: url(@fontsource-variable/material-symbols-outlined/files/material-symbols-outlined-latin-wght-normal.woff2)
format('woff2-variations');
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}