diff --git a/apps/desktop/src/components/editor-area/index.tsx b/apps/desktop/src/components/editor-area/index.tsx index 811442373..ab1e574af 100644 --- a/apps/desktop/src/components/editor-area/index.tsx +++ b/apps/desktop/src/components/editor-area/index.tsx @@ -258,6 +258,28 @@ export function useEnhanceMutation({ const [actualIsLocalLlm, setActualIsLocalLlm] = useState(isLocalLlm); const queryClient = useQueryClient(); + // Extract H1 headers at component level (always available) + const extractH1Headers = useCallback((htmlContent: string): string[] => { + if (!htmlContent) { + return []; + } + + const h1Regex = /]*>(.*?)<\/h1>/gi; + const headers: string[] = []; + let match; + + while ((match = h1Regex.exec(htmlContent)) !== null) { + const headerText = match[1].replace(/<[^>]*>/g, "").trim(); + if (headerText) { + headers.push(headerText); + } + } + + return headers; + }, []); + + const h1Headers = useMemo(() => extractH1Headers(rawContent), [rawContent, extractH1Headers]); + const preMeetingText = extractTextFromHtml(preMeetingNote); const rawText = extractTextFromHtml(rawContent); @@ -315,11 +337,21 @@ export function useEnhanceMutation({ const selectedTemplate = await TemplateService.getTemplate(effectiveTemplateId ?? ""); + const shouldUseH1Headers = !effectiveTemplateId && h1Headers.length > 0; + const grammarSections = selectedTemplate?.sections.map(s => s.title) || null; + const participants = await dbCommands.sessionListParticipants(sessionId); const systemMessage = await templateCommands.render( "enhance.system", - { config, type, templateInfo: selectedTemplate }, + { + config, + type, + // Pass userHeaders when using H1 headers, templateInfo otherwise + ...(shouldUseH1Headers + ? { userHeaders: h1Headers } + : { templateInfo: selectedTemplate }), + }, ); const userMessage = await templateCommands.render( @@ -372,7 +404,7 @@ export function useEnhanceMutation({ metadata: { grammar: { task: "enhance", - sections: selectedTemplate?.sections.map(s => s.title) || null, + sections: grammarSections, } satisfies Grammar, }, }, diff --git a/apps/desktop/src/components/settings/views/ai.tsx b/apps/desktop/src/components/settings/views/ai.tsx index 3585d07df..62b74fc73 100644 --- a/apps/desktop/src/components/settings/views/ai.tsx +++ b/apps/desktop/src/components/settings/views/ai.tsx @@ -8,7 +8,9 @@ import { useForm } from "react-hook-form"; import { z } from "zod"; import { commands as connectorCommands, type Connection } from "@hypr/plugin-connector"; +import { commands as dbCommands } from "@hypr/plugin-db"; import { commands as localLlmCommands, SupportedModel } from "@hypr/plugin-local-llm"; + import { commands as localSttCommands } from "@hypr/plugin-local-stt"; import { Button } from "@hypr/ui/components/ui/button"; import { @@ -121,6 +123,34 @@ const initialLlmModels = [ }, ]; +const aiConfigSchema = z.object({ + aiSpecificity: z.number().int().min(1).max(4).optional(), +}); +type AIConfigValues = z.infer; + +const specificityLevels = { + 1: { + title: "Conservative", + description: + "Minimal creative changes. Preserves your original writing style and content while making only essential improvements to clarity and flow.", + }, + 2: { + title: "Balanced", + description: + "Moderate creative input. Enhances your content with some stylistic improvements while maintaining the core message and tone.", + }, + 3: { + title: "Creative", + description: + "More creative freedom. Actively improves and expands content with additional context, examples, and engaging language.", + }, + 4: { + title: "Innovative", + description: + "Maximum creativity. Transforms content with rich language, fresh perspectives, and creative restructuring while preserving key information.", + }, +} as const; + export default function LocalAI() { const queryClient = useQueryClient(); const [isWerModalOpen, setIsWerModalOpen] = useState(false); @@ -242,6 +272,49 @@ export default function LocalAI() { }, }); + const config = useQuery({ + queryKey: ["config", "ai"], + queryFn: async () => { + const result = await dbCommands.getConfig(); + return result; + }, + }); + + const aiConfigForm = useForm({ + resolver: zodResolver(aiConfigSchema), + defaultValues: { + aiSpecificity: 3, + }, + }); + + useEffect(() => { + if (config.data) { + aiConfigForm.reset({ + aiSpecificity: config.data.ai.ai_specificity ?? 3, + }); + } + }, [config.data, aiConfigForm]); + + const aiConfigMutation = useMutation({ + mutationFn: async (values: AIConfigValues) => { + if (!config.data) { + return; + } + + await dbCommands.setConfig({ + ...config.data, + ai: { + ...config.data.ai, + ai_specificity: values.aiSpecificity ?? 3, + }, + }); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["config", "ai"] }); + }, + onError: console.error, + }); + const form = useForm({ resolver: zodResolver(endpointSchema), mode: "onChange", @@ -682,6 +755,62 @@ export default function LocalAI() { )} /> + + {/* NEW: Detail Level Configuration */} +
+ ( + + + Creativity Level + + + Control how creative the AI enhancement should be + + +
+ {/* Button bar - matching form element width */} +
+
+ {[1, 2, 3, 4].map((level) => ( + + ))} +
+
+ + {/* Current selection description in card */} +
+
+ {specificityLevels[field.value as keyof typeof specificityLevels]?.description + || specificityLevels[3].description} +
+
+
+
+ +
+ )} + /> + diff --git a/apps/desktop/src/locales/en/messages.po b/apps/desktop/src/locales/en/messages.po index b695b1d13..7db2db91b 100644 --- a/apps/desktop/src/locales/en/messages.po +++ b/apps/desktop/src/locales/en/messages.po @@ -346,12 +346,12 @@ msgstr "Annual" msgid "Anyone with the link can view this page" msgstr "Anyone with the link can view this page" -#: src/components/settings/views/ai.tsx:585 +#: src/components/settings/views/ai.tsx:658 msgid "API Base URL" msgstr "API Base URL" #: src/components/settings/views/integrations.tsx:197 -#: src/components/settings/views/ai.tsx:611 +#: src/components/settings/views/ai.tsx:684 msgid "API Key" msgstr "API Key" @@ -460,7 +460,7 @@ msgstr "Company name" #~ msgid "Connect" #~ msgstr "Connect" -#: src/components/settings/views/ai.tsx:565 +#: src/components/settings/views/ai.tsx:638 msgid "Connect to a self-hosted or third-party LLM endpoint (OpenAI API compatible)." msgstr "Connect to a self-hosted or third-party LLM endpoint (OpenAI API compatible)." @@ -492,6 +492,10 @@ msgstr "Contacts Access" msgid "Continue" msgstr "Continue" +#: src/components/settings/views/ai.tsx:770 +msgid "Control how creative the AI enhancement should be" +msgstr "Control how creative the AI enhancement should be" + #: src/routes/app.human.$id.tsx:535 #: src/components/editor-area/note-header/chips/participants-chip.tsx:399 msgid "Create" @@ -513,11 +517,15 @@ msgstr "Create Note" msgid "Create your first template to get started" msgstr "Create your first template to get started" +#: src/components/settings/views/ai.tsx:767 +msgid "Creativity Level" +msgstr "Creativity Level" + #: src/components/settings/views/billing.tsx:66 msgid "Current Plan" msgstr "Current Plan" -#: src/components/settings/views/ai.tsx:562 +#: src/components/settings/views/ai.tsx:635 msgid "Custom Endpoint" msgstr "Custom Endpoint" @@ -580,7 +588,7 @@ msgstr "Enable" msgid "Enable Integration" msgstr "Enable Integration" -#: src/components/settings/views/ai.tsx:436 +#: src/components/settings/views/ai.tsx:509 msgid "Enhancing" msgstr "Enhancing" @@ -592,11 +600,11 @@ msgstr "Enter a section title" #~ msgid "Enter model name (e.g., gpt-4, llama3.2:3b)" #~ msgstr "Enter model name (e.g., gpt-4, llama3.2:3b)" -#: src/components/settings/views/ai.tsx:614 +#: src/components/settings/views/ai.tsx:687 msgid "Enter the API key for your custom LLM endpoint" msgstr "Enter the API key for your custom LLM endpoint" -#: src/components/settings/views/ai.tsx:588 +#: src/components/settings/views/ai.tsx:661 msgid "Enter the base URL for your custom LLM endpoint" msgstr "Enter the base URL for your custom LLM endpoint" @@ -759,7 +767,7 @@ msgstr "LinkedIn username" msgid "Live summary of the meeting" msgstr "Live summary of the meeting" -#: src/components/settings/views/ai.tsx:648 +#: src/components/settings/views/ai.tsx:721 msgid "Loading available models..." msgstr "Loading available models..." @@ -805,7 +813,7 @@ msgstr "Members" msgid "Microphone Access" msgstr "Microphone Access" -#: src/components/settings/views/ai.tsx:636 +#: src/components/settings/views/ai.tsx:709 msgid "Model Name" msgstr "Model Name" @@ -947,7 +955,7 @@ msgstr "Pause" msgid "people" msgstr "people" -#: src/components/settings/views/ai.tsx:298 +#: src/components/settings/views/ai.tsx:371 msgid "Performance difference between languages" msgstr "Performance difference between languages" @@ -1037,7 +1045,7 @@ msgstr "Search..." msgid "Sections" msgstr "Sections" -#: src/components/settings/views/ai.tsx:639 +#: src/components/settings/views/ai.tsx:712 msgid "Select a model from the dropdown (if available) or manually enter the model name required by your endpoint." msgstr "Select a model from the dropdown (if available) or manually enter the model name required by your endpoint." @@ -1179,7 +1187,7 @@ msgstr "Toggle left sidebar" msgid "Toggle widget panel" msgstr "Toggle widget panel" -#: src/components/settings/views/ai.tsx:289 +#: src/components/settings/views/ai.tsx:362 msgid "Transcribing" msgstr "Transcribing" diff --git a/apps/desktop/src/locales/ko/messages.po b/apps/desktop/src/locales/ko/messages.po index 11fc8b245..4ebfaec1b 100644 --- a/apps/desktop/src/locales/ko/messages.po +++ b/apps/desktop/src/locales/ko/messages.po @@ -346,12 +346,12 @@ msgstr "" msgid "Anyone with the link can view this page" msgstr "" -#: src/components/settings/views/ai.tsx:585 +#: src/components/settings/views/ai.tsx:658 msgid "API Base URL" msgstr "" #: src/components/settings/views/integrations.tsx:197 -#: src/components/settings/views/ai.tsx:611 +#: src/components/settings/views/ai.tsx:684 msgid "API Key" msgstr "" @@ -460,7 +460,7 @@ msgstr "" #~ msgid "Connect" #~ msgstr "" -#: src/components/settings/views/ai.tsx:565 +#: src/components/settings/views/ai.tsx:638 msgid "Connect to a self-hosted or third-party LLM endpoint (OpenAI API compatible)." msgstr "" @@ -492,6 +492,10 @@ msgstr "" msgid "Continue" msgstr "" +#: src/components/settings/views/ai.tsx:770 +msgid "Control how creative the AI enhancement should be" +msgstr "" + #: src/routes/app.human.$id.tsx:535 #: src/components/editor-area/note-header/chips/participants-chip.tsx:399 msgid "Create" @@ -513,11 +517,15 @@ msgstr "" msgid "Create your first template to get started" msgstr "" +#: src/components/settings/views/ai.tsx:767 +msgid "Creativity Level" +msgstr "" + #: src/components/settings/views/billing.tsx:66 msgid "Current Plan" msgstr "" -#: src/components/settings/views/ai.tsx:562 +#: src/components/settings/views/ai.tsx:635 msgid "Custom Endpoint" msgstr "" @@ -580,7 +588,7 @@ msgstr "" msgid "Enable Integration" msgstr "Enable Integration" -#: src/components/settings/views/ai.tsx:436 +#: src/components/settings/views/ai.tsx:509 msgid "Enhancing" msgstr "Enhancing" @@ -592,11 +600,11 @@ msgstr "" #~ msgid "Enter model name (e.g., gpt-4, llama3.2:3b)" #~ msgstr "" -#: src/components/settings/views/ai.tsx:614 +#: src/components/settings/views/ai.tsx:687 msgid "Enter the API key for your custom LLM endpoint" msgstr "" -#: src/components/settings/views/ai.tsx:588 +#: src/components/settings/views/ai.tsx:661 msgid "Enter the base URL for your custom LLM endpoint" msgstr "" @@ -759,7 +767,7 @@ msgstr "" msgid "Live summary of the meeting" msgstr "" -#: src/components/settings/views/ai.tsx:648 +#: src/components/settings/views/ai.tsx:721 msgid "Loading available models..." msgstr "" @@ -805,7 +813,7 @@ msgstr "" msgid "Microphone Access" msgstr "" -#: src/components/settings/views/ai.tsx:636 +#: src/components/settings/views/ai.tsx:709 msgid "Model Name" msgstr "" @@ -947,7 +955,7 @@ msgstr "" msgid "people" msgstr "" -#: src/components/settings/views/ai.tsx:298 +#: src/components/settings/views/ai.tsx:371 msgid "Performance difference between languages" msgstr "" @@ -1037,7 +1045,7 @@ msgstr "" msgid "Sections" msgstr "" -#: src/components/settings/views/ai.tsx:639 +#: src/components/settings/views/ai.tsx:712 msgid "Select a model from the dropdown (if available) or manually enter the model name required by your endpoint." msgstr "" @@ -1179,7 +1187,7 @@ msgstr "" msgid "Toggle widget panel" msgstr "" -#: src/components/settings/views/ai.tsx:289 +#: src/components/settings/views/ai.tsx:362 msgid "Transcribing" msgstr "" diff --git a/crates/db-user/src/config_types.rs b/crates/db-user/src/config_types.rs index 9940d13f4..b8b85977e 100644 --- a/crates/db-user/src/config_types.rs +++ b/crates/db-user/src/config_types.rs @@ -81,10 +81,20 @@ impl Default for ConfigNotification { } user_common_derives! { - #[derive(Default)] pub struct ConfigAI { pub api_base: Option, pub api_key: Option, + pub ai_specificity: Option, + } +} + +impl Default for ConfigAI { + fn default() -> Self { + Self { + api_base: None, + api_key: None, + ai_specificity: Some(3), + } } } diff --git a/crates/template/assets/enhance.system.jinja b/crates/template/assets/enhance.system.jinja index 0b7bf1a54..9721cd1cf 100644 --- a/crates/template/assets/enhance.system.jinja +++ b/crates/template/assets/enhance.system.jinja @@ -1,6 +1,33 @@ You are a professional assistant that generates enhanced meetings notes while maintaining accuracy, completeness, and professional terminology in {{ config.general.display_language | language }}. -{% if templateInfo %} +{%- set specificity = config.ai.ai_specificity | default(3) %} + +{% if userHeaders %} +The user has structured their content with these headers: +{% for header in userHeaders %}"{{ header }}"{% if not loop.last %}, {% endif %}{% endfor %} + +{%- if specificity == 1 %} + +**Adherence Level: Strict** +You must strictly follow these exact headers. Use them as-is without modifications. Focus on concise, high-level content under each header. + +{% elif specificity == 2 %} + +**Adherence Level: Mostly Strict** +You must mainly follow these headers, but are allowed to make minor changes or enhancements if necessary for clarity or professionalism. + +{% elif specificity == 3 %} + +**Adherence Level: Flexible** +You should follow the general structure of these headers, but feel free to rename them for better clarity, professionalism, or accuracy. + +{% elif specificity == 4 %} + +**Adherence Level: Creative** +Use these headers as inspiration for your enhanced notes. Generate new headers based on these, but make changes if you think you can improve them. Add additional sections if the content warrants it. +{% endif %} + +{% elif templateInfo %} The user has provided a custom template that defines the structure and sections for the enhanced meeting notes. Your response must strictly follow this template's format and section headers. @@ -54,6 +81,31 @@ You will be given multiple inputs from the user. Below are useful information th - Preserve essential details; avoid excessive abstraction. Ensure content remains concrete and specific. - Pay close attention to emphasized text in raw notes. Users highlight information using four styles: bold(**text**), italic(_text_), underline(text), strikethrough(~~text~~). - Recognize H3 headers (### Header) in raw notes—these indicate highly important topics that the user wants to retain no matter what. +- Below is the guideline on how many changes you should make to the original raw note, please pay close atteion: + + {%- if specificity == 1 %} + + **Creativity Level: low** + User already knows and has a specific taste/stance about the raw note. Make only minimal changes to the raw notes if necessary. Focus on fixing typos, improving readability, and organizing content while preserving the original structure and meaning. + Only add new contents to the raw note if user didn't write anything to section headers. + + {% elif specificity == 2 %} + + **Creativity Level: moderate** + User has overall idea about how the note should look like. Enhance readability and organization while maintaining core content. Add relevant details from the transcript to provide more context and clarity, but preserve the main structure and key points from raw notes. + + {% elif specificity == 3 %} + + **Creativity Level: high** + Significantly enhance the raw notes by incorporating relevant information from the transcript. Reorganize content into logical sections, expand on key points, and add important context while ensuring the original intent is preserved. Focus on creating a comprehensive and well-structured document. + + {% elif specificity == 4 %} + + **Creativity Level: very high** + Create a thorough and polished document by fully integrating raw notes with transcript content. Reorganize extensively, add detailed context and explanations, and create clear thematic sections. Focus on producing a professional-quality document while preserving key insights from the raw notes. + + {% endif %} + {% if config.general.display_language is not english %} - Keep technical terms (e.g., API, SDK, frontend, backend) and globally recognized product names (e.g., React, Vue.js, Django) in English. - When using technical terms in sentences, follow the grammatical rules of {{ config.general.display_language | language }}. @@ -62,6 +114,8 @@ You will be given multiple inputs from the user. Below are useful information th - 문장 끝을 **"-했습니다" 대신 "-했음"**처럼 간결하게 줄임. {% endif %} +{% if specificity == 3 or specificity == 4 %} + # Correct Examples of a Section ## Example 1 @@ -112,3 +166,5 @@ You will be given multiple inputs from the user. Below are useful information th ``` {% endif %} + +{% endif %} diff --git a/crates/template/assets/enhance.user.jinja b/crates/template/assets/enhance.user.jinja index 67b80c3b7..3d7f4b239 100644 --- a/crates/template/assets/enhance.user.jinja +++ b/crates/template/assets/enhance.user.jinja @@ -15,6 +15,9 @@ Your job is to write a perfect note based on the above informations. Note that above given informations like participants, transcript, etc. are already displayed in the UI, so you don't need to repeat them. +MAKE SURE THAT contents in the 'raw_note' is well incorporated in the final enhanced note. It is paramount that the enhanced note contains contents +of the raw note. + {% if type == "HyprLocal" %} Also, before writing enhanced note, write multiple top-level headers inside tags, and then write the note based on the headers. diff --git a/packages/ui/package.json b/packages/ui/package.json index ec18ec333..f994f02d9 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -30,6 +30,7 @@ "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.5", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", "@radix-ui/react-tabs": "^1.1.12", diff --git a/packages/ui/src/components/ui/slider.tsx b/packages/ui/src/components/ui/slider.tsx new file mode 100644 index 000000000..28da92cff --- /dev/null +++ b/packages/ui/src/components/ui/slider.tsx @@ -0,0 +1,81 @@ +import * as SliderPrimitive from "@radix-ui/react-slider"; +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "../../lib/utils"; + +const sliderVariants = cva( + "relative flex w-full touch-none select-none items-center", + { + variants: { + size: { + sm: "h-4", + default: "h-5", + lg: "h-6", + }, + }, + defaultVariants: { + size: "default", + }, + }, +); + +const trackVariants = cva( + "relative h-2 w-full grow overflow-hidden rounded-full bg-input", + { + variants: { + size: { + sm: "h-1.5", + default: "h-2", + lg: "h-2.5", + }, + }, + defaultVariants: { + size: "default", + }, + }, +); + +const rangeVariants = cva( + "absolute h-full bg-primary", + {}, +); + +const thumbVariants = cva( + "block rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + size: { + sm: "h-4 w-4", + default: "h-5 w-5", + lg: "h-6 w-6", + }, + }, + defaultVariants: { + size: "default", + }, + }, +); + +export interface SliderProps + extends React.ComponentPropsWithoutRef, VariantProps +{} + +const Slider = React.forwardRef< + React.ElementRef, + SliderProps +>(({ className, size, ...props }, ref) => ( + + + + + + +)); +Slider.displayName = SliderPrimitive.Root.displayName; + +export { Slider }; diff --git a/plugins/db/js/bindings.gen.ts b/plugins/db/js/bindings.gen.ts index 4c37881e8..a6855cb40 100644 --- a/plugins/db/js/bindings.gen.ts +++ b/plugins/db/js/bindings.gen.ts @@ -150,7 +150,7 @@ export type ChatGroup = { id: string; user_id: string; name: string | null; crea export type ChatMessage = { id: string; group_id: string; created_at: string; role: ChatMessageRole; content: string } export type ChatMessageRole = "User" | "Assistant" export type Config = { id: string; user_id: string; general: ConfigGeneral; notification: ConfigNotification; ai: ConfigAI } -export type ConfigAI = { api_base: string | null; api_key: string | null } +export type ConfigAI = { api_base: string | null; api_key: string | null; ai_specificity: number | null } export type ConfigGeneral = { autostart: boolean; display_language: string; jargons: string[]; telemetry_consent: boolean; save_recordings: boolean | null; selected_template_id: string | null } export type ConfigNotification = { before: boolean; auto: boolean; ignoredPlatforms: string[] | null } export type Event = { id: string; user_id: string; tracking_id: string; calendar_id: string | null; name: string; note: string; start_date: string; end_date: string; google_event_url: string | null } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e97b2e7a9..ec0a2a660 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -776,6 +776,9 @@ importers: '@radix-ui/react-separator': specifier: ^1.1.7 version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slider': + specifier: ^1.3.5 + version: 1.3.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@18.3.23)(react@18.3.1) @@ -2999,6 +3002,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-slider@1.3.5': + resolution: {integrity: sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -12028,6 +12044,25 @@ snapshots: '@types/react': 18.3.23 '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@radix-ui/react-slider@1.3.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@radix-ui/react-slot@1.2.3(@types/react@18.3.23)(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1)