diff --git a/packages/twenty-apps/public/twenty-fireflies/package.json b/packages/twenty-apps/public/twenty-fireflies/package.json index b7dcff7a29..9bb2dd528f 100644 --- a/packages/twenty-apps/public/twenty-fireflies/package.json +++ b/packages/twenty-apps/public/twenty-fireflies/package.json @@ -21,7 +21,12 @@ "test:watch": "vitest", "test:unit": "vitest run --config vitest.unit.config.ts" }, + "dependencies": { + "@sniptt/guards": "^0.2.0" + }, "devDependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", "@types/node": "^24.7.2", "@types/react": "^18.2.0", "@typescript/native-preview": "^7.0.0-dev.20260116.1", @@ -30,6 +35,7 @@ "react-dom": "^18.2.0", "twenty-client-sdk": "^2.18.0", "twenty-sdk": "^2.18.0", + "twenty-ui": "^1.0.0-alpha.1", "typescript": "^5.9.3", "vite-tsconfig-paths": "^4.2.1", "vitest": "^4.0.0" diff --git a/packages/twenty-apps/public/twenty-fireflies/src/constants/universal-identifiers.ts b/packages/twenty-apps/public/twenty-fireflies/src/constants/universal-identifiers.ts index a8adca1945..d94354baa6 100644 --- a/packages/twenty-apps/public/twenty-fireflies/src/constants/universal-identifiers.ts +++ b/packages/twenty-apps/public/twenty-fireflies/src/constants/universal-identifiers.ts @@ -21,3 +21,25 @@ export const FIREFLIES_LIST_CALLS_BY_PARTICIPANT_UNIVERSAL_IDENTIFIER = export const FIREFLIES_SEARCH_CALLS_UNIVERSAL_IDENTIFIER = 'cb2dd01d-8dca-4222-acce-1d1dbdef9146'; + +// Shared core CalendarEvent record page layout that apps attach tabs to. +export const CALENDAR_EVENT_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER = + 'b9b10e40-9ce2-4704-8ac6-c6e92e2563c1'; + +export const CALENDAR_EVENT_SUMMARY_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER = + 'f4fed96d-1551-40dc-a0c1-0b4d2a782733'; + +export const CALENDAR_EVENT_SUMMARY_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER = + '240925c4-7599-44e1-84ee-4330d450a736'; + +export const CALENDAR_EVENT_SUMMARY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER = + '64bd540d-f611-4f9e-8b8d-ae8443c310cd'; + +export const CALENDAR_EVENT_TRANSCRIPT_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER = + '02acda71-77e0-4176-a134-c79f40510419'; + +export const CALENDAR_EVENT_TRANSCRIPT_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER = + 'ac9d7c53-15f8-4c89-85ce-f6451ace2eb2'; + +export const CALENDAR_EVENT_TRANSCRIPT_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER = + '364642a6-addb-4df1-8c57-25228b826259'; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/calendar-event-summary.front-component.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/calendar-event-summary.front-component.tsx new file mode 100644 index 0000000000..0bc1271c21 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/calendar-event-summary.front-component.tsx @@ -0,0 +1,13 @@ +import { defineFrontComponent } from 'twenty-sdk/define'; + +import { CALENDAR_EVENT_SUMMARY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { CalendarEventSummary } from 'src/front-components/components/CalendarEventSummary'; + +export default defineFrontComponent({ + universalIdentifier: + CALENDAR_EVENT_SUMMARY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + name: 'calendar-event-summary', + description: + 'Read-only Fireflies AI summary viewer for the calendar event record page.', + component: CalendarEventSummary, +}); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/calendar-event-transcript.front-component.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/calendar-event-transcript.front-component.tsx new file mode 100644 index 0000000000..46e8e4abc5 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/calendar-event-transcript.front-component.tsx @@ -0,0 +1,13 @@ +import { defineFrontComponent } from 'twenty-sdk/define'; + +import { CALENDAR_EVENT_TRANSCRIPT_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from 'src/constants/universal-identifiers'; +import { CalendarEventTranscript } from 'src/front-components/components/CalendarEventTranscript'; + +export default defineFrontComponent({ + universalIdentifier: + CALENDAR_EVENT_TRANSCRIPT_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + name: 'calendar-event-transcript', + description: + 'Read-only diarized Fireflies transcript viewer for the calendar event record page.', + component: CalendarEventTranscript, +}); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummary.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummary.tsx new file mode 100644 index 0000000000..ffeed13539 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummary.tsx @@ -0,0 +1,39 @@ +import styled from '@emotion/styled'; +import { isUndefined } from '@sniptt/guards'; +import { useSelectedRecordIds } from 'twenty-sdk/front-component'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { CalendarEventSummaryContent } from 'src/front-components/components/CalendarEventSummaryContent'; + +const StyledCenteredState = styled.div` + align-items: center; + box-sizing: border-box; + color: ${() => themeCssVariables.font.color.tertiary}; + display: flex; + font-family: ${() => themeCssVariables.font.family}; + font-size: ${() => themeCssVariables.font.size.sm}; + height: 100%; + justify-content: center; + padding: ${() => themeCssVariables.spacing[4]}; +`; + +export const CalendarEventSummary = () => { + const selectedRecordIds = useSelectedRecordIds(); + const calendarEventId = + selectedRecordIds.length === 1 ? selectedRecordIds[0] : undefined; + + if (isUndefined(calendarEventId)) { + return ( + + Open a calendar event to see its summary. + + ); + } + + return ( + + ); +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummaryBody.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummaryBody.tsx new file mode 100644 index 0000000000..4cd4417862 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummaryBody.tsx @@ -0,0 +1,53 @@ +import styled from '@emotion/styled'; +import { isUndefined } from '@sniptt/guards'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { SummaryMarkdown } from 'src/front-components/components/SummaryMarkdown'; +import { TranscriptErrorBox } from 'src/front-components/components/TranscriptErrorBox'; + +const StyledCenteredState = styled.div` + align-items: center; + box-sizing: border-box; + color: ${() => themeCssVariables.font.color.tertiary}; + display: flex; + font-family: ${() => themeCssVariables.font.family}; + font-size: ${() => themeCssVariables.font.size.sm}; + justify-content: center; + min-height: 240px; + padding: ${() => themeCssVariables.spacing[4]}; +`; + +type CalendarEventSummaryBodyProps = { + summaryMarkdown: string | undefined; + isCalendarEventSummaryQueryLoading: boolean; + errorMessage: string | undefined; +}; + +export const CalendarEventSummaryBody = ({ + summaryMarkdown, + isCalendarEventSummaryQueryLoading, + errorMessage, +}: CalendarEventSummaryBodyProps) => { + if (!isUndefined(errorMessage)) { + return ( + + ); + } + + if (isCalendarEventSummaryQueryLoading) { + return Loading summary…; + } + + if (isUndefined(summaryMarkdown)) { + return ( + + No summary for this calendar event yet. + + ); + } + + return ; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummaryContent.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummaryContent.tsx new file mode 100644 index 0000000000..6656ff3067 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventSummaryContent.tsx @@ -0,0 +1,77 @@ +import styled from '@emotion/styled'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { CalendarEventSummaryBody } from 'src/front-components/components/CalendarEventSummaryBody'; +import { useCalendarEventSummary } from 'src/front-components/hooks/use-calendar-event-summary'; + +const StyledSummaryShell = styled.div` + background: ${() => themeCssVariables.background.primary}; + border: 1px solid transparent; + border-radius: ${() => themeCssVariables.border.radius.md}; + box-sizing: border-box; + font-family: ${() => themeCssVariables.font.family}; + padding: ${() => themeCssVariables.spacing[4]}; + position: relative; + width: 100%; +`; + +const StyledSummaryHeader = styled.div` + align-items: center; + box-sizing: border-box; + display: flex; + height: ${() => themeCssVariables.spacing[6]}; +`; + +const StyledSummaryTitle = styled.h2` + color: ${() => themeCssVariables.font.color.primary}; + flex: 1; + font-size: ${() => themeCssVariables.font.size.md}; + font-weight: ${() => themeCssVariables.font.weight.medium}; + margin: 0; + overflow: hidden; + padding-inline: ${() => themeCssVariables.spacing[1]}; + user-select: none; +`; + +const StyledSummaryBody = styled.div` + box-sizing: border-box; + margin-top: ${() => themeCssVariables.spacing[2]}; +`; + +const StyledSummaryContentFrame = styled.div` + background-color: ${() => themeCssVariables.background.secondary}; + border: 1px solid ${() => themeCssVariables.border.color.medium}; + border-radius: ${() => themeCssVariables.border.radius.md}; + box-sizing: border-box; + padding: ${() => themeCssVariables.spacing[3]}; +`; + +type CalendarEventSummaryContentProps = { + calendarEventId: string; +}; + +export const CalendarEventSummaryContent = ({ + calendarEventId, +}: CalendarEventSummaryContentProps) => { + const { summaryMarkdown, isCalendarEventSummaryQueryLoading, errorMessage } = + useCalendarEventSummary(calendarEventId); + + return ( + + + Summary + + + + + + + + ); +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscript.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscript.tsx new file mode 100644 index 0000000000..e58f620555 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscript.tsx @@ -0,0 +1,39 @@ +import styled from '@emotion/styled'; +import { isUndefined } from '@sniptt/guards'; +import { useSelectedRecordIds } from 'twenty-sdk/front-component'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { CalendarEventTranscriptContent } from 'src/front-components/components/CalendarEventTranscriptContent'; + +const StyledCenteredState = styled.div` + align-items: center; + box-sizing: border-box; + color: ${() => themeCssVariables.font.color.tertiary}; + display: flex; + font-family: ${() => themeCssVariables.font.family}; + font-size: ${() => themeCssVariables.font.size.sm}; + height: 100%; + justify-content: center; + padding: ${() => themeCssVariables.spacing[4]}; +`; + +export const CalendarEventTranscript = () => { + const selectedRecordIds = useSelectedRecordIds(); + const calendarEventId = + selectedRecordIds.length === 1 ? selectedRecordIds[0] : undefined; + + if (isUndefined(calendarEventId)) { + return ( + + Open a calendar event to see its transcript. + + ); + } + + return ( + + ); +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscriptBody.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscriptBody.tsx new file mode 100644 index 0000000000..861e861927 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscriptBody.tsx @@ -0,0 +1,74 @@ +import styled from '@emotion/styled'; +import { isUndefined } from '@sniptt/guards'; +import { useMemo } from 'react'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { TranscriptEntryList } from 'src/front-components/components/TranscriptEntryList'; +import { TranscriptErrorBox } from 'src/front-components/components/TranscriptErrorBox'; +import { parseTranscriptEntries } from 'src/front-components/utils/parse-transcript-entries.util'; + +const StyledCenteredState = styled.div` + align-items: center; + box-sizing: border-box; + color: ${() => themeCssVariables.font.color.tertiary}; + display: flex; + flex: 1; + font-family: ${() => themeCssVariables.font.family}; + font-size: ${() => themeCssVariables.font.size.sm}; + justify-content: center; + min-height: 240px; + padding: ${() => themeCssVariables.spacing[4]}; +`; + +type CalendarEventTranscriptBodyProps = { + transcript: unknown; + isCalendarEventTranscriptQueryLoading: boolean; + errorMessage: string | undefined; +}; + +export const CalendarEventTranscriptBody = ({ + transcript, + isCalendarEventTranscriptQueryLoading, + errorMessage, +}: CalendarEventTranscriptBodyProps) => { + const entries = useMemo( + () => parseTranscriptEntries(transcript), + [transcript], + ); + + if (!isUndefined(errorMessage)) { + return ( + + ); + } + + if (isCalendarEventTranscriptQueryLoading) { + return Loading transcript…; + } + + if (isUndefined(transcript)) { + return ( + + No transcript for this calendar event yet. + + ); + } + + if (isUndefined(entries)) { + return ( + + ); + } + + if (entries.length === 0) { + return The transcript is empty.; + } + + return ; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscriptContent.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscriptContent.tsx new file mode 100644 index 0000000000..db370435dd --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/CalendarEventTranscriptContent.tsx @@ -0,0 +1,84 @@ +import styled from '@emotion/styled'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { CalendarEventTranscriptBody } from 'src/front-components/components/CalendarEventTranscriptBody'; +import { useCalendarEventTranscript } from 'src/front-components/hooks/use-calendar-event-transcript'; + +const StyledTranscriptShell = styled.div` + background: ${() => themeCssVariables.background.primary}; + box-sizing: border-box; + display: flex; + flex-direction: column; + font-family: ${() => themeCssVariables.font.family}; + height: 100%; + padding: ${() => themeCssVariables.spacing[4]}; + width: 100%; +`; + +const StyledTranscriptHeader = styled.div` + align-items: center; + box-sizing: border-box; + display: flex; + height: ${() => themeCssVariables.spacing[6]}; +`; + +const StyledTranscriptTitle = styled.h2` + color: ${() => themeCssVariables.font.color.primary}; + flex: 1; + font-size: ${() => themeCssVariables.font.size.md}; + font-weight: ${() => themeCssVariables.font.weight.medium}; + margin: 0; + overflow: hidden; + padding-inline: ${() => themeCssVariables.spacing[1]}; + user-select: none; +`; + +const StyledTranscriptBody = styled.div` + box-sizing: border-box; + display: flex; + flex: 1; + margin-top: ${() => themeCssVariables.spacing[2]}; + min-height: 0; +`; + +const StyledTranscriptContentFrame = styled.div` + background-color: ${() => themeCssVariables.background.secondary}; + border: 1px solid ${() => themeCssVariables.border.color.medium}; + border-radius: ${() => themeCssVariables.border.radius.md}; + box-sizing: border-box; + display: flex; + flex: 1; + min-height: 0; + overflow-y: auto; + padding: ${() => themeCssVariables.spacing[3]}; +`; + +type CalendarEventTranscriptContentProps = { + calendarEventId: string; +}; + +export const CalendarEventTranscriptContent = ({ + calendarEventId, +}: CalendarEventTranscriptContentProps) => { + const { transcript, isCalendarEventTranscriptQueryLoading, errorMessage } = + useCalendarEventTranscript(calendarEventId); + + return ( + + + Transcript + + + + + + + + ); +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/SummaryInlineSegments.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/SummaryInlineSegments.tsx new file mode 100644 index 0000000000..4008facb2c --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/SummaryInlineSegments.tsx @@ -0,0 +1,28 @@ +import styled from '@emotion/styled'; +import { Fragment } from 'react'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { type SummaryInlineSegment } from 'src/front-components/types/summary-inline-segment.type'; + +const StyledBold = styled.strong` + color: ${() => themeCssVariables.font.color.primary}; + font-weight: ${() => themeCssVariables.font.weight.medium}; +`; + +type SummaryInlineSegmentsProps = { + segments: SummaryInlineSegment[]; +}; + +export const SummaryInlineSegments = ({ + segments, +}: SummaryInlineSegmentsProps) => ( + <> + {segments.map((segment, index) => + segment.isBold ? ( + {segment.text} + ) : ( + {segment.text} + ), + )} + +); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/SummaryMarkdown.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/SummaryMarkdown.tsx new file mode 100644 index 0000000000..a663b22b31 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/SummaryMarkdown.tsx @@ -0,0 +1,87 @@ +import styled from '@emotion/styled'; +import { useMemo } from 'react'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { SummaryInlineSegments } from 'src/front-components/components/SummaryInlineSegments'; +import { parseSummaryMarkdownBlocks } from 'src/front-components/utils/parse-summary-markdown-blocks.util'; + +const TOP_LEVEL_HEADING_MAX = 2; + +const StyledSummary = styled.div` + color: ${() => themeCssVariables.font.color.secondary}; + display: flex; + flex-direction: column; + font-family: ${() => themeCssVariables.font.family}; + font-size: ${() => themeCssVariables.font.size.sm}; + gap: ${() => themeCssVariables.spacing[2]}; + line-height: 1.5; +`; + +const StyledHeading = styled.h3<{ $isTopLevel: boolean }>` + color: ${() => themeCssVariables.font.color.primary}; + font-size: ${({ $isTopLevel }) => + $isTopLevel + ? themeCssVariables.font.size.md + : themeCssVariables.font.size.sm}; + font-weight: ${() => themeCssVariables.font.weight.medium}; + margin: 0; + margin-top: ${() => themeCssVariables.spacing[1]}; +`; + +const StyledParagraph = styled.p` + margin: 0; +`; + +const StyledList = styled.ul` + display: flex; + flex-direction: column; + gap: ${() => themeCssVariables.spacing[1]}; + margin: 0; + padding-left: ${() => themeCssVariables.spacing[3]}; +`; + +type SummaryMarkdownProps = { + markdown: string; +}; + +export const SummaryMarkdown = ({ markdown }: SummaryMarkdownProps) => { + const blocks = useMemo( + () => parseSummaryMarkdownBlocks(markdown), + [markdown], + ); + + return ( + + {blocks.map((block, index) => { + if (block.type === 'heading') { + return ( + + + + ); + } + + if (block.type === 'list') { + return ( + + {block.items.map((item, itemIndex) => ( +
  • + +
  • + ))} +
    + ); + } + + return ( + + + + ); + })} +
    + ); +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptEntryList.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptEntryList.tsx new file mode 100644 index 0000000000..83116e0f7a --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptEntryList.tsx @@ -0,0 +1,25 @@ +import styled from '@emotion/styled'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { TranscriptEntryListItem } from 'src/front-components/components/TranscriptEntryListItem'; +import { type TranscriptEntry } from 'src/front-components/types/transcript-entry.type'; + +const StyledTranscriptContainer = styled.div` + display: flex; + flex: 1; + flex-direction: column; + gap: ${() => themeCssVariables.spacing[2]}; + min-height: 0; +`; + +type TranscriptEntryListProps = { + entries: TranscriptEntry[]; +}; + +export const TranscriptEntryList = ({ entries }: TranscriptEntryListProps) => ( + + {entries.map((entry, entryIndex) => ( + + ))} + +); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptEntryListItem.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptEntryListItem.tsx new file mode 100644 index 0000000000..ae4a81e047 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptEntryListItem.tsx @@ -0,0 +1,76 @@ +import styled from '@emotion/styled'; +import { isUndefined } from '@sniptt/guards'; +import { Avatar, Chip, ChipVariant } from 'twenty-ui/data-display'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +import { type TranscriptEntry } from 'src/front-components/types/transcript-entry.type'; +import { formatSecondsAsClockTimestamp } from 'src/front-components/utils/format-seconds-as-clock-timestamp.util'; + +const StyledEntry = styled.div` + align-items: flex-start; + border-radius: ${() => themeCssVariables.border.radius.sm}; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: ${() => themeCssVariables.spacing[2]}; + justify-content: center; + padding: ${() => themeCssVariables.spacing[2]}; + width: 100%; +`; + +const StyledEntryHeader = styled.div` + align-items: center; + align-self: stretch; + display: flex; + gap: ${() => themeCssVariables.spacing[2]}; + min-height: ${() => themeCssVariables.spacing[6]}; + min-width: 0; +`; + +const StyledTimestamp = styled.span` + color: ${() => themeCssVariables.font.color.tertiary}; + font-size: ${() => themeCssVariables.font.size.xs}; + line-height: 1.4; +`; + +const StyledEntryText = styled.p` + align-self: stretch; + color: ${() => themeCssVariables.font.color.secondary}; + font-size: ${() => themeCssVariables.font.size.sm}; + line-height: 1.4; + margin: 0; +`; + +type TranscriptEntryListItemProps = { + entry: TranscriptEntry; +}; + +export const TranscriptEntryListItem = ({ + entry, +}: TranscriptEntryListItemProps) => ( + + + + } + /> + {!isUndefined(entry.startSeconds) && ( + + {formatSecondsAsClockTimestamp(entry.startSeconds)} + + )} + + {entry.text} + +); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptErrorBox.tsx b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptErrorBox.tsx new file mode 100644 index 0000000000..29020b4bb7 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/components/TranscriptErrorBox.tsx @@ -0,0 +1,47 @@ +import styled from '@emotion/styled'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; + +const StyledStateContainer = styled.div` + box-sizing: border-box; + font-family: ${() => themeCssVariables.font.family}; + height: 100%; + padding: ${() => themeCssVariables.spacing[4]}; +`; + +const StyledErrorBox = styled.div` + background: ${() => themeCssVariables.background.transparent.danger}; + border: 1px solid ${() => themeCssVariables.border.color.danger}; + border-radius: ${() => themeCssVariables.border.radius.md}; + display: flex; + flex-direction: column; + gap: ${() => themeCssVariables.spacing[1]}; + padding: ${() => themeCssVariables.spacing[3]}; +`; + +const StyledErrorTitle = styled.span` + color: ${() => themeCssVariables.font.color.danger}; + font-size: ${() => themeCssVariables.font.size.sm}; + font-weight: ${() => themeCssVariables.font.weight.medium}; +`; + +const StyledErrorDescription = styled.span` + color: ${() => themeCssVariables.font.color.secondary}; + font-size: ${() => themeCssVariables.font.size.sm}; +`; + +type TranscriptErrorBoxProps = { + title: string; + description: string; +}; + +export const TranscriptErrorBox = ({ + title, + description, +}: TranscriptErrorBoxProps) => ( + + + {title} + {description} + + +); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/hooks/use-calendar-event-summary.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/hooks/use-calendar-event-summary.ts new file mode 100644 index 0000000000..1844244f3a --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/hooks/use-calendar-event-summary.ts @@ -0,0 +1,115 @@ +import { isNonEmptyString, isUndefined } from '@sniptt/guards'; +import { useEffect, useState } from 'react'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +type CalendarEventSummaryState = { + summaryMarkdown: string | undefined; + isCalendarEventSummaryQueryLoading: boolean; + errorMessage: string | undefined; +}; + +type CalendarEventSummaryCallRecordingNode = { + id: string; + summary: { markdown: string | null } | null; +}; + +type CalendarEventSummaryCallRecordingEdge = { + node: CalendarEventSummaryCallRecordingNode; +}; + +const CALENDAR_EVENT_SUMMARY_LOOKUP_LIMIT = 10; +const CALENDAR_EVENT_SUMMARY_ERROR_MESSAGE = 'Please try again later.'; + +const selectSummaryMarkdown = ( + callRecordingNodes: CalendarEventSummaryCallRecordingNode[], +): string | undefined => { + const summaryMarkdown = callRecordingNodes.find((callRecordingNode) => + isNonEmptyString(callRecordingNode.summary?.markdown), + )?.summary?.markdown; + + return isNonEmptyString(summaryMarkdown) ? summaryMarkdown : undefined; +}; + +export const useCalendarEventSummary = ( + calendarEventId: string | undefined, +): CalendarEventSummaryState => { + const [state, setState] = useState({ + summaryMarkdown: undefined, + isCalendarEventSummaryQueryLoading: !isUndefined(calendarEventId), + errorMessage: undefined, + }); + + useEffect(() => { + if (isUndefined(calendarEventId)) { + setState({ + summaryMarkdown: undefined, + isCalendarEventSummaryQueryLoading: false, + errorMessage: undefined, + }); + return; + } + + let cancelled = false; + + const fetchSummary = async () => { + setState({ + summaryMarkdown: undefined, + isCalendarEventSummaryQueryLoading: true, + errorMessage: undefined, + }); + + try { + const client = new CoreApiClient(); + const queryResult = await client.query({ + callRecordings: { + __args: { + filter: { calendarEventId: { eq: calendarEventId } }, + orderBy: [{ startedAt: 'DescNullsLast' }], + first: CALENDAR_EVENT_SUMMARY_LOOKUP_LIMIT, + }, + edges: { + node: { + id: true, + summary: { markdown: true }, + }, + }, + }, + }); + + if (cancelled) { + return; + } + + const callRecordingEdges = (queryResult.callRecordings?.edges ?? + []) as CalendarEventSummaryCallRecordingEdge[]; + const callRecordingNodes = callRecordingEdges.map( + (callRecordingEdge) => callRecordingEdge.node, + ); + + setState({ + summaryMarkdown: selectSummaryMarkdown(callRecordingNodes), + isCalendarEventSummaryQueryLoading: false, + errorMessage: undefined, + }); + } catch { + if (cancelled) { + return; + } + + setState({ + summaryMarkdown: undefined, + isCalendarEventSummaryQueryLoading: false, + errorMessage: CALENDAR_EVENT_SUMMARY_ERROR_MESSAGE, + }); + } + }; + + fetchSummary(); + + return () => { + cancelled = true; + }; + }, [calendarEventId]); + + return state; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/hooks/use-calendar-event-transcript.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/hooks/use-calendar-event-transcript.ts new file mode 100644 index 0000000000..6e90efa413 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/hooks/use-calendar-event-transcript.ts @@ -0,0 +1,115 @@ +import { isUndefined } from '@sniptt/guards'; +import { useEffect, useState } from 'react'; +import { CoreApiClient } from 'twenty-client-sdk/core'; + +type CalendarEventTranscriptState = { + transcript: unknown; + isCalendarEventTranscriptQueryLoading: boolean; + errorMessage: string | undefined; +}; + +type CalendarEventTranscriptCallRecordingNode = { + id: string; + transcript: unknown; +}; + +type CalendarEventTranscriptCallRecordingEdge = { + node: CalendarEventTranscriptCallRecordingNode; +}; + +const CALENDAR_EVENT_TRANSCRIPT_LOOKUP_LIMIT = 10; +const CALENDAR_EVENT_TRANSCRIPT_ERROR_MESSAGE = 'Please try again later.'; + +const hasTranscript = ( + callRecordingNode: CalendarEventTranscriptCallRecordingNode, +): boolean => + !isUndefined(callRecordingNode.transcript) && + callRecordingNode.transcript !== null; + +const selectTranscript = ( + callRecordingNodes: CalendarEventTranscriptCallRecordingNode[], +): unknown => callRecordingNodes.find(hasTranscript)?.transcript ?? undefined; + +export const useCalendarEventTranscript = ( + calendarEventId: string | undefined, +): CalendarEventTranscriptState => { + const [state, setState] = useState({ + transcript: undefined, + isCalendarEventTranscriptQueryLoading: !isUndefined(calendarEventId), + errorMessage: undefined, + }); + + useEffect(() => { + if (isUndefined(calendarEventId)) { + setState({ + transcript: undefined, + isCalendarEventTranscriptQueryLoading: false, + errorMessage: undefined, + }); + return; + } + + let cancelled = false; + + const fetchTranscript = async () => { + setState({ + transcript: undefined, + isCalendarEventTranscriptQueryLoading: true, + errorMessage: undefined, + }); + + try { + const client = new CoreApiClient(); + const queryResult = await client.query({ + callRecordings: { + __args: { + filter: { calendarEventId: { eq: calendarEventId } }, + orderBy: [{ startedAt: 'DescNullsLast' }], + first: CALENDAR_EVENT_TRANSCRIPT_LOOKUP_LIMIT, + }, + edges: { + node: { + id: true, + transcript: true, + }, + }, + }, + }); + + if (cancelled) { + return; + } + + const callRecordingEdges = (queryResult.callRecordings?.edges ?? + []) as CalendarEventTranscriptCallRecordingEdge[]; + const callRecordingNodes = callRecordingEdges.map( + (callRecordingEdge) => callRecordingEdge.node, + ); + + setState({ + transcript: selectTranscript(callRecordingNodes), + isCalendarEventTranscriptQueryLoading: false, + errorMessage: undefined, + }); + } catch { + if (cancelled) { + return; + } + + setState({ + transcript: undefined, + isCalendarEventTranscriptQueryLoading: false, + errorMessage: CALENDAR_EVENT_TRANSCRIPT_ERROR_MESSAGE, + }); + } + }; + + fetchTranscript(); + + return () => { + cancelled = true; + }; + }, [calendarEventId]); + + return state; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/summary-inline-segment.type.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/summary-inline-segment.type.ts new file mode 100644 index 0000000000..474eff8e90 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/summary-inline-segment.type.ts @@ -0,0 +1,4 @@ +export type SummaryInlineSegment = { + text: string; + isBold: boolean; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/summary-markdown-block.type.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/summary-markdown-block.type.ts new file mode 100644 index 0000000000..2ba417b7d7 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/summary-markdown-block.type.ts @@ -0,0 +1,6 @@ +import { type SummaryInlineSegment } from 'src/front-components/types/summary-inline-segment.type'; + +export type SummaryMarkdownBlock = + | { type: 'heading'; level: number; segments: SummaryInlineSegment[] } + | { type: 'paragraph'; segments: SummaryInlineSegment[] } + | { type: 'list'; items: SummaryInlineSegment[][] }; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/transcript-entry.type.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/transcript-entry.type.ts new file mode 100644 index 0000000000..0be2540670 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/types/transcript-entry.type.ts @@ -0,0 +1,13 @@ +export type TranscriptWord = { + text: string; + startSeconds: number | undefined; + endSeconds: number | undefined; +}; + +export type TranscriptEntry = { + speakerName: string; + startSeconds: number | undefined; + endSeconds: number | undefined; + text: string; + words: TranscriptWord[]; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-summary-inline-segments.test.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-summary-inline-segments.test.ts new file mode 100644 index 0000000000..1cf010606c --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-summary-inline-segments.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it } from 'vitest'; + +import { parseSummaryInlineSegments } from 'src/front-components/utils/parse-summary-inline-segments.util'; + +describe('parseSummaryInlineSegments', () => { + it('should return a single plain segment for text without markers', () => { + expect(parseSummaryInlineSegments('plain text')).toEqual([ + { text: 'plain text', isBold: false }, + ]); + }); + + it('should parse **bold** runs', () => { + expect(parseSummaryInlineSegments('a **b** c')).toEqual([ + { text: 'a ', isBold: false }, + { text: 'b', isBold: true }, + { text: ' c', isBold: false }, + ]); + }); + + it('should parse __bold__ runs', () => { + expect(parseSummaryInlineSegments('a __b__ c')).toEqual([ + { text: 'a ', isBold: false }, + { text: 'b', isBold: true }, + { text: ' c', isBold: false }, + ]); + }); + + it('should not match mismatched delimiters', () => { + expect(parseSummaryInlineSegments('**b__')).toEqual([ + { text: '**b__', isBold: false }, + ]); + }); +}); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-summary-markdown-blocks.test.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-summary-markdown-blocks.test.ts new file mode 100644 index 0000000000..4a03e558cd --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-summary-markdown-blocks.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest'; + +import { parseSummaryMarkdownBlocks } from 'src/front-components/utils/parse-summary-markdown-blocks.util'; + +describe('parseSummaryMarkdownBlocks', () => { + it('should parse headings, paragraphs and lists preserving order', () => { + expect( + parseSummaryMarkdownBlocks('## Overview\n\nSome text\n\n- first\n- second'), + ).toEqual([ + { + type: 'heading', + level: 2, + segments: [{ text: 'Overview', isBold: false }], + }, + { type: 'paragraph', segments: [{ text: 'Some text', isBold: false }] }, + { + type: 'list', + items: [ + [{ text: 'first', isBold: false }], + [{ text: 'second', isBold: false }], + ], + }, + ]); + }); + + it('should flush a trailing list at end of input', () => { + expect(parseSummaryMarkdownBlocks('- only item')).toEqual([ + { type: 'list', items: [[{ text: 'only item', isBold: false }]] }, + ]); + }); + + it('should return an empty array for empty input', () => { + expect(parseSummaryMarkdownBlocks('')).toEqual([]); + }); +}); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-transcript-entries.test.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-transcript-entries.test.ts new file mode 100644 index 0000000000..855657260f --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/__tests__/parse-transcript-entries.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from 'vitest'; + +import { parseTranscriptEntries } from 'src/front-components/utils/parse-transcript-entries.util'; + +describe('parseTranscriptEntries', () => { + it('should parse a diarized entry with speaker and relative timestamps', () => { + expect( + parseTranscriptEntries([ + { + participant: { name: 'Sarah' }, + words: [ + { + text: 'Hello', + start_timestamp: { relative: 1.5 }, + end_timestamp: { relative: 2 }, + }, + ], + }, + ]), + ).toEqual([ + { + speakerName: 'Sarah', + startSeconds: 1.5, + endSeconds: 2, + text: 'Hello', + words: [{ text: 'Hello', startSeconds: 1.5, endSeconds: 2 }], + }, + ]); + }); + + it('should fall back to "Unknown speaker" for a whitespace-only name', () => { + const entries = parseTranscriptEntries([ + { participant: { name: ' ' }, words: [{ text: 'Hi' }] }, + ]); + + expect(entries?.[0]?.speakerName).toBe('Unknown speaker'); + }); + + it('should drop entries whose words are all whitespace', () => { + expect( + parseTranscriptEntries([ + { participant: { name: 'Sarah' }, words: [{ text: ' ' }] }, + ]), + ).toEqual([]); + }); + + it('should return undefined for a non-array transcript', () => { + expect(parseTranscriptEntries({})).toBeUndefined(); + }); +}); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/as-record.util.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/as-record.util.ts new file mode 100644 index 0000000000..63e7cfcddd --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/as-record.util.ts @@ -0,0 +1,6 @@ +import { isArray, isObject } from '@sniptt/guards'; + +export const asRecord = (value: unknown): Record | undefined => + isObject(value) && !isArray(value) + ? (value as Record) + : undefined; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/format-seconds-as-clock-timestamp.util.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/format-seconds-as-clock-timestamp.util.ts new file mode 100644 index 0000000000..c92cae504c --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/format-seconds-as-clock-timestamp.util.ts @@ -0,0 +1,20 @@ +const SECONDS_PER_MINUTE = 60; +const SECONDS_PER_HOUR = 3600; + +export const formatSecondsAsClockTimestamp = (totalSeconds: number): string => { + const safeSeconds = Number.isFinite(totalSeconds) + ? Math.max(0, Math.floor(totalSeconds)) + : 0; + const hours = Math.floor(safeSeconds / SECONDS_PER_HOUR); + const minutes = Math.floor( + (safeSeconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE, + ); + const seconds = safeSeconds % SECONDS_PER_MINUTE; + const paddedSeconds = String(seconds).padStart(2, '0'); + + if (hours > 0) { + return `${hours}:${String(minutes).padStart(2, '0')}:${paddedSeconds}`; + } + + return `${minutes}:${paddedSeconds}`; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/is-non-empty-string.util.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/is-non-empty-string.util.ts new file mode 100644 index 0000000000..175943f383 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/is-non-empty-string.util.ts @@ -0,0 +1,5 @@ +import { isString } from '@sniptt/guards'; + +// Trimming variant of @sniptt/guards isNonEmptyString, for normalizing at read boundaries. +export const isNonEmptyString = (value: unknown): value is string => + isString(value) && value.trim() !== ''; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-summary-inline-segments.util.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-summary-inline-segments.util.ts new file mode 100644 index 0000000000..d275f08182 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-summary-inline-segments.util.ts @@ -0,0 +1,39 @@ +import { type SummaryInlineSegment } from 'src/front-components/types/summary-inline-segment.type'; + +const BOLD_PATTERN = /(\*\*|__)(.+?)\1/g; + +// Splits a markdown line into plain and bold (**bold** or __bold__) runs, +// preserving surrounding whitespace so adjacent bold runs don't collapse together. +export const parseSummaryInlineSegments = ( + text: string, +): SummaryInlineSegment[] => { + const boldMatches = [...text.matchAll(BOLD_PATTERN)]; + + const { segments, cursor } = boldMatches.reduce<{ + segments: SummaryInlineSegment[]; + cursor: number; + }>( + (accumulator, match) => { + const matchStart = match.index ?? 0; + const leadingText = text.slice(accumulator.cursor, matchStart); + const leadingSegments = + leadingText.length > 0 ? [{ text: leadingText, isBold: false }] : []; + + return { + segments: [ + ...accumulator.segments, + ...leadingSegments, + { text: match[2], isBold: true }, + ], + cursor: matchStart + match[0].length, + }; + }, + { segments: [], cursor: 0 }, + ); + + const trailingText = text.slice(cursor); + + return trailingText.length > 0 + ? [...segments, { text: trailingText, isBold: false }] + : segments; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-summary-markdown-blocks.util.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-summary-markdown-blocks.util.ts new file mode 100644 index 0000000000..131043467e --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-summary-markdown-blocks.util.ts @@ -0,0 +1,58 @@ +import { type SummaryInlineSegment } from 'src/front-components/types/summary-inline-segment.type'; +import { type SummaryMarkdownBlock } from 'src/front-components/types/summary-markdown-block.type'; +import { parseSummaryInlineSegments } from 'src/front-components/utils/parse-summary-inline-segments.util'; + +const HEADING_PATTERN = /^(#{1,6})\s+(.+)$/; +const BULLET_PATTERN = /^[-*]\s+(.+)$/; + +export const parseSummaryMarkdownBlocks = ( + markdown: string, +): SummaryMarkdownBlock[] => { + const blocks: SummaryMarkdownBlock[] = []; + let pendingListItems: SummaryInlineSegment[][] = []; + + const flushPendingList = () => { + if (pendingListItems.length > 0) { + blocks.push({ type: 'list', items: pendingListItems }); + pendingListItems = []; + } + }; + + for (const rawLine of markdown.split('\n')) { + const line = rawLine.trim(); + + if (line === '') { + flushPendingList(); + continue; + } + + const headingMatch = HEADING_PATTERN.exec(line); + + if (headingMatch !== null) { + flushPendingList(); + blocks.push({ + type: 'heading', + level: headingMatch[1].length, + segments: parseSummaryInlineSegments(headingMatch[2]), + }); + continue; + } + + const bulletMatch = BULLET_PATTERN.exec(line); + + if (bulletMatch !== null) { + pendingListItems.push(parseSummaryInlineSegments(bulletMatch[1])); + continue; + } + + flushPendingList(); + blocks.push({ + type: 'paragraph', + segments: parseSummaryInlineSegments(line), + }); + } + + flushPendingList(); + + return blocks; +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-transcript-entries.util.ts b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-transcript-entries.util.ts new file mode 100644 index 0000000000..e15fa74c38 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/front-components/utils/parse-transcript-entries.util.ts @@ -0,0 +1,85 @@ +import { isArray, isNumber, isUndefined } from '@sniptt/guards'; + +import { + type TranscriptEntry, + type TranscriptWord, +} from 'src/front-components/types/transcript-entry.type'; +import { asRecord } from 'src/front-components/utils/as-record.util'; +import { isNonEmptyString } from 'src/front-components/utils/is-non-empty-string.util'; + +type TranscriptRecord = NonNullable>; + +const isTranscriptRecord = ( + candidate: TranscriptRecord | undefined, +): candidate is TranscriptRecord => !isUndefined(candidate); + +const readRelativeTimestamp = ( + timestamp: TranscriptRecord | undefined, +): number | undefined => { + const relativeTimestamp = timestamp?.relative; + + return isNumber(relativeTimestamp) && Number.isFinite(relativeTimestamp) + ? relativeTimestamp + : undefined; +}; + +const readTranscriptWord = ( + candidate: TranscriptRecord, +): TranscriptWord | undefined => { + if (!isNonEmptyString(candidate.text)) { + return undefined; + } + + return { + text: candidate.text.trim(), + startSeconds: readRelativeTimestamp(asRecord(candidate.start_timestamp)), + endSeconds: readRelativeTimestamp(asRecord(candidate.end_timestamp)), + }; +}; + +const readSpeakerName = (participant: TranscriptRecord | undefined): string => { + const name = participant?.name; + + return isNonEmptyString(name) ? name.trim() : 'Unknown speaker'; +}; + +const readTranscriptEntry = ( + candidate: TranscriptRecord, +): TranscriptEntry | undefined => { + if (!isArray(candidate.words)) { + return undefined; + } + + const words = candidate.words + .map(asRecord) + .filter(isTranscriptRecord) + .map(readTranscriptWord) + .filter((word): word is TranscriptWord => !isUndefined(word)); + + if (words.length === 0) { + return undefined; + } + + return { + speakerName: readSpeakerName(asRecord(candidate.participant)), + startSeconds: words[0].startSeconds, + endSeconds: words[words.length - 1].endSeconds, + text: words.map((word) => word.text).join(' '), + words, + }; +}; + +// Undefined means the value is not a diarized transcript; malformed entries are skipped, not fatal. +export const parseTranscriptEntries = ( + transcript: unknown, +): TranscriptEntry[] | undefined => { + if (!isArray(transcript)) { + return undefined; + } + + return transcript + .map(asRecord) + .filter(isTranscriptRecord) + .map(readTranscriptEntry) + .filter((entry): entry is TranscriptEntry => !isUndefined(entry)); +}; diff --git a/packages/twenty-apps/public/twenty-fireflies/src/page-layouts/calendar-event-summary-tab.ts b/packages/twenty-apps/public/twenty-fireflies/src/page-layouts/calendar-event-summary-tab.ts new file mode 100644 index 0000000000..675080ebb1 --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/page-layouts/calendar-event-summary-tab.ts @@ -0,0 +1,35 @@ +import { + definePageLayoutTab, + PageLayoutTabLayoutMode, +} from 'twenty-sdk/define'; + +import { + CALENDAR_EVENT_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, + CALENDAR_EVENT_SUMMARY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + CALENDAR_EVENT_SUMMARY_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER, + CALENDAR_EVENT_SUMMARY_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default definePageLayoutTab({ + universalIdentifier: + CALENDAR_EVENT_SUMMARY_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER, + title: 'Summary', + position: 16, + icon: 'IconFileText', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + pageLayoutUniversalIdentifier: + CALENDAR_EVENT_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, + widgets: [ + { + universalIdentifier: + CALENDAR_EVENT_SUMMARY_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER, + title: 'Summary', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + CALENDAR_EVENT_SUMMARY_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], +}); diff --git a/packages/twenty-apps/public/twenty-fireflies/src/page-layouts/calendar-event-transcript-tab.ts b/packages/twenty-apps/public/twenty-fireflies/src/page-layouts/calendar-event-transcript-tab.ts new file mode 100644 index 0000000000..f064599c2a --- /dev/null +++ b/packages/twenty-apps/public/twenty-fireflies/src/page-layouts/calendar-event-transcript-tab.ts @@ -0,0 +1,35 @@ +import { + definePageLayoutTab, + PageLayoutTabLayoutMode, +} from 'twenty-sdk/define'; + +import { + CALENDAR_EVENT_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, + CALENDAR_EVENT_TRANSCRIPT_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + CALENDAR_EVENT_TRANSCRIPT_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER, + CALENDAR_EVENT_TRANSCRIPT_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER, +} from 'src/constants/universal-identifiers'; + +export default definePageLayoutTab({ + universalIdentifier: + CALENDAR_EVENT_TRANSCRIPT_PAGE_LAYOUT_TAB_UNIVERSAL_IDENTIFIER, + title: 'Transcript', + position: 17, + icon: 'IconMicrophone', + layoutMode: PageLayoutTabLayoutMode.CANVAS, + pageLayoutUniversalIdentifier: + CALENDAR_EVENT_RECORD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIER, + widgets: [ + { + universalIdentifier: + CALENDAR_EVENT_TRANSCRIPT_PAGE_LAYOUT_WIDGET_UNIVERSAL_IDENTIFIER, + title: 'Transcript', + type: 'FRONT_COMPONENT', + configuration: { + configurationType: 'FRONT_COMPONENT', + frontComponentUniversalIdentifier: + CALENDAR_EVENT_TRANSCRIPT_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER, + }, + }, + ], +}); diff --git a/packages/twenty-apps/public/twenty-fireflies/yarn.lock b/packages/twenty-apps/public/twenty-fireflies/yarn.lock index 379ce447ee..e6163e95e7 100644 --- a/packages/twenty-apps/public/twenty-fireflies/yarn.lock +++ b/packages/twenty-apps/public/twenty-fireflies/yarn.lock @@ -15,6 +15,160 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/generator@npm:7.29.7" + dependencies: + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/9bf72b01b5bd0ea5b1288a0e37dbd360bff2f2b1ce73342c0d40fb3db2ec3dc004ada5ffa925c5e12939a416eed59e600d562b8ecd938ce0d27dfd0eb6c6c2b7 + languageName: node + linkType: hard + +"@babel/helper-globals@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-globals@npm:7.29.7" + checksum: 10c0/f38417c40b1129a1b2b519ca961b9040c8827d1444fd74068702286b91b77089431dc76b6b9d5c1496e5da2a4f3ad329c6946e688ba3fa0d1d0b3d2b4f34f36a + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.16.7": + version: 7.29.7 + resolution: "@babel/helper-module-imports@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/6adf60d97356027413342a092f818d9678c4f5caff716a33e3284b5ae14e47a9e88059d421dde4ee4894691260039a12602c0e7becadc175602194b40dfa345d + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + +"@babel/parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/parser@npm:7.29.7" + dependencies: + "@babel/types": "npm:^7.29.7" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/65133038f80b54a714d6027cb77cee3f9a6b5c4c6842ce674301e13947cbcbfa8055e63acaf1b84c085d34226a14425b2c2b97b829e0e226d2e8f1299942a51d + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.29.2": + version: 7.29.7 + resolution: "@babel/runtime@npm:7.29.7" + checksum: 10c0/ca11572f7146b21e0bde6a9ed4bb6a89eafbee5f0944c7eb54d0d8a2dac962c33638a1d611e14faa71dfbb92b4b5f9236232208568a6b7d5c6f3f39ddb91771e + languageName: node + linkType: hard + +"@babel/template@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/template@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8bb7f900dcab0e9e1c5ffbc33ca10e0d26b7b2e2ca804becb73ee771b9c4ed6e2908a4ae4a14c08560febb45d2b6b9a173955e42ad404d05f8b04840a14d9c58 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/traverse@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + debug: "npm:^4.3.1" + checksum: 10c0/e256a1fbdb956555b76f3c285b1e453f6bedec8b3afb61751d99d933efd11c7d79caf5ddf2493570058a9f7deaa1b48324380d7c1aa1443fd9508becbf56331a + languageName: node + linkType: hard + +"@babel/types@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/types@npm:7.29.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/b6623994c69717fa27294f5fa46d59140338e2d86c6c1c13085c84ef7d53086ee357fbf4fe9abe3dd3da75734dc77c4c0df2f90fb29e667558bb3b3fb705e88f + languageName: node + linkType: hard + +"@base-ui/react@npm:^1.5.0": + version: 1.6.0 + resolution: "@base-ui/react@npm:1.6.0" + dependencies: + "@babel/runtime": "npm:^7.29.2" + "@base-ui/utils": "npm:0.3.1" + "@floating-ui/react-dom": "npm:^2.1.8" + "@floating-ui/utils": "npm:^0.2.11" + use-sync-external-store: "npm:^1.6.0" + peerDependencies: + "@date-fns/tz": ^1.2.0 + "@types/react": ^17 || ^18 || ^19 + date-fns: ^4.0.0 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + "@date-fns/tz": + optional: true + "@types/react": + optional: true + date-fns: + optional: true + checksum: 10c0/5e5994dc782710eac900d68cc0691aea70a47fabc6ef8d2ea943103d9aa96c4b44f9fd76ba65d29fa2667a5fbd7ea7a1cc02fe111c4bf639d4e3e65a1c281118 + languageName: node + linkType: hard + +"@base-ui/utils@npm:0.3.1": + version: 0.3.1 + resolution: "@base-ui/utils@npm:0.3.1" + dependencies: + "@babel/runtime": "npm:^7.29.2" + "@floating-ui/utils": "npm:^0.2.11" + reselect: "npm:^5.2.0" + use-sync-external-store: "npm:^1.6.0" + peerDependencies: + "@types/react": ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/4de858c3e2c8a4486a549d51c5e887bba6921e5a7f8db54439ba558feaf1656c3592a873928aee8a1711ee9210be3a65ca43b26713e97f649695ed2fa8c3af2c + languageName: node + linkType: hard + "@emnapi/core@npm:1.10.0": version: 1.10.0 resolution: "@emnapi/core@npm:1.10.0" @@ -52,6 +206,152 @@ __metadata: languageName: node linkType: hard +"@emotion/babel-plugin@npm:^11.13.5": + version: 11.13.5 + resolution: "@emotion/babel-plugin@npm:11.13.5" + dependencies: + "@babel/helper-module-imports": "npm:^7.16.7" + "@babel/runtime": "npm:^7.18.3" + "@emotion/hash": "npm:^0.9.2" + "@emotion/memoize": "npm:^0.9.0" + "@emotion/serialize": "npm:^1.3.3" + babel-plugin-macros: "npm:^3.1.0" + convert-source-map: "npm:^1.5.0" + escape-string-regexp: "npm:^4.0.0" + find-root: "npm:^1.1.0" + source-map: "npm:^0.5.7" + stylis: "npm:4.2.0" + checksum: 10c0/8ccbfec7defd0e513cb8a1568fa179eac1e20c35fda18aed767f6c59ea7314363ebf2de3e9d2df66c8ad78928dc3dceeded84e6fa8059087cae5c280090aeeeb + languageName: node + linkType: hard + +"@emotion/cache@npm:^11.14.0": + version: 11.14.0 + resolution: "@emotion/cache@npm:11.14.0" + dependencies: + "@emotion/memoize": "npm:^0.9.0" + "@emotion/sheet": "npm:^1.4.0" + "@emotion/utils": "npm:^1.4.2" + "@emotion/weak-memoize": "npm:^0.4.0" + stylis: "npm:4.2.0" + checksum: 10c0/3fa3e7a431ab6f8a47c67132a00ac8358f428c1b6c8421d4b20de9df7c18e95eec04a5a6ff5a68908f98d3280044f247b4965ac63df8302d2c94dba718769724 + languageName: node + linkType: hard + +"@emotion/hash@npm:^0.9.2": + version: 0.9.2 + resolution: "@emotion/hash@npm:0.9.2" + checksum: 10c0/0dc254561a3cc0a06a10bbce7f6a997883fd240c8c1928b93713f803a2e9153a257a488537012efe89dbe1246f2abfe2add62cdb3471a13d67137fcb808e81c2 + languageName: node + linkType: hard + +"@emotion/is-prop-valid@npm:^1.3.0": + version: 1.4.0 + resolution: "@emotion/is-prop-valid@npm:1.4.0" + dependencies: + "@emotion/memoize": "npm:^0.9.0" + checksum: 10c0/5f857814ec7d8c7e727727346dfb001af6b1fb31d621a3ce9c3edf944a484d8b0d619546c30899ae3ade2f317c76390ba4394449728e9bf628312defc2c41ac3 + languageName: node + linkType: hard + +"@emotion/memoize@npm:^0.9.0": + version: 0.9.0 + resolution: "@emotion/memoize@npm:0.9.0" + checksum: 10c0/13f474a9201c7f88b543e6ea42f55c04fb2fdc05e6c5a3108aced2f7e7aa7eda7794c56bba02985a46d8aaa914fcdde238727a98341a96e2aec750d372dadd15 + languageName: node + linkType: hard + +"@emotion/react@npm:^11.14.0": + version: 11.14.0 + resolution: "@emotion/react@npm:11.14.0" + dependencies: + "@babel/runtime": "npm:^7.18.3" + "@emotion/babel-plugin": "npm:^11.13.5" + "@emotion/cache": "npm:^11.14.0" + "@emotion/serialize": "npm:^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.2.0" + "@emotion/utils": "npm:^1.4.2" + "@emotion/weak-memoize": "npm:^0.4.0" + hoist-non-react-statics: "npm:^3.3.1" + peerDependencies: + react: ">=16.8.0" + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/d0864f571a9f99ec643420ef31fde09e2006d3943a6aba079980e4d5f6e9f9fecbcc54b8f617fe003c00092ff9d5241179149ffff2810cb05cf72b4620cfc031 + languageName: node + linkType: hard + +"@emotion/serialize@npm:^1.3.3": + version: 1.3.3 + resolution: "@emotion/serialize@npm:1.3.3" + dependencies: + "@emotion/hash": "npm:^0.9.2" + "@emotion/memoize": "npm:^0.9.0" + "@emotion/unitless": "npm:^0.10.0" + "@emotion/utils": "npm:^1.4.2" + csstype: "npm:^3.0.2" + checksum: 10c0/b28cb7de59de382021de2b26c0c94ebbfb16967a1b969a56fdb6408465a8993df243bfbd66430badaa6800e1834724e84895f5a6a9d97d0d224de3d77852acb4 + languageName: node + linkType: hard + +"@emotion/sheet@npm:^1.4.0": + version: 1.4.0 + resolution: "@emotion/sheet@npm:1.4.0" + checksum: 10c0/3ca72d1650a07d2fbb7e382761b130b4a887dcd04e6574b2d51ce578791240150d7072a9bcb4161933abbcd1e38b243a6fb4464a7fe991d700c17aa66bb5acc7 + languageName: node + linkType: hard + +"@emotion/styled@npm:^11.14.0": + version: 11.14.1 + resolution: "@emotion/styled@npm:11.14.1" + dependencies: + "@babel/runtime": "npm:^7.18.3" + "@emotion/babel-plugin": "npm:^11.13.5" + "@emotion/is-prop-valid": "npm:^1.3.0" + "@emotion/serialize": "npm:^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.2.0" + "@emotion/utils": "npm:^1.4.2" + peerDependencies: + "@emotion/react": ^11.0.0-rc.0 + react: ">=16.8.0" + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/2bbf8451df49c967e41fbcf8111a7f6dafe6757f0cc113f2f6e287206c45ac1d54dc8a95a483b7c0cee8614b8a8d08155bded6453d6721de1f8cc8d5b9216963 + languageName: node + linkType: hard + +"@emotion/unitless@npm:^0.10.0": + version: 0.10.0 + resolution: "@emotion/unitless@npm:0.10.0" + checksum: 10c0/150943192727b7650eb9a6851a98034ddb58a8b6958b37546080f794696141c3760966ac695ab9af97efe10178690987aee4791f9f0ad1ff76783cdca83c1d49 + languageName: node + linkType: hard + +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0": + version: 1.2.0 + resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/074dbc92b96bdc09209871070076e3b0351b6b47efefa849a7d9c37ab142130767609ca1831da0055988974e3b895c1de7606e4c421fecaa27c3e56a2afd3b08 + languageName: node + linkType: hard + +"@emotion/utils@npm:^1.4.2": + version: 1.4.2 + resolution: "@emotion/utils@npm:1.4.2" + checksum: 10c0/7d0010bf60a2a8c1a033b6431469de4c80e47aeb8fd856a17c1d1f76bbc3a03161a34aeaa78803566e29681ca551e7bf9994b68e9c5f5c796159923e44f78d9a + languageName: node + linkType: hard + +"@emotion/weak-memoize@npm:^0.4.0": + version: 0.4.0 + resolution: "@emotion/weak-memoize@npm:0.4.0" + checksum: 10c0/64376af11f1266042d03b3305c30b7502e6084868e33327e944b539091a472f089db307af69240f7188f8bc6b319276fd7b141a36613f1160d73d12a60f6ca1a + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.28.1": version: 0.28.1 resolution: "@esbuild/aix-ppc64@npm:0.28.1" @@ -234,6 +534,44 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.7.5": + version: 1.7.5 + resolution: "@floating-ui/core@npm:1.7.5" + dependencies: + "@floating-ui/utils": "npm:^0.2.11" + checksum: 10c0/f9c52205e198b231d63a387b09c659aab08c46a1899e0b0bbe147b8b4f048b546f15ba17cb5d2a471da9534f1883d979425e13e5c4ceee67be63e4b0abd4db5d + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.7.6": + version: 1.7.6 + resolution: "@floating-ui/dom@npm:1.7.6" + dependencies: + "@floating-ui/core": "npm:^1.7.5" + "@floating-ui/utils": "npm:^0.2.11" + checksum: 10c0/5c098e0d7b58c9bc769f276cca1766994c2c9c70c92d091a61bba8b3e9be53c011e0a79a8457fc2fb2f3d91697a26eb52e0a4962ef936dc963b45f58613c212f + languageName: node + linkType: hard + +"@floating-ui/react-dom@npm:^2.1.8": + version: 2.1.8 + resolution: "@floating-ui/react-dom@npm:2.1.8" + dependencies: + "@floating-ui/dom": "npm:^1.7.6" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/26260ca4bb23b57c73b824062505abf977a008ce6e0463bdacca74f7e49853c4cd1d2bbf1a77c6caa17fa37dfffda2c6c4cd07a8737ebd7474aaff7818401d75 + languageName: node + linkType: hard + +"@floating-ui/utils@npm:^0.2.11": + version: 0.2.11 + resolution: "@floating-ui/utils@npm:0.2.11" + checksum: 10c0/f4bcea1559bdbb721ecc8e8ead423ac58d6a5b6e70b602cf0810ba6ad4ed1c77211b207faa88b278a9042f0c743133de08a203ed6741c1b6443423332884d5b3 + languageName: node + linkType: hard + "@genql/runtime@npm:^2.10.0": version: 2.10.0 resolution: "@genql/runtime@npm:2.10.0" @@ -731,13 +1069,62 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.5.5": +"@jridgewell/gen-mapping@npm:^0.3.12": + version: 0.3.13 + resolution: "@jridgewell/gen-mapping@npm:0.3.13" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/9a7d65fb13bd9aec1fbab74cda08496839b7e2ceb31f5ab922b323e94d7c481ce0fc4fd7e12e2610915ed8af51178bdc61e168e92a8c8b8303b030b03489b13b + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0, @jridgewell/sourcemap-codec@npm:^1.5.5": version: 1.5.5 resolution: "@jridgewell/sourcemap-codec@npm:1.5.5" checksum: 10c0/f9e538f302b63c0ebc06eecb1dd9918dd4289ed36147a0ddce35d6ea4d7ebbda243cda7b2213b6a5e1d8087a298d5cf630fb2bd39329cdecb82017023f6081a0 languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/4b30ec8cd56c5fd9a661f088230af01e0c1a3888d11ffb6b47639700f71225be21d1f7e168048d6d4f9449207b978a235c07c8f15c07705685d16dc06280e9d9 + languageName: node + linkType: hard + +"@monaco-editor/loader@npm:^1.5.0": + version: 1.7.0 + resolution: "@monaco-editor/loader@npm:1.7.0" + dependencies: + state-local: "npm:^1.0.6" + checksum: 10c0/1fd3579cb09b669493cf553dfc0723a93483140a44353ce9a739827edfa7b2c6541b254024d15c48176ece749ef666b6d16cce6cf0e4396c7fa1c5a48012d08a + languageName: node + linkType: hard + +"@monaco-editor/react@npm:^4.7.0": + version: 4.7.0 + resolution: "@monaco-editor/react@npm:4.7.0" + dependencies: + "@monaco-editor/loader": "npm:^1.5.0" + peerDependencies: + monaco-editor: ">= 0.25.0 < 1" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/a4e91c6eda1a71f5fd23871bd801de435490ccbc43934d23cba65cefe2be7e9d02c9a57c4ef80fc9fe99e4d4deea51e5db19cb4e0ecf3f51818dda0eb9cdbf12 + languageName: node + linkType: hard + "@napi-rs/wasm-runtime@npm:^1.1.4": version: 1.1.5 resolution: "@napi-rs/wasm-runtime@npm:1.1.5" @@ -813,6 +1200,20 @@ __metadata: languageName: node linkType: hard +"@radix-ui/colors@npm:^3.0.0": + version: 3.0.0 + resolution: "@radix-ui/colors@npm:3.0.0" + checksum: 10c0/612c6bb4efe987f2d49aaca2f785687d08150da59a248328bc2b4e187ece4ef731c1dc5c6a9430fffcac4af9e65cf2f89ceb9806f8809940c9eb238b79d2cf2f + languageName: node + linkType: hard + +"@remix-run/router@npm:1.23.3": + version: 1.23.3 + resolution: "@remix-run/router@npm:1.23.3" + checksum: 10c0/73622465dd9e9a2c5a7ced7d1151ea6e9195a8a979c99b4f70a67093eeff7f339daf03a7c6304709a9c27deb2081a8fff6737663aacb33529c1a12a0a0827a17 + languageName: node + linkType: hard + "@rolldown/binding-android-arm64@npm:1.0.3": version: 1.0.3 resolution: "@rolldown/binding-android-arm64@npm:1.0.3" @@ -943,10 +1344,31 @@ __metadata: languageName: node linkType: hard +"@tabler/icons-react@npm:^3.31.0": + version: 3.44.0 + resolution: "@tabler/icons-react@npm:3.44.0" + dependencies: + "@tabler/icons": "npm:3.44.0" + peerDependencies: + react: ">= 16" + checksum: 10c0/9a4bb6f1121d001049be8bb5757160ce4d5f2430671479ee94763f88ef43a69ab8d89b835d6c6451de22f3071793b779fefeed43db49d7993de1ea65c50145c9 + languageName: node + linkType: hard + +"@tabler/icons@npm:3.44.0": + version: 3.44.0 + resolution: "@tabler/icons@npm:3.44.0" + checksum: 10c0/0d5c1f9d6e68aa04c4a661e96035190e0884e0bc0022d31922efad3cfba210a25b2f013c0b6c0ab6aeb3d5a402a9b85a168966bd11a30ba6087f614f8521ccf3 + languageName: node + linkType: hard + "@twentyhq/twenty-fireflies@workspace:.": version: 0.0.0-use.local resolution: "@twentyhq/twenty-fireflies@workspace:." dependencies: + "@emotion/react": "npm:^11.14.0" + "@emotion/styled": "npm:^11.14.0" + "@sniptt/guards": "npm:^0.2.0" "@types/node": "npm:^24.7.2" "@types/react": "npm:^18.2.0" "@typescript/native-preview": "npm:^7.0.0-dev.20260116.1" @@ -955,6 +1377,7 @@ __metadata: react-dom: "npm:^18.2.0" twenty-client-sdk: "npm:^2.18.0" twenty-sdk: "npm:^2.18.0" + twenty-ui: "npm:^1.0.0-alpha.1" typescript: "npm:^5.9.3" vite-tsconfig-paths: "npm:^4.2.1" vitest: "npm:^4.0.0" @@ -1012,6 +1435,13 @@ __metadata: languageName: node linkType: hard +"@types/parse-json@npm:^4.0.0": + version: 4.0.2 + resolution: "@types/parse-json@npm:4.0.2" + checksum: 10c0/b1b863ac34a2c2172fbe0807a1ec4d5cb684e48d422d15ec95980b81475fac4fdb3768a8b13eef39130203a7c04340fc167bae057c7ebcafd7dec9fe6c36aeb1 + languageName: node + linkType: hard + "@types/prop-types@npm:*": version: 15.7.15 resolution: "@types/prop-types@npm:15.7.15" @@ -1301,6 +1731,17 @@ __metadata: languageName: node linkType: hard +"babel-plugin-macros@npm:^3.1.0": + version: 3.1.0 + resolution: "babel-plugin-macros@npm:3.1.0" + dependencies: + "@babel/runtime": "npm:^7.12.5" + cosmiconfig: "npm:^7.0.0" + resolve: "npm:^1.19.0" + checksum: 10c0/c6dfb15de96f67871d95bd2e8c58b0c81edc08b9b087dc16755e7157f357dc1090a8dc60ebab955e92587a9101f02eba07e730adc253a1e4cf593ca3ebd3839c + languageName: node + linkType: hard + "backo2@npm:^1.0.2": version: 1.0.2 resolution: "backo2@npm:1.0.2" @@ -1318,6 +1759,13 @@ __metadata: languageName: node linkType: hard +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 + languageName: node + linkType: hard + "chai@npm:^6.2.2": version: 6.2.2 resolution: "chai@npm:6.2.2" @@ -1388,6 +1836,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 + languageName: node + linkType: hard + "code-excerpt@npm:^4.0.0": version: 4.0.0 resolution: "code-excerpt@npm:4.0.0" @@ -1413,6 +1868,13 @@ __metadata: languageName: node linkType: hard +"convert-source-map@npm:^1.5.0": + version: 1.9.0 + resolution: "convert-source-map@npm:1.9.0" + checksum: 10c0/281da55454bf8126cbc6625385928c43479f2060984180c42f3a86c8b8c12720a24eac260624a7d1e090004028d2dee78602330578ceec1a08e27cb8bb0a8a5b + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -1427,14 +1889,34 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^3.2.2": +"cosmiconfig@npm:^7.0.0": + version: 7.1.0 + resolution: "cosmiconfig@npm:7.1.0" + dependencies: + "@types/parse-json": "npm:^4.0.0" + import-fresh: "npm:^3.2.1" + parse-json: "npm:^5.0.0" + path-type: "npm:^4.0.0" + yaml: "npm:^1.10.0" + checksum: 10c0/b923ff6af581638128e5f074a5450ba12c0300b71302398ea38dbeabd33bbcaa0245ca9adbedfcf284a07da50f99ede5658c80bb3e39e2ce770a99d28a21ef03 + languageName: node + linkType: hard + +"css-mediaquery@npm:^0.1.2": + version: 0.1.2 + resolution: "css-mediaquery@npm:0.1.2" + checksum: 10c0/b7825a78f52ce8a8198e004fcad0d7be1d3c9a0463ecd05ba31a0f2c94fb81468ad6f4d7bf715a6ca775696e7a17500c2a339b5216a6d0f789cbf78f9454d048 + languageName: node + linkType: hard + +"csstype@npm:^3.0.2, csstype@npm:^3.2.2": version: 3.2.3 resolution: "csstype@npm:3.2.3" checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.1": +"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.1": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -1492,6 +1974,15 @@ __metadata: languageName: node linkType: hard +"error-ex@npm:^1.3.1": + version: 1.3.4 + resolution: "error-ex@npm:1.3.4" + dependencies: + is-arrayish: "npm:^0.2.1" + checksum: 10c0/b9e34ff4778b8f3b31a8377e1c654456f4c41aeaa3d10a1138c3b7635d8b7b2e03eb2475d46d8ae055c1f180a1063e100bffabf64ea7e7388b37735df5328664 + languageName: node + linkType: hard + "es-define-property@npm:^1.0.1": version: 1.0.1 resolution: "es-define-property@npm:1.0.1" @@ -1642,6 +2133,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 + languageName: node + linkType: hard + "estree-walker@npm:^3.0.3": version: 3.0.3 resolution: "estree-walker@npm:3.0.3" @@ -1709,6 +2207,13 @@ __metadata: languageName: node linkType: hard +"find-root@npm:^1.1.0": + version: 1.1.0 + resolution: "find-root@npm:1.1.0" + checksum: 10c0/1abc7f3bf2f8d78ff26d9e00ce9d0f7b32e5ff6d1da2857bcdf4746134c422282b091c672cde0572cac3840713487e0a7a636af9aa1b74cb11894b447a521efa + languageName: node + linkType: hard + "follow-redirects@npm:^1.16.0": version: 1.16.0 resolution: "follow-redirects@npm:1.16.0" @@ -1872,7 +2377,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.4": +"hasown@npm:^2.0.3, hasown@npm:^2.0.4": version: 2.0.4 resolution: "hasown@npm:2.0.4" dependencies: @@ -1881,6 +2386,15 @@ __metadata: languageName: node linkType: hard +"hoist-non-react-statics@npm:^3.3.1": + version: 3.3.2 + resolution: "hoist-non-react-statics@npm:3.3.2" + dependencies: + react-is: "npm:^16.7.0" + checksum: 10c0/fe0889169e845d738b59b64badf5e55fa3cf20454f9203d1eb088df322d49d4318df774828e789898dcb280e8a5521bb59b3203385662ca5e9218a6ca5820e74 + languageName: node + linkType: hard + "https-proxy-agent@npm:^5.0.1": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" @@ -1891,6 +2405,13 @@ __metadata: languageName: node linkType: hard +"hyphenate-style-name@npm:^1.0.0": + version: 1.1.0 + resolution: "hyphenate-style-name@npm:1.1.0" + checksum: 10c0/bfe88deac2414a41a0d08811e277c8c098f23993d6a1eb17f14a0f11b54c4d42865a63d3cfe1914668eefb9a188e2de58f38b55a179a238fd1fef606893e194f + languageName: node + linkType: hard + "iconv-lite@npm:^0.7.2": version: 0.7.2 resolution: "iconv-lite@npm:0.7.2" @@ -1900,6 +2421,16 @@ __metadata: languageName: node linkType: hard +"import-fresh@npm:^3.2.1": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec + languageName: node + linkType: hard + "indent-string@npm:^5.0.0": version: 5.0.0 resolution: "indent-string@npm:5.0.0" @@ -1968,6 +2499,22 @@ __metadata: languageName: node linkType: hard +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 + languageName: node + linkType: hard + +"is-core-module@npm:^2.16.1": + version: 2.16.2 + resolution: "is-core-module@npm:2.16.2" + dependencies: + hasown: "npm:^2.0.3" + checksum: 10c0/14b4258390283709c15476d023ec173e27458d5d014ccdb8ed39d576e551c3fa45498b7c9fe178f1529c4cb2648ddd58852a6a62107a019f6e349529f277518a + languageName: node + linkType: hard + "is-fullwidth-code-point@npm:^5.0.0, is-fullwidth-code-point@npm:^5.1.0": version: 5.1.0 resolution: "is-fullwidth-code-point@npm:5.1.0" @@ -2010,13 +2557,29 @@ __metadata: languageName: node linkType: hard -"js-tokens@npm:^3.0.0 || ^4.0.0": +"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed languageName: node linkType: hard +"jsesc@npm:^3.0.2": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.0": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 + languageName: node + linkType: hard + "jsonc-parser@npm:^3.2.0": version: 3.3.1 resolution: "jsonc-parser@npm:3.3.1" @@ -2144,6 +2707,13 @@ __metadata: languageName: node linkType: hard +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d + languageName: node + linkType: hard + "lodash@npm:^4.17.20, lodash@npm:^4.17.21": version: 4.18.1 resolution: "lodash@npm:4.18.1" @@ -2151,7 +2721,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.1.0": +"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -2171,6 +2741,15 @@ __metadata: languageName: node linkType: hard +"matchmediaquery@npm:^0.3.0": + version: 0.3.1 + resolution: "matchmediaquery@npm:0.3.1" + dependencies: + css-mediaquery: "npm:^0.1.2" + checksum: 10c0/19a254e0a1eb639fcc6aa7428a6e2d36cb5646a165579897e771580653e470b43d7f4a224c522700dbf1d8f19fb7c63a9719e0a6017727ade967155a2ae89fea + languageName: node + linkType: hard + "math-intrinsics@npm:^1.1.0": version: 1.1.0 resolution: "math-intrinsics@npm:1.1.0" @@ -2285,6 +2864,13 @@ __metadata: languageName: node linkType: hard +"object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + "obug@npm:^2.1.1": version: 2.1.2 resolution: "obug@npm:2.1.2" @@ -2337,6 +2923,27 @@ __metadata: languageName: node linkType: hard +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"parse-json@npm:^5.0.0": + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" + dependencies: + "@babel/code-frame": "npm:^7.0.0" + error-ex: "npm:^1.3.1" + json-parse-even-better-errors: "npm:^2.3.0" + lines-and-columns: "npm:^1.1.6" + checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 + languageName: node + linkType: hard + "patch-console@npm:^2.0.0": version: 2.0.0 resolution: "patch-console@npm:2.0.0" @@ -2344,6 +2951,20 @@ __metadata: languageName: node linkType: hard +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c + languageName: node + linkType: hard + "pathe@npm:^2.0.3": version: 2.0.3 resolution: "pathe@npm:2.0.3" @@ -2399,6 +3020,17 @@ __metadata: languageName: node linkType: hard +"prop-types@npm:^15.6.1": + version: 15.8.1 + resolution: "prop-types@npm:15.8.1" + dependencies: + loose-envify: "npm:^1.4.0" + object-assign: "npm:^4.1.1" + react-is: "npm:^16.13.1" + checksum: 10c0/59ece7ca2fb9838031d73a48d4becb9a7cc1ed10e610517c7d8f19a1e02fa47f7c27d557d8a5702bec3cfeccddc853579832b43f449e54635803f277b1c78077 + languageName: node + linkType: hard + "proxy-from-env@npm:^2.1.0": version: 2.1.0 resolution: "proxy-from-env@npm:2.1.0" @@ -2429,6 +3061,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^16.13.1, react-is@npm:^16.7.0": + version: 16.13.1 + resolution: "react-is@npm:16.13.1" + checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1 + languageName: node + linkType: hard + "react-reconciler@npm:^0.33.0": version: 0.33.0 resolution: "react-reconciler@npm:0.33.0" @@ -2440,6 +3079,44 @@ __metadata: languageName: node linkType: hard +"react-responsive@npm:^9.0.2": + version: 9.0.2 + resolution: "react-responsive@npm:9.0.2" + dependencies: + hyphenate-style-name: "npm:^1.0.0" + matchmediaquery: "npm:^0.3.0" + prop-types: "npm:^15.6.1" + shallow-equal: "npm:^1.2.1" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/c58b30d8b8ff993edbc73f7de59a2d86d8abd6380b06a9b41667c3bdd5401e5e710fa1d0fdac21368759ab9b958e8b479a111329a06e9b0241ee08f500b37a20 + languageName: node + linkType: hard + +"react-router-dom@npm:^6.4.4": + version: 6.30.4 + resolution: "react-router-dom@npm:6.30.4" + dependencies: + "@remix-run/router": "npm:1.23.3" + react-router: "npm:6.30.4" + peerDependencies: + react: ">=16.8" + react-dom: ">=16.8" + checksum: 10c0/1b25ab26a288da852f7b58eec5c14b3f37919e6773a557cd846d3a05d7a7d890c8d49bda93c0a7f497f240df1df8b0ad50635f990aafb55f2fc545ad8269a822 + languageName: node + linkType: hard + +"react-router@npm:6.30.4": + version: 6.30.4 + resolution: "react-router@npm:6.30.4" + dependencies: + "@remix-run/router": "npm:1.23.3" + peerDependencies: + react: ">=16.8" + checksum: 10c0/fb6de7d1002bcab9ea12c4072d93792eca494f1e4f30cfec334ccb6523756d23ce3e093c7c1241399296cebed94789a3fb89f96ee76004e0e746458a8f6bab33 + languageName: node + linkType: hard + "react@npm:^18.2.0": version: 18.3.1 resolution: "react@npm:18.3.1" @@ -2463,6 +3140,48 @@ __metadata: languageName: node linkType: hard +"reselect@npm:^5.2.0": + version: 5.2.0 + resolution: "reselect@npm:5.2.0" + checksum: 10c0/d0a8497e404b25a769fe792eacae88b9b576c30870450cd243d76ec9427d91a59c4a2cc00d824d283448b444c4c698a8f961d771c8ae39c759313afb31950e2e + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 + languageName: node + linkType: hard + +"resolve@npm:^1.19.0": + version: 1.22.12 + resolution: "resolve@npm:1.22.12" + dependencies: + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/b16dc9b537c02e8c3388f7d3dcff9741d3071625f9a97ac1c885f2b0ca51e78df22328fb6d6ef214dd9101fb7cfc19aa2836fe3410402a94f3f7b8639c7149bf + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.19.0#optional!builtin": + version: 1.22.12 + resolution: "resolve@patch:resolve@npm%3A1.22.12#optional!builtin::version=1.22.12&hash=c3c19d" + dependencies: + es-errors: "npm:^1.3.0" + is-core-module: "npm:^2.16.1" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/fc6519984ae1f894d877c0060ba8b1f5ba3bc0e85a02f74e141929c118c23d74d9735619a9cc2965397387e514884245c65d72a40731dcb6cfc84c7bcdc8321e + languageName: node + linkType: hard + "restore-cursor@npm:^4.0.0": version: 4.0.0 resolution: "restore-cursor@npm:4.0.0" @@ -2588,6 +3307,13 @@ __metadata: languageName: node linkType: hard +"shallow-equal@npm:^1.2.1": + version: 1.2.1 + resolution: "shallow-equal@npm:1.2.1" + checksum: 10c0/51e03abadd97c9ebe590547d92db9148446962a3f23a3a0fb1ba2fccab80af881eef0ff1f8ccefd3f066c0bc5a4c8ca53706194813b95c8835fa66448a843a26 + languageName: node + linkType: hard + "sharp@npm:^0.34.5": version: 0.34.5 resolution: "sharp@npm:0.34.5" @@ -2710,6 +3436,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.5.7": + version: 0.5.7 + resolution: "source-map@npm:0.5.7" + checksum: 10c0/904e767bb9c494929be013017380cbba013637da1b28e5943b566031e29df04fba57edf3f093e0914be094648b577372bd8ad247fa98cfba9c600794cd16b599 + languageName: node + linkType: hard + "stack-utils@npm:^2.0.6": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" @@ -2726,6 +3459,13 @@ __metadata: languageName: node linkType: hard +"state-local@npm:^1.0.6": + version: 1.0.7 + resolution: "state-local@npm:1.0.7" + checksum: 10c0/8dc7daeac71844452fafb514a6d6b6f40d7e2b33df398309ea1c7b3948d6110c57f112b7196500a10c54fdde40291488c52c875575670fb5c819602deca48bd9 + languageName: node + linkType: hard + "std-env@npm:^4.0.0-rc.1": version: 4.1.0 resolution: "std-env@npm:4.1.0" @@ -2763,6 +3503,13 @@ __metadata: languageName: node linkType: hard +"stylis@npm:4.2.0": + version: 4.2.0 + resolution: "stylis@npm:4.2.0" + checksum: 10c0/a7128ad5a8ed72652c6eba46bed4f416521bc9745a460ef5741edc725252cebf36ee45e33a8615a7057403c93df0866ab9ee955960792db210bb80abd5ac6543 + languageName: node + linkType: hard + "subscriptions-transport-ws@npm:^0.9.16": version: 0.9.19 resolution: "subscriptions-transport-ws@npm:0.9.19" @@ -2778,6 +3525,13 @@ __metadata: languageName: node linkType: hard +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 + languageName: node + linkType: hard + "symbol-observable@npm:^1.0.4": version: 1.2.0 resolution: "symbol-observable@npm:1.2.0" @@ -2931,6 +3685,35 @@ __metadata: languageName: node linkType: hard +"twenty-ui@npm:^1.0.0-alpha.1": + version: 1.0.0-alpha.1 + resolution: "twenty-ui@npm:1.0.0-alpha.1" + dependencies: + "@base-ui/react": "npm:^1.5.0" + "@monaco-editor/react": "npm:^4.7.0" + "@radix-ui/colors": "npm:^3.0.0" + "@sniptt/guards": "npm:^0.2.0" + "@tabler/icons-react": "npm:^3.31.0" + clsx: "npm:^2.1.1" + react-responsive: "npm:^9.0.2" + react-router-dom: "npm:^6.4.4" + type-fest: "npm:^4.10.1" + zod: "npm:^4.1.11" + peerDependencies: + monaco-editor: ">= 0.25.0 < 1" + react: ^19.0.0 + react-dom: ^19.0.0 + checksum: 10c0/213582e582949d6ffe90a7a8ab89a1d4e9dc724d34ed1b80bd7d50e834a8cb8539723556d41b39dc658f76f176bd8490ae0da8a3d6a713e79e6573b732084179 + languageName: node + linkType: hard + +"type-fest@npm:^4.10.1": + version: 4.41.0 + resolution: "type-fest@npm:4.41.0" + checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4 + languageName: node + linkType: hard + "type-fest@npm:^5.4.1": version: 5.6.0 resolution: "type-fest@npm:5.6.0" @@ -2988,6 +3771,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.6.0": + version: 1.6.0 + resolution: "use-sync-external-store@npm:1.6.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/35e1179f872a53227bdf8a827f7911da4c37c0f4091c29b76b1e32473d1670ebe7bcd880b808b7549ba9a5605c233350f800ffab963ee4a4ee346ee983b6019b + languageName: node + linkType: hard + "utility-types@npm:^3.10.0": version: 3.11.0 resolution: "utility-types@npm:3.11.0" @@ -3251,6 +4043,13 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^1.10.0": + version: 1.10.3 + resolution: "yaml@npm:1.10.3" + checksum: 10c0/c309ff85a0a569a981d71ab9cf0fef68672a16b9cdf40639d1c3b30034f6cd16ee428602bd6d64ecf006f8c8bee499023cac236538f79898aa99fb5db529a2ed + languageName: node + linkType: hard + "yoga-layout@npm:~3.2.1": version: 3.2.1 resolution: "yoga-layout@npm:3.2.1" @@ -3274,3 +4073,10 @@ __metadata: checksum: 10c0/71cc2f2bbb537300c3f569e25693d37b3bc91f225cefce251a71c30bc6bb3e7f8e9420ca0eb57f2ac9e492b085b8dfa075fd1e8195c40b83c951dd59c6e4fbf8 languageName: node linkType: hard + +"zod@npm:^4.1.11": + version: 4.4.3 + resolution: "zod@npm:4.4.3" + checksum: 10c0/7ea31b558e88f9faf44f31dd185e2e1cbf51fed3081787fb96cc2534749b50c0acfc6da7f0922a7353ed092dd358c7d50c28ea96c94d04af64191bd33152eca3 + languageName: node + linkType: hard