@iblai/iblai-js 2.2.8 → 2.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- import React__default, { useState, useEffect, forwardRef, createElement as createElement$3, useLayoutEffect, createContext, useContext, useMemo, useCallback, useReducer, useRef, useImperativeHandle, isValidElement, Children, PureComponent, cloneElement, Component as Component$1, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
3
+ import React__default, { useState, useEffect, forwardRef, createElement as createElement$3, useLayoutEffect, createContext, useContext, useMemo, useReducer, useRef, useImperativeHandle, useCallback, isValidElement, Children, PureComponent, cloneElement, Component as Component$1, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
4
4
  import * as dataLayer from '@iblai/data-layer';
5
5
  import { useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useDeleteGlobalMemoryMutation, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useDeleteApiKeyMutation, useGetApiKeysQuery, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useGetLlmsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, useGetCustomDomainsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useUpdateTenantMetadataMutation, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useGetAiSearchMentorsQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useGetVectorDocumentsQuery, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useDeleteMentorMemoryMutation, useUpdateMentorMemoryMutation, useCreateMentorMemoryMutation, useLazyGetConnectedServiceAuthUrlQuery, useStarMentorMutation, useUnstarMentorMutation, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorSettingsQuery, useEditMentorMutation, useUploadLightLogoMutation, useUploadDarkLogoMutation, useUpdatePlatformInfoMutation, LOGO_ENDPOINTS, useCreateEvalDatasetMutation, useAddEvalDatasetItemsMutation, useUploadEvalDatasetCsvMutation, useDeleteEvalDatasetItemMutation, useListEvalDatasetItemsQuery, evalTotalPages, useListEvalDatasetsQuery, useGetUserProjectsQuery, useCreateSessionIdMutation, useGetMentorCategoriesQuery, useGetTenantChatPrivacyConfigQuery as useGetTenantChatPrivacyConfigQuery$1, useGetCallConfigurationsQuery, useCreateCallConfigurationMutation, useUpdateCallConfigurationMutation, chatPrivacyApiSlice, useDeleteMentorMutation, useForkMentorMutation, useGetToolsQuery, useLazyGetMCPServersQuery, useOauthFindMutation, useLazyStartOAuthFlowQuery, useCreateMCPServerMutation, usePartialUpdateMCPServerMutation, useCreateMCPServerConnectionMutation, usePatchMCPServerConnectionMutation, useGetConnectedServicesQuery, useGetMCPServersQuery, useGetMCPServerConnectionsQuery, useUpdateMCPServerMutation, useDeleteMCPServerMutation, useEditMentorJsonMutation, useDisconnectServiceMutation, useGetPromptCategoriesQuery, PRIVACY_ACTIONS, PRIVACY_ENTITY_TYPES, useGetVoicesQuery, useGetVoiceQuery, useGetMentorGraderConfigurationQuery, useListGraderCriteriaQuery, useCreateMentorGraderConfigurationMutation, useUpdateMentorGraderConfigurationMutation, useCreateGraderCriterionMutation, useUpdateGraderCriterionMutation, useDeleteGraderCriterionMutation, useGetMentorPublicSettingsQuery, useGetChatHistoryFilterQuery, useGetChatHistoryQuery, useGetMentorSummariesQuery, useGetConversationMemoriesQuery, useGetSupportTicketsQuery, useGetSupportTicketMessagesQuery, useCreateSupportTicketMessageMutation, usePatchSupportTicketMutation, useCloseSupportTicketMutation, useCreateLtiMentorMutation, useUpdateLtiMentorMutation, useGetLtiMentorsQuery, useCreateLtiKeyMutation, useUpdateLtiKeyMutation, useDeleteLtiKeyMutation, useGetLtiKeysQuery, useCreateLtiToolMutation, useUpdateLtiToolMutation, useGetLtiToolsQuery, useGetPlatformInfoQuery, useCreatePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useCreateRedirectTokenMutation, useGetShareableLinkQuery, useCreateShareableLinkMutation, useUpdateShareableLinkMutation, useCreateDisclaimerMutation, useUpdateDisclaimerMutation, useGetDisclaimersQuery, useUpdateRbacMentorAccessMutation, useGetRbacMentorAccessListQuery, useStartEvalRunMutation, useEvaluateEvalRunMutation, useGetEvalRunQuery, useDeleteEvalScoreMutation, useListEvalRunJudgesQuery, useCreateEvalScoreMutation, useDeleteEvalRunMutation, useExportEvalRunMutation, useListEvalRunsQuery, useLazyGetEvalRunQuery, evaluationsApiSlice, EVALUATIONS_TAGS, useGetPersonnalizedMentorsQuery, useCreateCallCredentialsMutation, useGetGuidedPromptsQuery, useUpdateChatSessionSharedMutation, useUpdateMessageFeedbackMutation, useLazyGetPromptsSearchQuery, useLazyGetGuidedPromptsQuery, useGetPublicMentorsQuery, useGetPeriodicAgentsQuery, useGetPeriodicAgentLogsListQuery, useCreatePeriodicAgentMutation, useDeletePeriodicAgentMutation, useDeletePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useUpdateArtifactMutation, useLazyGetArtifactVersionQuery, useLazyListArtifactVersionsQuery, useSetCurrentVersionMutation, useListArtifactVersionsQuery, useLazyGetArtifactQuery, useLazyListArtifactsQuery, useEditSessionMutation } from '@iblai/data-layer';
6
- import { getInitials, useTenantMetadata, WithPermissions, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, selectRbacPermissions, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
6
+ import { getInitials, useTenantMetadata, WithPermissions, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, selectNumberOfActiveChatMessages, useUsername, selectStreaming, isLoggedIn, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, selectRbacPermissions, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, removeFile, chatInputSliceActions, chatActions, WithFormPermissions, useTenantContext, TimeTracker, advancedTabsProperties, defaultSessionIds, pickWritableFields, ANONYMOUS_USERNAME as ANONYMOUS_USERNAME$3, redirectToAuthSpaJoinTenant, selectActiveTab, useAxdToken, useWelcomeMessage, markdownToPlainText, useCachedSessionId, use402ErrorCheck, selectTokenEnabled, selectToken, useServiceWorker, useUserAgreement, useAdvancedChat, sendMessageToParentWebsite, useMentorTools, useFileDragDrop, eventBus, RemoteEvents, selectEnableChatActionsPopup, advancedTabs, isInIframe, getAuthSpaJoinUrl, addMessage, clearFiles, MENTOR_VISIBILITY } from '@iblai/web-utils';
7
7
  import { useDispatch, useSelector } from 'react-redux';
8
8
  import { toast } from 'sonner';
9
9
  import { useSearchParams, useParams, useRouter, usePathname } from 'next/navigation';
@@ -98334,6 +98334,9 @@ var chatInputFormInsideButtons$3 = {
98334
98334
  studyMode: "Study Mode",
98335
98335
  deepResearch: "Deep Research",
98336
98336
  memory: "Memory",
98337
+ cowork: "Cowork",
98338
+ coworkNeedsBackend: "Enable a large local model or sign in to a workspace to use Cowork.",
98339
+ coworkModelTooSmall: "Your local model is too small for Cowork. Pick a model of at least 14GB in Local Models.",
98337
98340
  moreOptions: "More options"
98338
98341
  };
98339
98342
  var chatInputFormMemoryMenu$3 = {
@@ -104452,6 +104455,9 @@ var chatInputFormInsideButtons$2 = {
104452
104455
  studyMode: "Mode Étude",
104453
104456
  deepResearch: "Recherche Approfondie",
104454
104457
  memory: "Mémoire",
104458
+ cowork: "Cowork",
104459
+ coworkNeedsBackend: "Activez un grand modèle local ou connectez-vous à un espace de travail pour utiliser Cowork.",
104460
+ coworkModelTooSmall: "Votre modèle local est trop petit pour Cowork. Choisissez un modèle d’au moins 14 Go dans Modèles Locaux.",
104455
104461
  moreOptions: "Plus d'options"
104456
104462
  };
104457
104463
  var chatInputFormMemoryMenu$2 = {
@@ -110570,6 +110576,9 @@ var chatInputFormInsideButtons$1 = {
110570
110576
  studyMode: "Modo Estudio",
110571
110577
  deepResearch: "Investigación Profunda",
110572
110578
  memory: "Memoria",
110579
+ cowork: "Cowork",
110580
+ coworkNeedsBackend: "Activa un modelo local grande o inicia sesión en un espacio de trabajo para usar Cowork.",
110581
+ coworkModelTooSmall: "Tu modelo local es demasiado pequeño para Cowork. Elige un modelo de al menos 14 GB en Modelos Locales.",
110573
110582
  moreOptions: "Más opciones"
110574
110583
  };
110575
110584
  var chatInputFormMemoryMenu$1 = {
@@ -116688,6 +116697,9 @@ var chatInputFormInsideButtons = {
116688
116697
  studyMode: "学习模式",
116689
116698
  deepResearch: "深度研究",
116690
116699
  memory: "记忆",
116700
+ cowork: "Cowork",
116701
+ coworkNeedsBackend: "启用大型本地模型或登录工作区即可使用 Cowork。",
116702
+ coworkModelTooSmall: "您的本地模型太小,无法用于 Cowork。请在“本地模型”中选择至少 14GB 的模型。",
116691
116703
  moreOptions: "更多选项"
116692
116704
  };
116693
116705
  var chatInputFormMemoryMenu = {
@@ -127458,344 +127470,8 @@ var Indicator$2 = ProgressIndicator;
127458
127470
  const Progress = React.forwardRef(({ className, value, ...props }, ref) => (jsx(Root$9, { ref: ref, className: cn('bg-secondary relative h-4 w-full overflow-hidden rounded-full', className), ...props, children: jsx(Indicator$2, { className: "bg-primary h-full w-full flex-1 transition-all", style: { transform: `translateX(-${100 - (value || 0)}%)` } }) })));
127459
127471
  Progress.displayName = Root$9.displayName;
127460
127472
 
127461
- var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
127462
- // @__NO_SIDE_EFFECTS__
127463
- function createSlottable(ownerName) {
127464
- const Slottable2 = ({ children }) => {
127465
- return /* @__PURE__ */ jsx(Fragment$1, { children });
127466
- };
127467
- Slottable2.displayName = `${ownerName}.Slottable`;
127468
- Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
127469
- return Slottable2;
127470
- }
127471
-
127472
- var ROOT_NAME = "AlertDialog";
127473
- var [createAlertDialogContext] = createContextScope$1(ROOT_NAME, [
127474
- createDialogScope
127475
- ]);
127476
- var useDialogScope = createDialogScope();
127477
- var AlertDialog$1 = (props) => {
127478
- const { __scopeAlertDialog, ...alertDialogProps } = props;
127479
- const dialogScope = useDialogScope(__scopeAlertDialog);
127480
- return /* @__PURE__ */ jsx(DialogPrimitive.Root, { ...dialogScope, ...alertDialogProps, modal: true });
127481
- };
127482
- AlertDialog$1.displayName = ROOT_NAME;
127483
- var TRIGGER_NAME$3 = "AlertDialogTrigger";
127484
- var AlertDialogTrigger = React.forwardRef(
127485
- (props, forwardedRef) => {
127486
- const { __scopeAlertDialog, ...triggerProps } = props;
127487
- const dialogScope = useDialogScope(__scopeAlertDialog);
127488
- return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { ...dialogScope, ...triggerProps, ref: forwardedRef });
127489
- }
127490
- );
127491
- AlertDialogTrigger.displayName = TRIGGER_NAME$3;
127492
- var PORTAL_NAME$2 = "AlertDialogPortal";
127493
- var AlertDialogPortal$1 = (props) => {
127494
- const { __scopeAlertDialog, ...portalProps } = props;
127495
- const dialogScope = useDialogScope(__scopeAlertDialog);
127496
- return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { ...dialogScope, ...portalProps });
127497
- };
127498
- AlertDialogPortal$1.displayName = PORTAL_NAME$2;
127499
- var OVERLAY_NAME = "AlertDialogOverlay";
127500
- var AlertDialogOverlay$1 = React.forwardRef(
127501
- (props, forwardedRef) => {
127502
- const { __scopeAlertDialog, ...overlayProps } = props;
127503
- const dialogScope = useDialogScope(__scopeAlertDialog);
127504
- return /* @__PURE__ */ jsx(DialogPrimitive.Overlay, { ...dialogScope, ...overlayProps, ref: forwardedRef });
127505
- }
127506
- );
127507
- AlertDialogOverlay$1.displayName = OVERLAY_NAME;
127508
- var CONTENT_NAME$4 = "AlertDialogContent";
127509
- var [AlertDialogContentProvider, useAlertDialogContentContext] = createAlertDialogContext(CONTENT_NAME$4);
127510
- var Slottable = createSlottable("AlertDialogContent");
127511
- var AlertDialogContent$1 = React.forwardRef(
127512
- (props, forwardedRef) => {
127513
- const { __scopeAlertDialog, children, ...contentProps } = props;
127514
- const dialogScope = useDialogScope(__scopeAlertDialog);
127515
- const contentRef = React.useRef(null);
127516
- const composedRefs = useComposedRefs$1(forwardedRef, contentRef);
127517
- const cancelRef = React.useRef(null);
127518
- return /* @__PURE__ */ jsx(
127519
- DialogPrimitive.WarningProvider,
127520
- {
127521
- contentName: CONTENT_NAME$4,
127522
- titleName: TITLE_NAME,
127523
- docsSlug: "alert-dialog",
127524
- children: /* @__PURE__ */ jsx(AlertDialogContentProvider, { scope: __scopeAlertDialog, cancelRef, children: /* @__PURE__ */ jsxs(
127525
- DialogPrimitive.Content,
127526
- {
127527
- role: "alertdialog",
127528
- ...dialogScope,
127529
- ...contentProps,
127530
- ref: composedRefs,
127531
- onOpenAutoFocus: composeEventHandlers$2(contentProps.onOpenAutoFocus, (event) => {
127532
- event.preventDefault();
127533
- cancelRef.current?.focus({ preventScroll: true });
127534
- }),
127535
- onPointerDownOutside: (event) => event.preventDefault(),
127536
- onInteractOutside: (event) => event.preventDefault(),
127537
- children: [
127538
- /* @__PURE__ */ jsx(Slottable, { children }),
127539
- /* @__PURE__ */ jsx(DescriptionWarning, { contentRef })
127540
- ]
127541
- }
127542
- ) })
127543
- }
127544
- );
127545
- }
127546
- );
127547
- AlertDialogContent$1.displayName = CONTENT_NAME$4;
127548
- var TITLE_NAME = "AlertDialogTitle";
127549
- var AlertDialogTitle$1 = React.forwardRef(
127550
- (props, forwardedRef) => {
127551
- const { __scopeAlertDialog, ...titleProps } = props;
127552
- const dialogScope = useDialogScope(__scopeAlertDialog);
127553
- return /* @__PURE__ */ jsx(DialogPrimitive.Title, { ...dialogScope, ...titleProps, ref: forwardedRef });
127554
- }
127555
- );
127556
- AlertDialogTitle$1.displayName = TITLE_NAME;
127557
- var DESCRIPTION_NAME = "AlertDialogDescription";
127558
- var AlertDialogDescription$1 = React.forwardRef((props, forwardedRef) => {
127559
- const { __scopeAlertDialog, ...descriptionProps } = props;
127560
- const dialogScope = useDialogScope(__scopeAlertDialog);
127561
- return /* @__PURE__ */ jsx(DialogPrimitive.Description, { ...dialogScope, ...descriptionProps, ref: forwardedRef });
127562
- });
127563
- AlertDialogDescription$1.displayName = DESCRIPTION_NAME;
127564
- var ACTION_NAME = "AlertDialogAction";
127565
- var AlertDialogAction$1 = React.forwardRef(
127566
- (props, forwardedRef) => {
127567
- const { __scopeAlertDialog, ...actionProps } = props;
127568
- const dialogScope = useDialogScope(__scopeAlertDialog);
127569
- return /* @__PURE__ */ jsx(DialogPrimitive.Close, { ...dialogScope, ...actionProps, ref: forwardedRef });
127570
- }
127571
- );
127572
- AlertDialogAction$1.displayName = ACTION_NAME;
127573
- var CANCEL_NAME = "AlertDialogCancel";
127574
- var AlertDialogCancel$1 = React.forwardRef(
127575
- (props, forwardedRef) => {
127576
- const { __scopeAlertDialog, ...cancelProps } = props;
127577
- const { cancelRef } = useAlertDialogContentContext(CANCEL_NAME, __scopeAlertDialog);
127578
- const dialogScope = useDialogScope(__scopeAlertDialog);
127579
- const ref = useComposedRefs$1(forwardedRef, cancelRef);
127580
- return /* @__PURE__ */ jsx(DialogPrimitive.Close, { ...dialogScope, ...cancelProps, ref });
127581
- }
127582
- );
127583
- AlertDialogCancel$1.displayName = CANCEL_NAME;
127584
- var DescriptionWarning = ({ contentRef }) => {
127585
- const MESSAGE = `\`${CONTENT_NAME$4}\` requires a description for the component to be accessible for screen reader users.
127586
-
127587
- You can add a description to the \`${CONTENT_NAME$4}\` by passing a \`${DESCRIPTION_NAME}\` component as a child, which also benefits sighted users by adding visible context to the dialog.
127588
-
127589
- Alternatively, you can use your own component as a description by assigning it an \`id\` and passing the same value to the \`aria-describedby\` prop in \`${CONTENT_NAME$4}\`. If the description is confusing or duplicative for sighted users, you can use the \`@radix-ui/react-visually-hidden\` primitive as a wrapper around your description component.
127590
-
127591
- For more information, see https://radix-ui.com/primitives/docs/components/alert-dialog`;
127592
- React.useEffect(() => {
127593
- const hasDescription = document.getElementById(
127594
- contentRef.current?.getAttribute("aria-describedby")
127595
- );
127596
- if (!hasDescription) console.warn(MESSAGE);
127597
- }, [MESSAGE, contentRef]);
127598
- return null;
127599
- };
127600
- var Root2$4 = AlertDialog$1;
127601
- var Portal2 = AlertDialogPortal$1;
127602
- var Overlay2 = AlertDialogOverlay$1;
127603
- var Content2$2 = AlertDialogContent$1;
127604
- var Action = AlertDialogAction$1;
127605
- var Cancel = AlertDialogCancel$1;
127606
- var Title2 = AlertDialogTitle$1;
127607
- var Description2 = AlertDialogDescription$1;
127608
-
127609
- const AlertDialog = Root2$4;
127610
- const AlertDialogPortal = Portal2;
127611
- const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (jsx(Overlay2, { className: cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', className), ...props, ref: ref })));
127612
- AlertDialogOverlay.displayName = Overlay2.displayName;
127613
- const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (jsxs(AlertDialogPortal, { children: [jsx(AlertDialogOverlay, {}), jsx(Content2$2, { ref: ref, className: cn('bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg', className), ...props })] })));
127614
- AlertDialogContent.displayName = Content2$2.displayName;
127615
- const AlertDialogHeader = ({ className, ...props }) => (jsx("div", { className: cn('flex flex-col space-y-2 text-center sm:text-left', className), ...props }));
127616
- AlertDialogHeader.displayName = 'AlertDialogHeader';
127617
- const AlertDialogFooter = ({ className, ...props }) => (jsx("div", { className: cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className), ...props }));
127618
- AlertDialogFooter.displayName = 'AlertDialogFooter';
127619
- const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (jsx(Title2, { ref: ref, className: cn('text-lg font-semibold', className), ...props })));
127620
- AlertDialogTitle.displayName = Title2.displayName;
127621
- const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (jsx(Description2, { ref: ref, className: cn('text-muted-foreground text-sm', className), ...props })));
127622
- AlertDialogDescription.displayName = Description2.displayName;
127623
- const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (jsx(Action, { ref: ref, className: cn(buttonVariants(), className), ...props })));
127624
- AlertDialogAction.displayName = Action.displayName;
127625
- const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (jsx(Cancel, { ref: ref, className: cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className), ...props })));
127626
- AlertDialogCancel.displayName = Cancel.displayName;
127627
-
127628
- // Tauri types for model download functionality
127629
- /**
127630
- * Initial state for model download
127631
- */
127632
- const initialModelDownloadState = {
127633
- status: 'idle',
127634
- progress: 0,
127635
- message: '',
127636
- logs: [],
127637
- lastUpdated: new Date().toISOString(),
127638
- };
127639
- /**
127640
- * Check if the app is running inside Tauri
127641
- * In Tauri 2.0, the global is __TAURI_INTERNALS__ (with withGlobalTauri: true)
127642
- * We also check for __TAURI__ for backwards compatibility
127643
- */
127644
- const isTauriApp$1 = () => {
127645
- if (typeof window === 'undefined')
127646
- return false;
127647
- return '__TAURI_INTERNALS__' in window || '__TAURI__' in window;
127648
- };
127649
- /**
127650
- * Tauri event names
127651
- */
127652
- const TAURI_EVENTS = {
127653
- DOWNLOAD_PROGRESS: 'model:download-progress',
127654
- INSTALLATION_LOG: 'model:installation-log',
127655
- DISK_SPACE_ERROR: 'model:disk-space-error',
127656
- OLLAMA_STATUS: 'model:ollama-status',
127657
- };
127658
- /**
127659
- * Tauri command names
127660
- */
127661
- const TAURI_COMMANDS = {
127662
- INSTALL_OLLAMA: 'install_ollama',
127663
- STOP_OLLAMA: 'stop_ollama',
127664
- CHECK_OLLAMA_STATUS: 'check_ollama_status',
127665
- CHECK_DISK_SPACE: 'check_disk_space_for_model',
127666
- GET_SYSTEM_MEMORY: 'get_system_memory',
127667
- DOWNLOAD_MODEL: 'download_model',
127668
- CANCEL_DOWNLOAD: 'cancel_model_download',
127669
- CHECK_NETWORK_STATUS: 'check_network_status',
127670
- GET_OS_TYPE: 'get_os_type',
127671
- CHECK_FOUNDRY_STATUS: 'check_foundry_local_status',
127672
- START_FOUNDRY_SERVICE: 'start_foundry_local_service',
127673
- LOAD_FOUNDRY_MODEL: 'load_foundry_local_model',
127674
- SET_SELECTED_FOUNDRY_MODEL: 'set_selected_foundry_model',
127675
- GET_SELECTED_FOUNDRY_MODEL: 'get_selected_foundry_model',
127676
- INSTALL_FOUNDRY: 'install_foundry',
127677
- DOWNLOAD_FOUNDRY_MODEL: 'download_foundry_model_cmd',
127678
- GET_RECOMMENDED_FOUNDRY_MODELS: 'get_recommended_foundry_models',
127679
- LOG_STDOUT: 'log_stdout',
127680
- };
127681
- /**
127682
- * Initial state for the System Control install flow.
127683
- */
127684
- const initialGhostOsInstallState = {
127685
- status: 'idle',
127686
- progress: 0,
127687
- message: '',
127688
- logs: [],
127689
- lastUpdated: new Date().toISOString(),
127690
- };
127691
- /**
127692
- * Tauri event names emitted by the host during GhostOS install/setup.
127693
- */
127694
- const GHOST_OS_TAURI_EVENTS = {
127695
- INSTALL_PROGRESS: 'ghost-os:install-progress',
127696
- INSTALLATION_LOG: 'ghost-os:installation-log',
127697
- STATUS: 'ghost-os:status',
127698
- };
127699
- /**
127700
- * Tauri command names the host implements for GhostOS install/setup. The web
127701
- * layer invokes these; the native side performs the clone + setup.
127702
- */
127703
- const GHOST_OS_TAURI_COMMANDS = {
127704
- /** Install and set up GhostOS (clone repo + run setup). */
127705
- INSTALL_GHOST_OS: 'install_ghost_os',
127706
- /** Stop the running GhostOS manager. */
127707
- STOP_GHOST_OS: 'stop_ghost_os',
127708
- /** Report current {@link GhostOsStatus}. */
127709
- CHECK_GHOST_OS_STATUS: 'check_ghost_os_status',
127710
- };
127711
- /**
127712
- * Tauri command strings exposed by `tauri-plugin-macos-permissions`
127713
- * (https://github.com/ayangweb/tauri-plugin-macos-permissions). GhostOS needs
127714
- * macOS Accessibility permission to control the device, so the System Control
127715
- * card surfaces it. These are invoked directly via `useTauri().invoke(...)` —
127716
- * no extra npm dependency. The host must register the plugin in its Tauri app
127717
- * (`tauri_plugin_macos_permissions::init()`). Each `CHECK_*` returns a boolean;
127718
- * each `REQUEST_*` prompts / opens the relevant System Settings pane.
127719
- */
127720
- const MACOS_PERMISSIONS_COMMANDS = {
127721
- CHECK_ACCESSIBILITY: 'plugin:macos-permissions|check_accessibility_permission',
127722
- REQUEST_ACCESSIBILITY: 'plugin:macos-permissions|request_accessibility_permission',
127723
- CHECK_SCREEN_RECORDING: 'plugin:macos-permissions|check_screen_recording_permission',
127724
- REQUEST_SCREEN_RECORDING: 'plugin:macos-permissions|request_screen_recording_permission',
127725
- CHECK_INPUT_MONITORING: 'plugin:macos-permissions|check_input_monitoring_permission',
127726
- REQUEST_INPUT_MONITORING: 'plugin:macos-permissions|request_input_monitoring_permission',
127727
- CHECK_FULL_DISK_ACCESS: 'plugin:macos-permissions|check_full_disk_access_permission',
127728
- REQUEST_FULL_DISK_ACCESS: 'plugin:macos-permissions|request_full_disk_access_permission',
127729
- };
127730
-
127731
- /**
127732
- * Dynamically import Tauri APIs to avoid SSR issues
127733
- */
127734
- const getTauriAPIs = async () => {
127735
- try {
127736
- const { invoke } = await import('@tauri-apps/api/core');
127737
- const { listen } = await import('@tauri-apps/api/event');
127738
- return { invoke, listen };
127739
- }
127740
- catch (error) {
127741
- console.error('Failed to load Tauri APIs:', error);
127742
- return null;
127743
- }
127744
- };
127745
- /**
127746
- * Hook to access Tauri APIs with SSR safety
127747
- *
127748
- * @returns Object with isAvailable flag and Tauri invoke/listen functions
127749
- */
127750
- function useTauri() {
127751
- const [isAvailable, setIsAvailable] = useState(false);
127752
- const [apis, setApis] = useState(null);
127753
- useEffect(() => {
127754
- // Check if we're in a Tauri environment
127755
- const inTauri = isTauriApp$1();
127756
- if (inTauri) {
127757
- getTauriAPIs().then((result) => {
127758
- setApis(result);
127759
- setIsAvailable(!!result);
127760
- });
127761
- }
127762
- }, []);
127763
- /**
127764
- * Invoke a Tauri command
127765
- */
127766
- const invoke = useCallback(async (command, args) => {
127767
- if (!(apis === null || apis === void 0 ? void 0 : apis.invoke)) {
127768
- throw new Error('Tauri is not available');
127769
- }
127770
- return apis.invoke(command, args);
127771
- }, [apis]);
127772
- /**
127773
- * Listen to a Tauri event
127774
- * Returns an unlisten function that should be called on cleanup
127775
- */
127776
- const listen = useCallback(async (event, handler) => {
127777
- // The synchronous global (`__TAURI_INTERNALS__`) exposes `invoke` but
127778
- // frequently NOT `event.listen`, so `apis.listen` can be undefined even
127779
- // though Tauri is fully available. Fall back to the real event API via
127780
- // dynamic import so download progress/completion events are actually
127781
- // delivered — otherwise the UI never leaves the "downloading" state.
127782
- let listenFn = apis === null || apis === void 0 ? void 0 : apis.listen;
127783
- if (!listenFn) {
127784
- const { listen: importedListen } = await import('@tauri-apps/api/event');
127785
- listenFn = importedListen;
127786
- }
127787
- return listenFn(event, (e) => handler(e.payload));
127788
- }, [apis]);
127789
- return {
127790
- isAvailable,
127791
- invoke,
127792
- listen,
127793
- };
127794
- }
127795
-
127796
127473
  const LOCAL_LLM_ENABLED_KEY = 'ibl_local_llm_enabled';
127797
127474
  const LOCAL_LLM_MODEL_KEY = 'ibl_local_llm_model';
127798
- const LOCAL_LLM_TOOL_SUPPORT_KEY = 'ibl_local_llm_tool_support';
127799
127475
  /**
127800
127476
  * Curated catalog of small, on-device-friendly models from the Ollama library
127801
127477
  * (https://ollama.com/library). The model manager itself (Ollama) is not a
@@ -127828,8 +127504,8 @@ const LOCAL_MODELS = [
127828
127504
  },
127829
127505
  { name: 'Granite 4.1', provider: 'IBM', id: 'granite4.1:8b', size: '5.3 GB', tool_support: true },
127830
127506
  { name: 'GPT-OSS 20B', provider: 'OpenAI', id: 'gpt-oss:20b', size: '14 GB', tool_support: true },
127831
- // Larger models. Sizes use "GB" so they parse for the System Control size gate
127832
- // (only models above the configured gate can use System Control).
127507
+ // Larger models. Sizes use "GB" so they parse for the Cowork size gate
127508
+ // (only models above the configured gate can use Cowork).
127833
127509
  {
127834
127510
  name: 'Qwen 3.6',
127835
127511
  provider: 'Alibaba',
@@ -127853,34 +127529,6 @@ const LOCAL_MODELS = [
127853
127529
  tool_support: true,
127854
127530
  },
127855
127531
  ];
127856
- /** The model wired to the existing single-model download flow (default selection). */
127857
- const PRIMARY_MODEL_ID = 'phi4-mini:latest';
127858
- /**
127859
- * Whether a catalog model id (e.g. "llama3.2", "phi3:mini") is present among the
127860
- * installed Ollama tags reported by the backend (e.g. "llama3.2:latest"). Matches
127861
- * an exact tag or the same base model with any tag suffix, so a model pulled as
127862
- * `<id>:latest` still counts as installed.
127863
- */
127864
- function isModelInstalled(modelId, installedTags) {
127865
- if (!installedTags)
127866
- return false;
127867
- return installedTags.some((tag) => tag === modelId || tag.startsWith(`${modelId}:`));
127868
- }
127869
- /**
127870
- * Whether a model id is an Ollama cloud-hosted model — either the plain ":cloud"
127871
- * tag (e.g. "glm-5.2:cloud") or a sized cloud tag (e.g. "gemma4:31b-cloud").
127872
- * Cloud models run on Ollama's servers, so they are available by default — never
127873
- * downloaded, no local memory required, always "ready".
127874
- */
127875
- function isCloudModelId(modelId) {
127876
- return modelId.endsWith(':cloud') || (modelId.includes(':') && modelId.endsWith('-cloud'));
127877
- }
127878
- /**
127879
- * Fraction of the machine's memory above which a model is treated as "too large"
127880
- * and a confirmation is required before downloading. TESTING VALUE — set low (1%)
127881
- * so the warning is easy to trigger; raise toward real capacity (e.g. 0.8) later.
127882
- */
127883
- const MODEL_SIZE_WARN_FRACTION = 0.01;
127884
127532
  /** Multipliers for the size units used in the catalog (binary / 1024-based). */
127885
127533
  const SIZE_UNIT_BYTES = {
127886
127534
  B: 1,
@@ -127902,54 +127550,19 @@ function parseModelSizeBytes(size) {
127902
127550
  return null;
127903
127551
  return value * ((_a = SIZE_UNIT_BYTES[match[2].toUpperCase()]) !== null && _a !== void 0 ? _a : 1);
127904
127552
  }
127905
- /** Format a byte count as a short, human-readable size (e.g. "16.0 GB"). */
127906
- function formatBytes(bytes) {
127907
- if (bytes <= 0)
127908
- return '0 GB';
127909
- const gb = bytes / 1024 ** 3;
127910
- if (gb >= 1)
127911
- return `${gb.toFixed(1)} GB`;
127912
- return `${Math.round(bytes / 1024 ** 2)} MB`;
127913
- }
127914
127553
  /**
127915
- * The memory a model can use on this machine: the larger of system RAM and GPU
127916
- * VRAM (a model runs in one or the other). Returns 0 when memory is unknown.
127917
- */
127918
- function usableMemoryBytes(systemMemory) {
127919
- if (!systemMemory)
127920
- return 0;
127921
- return Math.max(systemMemory.ram_total, systemMemory.vram_total);
127922
- }
127923
- /**
127924
- * Whether `model` is large enough relative to this machine's memory to warrant a
127925
- * "might not run" confirmation. False when memory or the model size is unknown,
127926
- * so the download simply proceeds rather than warning on incomplete data.
127927
- */
127928
- function modelExceedsCapacity(model, systemMemory) {
127929
- const capacity = usableMemoryBytes(systemMemory);
127930
- const modelBytes = parseModelSizeBytes(model.size);
127931
- if (modelBytes == null)
127932
- return false;
127933
- // Unknown memory (capacity 0) → don't warn; the download simply proceeds
127934
- // rather than blocking on incomplete data.
127935
- if (capacity <= 0)
127936
- return false;
127937
- return modelBytes > capacity * MODEL_SIZE_WARN_FRACTION;
127938
- }
127939
- /**
127940
- * Default minimum model size (GB) that can use the System Control Manager
127554
+ * Default minimum model size (GB) that can use the Cowork
127941
127555
  * (GhostOS MCP/tools). Smaller models don't support tools/MCP well, so the
127942
- * manager is gated to models above this size. Per-app overridable via the
127943
- * System Control card's `requiredSizeGb` prop (see SystemControlTabProps).
127556
+ * manager is gated to models above this size.
127944
127557
  */
127945
- const DEFAULT_SYSTEM_CONTROL_REQUIRED_SIZE_GB = 12;
127558
+ const DEFAULT_COWORK_REQUIRED_SIZE_GB = 12;
127946
127559
  /**
127947
127560
  * Whether the given model (by Ollama id) is large enough to use the System
127948
127561
  * Control Manager — i.e. its catalog size exceeds `gb` (default
127949
- * {@link DEFAULT_SYSTEM_CONTROL_REQUIRED_SIZE_GB}). Unknown ids or unparseable
127562
+ * {@link DEFAULT_COWORK_REQUIRED_SIZE_GB}). Unknown ids or unparseable
127950
127563
  * sizes return false (gated off).
127951
127564
  */
127952
- function modelSupportsSystemControl(modelId, gb = DEFAULT_SYSTEM_CONTROL_REQUIRED_SIZE_GB) {
127565
+ function modelSupportsCowork(modelId, gb = DEFAULT_COWORK_REQUIRED_SIZE_GB) {
127953
127566
  if (!modelId)
127954
127567
  return false;
127955
127568
  const model = LOCAL_MODELS.find((m) => m.id === modelId);
@@ -127960,22 +127573,6 @@ function modelSupportsSystemControl(modelId, gb = DEFAULT_SYSTEM_CONTROL_REQUIRE
127960
127573
  return false;
127961
127574
  return bytes > gb * 1024 ** 3;
127962
127575
  }
127963
- /**
127964
- * The smallest catalog model that can use the System Control Manager (size
127965
- * exceeds `gb`), or null if none qualify. Used by the "Upgrade" action to pick
127966
- * the lightest capable model. Ties / unparseable sizes sort last.
127967
- */
127968
- function smallestSystemControlModel(gb = DEFAULT_SYSTEM_CONTROL_REQUIRED_SIZE_GB) {
127969
- const usable = LOCAL_MODELS.filter((m) => modelSupportsSystemControl(m.id, gb));
127970
- if (usable.length === 0)
127971
- return null;
127972
- return usable.reduce((smallest, m) => {
127973
- var _a, _b;
127974
- const a = (_a = parseModelSizeBytes(m.size)) !== null && _a !== void 0 ? _a : Infinity;
127975
- const b = (_b = parseModelSizeBytes(smallest.size)) !== null && _b !== void 0 ? _b : Infinity;
127976
- return a < b ? m : smallest;
127977
- });
127978
- }
127979
127576
  /**
127980
127577
  * Check if local LLM is enabled from localStorage
127981
127578
  */
@@ -128000,57 +127597,21 @@ function getLocalLLMModel() {
128000
127597
  return null;
128001
127598
  return localStorage.getItem(LOCAL_LLM_MODEL_KEY);
128002
127599
  }
128003
- /**
128004
- * Set the model the user has chosen to chat with (Ollama pull id).
128005
- */
128006
- function setLocalLLMModel(modelId) {
128007
- if (typeof window === 'undefined')
128008
- return;
128009
- localStorage.setItem(LOCAL_LLM_MODEL_KEY, modelId);
128010
- }
128011
- /**
128012
- * Persist whether the selected model supports tools (set when a model is chosen).
128013
- */
128014
- function setLocalLLMToolSupport(toolSupport) {
128015
- if (typeof window === 'undefined')
128016
- return;
128017
- localStorage.setItem(LOCAL_LLM_TOOL_SUPPORT_KEY, String(toolSupport));
128018
- }
128019
- function LocalLLMTab({ isAvailable, state, ollamaStatus, systemMemory, isUsingFoundry = false, foundryModels = [], selectedFoundryModel = null, foundryStatus, onStartDownload, onCancelDownload, onInstallOllama, onStopManager, onInstallFoundry, onCheckStatus, onResetState, onSelectFoundryModel, }) {
128020
- var _a, _b, _c, _d, _e, _f, _g;
127600
+ function LocalLLMTab({ isAvailable, state, ollamaStatus, isUsingFoundry = false, foundryModels = [], selectedFoundryModel = null, foundryStatus, onInstallOllama, onStopManager, onInstallFoundry, onCheckStatus, onSelectFoundryModel, }) {
127601
+ var _a, _b, _c, _d, _e;
128021
127602
  const t = useT();
128022
- // Direct Tauri access so the model-size check can read system memory fresh at
128023
- // download-click time (the threaded `systemMemory` prop is only a fast path).
128024
- const { invoke } = useTauri();
128025
127603
  const isDownloading = state.status === 'downloading';
128026
127604
  const isManagerInstalling = state.managerInstalling || (ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.installing);
128027
127605
  // User preference state - independent of whether model is installed
128028
127606
  const [isEnabled, setIsEnabled] = useState(() => isLocalLLMEnabled());
128029
- // Which model the shared download flow currently targets. Prefer the model
128030
- // recorded in the persisted download state, so the correct row shows progress
128031
- // even after the dialog is closed and reopened mid-download; fall back to a
128032
- // local value for immediate feedback the instant a Download button is clicked.
128033
- const [localActiveModelId, setLocalActiveModelId] = useState(PRIMARY_MODEL_ID);
128034
- const activeModelId = state.status === 'downloading' && state.activeModel ? state.activeModel : localActiveModelId;
128035
- // Which model the user has chosen to chat with (exclusive selection).
128036
- const [selectedModelId, setSelectedModelId] = useState(() => getLocalLLMModel() || PRIMARY_MODEL_ID);
128037
- // A model the user asked to download that is large relative to this machine's
128038
- // memory. Set when we need the user to confirm a "might not run" warning before
128039
- // starting the download; null when no confirmation is pending.
128040
- const [pendingModel, setPendingModel] = useState(null);
128041
127607
  // "Enable Local Models" === run the model manager (Ollama). The toggle is a
128042
127608
  // user preference that is always operable: turning it on installs (if needed)
128043
- // and starts the manager; turning it off stops it. The card + model table are
128044
- // shown whenever the toggle is on (or mid-install), independent of install
128045
- // state, so the user always gets feedback for their choice.
127609
+ // and starts the manager; turning it off stops it. Only the manager status
127610
+ // card is shown here (whenever the toggle is on or mid-install) the per-model
127611
+ // list lives in Agent settings LLM tab, where local models are picked and
127612
+ // downloaded alongside cloud models.
128046
127613
  const managerRunning = (ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.running) === true;
128047
127614
  const localModelsEnabled = isEnabled || isManagerInstalling;
128048
- // A model can be chosen for chat only once it is downloaded/ready. The backend
128049
- // reports every installed model tag, so any downloaded model qualifies.
128050
- const selectedModelReady = isCloudModelId(selectedModelId) ||
128051
- isModelInstalled(selectedModelId, ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.installed_models) ||
128052
- (selectedModelId === PRIMARY_MODEL_ID &&
128053
- (state.status === 'completed' || (ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.model_installed) === true));
128054
127615
  // Track previous model installation state to detect when it first becomes installed
128055
127616
  const [, setWasModelInstalled] = useState(() => {
128056
127617
  return (ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.model_installed) === true;
@@ -128164,62 +127725,6 @@ function LocalLLMTab({ isAvailable, state, ollamaStatus, systemMemory, isUsingFo
128164
127725
  onStopManager === null || onStopManager === void 0 ? void 0 : onStopManager();
128165
127726
  }
128166
127727
  };
128167
- const startDownloadNow = (modelId) => {
128168
- setLocalActiveModelId(modelId);
128169
- onStartDownload(modelId);
128170
- };
128171
- const handleDownloadModel = async (modelId) => {
128172
- // Always read system memory FIRST — this is the call that hits the backend
128173
- // `get_system_memory`. Doing it before the catalog lookup guarantees it runs
128174
- // on every Download click (falling back to the value fetched on mount).
128175
- const model = LOCAL_MODELS.find((m) => modelId.startsWith(m.id));
128176
- if (!model) {
128177
- await invoke(TAURI_COMMANDS.LOG_STDOUT, {
128178
- s: `[Local Models] -> Download: bad download request, model ${model}`,
128179
- });
128180
- console.log(`[Local Models] -> Download: bad download request, model ${model}`);
128181
- return;
128182
- }
128183
- let mem = null;
128184
- try {
128185
- mem = await invoke(TAURI_COMMANDS.GET_SYSTEM_MEMORY);
128186
- }
128187
- catch (e) {
128188
- await invoke(TAURI_COMMANDS.LOG_STDOUT, {
128189
- s: `[Local Models] -> Download: couldn't detect system memory ${modelId} ${e}`,
128190
- });
128191
- console.log(`[Local Models] -> Download: couldn't detect system memory {e}`);
128192
- return;
128193
- }
128194
- const exceeds = modelExceedsCapacity(model, mem);
128195
- console.warn('[Local Models] -> Download: download click', {
128196
- modelId,
128197
- systemMemory: mem,
128198
- capacityBytes: usableMemoryBytes(mem),
128199
- modelBytes: parseModelSizeBytes(model.size),
128200
- thresholdBytes: usableMemoryBytes(mem) * MODEL_SIZE_WARN_FRACTION,
128201
- exceeds,
128202
- });
128203
- if (exceeds) {
128204
- setPendingModel(model);
128205
- return;
128206
- }
128207
- startDownloadNow(modelId);
128208
- };
128209
- const confirmPendingDownload = () => {
128210
- if (pendingModel) {
128211
- startDownloadNow(pendingModel.id);
128212
- }
128213
- setPendingModel(null);
128214
- };
128215
- const handleSelectModel = (modelId) => {
128216
- var _a, _b;
128217
- setSelectedModelId(modelId);
128218
- setLocalLLMModel(modelId);
128219
- // Persist the model's tool-calling support so the host can route chat to the
128220
- // MCP/tool proxy (:8000) vs plain Ollama (:11434).
128221
- setLocalLLMToolSupport((_b = (_a = LOCAL_MODELS.find((m) => m.id === modelId)) === null || _a === void 0 ? void 0 : _a.tool_support) !== null && _b !== void 0 ? _b : false);
128222
- };
128223
127728
  // For Ollama the switch reflects the user's toggle preference (always
128224
127729
  // operable, so it can be turned off). Foundry additionally requires a
128225
127730
  // downloaded model to be considered "on".
@@ -128233,54 +127738,16 @@ function LocalLLMTab({ isAvailable, state, ollamaStatus, systemMemory, isUsingFo
128233
127738
  const switchDisabled = isUsingFoundry
128234
127739
  ? !hasDownloadedFoundryModel || isDownloading || state.status === 'checking'
128235
127740
  : isDownloading || isManagerInstalling || state.status === 'checking';
128236
- return (jsxs("div", { className: "space-y-6", children: [jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between rounded-lg border px-6 py-4", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-[#646464]", children: t('localLlmLocalLlmTab.enableLocalLlm') }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('localLlmLocalLlmTab.moreInfoAriaLabel'), className: "hidden sm:block", children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium max-w-xs text-white shadow-sm transition-opacity duration-300 z-50", children: jsxs("div", { className: "space-y-2", children: [jsx("p", { children: t('localLlmLocalLlmTab.tooltipWorkOffline') }), (foundryStatus === null || foundryStatus === void 0 ? void 0 : foundryStatus.is_supported) && (jsx("p", { className: "text-xs text-gray-300", children: t('localLlmLocalLlmTab.tooltipFoundrySupported') }))] }) })] }) })] }), jsxs("div", { className: "flex items-center gap-2", children: [isManagerInstalling && !isUsingFoundry && (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500", "aria-label": t('localLlmLocalLlmTab.startingModelManagerAriaLabel') })), jsx(Switch, { checked: switchChecked, onCheckedChange: handleToggle, disabled: switchDisabled, "aria-label": t('localLlmLocalLlmTab.switchAriaLabel', {
128237
- state: switchChecked
128238
- ? t('localLlmLocalLlmTab.switchStateEnabled')
128239
- : t('localLlmLocalLlmTab.switchStateDisabled'),
128240
- }), className: "cursor-pointer data-[state=checked]:bg-blue-500" })] })] }), shouldShowFoundryInstall ? (jsx("div", { className: "rounded-lg border px-6 py-4 bg-blue-50/30 dark:bg-blue-950/10", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "flex items-start gap-3", children: jsxs("div", { className: "flex items-center gap-2 flex-shrink-0 mt-0.5", children: [jsx(Star$1, { className: "h-5 w-5 text-blue-600 fill-blue-600" }), jsx("span", { className: "px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 rounded-full", children: t('localLlmLocalLlmTab.preferredBadge') })] }) }), jsxs("div", { className: "flex items-start gap-3", children: [jsx(Info$3, { className: "h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" }), jsxs("div", { className: "flex-1", children: [jsx("h3", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.settingUpOfflineAi') }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: t('localLlmLocalLlmTab.foundryInstallDescription') })] })] }), state.status === 'downloading' || state.status === 'checking' ? (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: state.message || t('localLlmLocalLlmTab.preparingDownload') })] }), state.progress > 0 && jsx(Progress, { value: state.progress, className: "h-2" })] })) : state.status === 'error' ? (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2 text-red-600 dark:text-red-400", children: [jsx(CircleX, { className: "h-4 w-4" }), jsx("span", { className: "text-sm", children: state.error || t('localLlmLocalLlmTab.downloadFailed') })] }), jsx("div", { className: "flex gap-2", children: jsxs(Button$1, { onClick: onInstallFoundry, size: "sm", variant: "outline", children: [jsx(RefreshCw, { className: "h-4 w-4 mr-2" }), t('localLlmLocalLlmTab.tryAgain')] }) })] })) : (jsx("div", { className: "space-y-2", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: t('localLlmLocalLlmTab.startingDownload') })] }) })), jsxs("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: [jsx("p", { className: "font-medium mb-1", children: t('localLlmLocalLlmTab.downloadSize') }), jsx("p", { children: t('localLlmLocalLlmTab.oneTimeSetup') })] })] }) })) : /* Requirements Table - Hidden when using Foundry Local */ !isUsingFoundry ? (jsxs(Fragment$1, { children: [(foundryStatus === null || foundryStatus === void 0 ? void 0 : foundryStatus.is_supported) && (jsx("div", { className: "rounded-lg border px-4 py-3 bg-blue-50/20 dark:bg-blue-950/10", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Info$3, { className: "h-4 w-4 text-blue-600 flex-shrink-0 mt-0.5" }), jsxs("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: [jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.noteLabel') }), ' ', t('localLlmLocalLlmTab.foundryAlternativeNote')] })] }) })), localModelsEnabled && (jsxs("div", { className: "rounded-lg border px-6 py-4", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx(Server, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }), jsxs("div", { children: [jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.modelManagerName') }), jsx("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: t('localLlmLocalLlmTab.modelManagerDescription') })] })] }), jsx("div", { className: "flex items-center gap-2", children: managerRunning ? (jsxs(Fragment$1, { children: [jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300 rounded-full", children: [jsx(CircleCheckBig, { className: "h-3 w-3" }), t('localLlmLocalLlmTab.running')] }), jsx(Button$1, { onClick: onCheckStatus, variant: "ghost", size: "sm", "aria-label": t('localLlmLocalLlmTab.refreshManagerStatusAriaLabel'), children: jsx(RefreshCw, { className: "h-4 w-4" }) })] })) : (jsxs(Fragment$1, { children: [jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300 rounded-full", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), t('localLlmLocalLlmTab.starting')] }), jsx(Button$1, { onClick: onCheckStatus, variant: "ghost", size: "sm", "aria-label": t('localLlmLocalLlmTab.refreshManagerStatusAriaLabel'), children: jsx(RefreshCw, { className: "h-4 w-4" }) })] })) })] }), isManagerInstalling && (jsx("div", { className: "mt-3 w-full max-w-[240px]", children: jsx(Progress, { value: state.managerInstallProgress || 0, className: "h-1.5" }) }))] })), localModelsEnabled && (jsxs("div", { className: "rounded-lg border overflow-hidden", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("table", { className: "w-full", children: [jsx("thead", { children: jsxs("tr", { className: "bg-gray-50 dark:bg-gray-800/50", children: [jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider w-12", children: t('localLlmLocalLlmTab.tableHeaderUse') }), jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: t('localLlmLocalLlmTab.tableHeaderModel') }), jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: t('localLlmLocalLlmTab.tableHeaderStatus') }), jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: t('localLlmLocalLlmTab.tableHeaderAction') })] }) }), jsx("tbody", { className: "bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-800", children: LOCAL_MODELS.map((model) => {
128241
- const isPrimary = model.id === PRIMARY_MODEL_ID;
128242
- const isActive = activeModelId === model.id;
128243
- // Cloud-hosted models are always available — they run on
128244
- // Ollama's cloud, so there's nothing to download.
128245
- const isCloud = isCloudModelId(model.id);
128246
- // A model is ready once Ollama reports its tag as installed.
128247
- // Fall back to (a) this row's own download having just
128248
- // completed — so it flips to "Ready" before the status
128249
- // refresh lands — and (b) the primary-model status flag.
128250
- // Cloud models are ready by default.
128251
- const isInstalled = isCloud ||
128252
- isModelInstalled(model.id, ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.installed_models) ||
128253
- (isActive && state.status === 'completed') ||
128254
- (isPrimary && (ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.model_installed) === true);
128255
- // An installed model must never render a download/cancel
128256
- // state — guards against a stale persisted "downloading"
128257
- // status making an already-ready model look like it's
128258
- // still downloading (and wrongly cancellable) on reopen.
128259
- const rowDownloading = isActive && isDownloading && !isInstalled;
128260
- const rowChecking = isActive && state.status === 'checking';
128261
- const rowError = isActive && state.status === 'error';
128262
- const rowCancelled = isActive && state.status === 'cancelled';
128263
- const canDownload = !!(ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.installed) &&
128264
- !isInstalled &&
128265
- !rowDownloading &&
128266
- !rowChecking;
128267
- const isSelected = selectedModelId === model.id;
128268
- return (jsxs("tr", { className: isSelected ? 'bg-blue-50/40 dark:bg-blue-950/20' : undefined, children: [jsx("td", { className: "px-6 py-4", children: jsx("input", { type: "radio", name: "local-llm-model", checked: isSelected, disabled: !isInstalled, onChange: () => handleSelectModel(model.id), "aria-label": t('localLlmLocalLlmTab.useModelForChatAriaLabel', {
128269
- name: model.name,
128270
- }), title: isInstalled
128271
- ? undefined
128272
- : t('localLlmLocalLlmTab.downloadToChatTitle'), className: isInstalled
128273
- ? 'h-4 w-4 cursor-pointer accent-blue-500'
128274
- : 'h-4 w-4 cursor-not-allowed opacity-40' }) }), jsxs("td", { className: "px-6 py-4 whitespace-nowrap", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: model.name }), jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: model.size })] }), jsx("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: model.provider })] }), jsx("td", { className: "px-6 py-4", children: jsx("div", { className: "space-y-2", children: isCloud ? (jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300 rounded-full", children: [jsx(Cloud, { className: "h-3 w-3" }), t('localLlmLocalLlmTab.cloud')] })) : rowDownloading ? (jsxs("div", { className: "space-y-2", children: [jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300 rounded-full", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), Math.round(state.progress), "%"] }), jsx("div", { className: "w-full max-w-[200px]", children: jsx(Progress, { value: state.progress, className: "h-1.5" }) })] })) : rowChecking ? (jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300 rounded-full", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), t('localLlmLocalLlmTab.checking')] })) : isInstalled ? (jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300 rounded-full", children: [jsx(CircleCheckBig, { className: "h-3 w-3" }), t('localLlmLocalLlmTab.ready')] })) : rowError ? (jsxs("div", { className: "space-y-1", children: [jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300 rounded-full", children: [jsx(CircleX, { className: "h-3 w-3" }), t('localLlmLocalLlmTab.error')] }), state.error && (jsx("p", { className: "text-xs text-red-600 dark:text-red-400 max-w-[200px]", children: state.error }))] })) : rowCancelled ? (jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300 rounded-full", children: [jsx(X$2, { className: "h-3 w-3" }), t('localLlmLocalLlmTab.cancelled')] })) : (jsx("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300 rounded-full", children: t('localLlmLocalLlmTab.notDownloaded') })) }) }), jsx("td", { className: "px-6 py-4 text-right", children: jsxs("div", { className: "flex items-center justify-end gap-2", children: [rowDownloading && (jsxs(Button$1, { onClick: onCancelDownload, variant: "outline", size: "sm", children: [jsx(X$2, { className: "h-4 w-4 mr-2" }), t('localLlmLocalLlmTab.cancel')] })), canDownload && (jsxs(Button$1, { onClick: () => handleDownloadModel(model.id), size: "sm", variant: "outline", children: [jsx(Download, { className: "h-4 w-4 mr-2" }), t('localLlmLocalLlmTab.download')] })), rowError && (jsx(Button$1, { onClick: onResetState, variant: "ghost", size: "sm", children: t('localLlmLocalLlmTab.reset') })), isInstalled && !isCloud && (jsx(Button$1, { onClick: onCheckStatus, variant: "ghost", size: "sm", children: jsx(RefreshCw, { className: "h-4 w-4" }) }))] }) })] }, model.id));
128275
- }) })] }), jsx("div", { className: "px-6 py-3 border-t text-xs text-gray-500 dark:text-gray-400", style: { borderColor: 'oklch(.922 0 0)' }, children: selectedModelReady ? (jsxs(Fragment$1, { children: [t('localLlmLocalLlmTab.chattingWith'), ' ', jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100", children: (_c = (_b = LOCAL_MODELS.find((m) => m.id === selectedModelId)) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : selectedModelId })] })) : (jsx(Fragment$1, { children: t('localLlmLocalLlmTab.noModelSelectedHint') })) })] }))] })) : (jsx("div", { className: "rounded-lg border px-6 py-4 bg-green-50/30 dark:bg-green-950/10", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "flex items-center justify-between", children: jsxs("div", { className: "flex items-center gap-3", children: [jsx(CircleCheckBig, { className: "h-8 w-8 text-green-500 flex-shrink-0" }), jsxs("div", { className: "flex-1", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("h3", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.offlineAiReady') }), jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 rounded-full", children: [jsx(Star$1, { className: "h-3 w-3 fill-blue-700 dark:fill-blue-300" }), t('localLlmLocalLlmTab.preferredBadge')] })] }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: t('localLlmLocalLlmTab.foundryReadyDescription') })] })] }) }), foundryModels.length > 0 && (jsxs("div", { className: "space-y-2", children: [jsx("label", { className: "text-xs font-medium text-gray-700 dark:text-gray-300", children: t('localLlmLocalLlmTab.selectModelLabel') }), jsxs(Select$1, { value: selectedFoundryModel || ((_d = foundryModels[0]) === null || _d === void 0 ? void 0 : _d.id), onValueChange: (value) => onSelectFoundryModel === null || onSelectFoundryModel === void 0 ? void 0 : onSelectFoundryModel(value), children: [jsx(SelectTrigger, { className: "w-full bg-gray-50 border-gray-200 dark:bg-gray-800 dark:border-gray-700", children: jsx(SelectValue, { placeholder: t('localLlmLocalLlmTab.selectModelPlaceholder') }) }), jsx(SelectContent, { children: foundryModels.map((model) => (jsx(SelectItem, { value: model.id, children: jsxs("div", { className: "flex items-center justify-between w-full gap-4", children: [jsx("span", { className: "font-medium", children: model.name || model.id }), jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500", children: [model.device && (jsx("span", { className: "px-2 py-0.5 bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300 rounded", children: model.device })), model.size && jsx("span", { children: model.size }), !model.is_downloaded && (jsx("span", { className: "px-2 py-0.5 bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300 rounded", children: t('localLlmLocalLlmTab.notDownloaded') })), model.is_downloaded && (jsx("span", { className: "px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded", children: t('localLlmLocalLlmTab.downloaded') }))] })] }) }, model.id))) })] }), jsxs("div", { className: "flex flex-col gap-1", children: [jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t('localLlmLocalLlmTab.modelsAvailable', { count: foundryModels.length }) }), selectedFoundryModel && (jsxs("div", { className: "flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400", children: [jsx(CircleCheckBig, { className: "h-3.5 w-3.5" }), jsxs("span", { children: [t('localLlmLocalLlmTab.usingModelPrefix'), ' ', ((_e = foundryModels.find((m) => m.id === selectedFoundryModel)) === null || _e === void 0 ? void 0 : _e.name) ||
128276
- selectedFoundryModel, ((_f = foundryModels.find((m) => m.id === selectedFoundryModel)) === null || _f === void 0 ? void 0 : _f.device) && (jsxs("span", { className: "ml-1 text-gray-500 dark:text-gray-400", children: ["(", (_g = foundryModels.find((m) => m.id === selectedFoundryModel)) === null || _g === void 0 ? void 0 : _g.device, ")"] }))] })] }))] })] })), state.status === 'downloading' && (jsxs("div", { className: "mt-4 p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("div", { className: "animate-spin h-5 w-5 border-2 border-blue-600 dark:border-blue-400 border-t-transparent rounded-full" }), jsxs("div", { className: "flex-1", children: [jsx("p", { className: "text-sm font-medium text-blue-900 dark:text-blue-100", children: state.message }), state.progress > 0 && (jsx("div", { className: "mt-2 w-full bg-blue-200 dark:bg-blue-800 rounded-full h-2", children: jsx("div", { className: "bg-blue-600 dark:bg-blue-400 h-2 rounded-full transition-all duration-300", style: { width: `${state.progress}%` } }) }))] })] }), state.logs.length > 0 && (jsx("div", { className: "mt-3 max-h-32 overflow-y-auto bg-white dark:bg-gray-800 rounded border border-blue-200 dark:border-blue-700 p-2", children: state.logs.slice(-10).map((log, idx) => (jsx("div", { className: `text-xs font-mono ${log.level === 'error'
128277
- ? 'text-red-600 dark:text-red-400'
128278
- : log.level === 'warn'
128279
- ? 'text-yellow-600 dark:text-yellow-400'
128280
- : 'text-gray-700 dark:text-gray-300'}`, children: log.message }, idx))) }))] }))] }) }))] }), jsx(AlertDialog, { open: !!pendingModel, onOpenChange: (open) => {
128281
- if (!open)
128282
- setPendingModel(null);
128283
- }, children: jsxs(AlertDialogContent, { children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: "This model may be too large for your system" }), jsxs(AlertDialogDescription, { children: [pendingModel === null || pendingModel === void 0 ? void 0 : pendingModel.name, " needs about ", pendingModel === null || pendingModel === void 0 ? void 0 : pendingModel.size, ", but your system has", ' ', formatBytes(usableMemoryBytes(systemMemory)), " of memory available to run it. It may run very slowly or fail to load. Download anyway?"] })] }), jsxs(AlertDialogFooter, { children: [jsx(AlertDialogCancel, { onClick: () => setPendingModel(null), children: "Cancel" }), jsx(AlertDialogAction, { onClick: confirmPendingDownload, children: "Download anyway" })] })] }) })] }));
127741
+ return (jsx("div", { className: "space-y-6", children: jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between rounded-lg border px-6 py-4", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: "text-sm font-medium text-[#646464]", children: t('localLlmLocalLlmTab.enableLocalLlm') }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": t('localLlmLocalLlmTab.moreInfoAriaLabel'), className: "hidden sm:block", children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium max-w-xs text-white shadow-sm transition-opacity duration-300 z-50", children: jsxs("div", { className: "space-y-2", children: [jsx("p", { children: t('localLlmLocalLlmTab.tooltipWorkOffline') }), (foundryStatus === null || foundryStatus === void 0 ? void 0 : foundryStatus.is_supported) && (jsx("p", { className: "text-xs text-gray-300", children: t('localLlmLocalLlmTab.tooltipFoundrySupported') }))] }) })] }) })] }), jsxs("div", { className: "flex items-center gap-2", children: [isManagerInstalling && !isUsingFoundry && (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500", "aria-label": t('localLlmLocalLlmTab.startingModelManagerAriaLabel') })), jsx(Switch, { checked: switchChecked, onCheckedChange: handleToggle, disabled: switchDisabled, "aria-label": t('localLlmLocalLlmTab.switchAriaLabel', {
127742
+ state: switchChecked
127743
+ ? t('localLlmLocalLlmTab.switchStateEnabled')
127744
+ : t('localLlmLocalLlmTab.switchStateDisabled'),
127745
+ }), className: "cursor-pointer data-[state=checked]:bg-blue-500" })] })] }), shouldShowFoundryInstall ? (jsx("div", { className: "rounded-lg border px-6 py-4 bg-blue-50/30 dark:bg-blue-950/10", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "flex items-start gap-3", children: jsxs("div", { className: "flex items-center gap-2 flex-shrink-0 mt-0.5", children: [jsx(Star$1, { className: "h-5 w-5 text-blue-600 fill-blue-600" }), jsx("span", { className: "px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 rounded-full", children: t('localLlmLocalLlmTab.preferredBadge') })] }) }), jsxs("div", { className: "flex items-start gap-3", children: [jsx(Info$3, { className: "h-5 w-5 text-blue-500 flex-shrink-0 mt-0.5" }), jsxs("div", { className: "flex-1", children: [jsx("h3", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.settingUpOfflineAi') }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: t('localLlmLocalLlmTab.foundryInstallDescription') })] })] }), state.status === 'downloading' || state.status === 'checking' ? (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: state.message || t('localLlmLocalLlmTab.preparingDownload') })] }), state.progress > 0 && jsx(Progress, { value: state.progress, className: "h-2" })] })) : state.status === 'error' ? (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "flex items-center gap-2 text-red-600 dark:text-red-400", children: [jsx(CircleX, { className: "h-4 w-4" }), jsx("span", { className: "text-sm", children: state.error || t('localLlmLocalLlmTab.downloadFailed') })] }), jsx("div", { className: "flex gap-2", children: jsxs(Button$1, { onClick: onInstallFoundry, size: "sm", variant: "outline", children: [jsx(RefreshCw, { className: "h-4 w-4 mr-2" }), t('localLlmLocalLlmTab.tryAgain')] }) })] })) : (jsx("div", { className: "space-y-2", children: jsxs("div", { className: "flex items-center gap-2", children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500" }), jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: t('localLlmLocalLlmTab.startingDownload') })] }) })), jsxs("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: [jsx("p", { className: "font-medium mb-1", children: t('localLlmLocalLlmTab.downloadSize') }), jsx("p", { children: t('localLlmLocalLlmTab.oneTimeSetup') })] })] }) })) : /* Requirements Table - Hidden when using Foundry Local */ !isUsingFoundry ? (jsxs(Fragment$1, { children: [(foundryStatus === null || foundryStatus === void 0 ? void 0 : foundryStatus.is_supported) && (jsx("div", { className: "rounded-lg border px-4 py-3 bg-blue-50/20 dark:bg-blue-950/10", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs("div", { className: "flex items-start gap-2", children: [jsx(Info$3, { className: "h-4 w-4 text-blue-600 flex-shrink-0 mt-0.5" }), jsxs("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: [jsx("span", { className: "font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.noteLabel') }), ' ', t('localLlmLocalLlmTab.foundryAlternativeNote')] })] }) })), localModelsEnabled && (jsxs("div", { className: "rounded-lg border px-6 py-4", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx(Server, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }), jsxs("div", { children: [jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.modelManagerName') }), jsx("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: t('localLlmLocalLlmTab.modelManagerDescription') })] })] }), jsx("div", { className: "flex items-center gap-2", children: managerRunning ? (jsxs(Fragment$1, { children: [jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300 rounded-full", children: [jsx(CircleCheckBig, { className: "h-3 w-3" }), t('localLlmLocalLlmTab.running')] }), jsx(Button$1, { onClick: onCheckStatus, variant: "ghost", size: "sm", "aria-label": t('localLlmLocalLlmTab.refreshManagerStatusAriaLabel'), children: jsx(RefreshCw, { className: "h-4 w-4" }) })] })) : (jsxs(Fragment$1, { children: [jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300 rounded-full", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), t('localLlmLocalLlmTab.starting')] }), jsx(Button$1, { onClick: onCheckStatus, variant: "ghost", size: "sm", "aria-label": t('localLlmLocalLlmTab.refreshManagerStatusAriaLabel'), children: jsx(RefreshCw, { className: "h-4 w-4" }) })] })) })] }), isManagerInstalling && (jsx("div", { className: "mt-3 w-full max-w-[240px]", children: jsx(Progress, { value: state.managerInstallProgress || 0, className: "h-1.5" }) }))] }))] })) : (jsx("div", { className: "rounded-lg border px-6 py-4 bg-green-50/30 dark:bg-green-950/10", style: { borderColor: 'oklch(.922 0 0)' }, children: jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "flex items-center justify-between", children: jsxs("div", { className: "flex items-center gap-3", children: [jsx(CircleCheckBig, { className: "h-8 w-8 text-green-500 flex-shrink-0" }), jsxs("div", { className: "flex-1", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("h3", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: t('localLlmLocalLlmTab.offlineAiReady') }), jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 rounded-full", children: [jsx(Star$1, { className: "h-3 w-3 fill-blue-700 dark:fill-blue-300" }), t('localLlmLocalLlmTab.preferredBadge')] })] }), jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: t('localLlmLocalLlmTab.foundryReadyDescription') })] })] }) }), foundryModels.length > 0 && (jsxs("div", { className: "space-y-2", children: [jsx("label", { className: "text-xs font-medium text-gray-700 dark:text-gray-300", children: t('localLlmLocalLlmTab.selectModelLabel') }), jsxs(Select$1, { value: selectedFoundryModel || ((_b = foundryModels[0]) === null || _b === void 0 ? void 0 : _b.id), onValueChange: (value) => onSelectFoundryModel === null || onSelectFoundryModel === void 0 ? void 0 : onSelectFoundryModel(value), children: [jsx(SelectTrigger, { className: "w-full bg-gray-50 border-gray-200 dark:bg-gray-800 dark:border-gray-700", children: jsx(SelectValue, { placeholder: t('localLlmLocalLlmTab.selectModelPlaceholder') }) }), jsx(SelectContent, { children: foundryModels.map((model) => (jsx(SelectItem, { value: model.id, children: jsxs("div", { className: "flex items-center justify-between w-full gap-4", children: [jsx("span", { className: "font-medium", children: model.name || model.id }), jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500", children: [model.device && (jsx("span", { className: "px-2 py-0.5 bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300 rounded", children: model.device })), model.size && jsx("span", { children: model.size }), !model.is_downloaded && (jsx("span", { className: "px-2 py-0.5 bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300 rounded", children: t('localLlmLocalLlmTab.notDownloaded') })), model.is_downloaded && (jsx("span", { className: "px-2 py-0.5 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300 rounded", children: t('localLlmLocalLlmTab.downloaded') }))] })] }) }, model.id))) })] }), jsxs("div", { className: "flex flex-col gap-1", children: [jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t('localLlmLocalLlmTab.modelsAvailable', { count: foundryModels.length }) }), selectedFoundryModel && (jsxs("div", { className: "flex items-center gap-1.5 text-xs text-green-600 dark:text-green-400", children: [jsx(CircleCheckBig, { className: "h-3.5 w-3.5" }), jsxs("span", { children: [t('localLlmLocalLlmTab.usingModelPrefix'), ' ', ((_c = foundryModels.find((m) => m.id === selectedFoundryModel)) === null || _c === void 0 ? void 0 : _c.name) ||
127746
+ selectedFoundryModel, ((_d = foundryModels.find((m) => m.id === selectedFoundryModel)) === null || _d === void 0 ? void 0 : _d.device) && (jsxs("span", { className: "ml-1 text-gray-500 dark:text-gray-400", children: ["(", (_e = foundryModels.find((m) => m.id === selectedFoundryModel)) === null || _e === void 0 ? void 0 : _e.device, ")"] }))] })] }))] })] })), state.status === 'downloading' && (jsxs("div", { className: "mt-4 p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("div", { className: "animate-spin h-5 w-5 border-2 border-blue-600 dark:border-blue-400 border-t-transparent rounded-full" }), jsxs("div", { className: "flex-1", children: [jsx("p", { className: "text-sm font-medium text-blue-900 dark:text-blue-100", children: state.message }), state.progress > 0 && (jsx("div", { className: "mt-2 w-full bg-blue-200 dark:bg-blue-800 rounded-full h-2", children: jsx("div", { className: "bg-blue-600 dark:bg-blue-400 h-2 rounded-full transition-all duration-300", style: { width: `${state.progress}%` } }) }))] })] }), state.logs.length > 0 && (jsx("div", { className: "mt-3 max-h-32 overflow-y-auto bg-white dark:bg-gray-800 rounded border border-blue-200 dark:border-blue-700 p-2", children: state.logs.slice(-10).map((log, idx) => (jsx("div", { className: `text-xs font-mono ${log.level === 'error'
127747
+ ? 'text-red-600 dark:text-red-400'
127748
+ : log.level === 'warn'
127749
+ ? 'text-yellow-600 dark:text-yellow-400'
127750
+ : 'text-gray-700 dark:text-gray-300'}`, children: log.message }, idx))) }))] }))] }) }))] }) }));
128284
127751
  }
128285
127752
 
128286
127753
  function LocalModelsContent(props) {
@@ -128291,195 +127758,6 @@ function LocalModelsContent(props) {
128291
127758
  return (jsxs("div", { className: "rounded-lg px-6 py-5 border border-gray-200 dark:border-gray-700", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { className: "text-sm font-medium text-[#646464]", children: "Local Models" }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": "More info about Local Models", className: "hidden sm:block", children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium max-w-xs text-white shadow-sm transition-opacity duration-300 z-50", children: jsx("p", { children: "Manage on-device AI models for offline use." }) })] }) })] }), jsx("button", { type: "button", onClick: () => setIsCollapsed((previous) => !previous), className: "p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1", "aria-label": isCollapsed ? 'Expand Local Models' : 'Collapse Local Models', children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: jsx(LocalLLMTab, { ...props }) }))] }));
128292
127759
  }
128293
127760
 
128294
- const SYSTEM_CONTROL_ENABLED_KEY = 'ibl_system_control_enabled';
128295
- /**
128296
- * Whether the Computer Assistant is enabled, read from localStorage. This is a
128297
- * user preference (mirrors {@link isLocalLLMEnabled}); it reflects the toggle the
128298
- * user last chose, independent of whether the helper is installed yet.
128299
- */
128300
- function isSystemControlEnabled() {
128301
- if (typeof window === 'undefined')
128302
- return false;
128303
- return localStorage.getItem(SYSTEM_CONTROL_ENABLED_KEY) === 'true';
128304
- }
128305
- /**
128306
- * Persist the Computer Assistant enabled preference to localStorage.
128307
- */
128308
- function setSystemControlEnabled(enabled) {
128309
- if (typeof window === 'undefined')
128310
- return;
128311
- localStorage.setItem(SYSTEM_CONTROL_ENABLED_KEY, String(enabled));
128312
- }
128313
- /** Green "done" pill. */
128314
- function DonePill({ children }) {
128315
- return (jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900/30 dark:text-green-300", children: [jsx(Check, { className: "h-3 w-3" }), children] }));
128316
- }
128317
- /** Blue "in progress" pill. */
128318
- function WorkingPill({ children }) {
128319
- return (jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-800 dark:bg-blue-900/30 dark:text-blue-300", children: [jsx(LoaderCircle, { className: "h-3 w-3 animate-spin" }), children] }));
128320
- }
128321
- /** A single numbered step row. */
128322
- function Step$2({ number, done, title, description, action, }) {
128323
- return (jsxs("div", { className: "flex items-start justify-between gap-3", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx("span", { className: `flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full text-xs font-semibold ${done
128324
- ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300'
128325
- : 'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400'}`, "aria-hidden": "true", children: done ? jsx(Check, { className: "h-3.5 w-3.5" }) : number }), jsxs("div", { children: [jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: title }), jsx("div", { className: "mt-0.5 text-xs text-gray-500 dark:text-gray-400", children: description })] })] }), jsx("div", { className: "flex-shrink-0", children: action })] }));
128326
- }
128327
- /**
128328
- * Body of the "Computer Assistant" card: a plain-language on/off with two simple
128329
- * steps — set up, then allow control — so non-technical users can follow along.
128330
- * Mirrors the Local Models tab so the two cards behave the same.
128331
- */
128332
- function SystemControlTab({ isAvailable, state, status, requiredSizeGb = DEFAULT_SYSTEM_CONTROL_REQUIRED_SIZE_GB, ollamaStatus, systemMemory, accessibilityPermission, onInstall, onStop, onCheckStatus, onResetState, onRequestAccessibilityPermission, onDownloadModel, }) {
128333
- const isInstalling = state.status === 'installing' || (status === null || status === void 0 ? void 0 : status.installing) === true;
128334
- // User preference — independent of whether the helper is installed. Always
128335
- // operable so the user can turn it off again.
128336
- const [isEnabled, setIsEnabled] = useState(() => isSystemControlEnabled());
128337
- // The currently-selected local model (persisted by the Local Models card).
128338
- // Polled so a selection change in the sibling card is reflected here without a
128339
- // remount. The assistant only works well with larger models, so it's gated to
128340
- // a selected model above the size threshold.
128341
- const [selectedModel, setSelectedModel] = useState(() => getLocalLLMModel());
128342
- // Whether "Local Models" is turned on. The assistant runs on a local model, so
128343
- // it's gated off until Local Models is enabled. Polled alongside the model so a
128344
- // toggle in the sibling card is reflected without a remount.
128345
- const [localModelsEnabled, setLocalModelsEnabled] = useState(() => isLocalLLMEnabled());
128346
- useEffect(() => {
128347
- const id = setInterval(() => {
128348
- setSelectedModel(getLocalLLMModel());
128349
- setLocalModelsEnabled(isLocalLLMEnabled());
128350
- }, 1500);
128351
- return () => clearInterval(id);
128352
- }, []);
128353
- const localModelsOff = !localModelsEnabled;
128354
- const modelTooSmall = !modelSupportsSystemControl(selectedModel, requiredSizeGb);
128355
- // "Upgrade" target: the smallest catalog model that clears the size gate.
128356
- const upgradeTarget = smallestSystemControlModel(requiredSizeGb);
128357
- // Set while an upgrade is downloading a model, before we auto-enable.
128358
- const [pendingEnableModelId, setPendingEnableModelId] = useState(null);
128359
- // Model awaiting a RAM/VRAM "may be too large" confirmation before download.
128360
- const [pendingCapacityModel, setPendingCapacityModel] = useState(null);
128361
- const isUpgrading = pendingEnableModelId !== null;
128362
- const managerRunning = (status === null || status === void 0 ? void 0 : status.running) === true;
128363
- // Steps are shown whenever the toggle is on (or mid-setup), so the user always
128364
- // gets feedback for their choice.
128365
- const managerEnabled = isEnabled || isInstalling;
128366
- // Permission is known (macOS + plugin wired) and granted.
128367
- const permissionKnown = typeof accessibilityPermission === 'boolean';
128368
- const permissionGranted = accessibilityPermission === true;
128369
- const allSet = managerRunning && (!permissionKnown || permissionGranted);
128370
- // Keep the app-facing preference flag in sync with the toggle.
128371
- useEffect(() => {
128372
- setSystemControlEnabled(isEnabled);
128373
- }, [isEnabled]);
128374
- // While enabled but not yet running, keep polling so the step flips to "Ready"
128375
- // as soon as the host reports the helper is up.
128376
- useEffect(() => {
128377
- if (!isEnabled || managerRunning)
128378
- return;
128379
- const id = setInterval(() => onCheckStatus(), 2500);
128380
- return () => clearInterval(id);
128381
- }, [isEnabled, managerRunning, onCheckStatus]);
128382
- // While permission isn't granted yet, keep re-checking (the grant happens in
128383
- // System Settings) so the step flips to "Allowed" on its own.
128384
- useEffect(() => {
128385
- if (accessibilityPermission !== false)
128386
- return;
128387
- const id = setInterval(() => onCheckStatus(), 2500);
128388
- return () => clearInterval(id);
128389
- }, [accessibilityPermission, onCheckStatus]);
128390
- // After an upgrade kicks off a download, turn the assistant on once the model
128391
- // finishes downloading (it shows up in the installed list).
128392
- useEffect(() => {
128393
- if (!pendingEnableModelId)
128394
- return;
128395
- if (isModelInstalled(pendingEnableModelId, ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.installed_models)) {
128396
- setPendingEnableModelId(null);
128397
- setIsEnabled(true);
128398
- onInstall();
128399
- }
128400
- }, [pendingEnableModelId, ollamaStatus, onInstall]);
128401
- if (!isAvailable) {
128402
- return (jsx("div", { className: "text-sm text-gray-500 dark:text-gray-400", children: "The Computer Assistant is only available in the desktop app." }));
128403
- }
128404
- const handleToggle = (checked) => {
128405
- // Guard: needs Local Models on and a large-enough model.
128406
- if (checked && (localModelsOff || modelTooSmall))
128407
- return;
128408
- setIsEnabled(checked);
128409
- if (checked) {
128410
- if (!isInstalling)
128411
- onInstall();
128412
- }
128413
- else {
128414
- onStop === null || onStop === void 0 ? void 0 : onStop();
128415
- }
128416
- };
128417
- // Upgrade = switch to the smallest usable model: select it, then (if it isn't
128418
- // already downloaded) download it and auto-enable the assistant once it's
128419
- // ready. The RAM/VRAM check is preserved via the confirm below.
128420
- const runUpgrade = (model) => {
128421
- setLocalLLMModel(model.id);
128422
- setLocalLLMToolSupport(model.tool_support);
128423
- setLocalLLMEnabled(true);
128424
- setSelectedModel(model.id); // recompute the size gate immediately
128425
- if (isModelInstalled(model.id, ollamaStatus === null || ollamaStatus === void 0 ? void 0 : ollamaStatus.installed_models)) {
128426
- // Already downloaded → enable now.
128427
- setIsEnabled(true);
128428
- if (!isInstalling)
128429
- onInstall();
128430
- }
128431
- else {
128432
- // Not downloaded → download, then auto-enable when it lands (effect above).
128433
- setPendingEnableModelId(model.id);
128434
- onDownloadModel === null || onDownloadModel === void 0 ? void 0 : onDownloadModel(model.id);
128435
- }
128436
- };
128437
- const handleUpgrade = () => {
128438
- if (!upgradeTarget)
128439
- return;
128440
- // Keep the RAM/VRAM check: confirm before pulling a model that may not fit.
128441
- if (modelExceedsCapacity(upgradeTarget, systemMemory)) {
128442
- setPendingCapacityModel(upgradeTarget);
128443
- return;
128444
- }
128445
- runUpgrade(upgradeTarget);
128446
- };
128447
- const switchChecked = isEnabled;
128448
- const switchDisabled = isInstalling ||
128449
- state.status === 'checking' ||
128450
- ((localModelsOff || modelTooSmall) && !isEnabled);
128451
- return (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-4 rounded-lg border px-6 py-4", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { children: [jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Let your assistant control this computer" }), jsx("div", { className: "mt-0.5 text-xs text-gray-500 dark:text-gray-400", children: "It can click, type, and open apps to do tasks for you. You stay in control and can turn this off anytime." })] }), jsxs("div", { className: "flex flex-shrink-0 items-center gap-2", children: [isInstalling && (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin text-blue-500", "aria-label": "Setting up" })), jsx(Switch, { checked: switchChecked, onCheckedChange: handleToggle, disabled: switchDisabled, "aria-label": "Let your assistant control this computer", className: "cursor-pointer data-[state=checked]:bg-blue-500" })] })] }), localModelsOff && (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 dark:border-amber-900/40 dark:bg-amber-900/10", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-amber-600", "aria-hidden": "true" }), jsx("p", { className: "text-xs text-amber-800 dark:text-amber-300", children: "Turn on \u201CLocal Models\u201D above first \u2014 the Computer Assistant uses a local AI model to do its work." })] })), !localModelsOff && modelTooSmall && (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 dark:border-amber-900/40 dark:bg-amber-900/10", children: [jsx(Info$3, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-amber-600", "aria-hidden": "true" }), jsxs("div", { className: "flex-1", children: [jsxs("p", { className: "text-xs text-amber-800 dark:text-amber-300", children: ["Your current AI model is too small for this.", ' ', upgradeTarget && onDownloadModel
128452
- ? `Upgrade to ${upgradeTarget.name} (${upgradeTarget.size}) to use it.`
128453
- : 'Pick a larger one under “Local Models” above, then come back.'] }), upgradeTarget && onDownloadModel && (jsx(Button$1, { onClick: handleUpgrade, size: "sm", className: "mt-2", disabled: isUpgrading, "aria-label": `Upgrade to ${upgradeTarget.name}`, children: isUpgrading ? (jsxs(Fragment$1, { children: [jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Upgrading\u2026"] })) : (jsxs(Fragment$1, { children: [jsx(CircleArrowUp, { className: "mr-2 h-4 w-4" }), "Upgrade to ", upgradeTarget.name] })) }))] })] })), isUpgrading && !modelTooSmall && (jsxs("div", { className: "flex items-start gap-2 rounded-lg border px-4 py-3", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsx(LoaderCircle, { className: "mt-0.5 h-4 w-4 flex-shrink-0 animate-spin text-blue-500", "aria-hidden": "true" }), jsx("p", { className: "text-xs text-gray-600 dark:text-gray-300", children: "Downloading your new model\u2026 it\u2019ll turn on automatically when it\u2019s ready. You can watch progress under \u201CLocal Models\u201D above." })] })), managerEnabled && (jsxs("div", { className: "space-y-4 rounded-lg border px-6 py-5", style: { borderColor: 'oklch(.922 0 0)' }, children: [allSet ? (jsxs("div", { className: "flex items-start gap-2", children: [jsx(Check, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-green-600", "aria-hidden": "true" }), jsx("p", { className: "text-sm text-gray-700 dark:text-gray-300", children: "You\u2019re all set \u2014 your assistant can now help out on this computer." })] })) : (jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-gray-400", children: "A couple of quick steps" })), jsx(Step$2, { number: 1, done: managerRunning, title: "Set up your assistant", description: "A quick, one-time setup so it can work on this computer.", action: managerRunning ? (jsx(DonePill, { children: "Ready" })) : (jsx(WorkingPill, { children: isInstalling ? 'Setting up…' : 'Starting…' })) }), (state.status === 'installing' || state.status === 'checking') && state.progress > 0 && (jsx(Progress, { value: state.progress, className: "h-1.5" })), permissionKnown && (jsx(Step$2, { number: 2, done: permissionGranted, title: "Allow it to control the computer", description: "Your Mac will ask you to confirm. This lets your assistant move the mouse and type for you.", action: permissionGranted ? (jsx(DonePill, { children: "Allowed" })) : onRequestAccessibilityPermission ? (jsx(Button$1, { onClick: onRequestAccessibilityPermission, size: "sm", "aria-label": "Allow your assistant to control the computer", children: "Allow" })) : (jsx("span", { className: "inline-flex items-center rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800 dark:bg-amber-900/30 dark:text-amber-300", children: "Needed" })) }))] })), state.status === 'error' && (jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 dark:border-red-900/40 dark:bg-red-900/10", children: [jsxs("div", { className: "flex items-center gap-2 text-sm text-red-700 dark:text-red-300", children: [jsx(CircleX, { className: "h-4 w-4 flex-shrink-0" }), jsx("span", { children: "Something went wrong while setting up. Please try again." })] }), jsxs(Button$1, { onClick: () => {
128454
- onResetState();
128455
- onInstall();
128456
- }, size: "sm", variant: "outline", className: "mt-3", children: [jsx(RefreshCw, { className: "mr-2 h-4 w-4" }), "Try again"] })] })), jsx(AlertDialog, { open: pendingCapacityModel !== null, onOpenChange: (open) => {
128457
- if (!open)
128458
- setPendingCapacityModel(null);
128459
- }, children: jsxs(AlertDialogContent, { children: [jsxs(AlertDialogHeader, { children: [jsx(AlertDialogTitle, { children: "This model may be large for your computer" }), jsxs(AlertDialogDescription, { children: [pendingCapacityModel === null || pendingCapacityModel === void 0 ? void 0 : pendingCapacityModel.name, " (", pendingCapacityModel === null || pendingCapacityModel === void 0 ? void 0 : pendingCapacityModel.size, ") may be more than your computer\u2019s memory can handle, so it could run slowly or fail to load. Download and use it anyway?"] })] }), jsxs(AlertDialogFooter, { children: [jsx(AlertDialogCancel, { children: "Cancel" }), jsx(AlertDialogAction, { onClick: () => {
128460
- const model = pendingCapacityModel;
128461
- setPendingCapacityModel(null);
128462
- if (model)
128463
- runUpgrade(model);
128464
- }, children: "Download anyway" })] })] }) })] }));
128465
- }
128466
-
128467
- /**
128468
- * Collapsible "System Control" card for the UserProfile → Advanced section.
128469
- * Mirrors {@link LocalModelsContent}: a purely presentational, titled,
128470
- * collapsible container whose body is the {@link SystemControlTab}. Availability
128471
- * and handlers are threaded in (see UserProfileDropdown, which falls back to
128472
- * {@link useGhostOs} when the host doesn't supply them). Renders nothing outside
128473
- * the desktop app.
128474
- */
128475
- function SystemControlContent(props) {
128476
- const [isCollapsed, setIsCollapsed] = useState(true);
128477
- if (!props.isAvailable) {
128478
- return null;
128479
- }
128480
- return (jsxs("div", { className: "rounded-lg px-6 py-5 border border-gray-200 dark:border-gray-700", style: { borderColor: 'oklch(.922 0 0)' }, children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("span", { className: "text-sm font-medium text-[#646464]", children: "Computer Assistant" }), jsx(TooltipProvider, { children: jsxs(Tooltip$1, { children: [jsx(TooltipTrigger, { "aria-label": "More info about the Computer Assistant", className: "hidden sm:block", children: jsx(Info$3, { className: "h-4 w-4 text-gray-400" }) }), jsx(TooltipContent, { className: "rounded-lg bg-gray-700 px-3 py-2 text-sm font-medium max-w-xs text-white shadow-sm transition-opacity duration-300 z-50", children: jsx("p", { children: "Let your assistant do things on your computer \u2014 click, type, and open apps. You stay in control and can turn it off anytime." }) })] }) })] }), jsx("button", { type: "button", onClick: () => setIsCollapsed((previous) => !previous), className: "p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1", "aria-label": isCollapsed ? 'Expand Computer Assistant' : 'Collapse Computer Assistant', children: isCollapsed ? (jsx(ChevronDown, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) : (jsx(ChevronUp, { className: "h-5 w-5 text-gray-400", "aria-hidden": "true" })) })] }), !isCollapsed && (jsx("div", { className: "mt-4", children: jsx(SystemControlTab, { ...props }) }))] }));
128481
- }
128482
-
128483
127761
  // src/primitive.tsx
128484
127762
  var NODES = [
128485
127763
  "a",
@@ -176503,6 +175781,26 @@ function ChatPrivacyTab({ org, username }) {
176503
175781
  }) })] }));
176504
175782
  }
176505
175783
 
175784
+ const COWORK_ENABLED_KEY = 'ibl_cowork_enabled';
175785
+ /**
175786
+ * Whether Cowork is enabled, read from localStorage. This is a user preference
175787
+ * (mirrors isLocalLLMEnabled); it reflects the toggle the user last chose,
175788
+ * independent of whether the assistant is installed yet.
175789
+ */
175790
+ function isCoworkEnabled() {
175791
+ if (typeof window === 'undefined')
175792
+ return false;
175793
+ return localStorage.getItem(COWORK_ENABLED_KEY) === 'true';
175794
+ }
175795
+ /**
175796
+ * Persist the Cowork enabled preference to localStorage.
175797
+ */
175798
+ function setCoworkEnabled(enabled) {
175799
+ if (typeof window === 'undefined')
175800
+ return;
175801
+ localStorage.setItem(COWORK_ENABLED_KEY, String(enabled));
175802
+ }
175803
+
176506
175804
  const renderLucideIcon = (Icon) => function RenderedIcon(props) {
176507
175805
  return jsx(Icon, { ...props });
176508
175806
  };
@@ -176529,7 +175827,7 @@ const TAB_DESC_PREVIEW_KEYS = {
176529
175827
  memory: 'descMemoryPreview',
176530
175828
  privacy: 'descPrivacyPreview',
176531
175829
  };
176532
- function Profile({ tenant, username, tenants, onClose, customization = {}, isAdmin = false, targetTab = 'basic', onAccountDeleted, enableMemoryTab = false, localLLMProps, systemControlProps, currentSPA = '', }) {
175830
+ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdmin = false, targetTab = 'basic', onAccountDeleted, enableMemoryTab = false, localLLMProps, currentSPA = '', }) {
176533
175831
  var _a, _b, _c, _d, _e, _f, _g, _h;
176534
175832
  const t = useT();
176535
175833
  const { data: memsearchStatus, isError: isMemsearchStatusError } = useGetMemsearchStatusQuery({ org: tenant, userId: username }, { skip: !enableMemoryTab });
@@ -176606,9 +175904,8 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
176606
175904
  ]
176607
175905
  : []),
176608
175906
  ];
176609
- // Add Advanced tab only when in Tauri app (Local Models or System Control is
176610
- // provided and available)
176611
- const isAdvancedAvailable = (localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.isAvailable) === true || (systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.isAvailable) === true;
175907
+ // Add Advanced tab only when in Tauri app (Local Models is provided and available)
175908
+ const isAdvancedAvailable = (localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.isAvailable) === true;
176612
175909
  const TABS = isAdvancedAvailable
176613
175910
  ? [
176614
175911
  ...baseTabs,
@@ -176774,7 +176071,7 @@ function Profile({ tenant, username, tenants, onClose, customization = {}, isAdm
176774
176071
  var _a;
176775
176072
  return (jsxs(Fragment$1, { children: [jsxs("div", { className: "relative mb-0", children: [jsx("div", { className: "absolute inset-y-0 left-4 flex items-center pointer-events-none", "aria-hidden": "true", children: jsx("div", { className: "bg-black text-white rounded w-8 h-8 flex items-center justify-center", children: jsx("span", { className: "font-bold text-sm", children: "X" }) }) }), jsx(Input, { id: "x", className: "w-full bg-gray-50 border-gray-200 focus:ring-blue-500 focus:border-blue-500 text-base py-3 pl-16 h-10", placeholder: t('profileIndex.placeholderX'), value: field.state.value, onChange: (e) => handleSocialLinkChange(e.target.value, field.handleChange, X_URL_PREFIX), disabled: socialForm.state.isSubmitting || isReadOnly })] }), jsx("p", { className: "text-red-500 dark:text-gray-400 text-sm", role: ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? 'alert' : undefined, "aria-live": "polite", children: field.state.meta.errors })] }));
176776
176073
  },
176777
- })] })] }) })), activeTab === 'education' && (jsxs(Tabs, { value: educationSubTab, onValueChange: setEducationSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "education", children: t('profileIndex.tabEducation') }), jsx(TabsTrigger, { value: "credentials", children: t('gradebookGradebookTab.typeCredentials') }), jsx(TabsTrigger, { value: "skills", children: t('gradebookGradebookTab.typeSkills') })] }), headerAddButton] }), jsx(TabsContent, { value: "education", children: jsx(EducationTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "credentials", children: jsx(CredentialsList, { org: tenant, username: username }) }), jsx(TabsContent, { value: "skills", children: jsx(SkillsList, { org: tenant, username: username }) })] })), activeTab === 'gradebook' && jsx(GradebookTab, { org: tenant, username: username }), activeTab === 'experience' && (jsxs(Tabs, { value: experienceSubTab, onValueChange: setExperienceSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "experience", children: t('profileIndex.tabExperience') }), jsx(TabsTrigger, { value: "resume", children: t('profileIndex.tabResume') })] }), headerAddButton] }), jsx(TabsContent, { value: "experience", children: jsx(ExperienceTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "resume", children: jsx(ResumeTab, { org: tenant, username: username }) })] })), activeTab === 'purchases' && jsx(PurchasesTab, { org: tenant, username: username }), activeTab === 'memory' && jsx(MemoryTab, { org: tenant, username: username }), activeTab === 'privacy' && jsx(ChatPrivacyTab, { org: tenant, username: username }), !isReadOnly && activeTab === 'security' && (jsx(Security, { app: currentSPA || '', email: (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || '', platformKey: tenant, onAccountDeleted: onAccountDeleted })), activeTab === 'advanced' && (localLLMProps || systemControlProps) && (jsxs("div", { className: "space-y-6", children: [localLLMProps && (jsx(LocalModelsContent, { isAvailable: localLLMProps.isAvailable, state: localLLMProps.state, ollamaStatus: localLLMProps.ollamaStatus, systemMemory: localLLMProps.systemMemory, isUsingFoundry: localLLMProps.isUsingFoundry, foundryModels: localLLMProps.foundryModels, selectedFoundryModel: localLLMProps.selectedFoundryModel, foundryStatus: localLLMProps.foundryStatus, onStartDownload: localLLMProps.onStartDownload, onCancelDownload: localLLMProps.onCancelDownload, onInstallOllama: localLLMProps.onInstallOllama, onStopManager: localLLMProps.onStopManager, onInstallFoundry: localLLMProps.onInstallFoundry, onCheckStatus: localLLMProps.onCheckStatus, onResetState: localLLMProps.onResetState, onSelectFoundryModel: localLLMProps.onSelectFoundryModel })), systemControlProps && (jsx(SystemControlContent, { isAvailable: systemControlProps.isAvailable, state: systemControlProps.state, status: systemControlProps.status, requiredSizeGb: systemControlProps.requiredSizeGb, ollamaStatus: systemControlProps.ollamaStatus, systemMemory: systemControlProps.systemMemory, accessibilityPermission: systemControlProps.accessibilityPermission, onInstall: systemControlProps.onInstall, onStop: systemControlProps.onStop, onCheckStatus: systemControlProps.onCheckStatus, onResetState: systemControlProps.onResetState, onRequestAccessibilityPermission: systemControlProps.onRequestAccessibilityPermission, onDownloadModel: systemControlProps.onDownloadModel }))] }))] }), !isReadOnly && ['basic', 'social'].includes(activeTab) && (jsx("div", { className: "flex-shrink-0 border-t border-gray-200 p-6 bg-white dark:bg-gray-900", children: jsxs("div", { className: "flex justify-end gap-3", children: [jsx(Button$1, { onClick: onClose, variant: "outline", children: t('profileIndex.buttonCancel') }), jsx(Button$1, { onClick: () => {
176074
+ })] })] }) })), activeTab === 'education' && (jsxs(Tabs, { value: educationSubTab, onValueChange: setEducationSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "education", children: t('profileIndex.tabEducation') }), jsx(TabsTrigger, { value: "credentials", children: t('gradebookGradebookTab.typeCredentials') }), jsx(TabsTrigger, { value: "skills", children: t('gradebookGradebookTab.typeSkills') })] }), headerAddButton] }), jsx(TabsContent, { value: "education", children: jsx(EducationTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "credentials", children: jsx(CredentialsList, { org: tenant, username: username }) }), jsx(TabsContent, { value: "skills", children: jsx(SkillsList, { org: tenant, username: username }) })] })), activeTab === 'gradebook' && jsx(GradebookTab, { org: tenant, username: username }), activeTab === 'experience' && (jsxs(Tabs, { value: experienceSubTab, onValueChange: setExperienceSubTab, className: "space-y-4", children: [jsxs("div", { className: "flex items-center justify-between gap-3", children: [jsxs(TabsList, { children: [jsx(TabsTrigger, { value: "experience", children: t('profileIndex.tabExperience') }), jsx(TabsTrigger, { value: "resume", children: t('profileIndex.tabResume') })] }), headerAddButton] }), jsx(TabsContent, { value: "experience", children: jsx(ExperienceTab, { org: tenant, username: username }) }), jsx(TabsContent, { value: "resume", children: jsx(ResumeTab, { org: tenant, username: username }) })] })), activeTab === 'purchases' && jsx(PurchasesTab, { org: tenant, username: username }), activeTab === 'memory' && jsx(MemoryTab, { org: tenant, username: username }), activeTab === 'privacy' && jsx(ChatPrivacyTab, { org: tenant, username: username }), !isReadOnly && activeTab === 'security' && (jsx(Security, { app: currentSPA || '', email: (userMetadata === null || userMetadata === void 0 ? void 0 : userMetadata.email) || '', platformKey: tenant, onAccountDeleted: onAccountDeleted })), activeTab === 'advanced' && localLLMProps && (jsx("div", { className: "space-y-6", children: jsx(LocalModelsContent, { isAvailable: localLLMProps.isAvailable, state: localLLMProps.state, ollamaStatus: localLLMProps.ollamaStatus, systemMemory: localLLMProps.systemMemory, isUsingFoundry: localLLMProps.isUsingFoundry, foundryModels: localLLMProps.foundryModels, selectedFoundryModel: localLLMProps.selectedFoundryModel, foundryStatus: localLLMProps.foundryStatus, onStartDownload: localLLMProps.onStartDownload, onCancelDownload: localLLMProps.onCancelDownload, onInstallOllama: localLLMProps.onInstallOllama, onStopManager: localLLMProps.onStopManager, onInstallFoundry: localLLMProps.onInstallFoundry, onCheckStatus: localLLMProps.onCheckStatus, onResetState: localLLMProps.onResetState, onSelectFoundryModel: localLLMProps.onSelectFoundryModel }) }))] }), !isReadOnly && ['basic', 'social'].includes(activeTab) && (jsx("div", { className: "flex-shrink-0 border-t border-gray-200 p-6 bg-white dark:bg-gray-900", children: jsxs("div", { className: "flex justify-end gap-3", children: [jsx(Button$1, { onClick: onClose, variant: "outline", children: t('profileIndex.buttonCancel') }), jsx(Button$1, { onClick: () => {
176778
176075
  if (activeTab === 'basic') {
176779
176076
  basicForm.handleSubmit();
176780
176077
  }
@@ -178253,11 +177550,11 @@ var PopoverAnchor$1 = React.forwardRef(
178253
177550
  }
178254
177551
  );
178255
177552
  PopoverAnchor$1.displayName = ANCHOR_NAME$1;
178256
- var TRIGGER_NAME$2 = "PopoverTrigger";
177553
+ var TRIGGER_NAME$3 = "PopoverTrigger";
178257
177554
  var PopoverTrigger$1 = React.forwardRef(
178258
177555
  (props, forwardedRef) => {
178259
177556
  const { __scopePopover, ...triggerProps } = props;
178260
- const context = usePopoverContext(TRIGGER_NAME$2, __scopePopover);
177557
+ const context = usePopoverContext(TRIGGER_NAME$3, __scopePopover);
178261
177558
  const popperScope = usePopperScope$1(__scopePopover);
178262
177559
  const composedTriggerRef = useComposedRefs$1(forwardedRef, context.triggerRef);
178263
177560
  const trigger = /* @__PURE__ */ jsx(
@@ -178276,31 +177573,31 @@ var PopoverTrigger$1 = React.forwardRef(
178276
177573
  return context.hasCustomAnchor ? trigger : /* @__PURE__ */ jsx(Anchor$3, { asChild: true, ...popperScope, children: trigger });
178277
177574
  }
178278
177575
  );
178279
- PopoverTrigger$1.displayName = TRIGGER_NAME$2;
178280
- var PORTAL_NAME$1 = "PopoverPortal";
178281
- var [PortalProvider$1, usePortalContext$1] = createPopoverContext(PORTAL_NAME$1, {
177576
+ PopoverTrigger$1.displayName = TRIGGER_NAME$3;
177577
+ var PORTAL_NAME$2 = "PopoverPortal";
177578
+ var [PortalProvider$1, usePortalContext$1] = createPopoverContext(PORTAL_NAME$2, {
178282
177579
  forceMount: void 0
178283
177580
  });
178284
177581
  var PopoverPortal = (props) => {
178285
177582
  const { __scopePopover, forceMount, children, container } = props;
178286
- const context = usePopoverContext(PORTAL_NAME$1, __scopePopover);
177583
+ const context = usePopoverContext(PORTAL_NAME$2, __scopePopover);
178287
177584
  return /* @__PURE__ */ jsx(PortalProvider$1, { scope: __scopePopover, forceMount, children: /* @__PURE__ */ jsx(Presence$3, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(Portal$7, { asChild: true, container, children }) }) });
178288
177585
  };
178289
- PopoverPortal.displayName = PORTAL_NAME$1;
178290
- var CONTENT_NAME$3 = "PopoverContent";
177586
+ PopoverPortal.displayName = PORTAL_NAME$2;
177587
+ var CONTENT_NAME$4 = "PopoverContent";
178291
177588
  var PopoverContent$1 = React.forwardRef(
178292
177589
  (props, forwardedRef) => {
178293
- const portalContext = usePortalContext$1(CONTENT_NAME$3, props.__scopePopover);
177590
+ const portalContext = usePortalContext$1(CONTENT_NAME$4, props.__scopePopover);
178294
177591
  const { forceMount = portalContext.forceMount, ...contentProps } = props;
178295
- const context = usePopoverContext(CONTENT_NAME$3, props.__scopePopover);
177592
+ const context = usePopoverContext(CONTENT_NAME$4, props.__scopePopover);
178296
177593
  return /* @__PURE__ */ jsx(Presence$3, { present: forceMount || context.open, children: context.modal ? /* @__PURE__ */ jsx(PopoverContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsx(PopoverContentNonModal, { ...contentProps, ref: forwardedRef }) });
178297
177594
  }
178298
177595
  );
178299
- PopoverContent$1.displayName = CONTENT_NAME$3;
177596
+ PopoverContent$1.displayName = CONTENT_NAME$4;
178300
177597
  var Slot = createSlot$1("PopoverContent.RemoveScroll");
178301
177598
  var PopoverContentModal = React.forwardRef(
178302
177599
  (props, forwardedRef) => {
178303
- const context = usePopoverContext(CONTENT_NAME$3, props.__scopePopover);
177600
+ const context = usePopoverContext(CONTENT_NAME$4, props.__scopePopover);
178304
177601
  const contentRef = React.useRef(null);
178305
177602
  const composedRefs = useComposedRefs$1(forwardedRef, contentRef);
178306
177603
  const isRightClickOutsideRef = React.useRef(false);
@@ -178340,7 +177637,7 @@ var PopoverContentModal = React.forwardRef(
178340
177637
  );
178341
177638
  var PopoverContentNonModal = React.forwardRef(
178342
177639
  (props, forwardedRef) => {
178343
- const context = usePopoverContext(CONTENT_NAME$3, props.__scopePopover);
177640
+ const context = usePopoverContext(CONTENT_NAME$4, props.__scopePopover);
178344
177641
  const hasInteractedOutsideRef = React.useRef(false);
178345
177642
  const hasPointerDownOutsideRef = React.useRef(false);
178346
177643
  return /* @__PURE__ */ jsx(
@@ -178392,7 +177689,7 @@ var PopoverContentImpl = React.forwardRef(
178392
177689
  onInteractOutside,
178393
177690
  ...contentProps
178394
177691
  } = props;
178395
- const context = usePopoverContext(CONTENT_NAME$3, __scopePopover);
177692
+ const context = usePopoverContext(CONTENT_NAME$4, __scopePopover);
178396
177693
  const popperScope = usePopperScope$1(__scopePopover);
178397
177694
  useFocusGuards();
178398
177695
  return /* @__PURE__ */ jsx(
@@ -178470,26 +177767,26 @@ PopoverArrow.displayName = ARROW_NAME$2;
178470
177767
  function getState$2(open) {
178471
177768
  return open ? "open" : "closed";
178472
177769
  }
178473
- var Root2$3 = Popover$1;
177770
+ var Root2$4 = Popover$1;
178474
177771
  var Anchor2 = PopoverAnchor$1;
178475
177772
  var Trigger$1 = PopoverTrigger$1;
178476
177773
  var Portal$1 = PopoverPortal;
178477
- var Content2$1 = PopoverContent$1;
177774
+ var Content2$2 = PopoverContent$1;
178478
177775
 
178479
- const Popover = Root2$3;
177776
+ const Popover = Root2$4;
178480
177777
  const PopoverTrigger = Trigger$1;
178481
177778
  const PopoverAnchor = Anchor2;
178482
177779
  const PopoverContent = React.forwardRef(({ className, align = 'center', sideOffset = 4, portalled = true, container, ...props }, ref) => {
178483
177780
  var _a;
178484
177781
  const portalContainer = useFloatingPortalContainer();
178485
177782
  const resolvedContainer = (_a = container !== null && container !== void 0 ? container : portalContainer) !== null && _a !== void 0 ? _a : undefined;
178486
- const content = (jsx(Content2$1, { ref: ref, "data-iblai-dialog-interaction-layer": true, align: align, sideOffset: sideOffset, className: cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-none', className), ...props }));
177783
+ const content = (jsx(Content2$2, { ref: ref, "data-iblai-dialog-interaction-layer": true, align: align, sideOffset: sideOffset, className: cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-none', className), ...props }));
178487
177784
  if (!portalled) {
178488
177785
  return content;
178489
177786
  }
178490
177787
  return jsx(Portal$1, { container: resolvedContainer, children: content });
178491
177788
  });
178492
- PopoverContent.displayName = Content2$1.displayName;
177789
+ PopoverContent.displayName = Content2$2.displayName;
178493
177790
 
178494
177791
  function UsersTab({ tenant, onInviteClick, currentSpa, showGradebookTab = false, }) {
178495
177792
  const t = useT();
@@ -180371,11 +179668,11 @@ var Collapsible$1 = React.forwardRef(
180371
179668
  }
180372
179669
  );
180373
179670
  Collapsible$1.displayName = COLLAPSIBLE_NAME;
180374
- var TRIGGER_NAME$1 = "CollapsibleTrigger";
179671
+ var TRIGGER_NAME$2 = "CollapsibleTrigger";
180375
179672
  var CollapsibleTrigger$1 = React.forwardRef(
180376
179673
  (props, forwardedRef) => {
180377
179674
  const { __scopeCollapsible, ...triggerProps } = props;
180378
- const context = useCollapsibleContext(TRIGGER_NAME$1, __scopeCollapsible);
179675
+ const context = useCollapsibleContext(TRIGGER_NAME$2, __scopeCollapsible);
180379
179676
  return /* @__PURE__ */ jsx(
180380
179677
  Primitive$2.button,
180381
179678
  {
@@ -180392,19 +179689,19 @@ var CollapsibleTrigger$1 = React.forwardRef(
180392
179689
  );
180393
179690
  }
180394
179691
  );
180395
- CollapsibleTrigger$1.displayName = TRIGGER_NAME$1;
180396
- var CONTENT_NAME$2 = "CollapsibleContent";
179692
+ CollapsibleTrigger$1.displayName = TRIGGER_NAME$2;
179693
+ var CONTENT_NAME$3 = "CollapsibleContent";
180397
179694
  var CollapsibleContent$1 = React.forwardRef(
180398
179695
  (props, forwardedRef) => {
180399
179696
  const { forceMount, ...contentProps } = props;
180400
- const context = useCollapsibleContext(CONTENT_NAME$2, props.__scopeCollapsible);
179697
+ const context = useCollapsibleContext(CONTENT_NAME$3, props.__scopeCollapsible);
180401
179698
  return /* @__PURE__ */ jsx(Presence$2, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
180402
179699
  }
180403
179700
  );
180404
- CollapsibleContent$1.displayName = CONTENT_NAME$2;
179701
+ CollapsibleContent$1.displayName = CONTENT_NAME$3;
180405
179702
  var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
180406
179703
  const { __scopeCollapsible, present, children, ...contentProps } = props;
180407
- const context = useCollapsibleContext(CONTENT_NAME$2, __scopeCollapsible);
179704
+ const context = useCollapsibleContext(CONTENT_NAME$3, __scopeCollapsible);
180408
179705
  const [isPresent, setIsPresent] = React.useState(present);
180409
179706
  const ref = React.useRef(null);
180410
179707
  const composedRefs = useComposedRefs$1(forwardedRef, ref);
@@ -203640,7 +202937,7 @@ var joinTextblockForward = () => ({ state, dispatch }) => {
203640
202937
  };
203641
202938
 
203642
202939
  // src/utilities/isMacOS.ts
203643
- function isMacOS() {
202940
+ function isMacOS$1() {
203644
202941
  return typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
203645
202942
  }
203646
202943
 
@@ -203666,7 +202963,7 @@ function normalizeKeyName(name) {
203666
202963
  } else if (/^s(hift)?$/i.test(mod)) {
203667
202964
  shift = true;
203668
202965
  } else if (/^mod$/i.test(mod)) {
203669
- if (isiOS() || isMacOS()) {
202966
+ if (isiOS() || isMacOS$1()) {
203670
202967
  meta = true;
203671
202968
  } else {
203672
202969
  ctrl = true;
@@ -206643,7 +205940,7 @@ var Keymap = Extension.create({
206643
205940
  "Ctrl-a": () => this.editor.commands.selectTextblockStart(),
206644
205941
  "Ctrl-e": () => this.editor.commands.selectTextblockEnd()
206645
205942
  };
206646
- if (isiOS() || isMacOS()) {
205943
+ if (isiOS() || isMacOS$1()) {
206647
205944
  return macKeymap;
206648
205945
  }
206649
205946
  return pcKeymap;
@@ -217172,17 +216469,184 @@ var RadioGroupIndicator = React.forwardRef(
217172
216469
  }
217173
216470
  );
217174
216471
  RadioGroupIndicator.displayName = INDICATOR_NAME2;
217175
- var Root2$2 = RadioGroup$1;
216472
+ var Root2$3 = RadioGroup$1;
217176
216473
  var Item2 = RadioGroupItem$1;
217177
216474
  var Indicator = RadioGroupIndicator;
217178
216475
 
217179
216476
  function RadioGroup({ className, ...props }) {
217180
- return (jsx(Root2$2, { "data-slot": "radio-group", className: cn('grid gap-3', className), ...props }));
216477
+ return (jsx(Root2$3, { "data-slot": "radio-group", className: cn('grid gap-3', className), ...props }));
217181
216478
  }
217182
216479
  function RadioGroupItem({ className, ...props }) {
217183
216480
  return (jsx(Item2, { "data-slot": "radio-group-item", className: cn('border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50', className), ...props, children: jsx(Indicator, { "data-slot": "radio-group-indicator", className: "relative flex items-center justify-center", children: jsx(Circle, { className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" }) }) }));
217184
216481
  }
217185
216482
 
216483
+ var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
216484
+ // @__NO_SIDE_EFFECTS__
216485
+ function createSlottable(ownerName) {
216486
+ const Slottable2 = ({ children }) => {
216487
+ return /* @__PURE__ */ jsx(Fragment$1, { children });
216488
+ };
216489
+ Slottable2.displayName = `${ownerName}.Slottable`;
216490
+ Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
216491
+ return Slottable2;
216492
+ }
216493
+
216494
+ var ROOT_NAME = "AlertDialog";
216495
+ var [createAlertDialogContext] = createContextScope$1(ROOT_NAME, [
216496
+ createDialogScope
216497
+ ]);
216498
+ var useDialogScope = createDialogScope();
216499
+ var AlertDialog$1 = (props) => {
216500
+ const { __scopeAlertDialog, ...alertDialogProps } = props;
216501
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216502
+ return /* @__PURE__ */ jsx(DialogPrimitive.Root, { ...dialogScope, ...alertDialogProps, modal: true });
216503
+ };
216504
+ AlertDialog$1.displayName = ROOT_NAME;
216505
+ var TRIGGER_NAME$1 = "AlertDialogTrigger";
216506
+ var AlertDialogTrigger = React.forwardRef(
216507
+ (props, forwardedRef) => {
216508
+ const { __scopeAlertDialog, ...triggerProps } = props;
216509
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216510
+ return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { ...dialogScope, ...triggerProps, ref: forwardedRef });
216511
+ }
216512
+ );
216513
+ AlertDialogTrigger.displayName = TRIGGER_NAME$1;
216514
+ var PORTAL_NAME$1 = "AlertDialogPortal";
216515
+ var AlertDialogPortal$1 = (props) => {
216516
+ const { __scopeAlertDialog, ...portalProps } = props;
216517
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216518
+ return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { ...dialogScope, ...portalProps });
216519
+ };
216520
+ AlertDialogPortal$1.displayName = PORTAL_NAME$1;
216521
+ var OVERLAY_NAME = "AlertDialogOverlay";
216522
+ var AlertDialogOverlay$1 = React.forwardRef(
216523
+ (props, forwardedRef) => {
216524
+ const { __scopeAlertDialog, ...overlayProps } = props;
216525
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216526
+ return /* @__PURE__ */ jsx(DialogPrimitive.Overlay, { ...dialogScope, ...overlayProps, ref: forwardedRef });
216527
+ }
216528
+ );
216529
+ AlertDialogOverlay$1.displayName = OVERLAY_NAME;
216530
+ var CONTENT_NAME$2 = "AlertDialogContent";
216531
+ var [AlertDialogContentProvider, useAlertDialogContentContext] = createAlertDialogContext(CONTENT_NAME$2);
216532
+ var Slottable = createSlottable("AlertDialogContent");
216533
+ var AlertDialogContent$1 = React.forwardRef(
216534
+ (props, forwardedRef) => {
216535
+ const { __scopeAlertDialog, children, ...contentProps } = props;
216536
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216537
+ const contentRef = React.useRef(null);
216538
+ const composedRefs = useComposedRefs$1(forwardedRef, contentRef);
216539
+ const cancelRef = React.useRef(null);
216540
+ return /* @__PURE__ */ jsx(
216541
+ DialogPrimitive.WarningProvider,
216542
+ {
216543
+ contentName: CONTENT_NAME$2,
216544
+ titleName: TITLE_NAME,
216545
+ docsSlug: "alert-dialog",
216546
+ children: /* @__PURE__ */ jsx(AlertDialogContentProvider, { scope: __scopeAlertDialog, cancelRef, children: /* @__PURE__ */ jsxs(
216547
+ DialogPrimitive.Content,
216548
+ {
216549
+ role: "alertdialog",
216550
+ ...dialogScope,
216551
+ ...contentProps,
216552
+ ref: composedRefs,
216553
+ onOpenAutoFocus: composeEventHandlers$2(contentProps.onOpenAutoFocus, (event) => {
216554
+ event.preventDefault();
216555
+ cancelRef.current?.focus({ preventScroll: true });
216556
+ }),
216557
+ onPointerDownOutside: (event) => event.preventDefault(),
216558
+ onInteractOutside: (event) => event.preventDefault(),
216559
+ children: [
216560
+ /* @__PURE__ */ jsx(Slottable, { children }),
216561
+ /* @__PURE__ */ jsx(DescriptionWarning, { contentRef })
216562
+ ]
216563
+ }
216564
+ ) })
216565
+ }
216566
+ );
216567
+ }
216568
+ );
216569
+ AlertDialogContent$1.displayName = CONTENT_NAME$2;
216570
+ var TITLE_NAME = "AlertDialogTitle";
216571
+ var AlertDialogTitle$1 = React.forwardRef(
216572
+ (props, forwardedRef) => {
216573
+ const { __scopeAlertDialog, ...titleProps } = props;
216574
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216575
+ return /* @__PURE__ */ jsx(DialogPrimitive.Title, { ...dialogScope, ...titleProps, ref: forwardedRef });
216576
+ }
216577
+ );
216578
+ AlertDialogTitle$1.displayName = TITLE_NAME;
216579
+ var DESCRIPTION_NAME = "AlertDialogDescription";
216580
+ var AlertDialogDescription$1 = React.forwardRef((props, forwardedRef) => {
216581
+ const { __scopeAlertDialog, ...descriptionProps } = props;
216582
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216583
+ return /* @__PURE__ */ jsx(DialogPrimitive.Description, { ...dialogScope, ...descriptionProps, ref: forwardedRef });
216584
+ });
216585
+ AlertDialogDescription$1.displayName = DESCRIPTION_NAME;
216586
+ var ACTION_NAME = "AlertDialogAction";
216587
+ var AlertDialogAction$1 = React.forwardRef(
216588
+ (props, forwardedRef) => {
216589
+ const { __scopeAlertDialog, ...actionProps } = props;
216590
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216591
+ return /* @__PURE__ */ jsx(DialogPrimitive.Close, { ...dialogScope, ...actionProps, ref: forwardedRef });
216592
+ }
216593
+ );
216594
+ AlertDialogAction$1.displayName = ACTION_NAME;
216595
+ var CANCEL_NAME = "AlertDialogCancel";
216596
+ var AlertDialogCancel$1 = React.forwardRef(
216597
+ (props, forwardedRef) => {
216598
+ const { __scopeAlertDialog, ...cancelProps } = props;
216599
+ const { cancelRef } = useAlertDialogContentContext(CANCEL_NAME, __scopeAlertDialog);
216600
+ const dialogScope = useDialogScope(__scopeAlertDialog);
216601
+ const ref = useComposedRefs$1(forwardedRef, cancelRef);
216602
+ return /* @__PURE__ */ jsx(DialogPrimitive.Close, { ...dialogScope, ...cancelProps, ref });
216603
+ }
216604
+ );
216605
+ AlertDialogCancel$1.displayName = CANCEL_NAME;
216606
+ var DescriptionWarning = ({ contentRef }) => {
216607
+ const MESSAGE = `\`${CONTENT_NAME$2}\` requires a description for the component to be accessible for screen reader users.
216608
+
216609
+ You can add a description to the \`${CONTENT_NAME$2}\` by passing a \`${DESCRIPTION_NAME}\` component as a child, which also benefits sighted users by adding visible context to the dialog.
216610
+
216611
+ Alternatively, you can use your own component as a description by assigning it an \`id\` and passing the same value to the \`aria-describedby\` prop in \`${CONTENT_NAME$2}\`. If the description is confusing or duplicative for sighted users, you can use the \`@radix-ui/react-visually-hidden\` primitive as a wrapper around your description component.
216612
+
216613
+ For more information, see https://radix-ui.com/primitives/docs/components/alert-dialog`;
216614
+ React.useEffect(() => {
216615
+ const hasDescription = document.getElementById(
216616
+ contentRef.current?.getAttribute("aria-describedby")
216617
+ );
216618
+ if (!hasDescription) console.warn(MESSAGE);
216619
+ }, [MESSAGE, contentRef]);
216620
+ return null;
216621
+ };
216622
+ var Root2$2 = AlertDialog$1;
216623
+ var Portal2 = AlertDialogPortal$1;
216624
+ var Overlay2 = AlertDialogOverlay$1;
216625
+ var Content2$1 = AlertDialogContent$1;
216626
+ var Action = AlertDialogAction$1;
216627
+ var Cancel = AlertDialogCancel$1;
216628
+ var Title2 = AlertDialogTitle$1;
216629
+ var Description2 = AlertDialogDescription$1;
216630
+
216631
+ const AlertDialog = Root2$2;
216632
+ const AlertDialogPortal = Portal2;
216633
+ const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (jsx(Overlay2, { className: cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', className), ...props, ref: ref })));
216634
+ AlertDialogOverlay.displayName = Overlay2.displayName;
216635
+ const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (jsxs(AlertDialogPortal, { children: [jsx(AlertDialogOverlay, {}), jsx(Content2$1, { ref: ref, className: cn('bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg', className), ...props })] })));
216636
+ AlertDialogContent.displayName = Content2$1.displayName;
216637
+ const AlertDialogHeader = ({ className, ...props }) => (jsx("div", { className: cn('flex flex-col space-y-2 text-center sm:text-left', className), ...props }));
216638
+ AlertDialogHeader.displayName = 'AlertDialogHeader';
216639
+ const AlertDialogFooter = ({ className, ...props }) => (jsx("div", { className: cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className), ...props }));
216640
+ AlertDialogFooter.displayName = 'AlertDialogFooter';
216641
+ const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (jsx(Title2, { ref: ref, className: cn('text-lg font-semibold', className), ...props })));
216642
+ AlertDialogTitle.displayName = Title2.displayName;
216643
+ const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (jsx(Description2, { ref: ref, className: cn('text-muted-foreground text-sm', className), ...props })));
216644
+ AlertDialogDescription.displayName = Description2.displayName;
216645
+ const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (jsx(Action, { ref: ref, className: cn(buttonVariants(), className), ...props })));
216646
+ AlertDialogAction.displayName = Action.displayName;
216647
+ const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (jsx(Cancel, { ref: ref, className: cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className), ...props })));
216648
+ AlertDialogCancel.displayName = Cancel.displayName;
216649
+
217186
216650
  const THEMES = { light: '', dark: '.dark' };
217187
216651
  const ChartContext = React.createContext(null);
217188
216652
  function useChart() {
@@ -254021,8 +253485,501 @@ function MemoryButton({ mentorId, tenantKey, username }) {
254021
253485
  } }))] }) }), jsx(PopoverContent, { className: "w-96 rounded-lg border border-gray-200 bg-white p-0 shadow-xl", children: jsx(MemoryMenu, { onClose: () => setIsOpen(false), mentorId: mentorId, tenantKey: tenantKey, username: username }) })] }));
254022
253486
  }
254023
253487
 
253488
+ // Tauri types for model download functionality
253489
+ /**
253490
+ * Initial state for model download
253491
+ */
253492
+ const initialModelDownloadState = {
253493
+ status: 'idle',
253494
+ progress: 0,
253495
+ message: '',
253496
+ logs: [],
253497
+ lastUpdated: new Date().toISOString(),
253498
+ };
253499
+ /**
253500
+ * Check if the app is running inside Tauri
253501
+ * In Tauri 2.0, the global is __TAURI_INTERNALS__ (with withGlobalTauri: true)
253502
+ * We also check for __TAURI__ for backwards compatibility
253503
+ */
253504
+ const isTauriApp$1 = () => {
253505
+ if (typeof window === 'undefined')
253506
+ return false;
253507
+ return '__TAURI_INTERNALS__' in window || '__TAURI__' in window;
253508
+ };
253509
+ /**
253510
+ * Tauri event names
253511
+ */
253512
+ const TAURI_EVENTS = {
253513
+ DOWNLOAD_PROGRESS: 'model:download-progress',
253514
+ INSTALLATION_LOG: 'model:installation-log',
253515
+ DISK_SPACE_ERROR: 'model:disk-space-error',
253516
+ OLLAMA_STATUS: 'model:ollama-status',
253517
+ };
253518
+ /**
253519
+ * Tauri command names
253520
+ */
253521
+ const TAURI_COMMANDS = {
253522
+ INSTALL_OLLAMA: 'install_ollama',
253523
+ STOP_OLLAMA: 'stop_ollama',
253524
+ CHECK_OLLAMA_STATUS: 'check_ollama_status',
253525
+ CHECK_DISK_SPACE: 'check_disk_space_for_model',
253526
+ GET_SYSTEM_MEMORY: 'get_system_memory',
253527
+ DOWNLOAD_MODEL: 'download_model',
253528
+ CANCEL_DOWNLOAD: 'cancel_model_download',
253529
+ CHECK_NETWORK_STATUS: 'check_network_status',
253530
+ GET_OS_TYPE: 'get_os_type',
253531
+ CHECK_FOUNDRY_STATUS: 'check_foundry_local_status',
253532
+ START_FOUNDRY_SERVICE: 'start_foundry_local_service',
253533
+ LOAD_FOUNDRY_MODEL: 'load_foundry_local_model',
253534
+ SET_SELECTED_FOUNDRY_MODEL: 'set_selected_foundry_model',
253535
+ GET_SELECTED_FOUNDRY_MODEL: 'get_selected_foundry_model',
253536
+ INSTALL_FOUNDRY: 'install_foundry',
253537
+ DOWNLOAD_FOUNDRY_MODEL: 'download_foundry_model_cmd',
253538
+ GET_RECOMMENDED_FOUNDRY_MODELS: 'get_recommended_foundry_models',
253539
+ LOG_STDOUT: 'log_stdout',
253540
+ };
253541
+ /**
253542
+ * Initial state for the Cowork install flow.
253543
+ */
253544
+ const initialGhostOsInstallState = {
253545
+ status: 'idle',
253546
+ progress: 0,
253547
+ message: '',
253548
+ logs: [],
253549
+ lastUpdated: new Date().toISOString(),
253550
+ };
253551
+ /**
253552
+ * Tauri event names emitted by the host during GhostOS install/setup.
253553
+ */
253554
+ const GHOST_OS_TAURI_EVENTS = {
253555
+ INSTALL_PROGRESS: 'ghost-os:install-progress',
253556
+ INSTALLATION_LOG: 'ghost-os:installation-log',
253557
+ STATUS: 'ghost-os:status',
253558
+ };
253559
+ /**
253560
+ * Tauri command names the host implements for GhostOS install/setup. The web
253561
+ * layer invokes these; the native side performs the clone + setup.
253562
+ */
253563
+ const GHOST_OS_TAURI_COMMANDS = {
253564
+ /** Install and set up GhostOS (clone repo + run setup). */
253565
+ INSTALL_GHOST_OS: 'install_ghost_os',
253566
+ /** Stop the running GhostOS manager. */
253567
+ STOP_GHOST_OS: 'stop_ghost_os',
253568
+ /** Report current {@link GhostOsStatus}. */
253569
+ CHECK_GHOST_OS_STATUS: 'check_ghost_os_status',
253570
+ };
253571
+ /**
253572
+ * Tauri command strings exposed by `tauri-plugin-macos-permissions`
253573
+ * (https://github.com/ayangweb/tauri-plugin-macos-permissions). GhostOS needs
253574
+ * macOS Accessibility permission to control the device, so the Cowork
253575
+ * card surfaces it. These are invoked directly via `useTauri().invoke(...)` —
253576
+ * no extra npm dependency. The host must register the plugin in its Tauri app
253577
+ * (`tauri_plugin_macos_permissions::init()`). Each `CHECK_*` returns a boolean;
253578
+ * each `REQUEST_*` prompts / opens the relevant System Settings pane.
253579
+ */
253580
+ const MACOS_PERMISSIONS_COMMANDS = {
253581
+ CHECK_ACCESSIBILITY: 'plugin:macos-permissions|check_accessibility_permission',
253582
+ REQUEST_ACCESSIBILITY: 'plugin:macos-permissions|request_accessibility_permission',
253583
+ CHECK_SCREEN_RECORDING: 'plugin:macos-permissions|check_screen_recording_permission',
253584
+ REQUEST_SCREEN_RECORDING: 'plugin:macos-permissions|request_screen_recording_permission',
253585
+ CHECK_INPUT_MONITORING: 'plugin:macos-permissions|check_input_monitoring_permission',
253586
+ REQUEST_INPUT_MONITORING: 'plugin:macos-permissions|request_input_monitoring_permission',
253587
+ CHECK_FULL_DISK_ACCESS: 'plugin:macos-permissions|check_full_disk_access_permission',
253588
+ REQUEST_FULL_DISK_ACCESS: 'plugin:macos-permissions|request_full_disk_access_permission',
253589
+ };
253590
+
253591
+ /**
253592
+ * Dynamically import Tauri APIs to avoid SSR issues
253593
+ */
253594
+ const getTauriAPIs = async () => {
253595
+ try {
253596
+ const { invoke } = await import('@tauri-apps/api/core');
253597
+ const { listen } = await import('@tauri-apps/api/event');
253598
+ return { invoke, listen };
253599
+ }
253600
+ catch (error) {
253601
+ console.error('Failed to load Tauri APIs:', error);
253602
+ return null;
253603
+ }
253604
+ };
253605
+ /**
253606
+ * Hook to access Tauri APIs with SSR safety
253607
+ *
253608
+ * @returns Object with isAvailable flag and Tauri invoke/listen functions
253609
+ */
253610
+ function useTauri() {
253611
+ const [isAvailable, setIsAvailable] = useState(false);
253612
+ const [apis, setApis] = useState(null);
253613
+ useEffect(() => {
253614
+ // Check if we're in a Tauri environment
253615
+ const inTauri = isTauriApp$1();
253616
+ if (inTauri) {
253617
+ getTauriAPIs().then((result) => {
253618
+ setApis(result);
253619
+ setIsAvailable(!!result);
253620
+ });
253621
+ }
253622
+ }, []);
253623
+ /**
253624
+ * Invoke a Tauri command
253625
+ */
253626
+ const invoke = useCallback(async (command, args) => {
253627
+ if (!(apis === null || apis === void 0 ? void 0 : apis.invoke)) {
253628
+ throw new Error('Tauri is not available');
253629
+ }
253630
+ return apis.invoke(command, args);
253631
+ }, [apis]);
253632
+ /**
253633
+ * Listen to a Tauri event
253634
+ * Returns an unlisten function that should be called on cleanup
253635
+ */
253636
+ const listen = useCallback(async (event, handler) => {
253637
+ // The synchronous global (`__TAURI_INTERNALS__`) exposes `invoke` but
253638
+ // frequently NOT `event.listen`, so `apis.listen` can be undefined even
253639
+ // though Tauri is fully available. Fall back to the real event API via
253640
+ // dynamic import so download progress/completion events are actually
253641
+ // delivered — otherwise the UI never leaves the "downloading" state.
253642
+ let listenFn = apis === null || apis === void 0 ? void 0 : apis.listen;
253643
+ if (!listenFn) {
253644
+ const { listen: importedListen } = await import('@tauri-apps/api/event');
253645
+ listenFn = importedListen;
253646
+ }
253647
+ return listenFn(event, (e) => handler(e.payload));
253648
+ }, [apis]);
253649
+ return {
253650
+ isAvailable,
253651
+ invoke,
253652
+ listen,
253653
+ };
253654
+ }
253655
+
253656
+ const IS_SERVER = typeof window === 'undefined';
253657
+ /**
253658
+ * A simplified useLocalStorage hook for persisting state to localStorage
253659
+ * with SSR safety.
253660
+ */
253661
+ function useLocalStorage(key, initialValue) {
253662
+ // Get the initial value
253663
+ const getInitialValue = useCallback(() => {
253664
+ const initial = initialValue instanceof Function ? initialValue() : initialValue;
253665
+ if (IS_SERVER) {
253666
+ return initial;
253667
+ }
253668
+ try {
253669
+ const item = window.localStorage.getItem(key);
253670
+ if (item === null) {
253671
+ return initial;
253672
+ }
253673
+ return JSON.parse(item);
253674
+ }
253675
+ catch (error) {
253676
+ console.warn(`Error reading localStorage key "${key}":`, error);
253677
+ return initial;
253678
+ }
253679
+ }, [key, initialValue]);
253680
+ const [storedValue, setStoredValue] = useState(getInitialValue);
253681
+ // Update stored value when key changes
253682
+ useEffect(() => {
253683
+ setStoredValue(getInitialValue());
253684
+ }, [key, getInitialValue]);
253685
+ // Persist to localStorage
253686
+ const setValue = useCallback((value) => {
253687
+ if (IS_SERVER) {
253688
+ console.warn(`Tried setting localStorage key "${key}" even though environment is not a client`);
253689
+ return;
253690
+ }
253691
+ try {
253692
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
253693
+ setStoredValue(valueToStore);
253694
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
253695
+ }
253696
+ catch (error) {
253697
+ console.warn(`Error setting localStorage key "${key}":`, error);
253698
+ }
253699
+ }, [key, storedValue]);
253700
+ // Remove from localStorage
253701
+ const removeValue = useCallback(() => {
253702
+ if (IS_SERVER) {
253703
+ console.warn(`Tried removing localStorage key "${key}" even though environment is not a client`);
253704
+ return;
253705
+ }
253706
+ try {
253707
+ const initial = initialValue instanceof Function ? initialValue() : initialValue;
253708
+ window.localStorage.removeItem(key);
253709
+ setStoredValue(initial);
253710
+ }
253711
+ catch (error) {
253712
+ console.warn(`Error removing localStorage key "${key}":`, error);
253713
+ }
253714
+ }, [key, initialValue]);
253715
+ return [storedValue, setValue, removeValue];
253716
+ }
253717
+
253718
+ const LOCAL_STORAGE_KEY$1 = 'ghost_os_install_state';
253719
+ /**
253720
+ * Manage installing & setting up GhostOS (https://github.com/ghostwright/ghost-os)
253721
+ * via the Tauri host. This is the self-contained backing hook for the System
253722
+ * Control card and deliberately mirrors `useModelDownload`:
253723
+ *
253724
+ * - checks GhostOS install/run status through the host
253725
+ * - installs/sets up GhostOS with real-time progress + logs
253726
+ * - persists state across app restarts
253727
+ *
253728
+ * Outside the desktop (Tauri) app every action is a no-op and `isAvailable` is
253729
+ * false, so the card hides itself.
253730
+ */
253731
+ function useGhostOs() {
253732
+ const { isAvailable, invoke, listen } = useTauri();
253733
+ const [state, setState] = useLocalStorage(LOCAL_STORAGE_KEY$1, initialGhostOsInstallState);
253734
+ const [status, setStatus] = useState(null);
253735
+ // macOS Accessibility permission, which GhostOS needs to control the device.
253736
+ // null = unknown/not-yet-checked or unsupported (non-macOS / plugin absent).
253737
+ const [accessibilityPermission, setAccessibilityPermission] = useState(null);
253738
+ const unlistenRefs = useRef([]);
253739
+ const hasCheckedStatus = useRef(false);
253740
+ /**
253741
+ * Refresh the macOS Accessibility permission via tauri-plugin-macos-permissions.
253742
+ * Independent of the GhostOS status check so a missing host command for one
253743
+ * doesn't suppress the other. Stays null where unsupported.
253744
+ */
253745
+ const refreshAccessibilityPermission = useCallback(async () => {
253746
+ if (!isAvailable)
253747
+ return;
253748
+ try {
253749
+ const granted = await invoke(MACOS_PERMISSIONS_COMMANDS.CHECK_ACCESSIBILITY);
253750
+ setAccessibilityPermission(granted);
253751
+ }
253752
+ catch (error) {
253753
+ // Plugin not registered or non-macOS host — leave permission unknown.
253754
+ console.warn('[useGhostOs] Accessibility permission check unavailable:', error);
253755
+ setAccessibilityPermission(null);
253756
+ }
253757
+ }, [isAvailable, invoke]);
253758
+ /**
253759
+ * Open the Accessibility pane / prompt so the user can grant permission, then
253760
+ * re-check (the grant happens in System Settings, so we refresh after).
253761
+ */
253762
+ const requestAccessibilityPermission = useCallback(async () => {
253763
+ if (!isAvailable)
253764
+ return;
253765
+ try {
253766
+ await invoke(MACOS_PERMISSIONS_COMMANDS.REQUEST_ACCESSIBILITY);
253767
+ }
253768
+ catch (error) {
253769
+ console.error('[useGhostOs] Failed to request accessibility permission:', error);
253770
+ }
253771
+ await refreshAccessibilityPermission();
253772
+ }, [isAvailable, invoke, refreshAccessibilityPermission]);
253773
+ const addLog = useCallback((log) => {
253774
+ setState((prev) => ({
253775
+ ...prev,
253776
+ logs: [...prev.logs.slice(-99), log],
253777
+ lastUpdated: new Date().toISOString(),
253778
+ }));
253779
+ }, [setState]);
253780
+ /**
253781
+ * Query GhostOS status from the host.
253782
+ */
253783
+ const checkStatus = useCallback(async () => {
253784
+ if (!isAvailable)
253785
+ return;
253786
+ // Refresh the accessibility permission alongside install status (independent).
253787
+ refreshAccessibilityPermission();
253788
+ try {
253789
+ setState((prev) => ({ ...prev, status: 'checking' }));
253790
+ const result = await invoke(GHOST_OS_TAURI_COMMANDS.CHECK_GHOST_OS_STATUS);
253791
+ setStatus(result);
253792
+ setState((prev) => ({
253793
+ ...prev,
253794
+ status: result.installed ? 'completed' : 'idle',
253795
+ progress: result.installed ? 100 : prev.progress,
253796
+ lastUpdated: new Date().toISOString(),
253797
+ }));
253798
+ }
253799
+ catch (error) {
253800
+ console.error('[useGhostOs] Failed to check status:', error);
253801
+ setState((prev) => ({ ...prev, status: 'idle' }));
253802
+ }
253803
+ }, [isAvailable, invoke, setState, refreshAccessibilityPermission]);
253804
+ /**
253805
+ * Install and set up GhostOS. The host clones the repo + runs setup and emits
253806
+ * progress/log/status events along the way.
253807
+ */
253808
+ const install = useCallback(async () => {
253809
+ if (!isAvailable)
253810
+ return;
253811
+ try {
253812
+ setState((prev) => ({
253813
+ ...prev,
253814
+ status: 'installing',
253815
+ progress: 0,
253816
+ message: 'Setting up your assistant…',
253817
+ error: undefined,
253818
+ logs: [],
253819
+ }));
253820
+ addLog({
253821
+ timestamp: new Date().toISOString(),
253822
+ level: 'info',
253823
+ message: 'Starting setup…',
253824
+ });
253825
+ const result = await invoke(GHOST_OS_TAURI_COMMANDS.INSTALL_GHOST_OS);
253826
+ addLog({
253827
+ timestamp: new Date().toISOString(),
253828
+ level: 'info',
253829
+ message: typeof result === 'string' && result ? result : 'Setup complete',
253830
+ });
253831
+ setState((prev) => ({
253832
+ ...prev,
253833
+ status: 'completed',
253834
+ progress: 100,
253835
+ message: 'Your assistant is ready',
253836
+ lastUpdated: new Date().toISOString(),
253837
+ }));
253838
+ await checkStatus();
253839
+ }
253840
+ catch (error) {
253841
+ const message = error instanceof Error ? error.message : String(error);
253842
+ setState((prev) => ({
253843
+ ...prev,
253844
+ status: 'error',
253845
+ error: message,
253846
+ message: 'Setup didn’t finish',
253847
+ lastUpdated: new Date().toISOString(),
253848
+ }));
253849
+ addLog({
253850
+ timestamp: new Date().toISOString(),
253851
+ level: 'error',
253852
+ message: `Install failed: ${message}`,
253853
+ });
253854
+ }
253855
+ }, [isAvailable, invoke, addLog, checkStatus, setState]);
253856
+ /**
253857
+ * Stop the running GhostOS manager.
253858
+ */
253859
+ const stop = useCallback(async () => {
253860
+ if (!isAvailable)
253861
+ return;
253862
+ try {
253863
+ await invoke(GHOST_OS_TAURI_COMMANDS.STOP_GHOST_OS);
253864
+ await checkStatus();
253865
+ }
253866
+ catch (error) {
253867
+ console.error('[useGhostOs] Failed to stop GhostOS:', error);
253868
+ }
253869
+ }, [isAvailable, invoke, checkStatus]);
253870
+ /**
253871
+ * Reset the install state (for retrying after an error).
253872
+ */
253873
+ const resetState = useCallback(() => {
253874
+ setState(initialGhostOsInstallState);
253875
+ }, [setState]);
253876
+ // Wire up host event listeners (progress / logs / status).
253877
+ useEffect(() => {
253878
+ if (!isAvailable)
253879
+ return;
253880
+ const setupListeners = async () => {
253881
+ try {
253882
+ const unlistenProgress = await listen(GHOST_OS_TAURI_EVENTS.INSTALL_PROGRESS, (payload) => {
253883
+ const nextStatus = payload.status === 'completed'
253884
+ ? 'completed'
253885
+ : payload.status === 'cancelled'
253886
+ ? 'cancelled'
253887
+ : payload.status === 'error'
253888
+ ? 'error'
253889
+ : 'installing';
253890
+ setState((prev) => ({
253891
+ ...prev,
253892
+ status: nextStatus,
253893
+ progress: payload.percentage,
253894
+ message: payload.message,
253895
+ lastUpdated: new Date().toISOString(),
253896
+ }));
253897
+ });
253898
+ unlistenRefs.current.push(unlistenProgress);
253899
+ const unlistenLogs = await listen(GHOST_OS_TAURI_EVENTS.INSTALLATION_LOG, addLog);
253900
+ unlistenRefs.current.push(unlistenLogs);
253901
+ const unlistenStatus = await listen(GHOST_OS_TAURI_EVENTS.STATUS, setStatus);
253902
+ unlistenRefs.current.push(unlistenStatus);
253903
+ }
253904
+ catch (error) {
253905
+ console.error('[useGhostOs] Failed to setup event listeners:', error);
253906
+ }
253907
+ };
253908
+ setupListeners();
253909
+ return () => {
253910
+ unlistenRefs.current.forEach((unlisten) => unlisten());
253911
+ unlistenRefs.current = [];
253912
+ };
253913
+ }, [isAvailable, listen, setState, addLog]);
253914
+ // Check status once on mount (in the desktop app).
253915
+ useEffect(() => {
253916
+ if (!isAvailable || hasCheckedStatus.current)
253917
+ return;
253918
+ hasCheckedStatus.current = true;
253919
+ checkStatus();
253920
+ }, [isAvailable, checkStatus]);
253921
+ // Only expose the feature as available inside the desktop (Tauri) app.
253922
+ const finalIsAvailable = isAvailable && isTauriApp$1();
253923
+ return {
253924
+ isAvailable: finalIsAvailable,
253925
+ state,
253926
+ status,
253927
+ accessibilityPermission,
253928
+ install,
253929
+ stop,
253930
+ checkStatus,
253931
+ resetState,
253932
+ requestAccessibilityPermission,
253933
+ };
253934
+ }
253935
+
253936
+ // Cowork is macOS-only in prod; the env flag bypasses the OS check so the
253937
+ // toggle can be exercised on Linux/Windows desktop builds during testing.
253938
+ const isMacOS = () => {
253939
+ if (typeof navigator === 'undefined')
253940
+ return false;
253941
+ return /mac/i.test(navigator.userAgent || '');
253942
+ };
253943
+ const allowNonMacOSCowork = () => process.env.NEXT_PUBLIC_ALLOW_NON_MACOS_COMPUTER_USE_TOGGLE === 'true';
253944
+ // 14GB floor per spec; modelSupportsCowork gates size > gb, so a model of
253945
+ // exactly 14GB is also off (no 14.0GB model in the catalog). Only the local-model
253946
+ // backend needs this; the remote AI has no size requirement.
253947
+ const COWORK_MIN_MODEL_GB = 14;
254024
253948
  const InsideButtons = ({ activeOptions, onOptionClick, deepResearch, studyMode, artifactsEnabled, containerWidth, disabled = false, onOpenPromptGallery, embedMode = false, promptsIsEnabled = false, memoryEnabled = false, mentorId, tenantKey, username, }) => {
254025
253949
  const t = useT();
253950
+ // Cowork = the Tauri GhostOS assistant. GhostOS runs the tools on-device;
253951
+ // the "brain" is EITHER a large local model (Local Models / Ollama) OR the
253952
+ // remote AI (DM OpenAI-compatible endpoint), whichever is set up. useGhostOs
253953
+ // handles install/stop; the localStorage pref (isCoworkEnabled) is read
253954
+ // by the chat hook to route the turn. Reads the pref on mount.
253955
+ // Local state is `coworkOn` so it doesn't shadow the imported setCoworkEnabled.
253956
+ const ghostOs = useGhostOs();
253957
+ const [coworkOn, setCoworkOn] = useState(isCoworkEnabled);
253958
+ const toggleCowork = () => {
253959
+ const next = !coworkOn;
253960
+ // Guard only when turning on. Cowork runs on either a large local model
253961
+ // or the remote AI — allow enabling when either backend is ready (the local
253962
+ // model + Cowork combo still works). The chatbox has no inline notice
253963
+ // space, so remind via toast. (RBAC is enforced separately: this button is
253964
+ // `disabled` when the user lacks the mentor `#chat` permission — see
253965
+ // chat-input-form's isChatDisabledByRbac.)
253966
+ if (next) {
253967
+ const localReady = isLocalLLMEnabled() &&
253968
+ modelSupportsCowork(getLocalLLMModel(), COWORK_MIN_MODEL_GB);
253969
+ if (!localReady && !hasRemoteAiConfig()) {
253970
+ toast.warning(isLocalLLMEnabled()
253971
+ ? t('chatInputFormInsideButtons.coworkModelTooSmall')
253972
+ : t('chatInputFormInsideButtons.coworkNeedsBackend'));
253973
+ return;
253974
+ }
253975
+ }
253976
+ setCoworkOn(next);
253977
+ setCoworkEnabled(next);
253978
+ if (next)
253979
+ ghostOs.install();
253980
+ else
253981
+ ghostOs.stop();
253982
+ };
254026
253983
  const allInsideButtons = [
254027
253984
  {
254028
253985
  name: 'Canvas',
@@ -254066,6 +254023,14 @@ const InsideButtons = ({ activeOptions, onOptionClick, deepResearch, studyMode,
254066
254023
  action: /* istanbul ignore next */ () => onOptionClick(TOOLS.MEMORY),
254067
254024
  isEnabled: memoryEnabled && !embedMode && !!username,
254068
254025
  },
254026
+ {
254027
+ name: 'Cowork',
254028
+ label: t('chatInputFormInsideButtons.cowork'),
254029
+ icon: jsx(Monitor, { className: "h-4 w-4" }),
254030
+ isActive: coworkOn,
254031
+ action: toggleCowork,
254032
+ isEnabled: ghostOs.isAvailable && (isMacOS() || allowNonMacOSCowork()),
254033
+ },
254069
254034
  ].filter((item) => item.isEnabled);
254070
254035
  // Get visible inside buttons based on screen size
254071
254036
  // Active buttons are always visible, inactive ones are hidden based on available width
@@ -257344,7 +257309,7 @@ function Account({ tenant, tenants = [], username, onInviteClick, email, mainPla
257344
257309
  }, children: [jsxs("div", { className: "lg:hidden mb-6", children: [jsx("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100 mb-2", children: (_d = (_c = TABS.find((t) => t.id === activeTab)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : activeTab }), jsxs("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: [activeTab === 'basic' && t('profileAccount.descBasic'), activeTab === 'social' && t('profileAccount.descSocial'), activeTab === 'security' && t('profileAccount.descSecurity'), activeTab === 'management' && t('profileAccount.descManagement'), activeTab === 'organization' && t('profileAccount.descOrganization'), activeTab === 'monetization' && t('profileAccount.descMonetization'), activeTab === 'datasets' && t('profileAccount.descBenchmarks'), activeTab === 'advanced' && t('profileAccount.descAdvanced')] })] }), activeTab === 'management' && hasManagementPermissions && (jsx(Admin, { authSpaUrl: authURL, onInviteClick: onInviteClick, tenant: tenant, currentSpa: currentSPA, hasUserTabPermission: hasUserTabPermission, hasGroupsTabPermission: hasGroupsTabPermission, hasPoliciesTabPermission: hasPoliciesTabPermission, hasRolesTabPermission: hasRolesTabPermission, hasTeamsTabPermission: hasTeamsTabPermission, hasInviteUserPermission: hasInviteUserPermission, hasCreateTeamPermission: hasCreateTeamPermission, onLoadGroupPermissions: onLoadGroupPermissions, hasAlertsTabPermission: hasAlertsTabPermission, rbacPermissions: rbacPermissions, enableRbac: enableRbac, showGradebookTab: showGradebookTab })), activeTab === 'organization' && (jsx(OrganizationTab, { platformKey: tenant, tenant: tenants.find((ten) => ten.key === tenant), onTenantUpdate: onTenantUpdate, setOrganizationLogoFromOutside: setOrganizationLogo, defaultSupportPhone: defaultSupportPhone, enableSupportPhone: enableSupportPhone })), activeTab === 'integrations' && (jsx(IntegrationsTab, { tenantKey: tenant, username: username })), activeTab === 'billing' && (jsx(BillingTab, { tenant: tenant, userActiveApp: userActiveApp, username: username, currentUserEmail: email, mainPlatformKey: mainPlatformKey })), activeTab === 'monetization' && (jsx(MonetizationTab, { platformKey: tenant, authURL: authURL })), activeTab === 'datasets' && showBenchmarks && (jsx(BenchmarksTab, { tenantKey: tenant, username: username })), activeTab === 'advanced' && (jsx(AdvancedTab, { platformKey: tenant, currentSPA: currentSPA, username: username, authURL: authURL, currentPlatformBaseDomain: currentPlatformBaseDomain }))] })] }), jsx(ToastProvider, {})] }));
257345
257310
  }
257346
257311
 
257347
- function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, useGravatarPicFallback = true, enableCatalogInvite = false, targetTab = 'basic', currentPlan = '', currentSPA = '', userActiveApp = null, authURL, tenants = [], onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', onBillingTabRequest, onAccountDeleted, enableMemoryTab = false, showBenchmarks = false, localLLMProps, systemControlProps, enableSupportPhone, }) {
257312
+ function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, useGravatarPicFallback = true, enableCatalogInvite = false, targetTab = 'basic', currentPlan = '', currentSPA = '', userActiveApp = null, authURL, tenants = [], onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, onTabChange, defaultSupportPhone = '', onBillingTabRequest, onAccountDeleted, enableMemoryTab = false, showBenchmarks = false, localLLMProps, enableSupportPhone, }) {
257348
257313
  const t = useT();
257349
257314
  console.log('[UserProfileModal] localLLMProps received:', {
257350
257315
  isAvailable: localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.isAvailable,
@@ -257410,7 +257375,7 @@ function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, sho
257410
257375
  showPlatformName: showPlatformName,
257411
257376
  useGravatarPicFallback: useGravatarPicFallback,
257412
257377
  showGradebookTab: showGradebookTab,
257413
- }, isAdmin: params.isAdmin, targetTab: targetTab, onClose: onClose, onAccountDeleted: onAccountDeleted, enableMemoryTab: enableMemoryTab, localLLMProps: localLLMProps, systemControlProps: systemControlProps })), [
257378
+ }, isAdmin: params.isAdmin, targetTab: targetTab, onClose: onClose, onAccountDeleted: onAccountDeleted, enableMemoryTab: enableMemoryTab, localLLMProps: localLLMProps })), [
257414
257379
  'organization',
257415
257380
  'management',
257416
257381
  'integrations',
@@ -257420,69 +257385,7 @@ function UserProfileModal({ isOpen, onClose, params, email, mainPlatformKey, sho
257420
257385
  ].includes(targetTab) && (jsxs(Fragment$1, { children: [jsx(Account, { onInviteClick: () => setIsInviteUserDialogOpen(true), tenant: params.tenantKey, tenants: tenants, username: getUserName$1(), email: email, mainPlatformKey: mainPlatformKey, showUsernameField: showUsernameField, showPlatformName: showPlatformName, useGravatarPicFallback: useGravatarPicFallback, onClose: onClose, isAdmin: params.isAdmin, targetTab: targetTab, currentPlan: currentPlan, currentSPA: currentSPA, userActiveApp: userActiveApp, authURL: authURL, onTenantUpdate: onTenantUpdate, currentPlatformBaseDomain: currentPlatformBaseDomain, rbacPermissions: rbacPermissions, enableRbac: enableRbac, onLoadGroupPermissions: onLoadGroupPermissions, onTabChange: onTabChange, defaultSupportPhone: defaultSupportPhone, showBenchmarks: showBenchmarks, enableSupportPhone: enableSupportPhone, showGradebookTab: showGradebookTab }), isInviteUserDialogOpen && (jsx(InviteUserDialog, { tenant: params.tenantKey, onClose: () => setIsInviteUserDialogOpen(false), isOpen: isInviteUserDialogOpen, enableCatalogInvite: enableCatalogInvite, hasManageUsersPermission: hasManageUsersPermission })), isInvitedUsersDialogOpen && (jsx(InvitedUsersDialog, { onClose: () => setIsInvitedUsersDialogOpen(false), tenant: params.tenantKey }))] }))] }) }) }));
257421
257386
  }
257422
257387
 
257423
- const IS_SERVER = typeof window === 'undefined';
257424
- /**
257425
- * A simplified useLocalStorage hook for persisting state to localStorage
257426
- * with SSR safety.
257427
- */
257428
- function useLocalStorage(key, initialValue) {
257429
- // Get the initial value
257430
- const getInitialValue = useCallback(() => {
257431
- const initial = initialValue instanceof Function ? initialValue() : initialValue;
257432
- if (IS_SERVER) {
257433
- return initial;
257434
- }
257435
- try {
257436
- const item = window.localStorage.getItem(key);
257437
- if (item === null) {
257438
- return initial;
257439
- }
257440
- return JSON.parse(item);
257441
- }
257442
- catch (error) {
257443
- console.warn(`Error reading localStorage key "${key}":`, error);
257444
- return initial;
257445
- }
257446
- }, [key, initialValue]);
257447
- const [storedValue, setStoredValue] = useState(getInitialValue);
257448
- // Update stored value when key changes
257449
- useEffect(() => {
257450
- setStoredValue(getInitialValue());
257451
- }, [key, getInitialValue]);
257452
- // Persist to localStorage
257453
- const setValue = useCallback((value) => {
257454
- if (IS_SERVER) {
257455
- console.warn(`Tried setting localStorage key "${key}" even though environment is not a client`);
257456
- return;
257457
- }
257458
- try {
257459
- const valueToStore = value instanceof Function ? value(storedValue) : value;
257460
- setStoredValue(valueToStore);
257461
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
257462
- }
257463
- catch (error) {
257464
- console.warn(`Error setting localStorage key "${key}":`, error);
257465
- }
257466
- }, [key, storedValue]);
257467
- // Remove from localStorage
257468
- const removeValue = useCallback(() => {
257469
- if (IS_SERVER) {
257470
- console.warn(`Tried removing localStorage key "${key}" even though environment is not a client`);
257471
- return;
257472
- }
257473
- try {
257474
- const initial = initialValue instanceof Function ? initialValue() : initialValue;
257475
- window.localStorage.removeItem(key);
257476
- setStoredValue(initial);
257477
- }
257478
- catch (error) {
257479
- console.warn(`Error removing localStorage key "${key}":`, error);
257480
- }
257481
- }, [key, initialValue]);
257482
- return [storedValue, setValue, removeValue];
257483
- }
257484
-
257485
- const LOCAL_STORAGE_KEY$1 = 'model_download_state';
257388
+ const LOCAL_STORAGE_KEY = 'model_download_state';
257486
257389
  const FIRST_LAUNCH_DISMISSED_KEY = 'model_download_prompt_dismissed';
257487
257390
  // Manager/download status is authoritative from Tauri, never the persisted
257488
257391
  // snapshot. This flips true the first time any hook instance mounts in a page
@@ -257502,7 +257405,7 @@ let didResetPersistedState = false;
257502
257405
  */
257503
257406
  function useModelDownload() {
257504
257407
  const { isAvailable, invoke, listen } = useTauri();
257505
- const [state, setState] = useLocalStorage(LOCAL_STORAGE_KEY$1, initialModelDownloadState);
257408
+ const [state, setState] = useLocalStorage(LOCAL_STORAGE_KEY, initialModelDownloadState);
257506
257409
  const [ollamaStatus, setOllamaStatus] = useState(null);
257507
257410
  const [systemMemory, setSystemMemory] = useState(null);
257508
257411
  const [isFirstLaunchDismissed, setIsFirstLaunchDismissed] = useLocalStorage(FIRST_LAUNCH_DISMISSED_KEY, false);
@@ -257930,224 +257833,6 @@ function useModelDownload() {
257930
257833
  };
257931
257834
  }
257932
257835
 
257933
- const LOCAL_STORAGE_KEY = 'ghost_os_install_state';
257934
- /**
257935
- * Manage installing & setting up GhostOS (https://github.com/ghostwright/ghost-os)
257936
- * via the Tauri host. This is the self-contained backing hook for the System
257937
- * Control card and deliberately mirrors `useModelDownload`:
257938
- *
257939
- * - checks GhostOS install/run status through the host
257940
- * - installs/sets up GhostOS with real-time progress + logs
257941
- * - persists state across app restarts
257942
- *
257943
- * Outside the desktop (Tauri) app every action is a no-op and `isAvailable` is
257944
- * false, so the card hides itself.
257945
- */
257946
- function useGhostOs() {
257947
- const { isAvailable, invoke, listen } = useTauri();
257948
- const [state, setState] = useLocalStorage(LOCAL_STORAGE_KEY, initialGhostOsInstallState);
257949
- const [status, setStatus] = useState(null);
257950
- // macOS Accessibility permission, which GhostOS needs to control the device.
257951
- // null = unknown/not-yet-checked or unsupported (non-macOS / plugin absent).
257952
- const [accessibilityPermission, setAccessibilityPermission] = useState(null);
257953
- const unlistenRefs = useRef([]);
257954
- const hasCheckedStatus = useRef(false);
257955
- /**
257956
- * Refresh the macOS Accessibility permission via tauri-plugin-macos-permissions.
257957
- * Independent of the GhostOS status check so a missing host command for one
257958
- * doesn't suppress the other. Stays null where unsupported.
257959
- */
257960
- const refreshAccessibilityPermission = useCallback(async () => {
257961
- if (!isAvailable)
257962
- return;
257963
- try {
257964
- const granted = await invoke(MACOS_PERMISSIONS_COMMANDS.CHECK_ACCESSIBILITY);
257965
- setAccessibilityPermission(granted);
257966
- }
257967
- catch (error) {
257968
- // Plugin not registered or non-macOS host — leave permission unknown.
257969
- console.warn('[useGhostOs] Accessibility permission check unavailable:', error);
257970
- setAccessibilityPermission(null);
257971
- }
257972
- }, [isAvailable, invoke]);
257973
- /**
257974
- * Open the Accessibility pane / prompt so the user can grant permission, then
257975
- * re-check (the grant happens in System Settings, so we refresh after).
257976
- */
257977
- const requestAccessibilityPermission = useCallback(async () => {
257978
- if (!isAvailable)
257979
- return;
257980
- try {
257981
- await invoke(MACOS_PERMISSIONS_COMMANDS.REQUEST_ACCESSIBILITY);
257982
- }
257983
- catch (error) {
257984
- console.error('[useGhostOs] Failed to request accessibility permission:', error);
257985
- }
257986
- await refreshAccessibilityPermission();
257987
- }, [isAvailable, invoke, refreshAccessibilityPermission]);
257988
- const addLog = useCallback((log) => {
257989
- setState((prev) => ({
257990
- ...prev,
257991
- logs: [...prev.logs.slice(-99), log],
257992
- lastUpdated: new Date().toISOString(),
257993
- }));
257994
- }, [setState]);
257995
- /**
257996
- * Query GhostOS status from the host.
257997
- */
257998
- const checkStatus = useCallback(async () => {
257999
- if (!isAvailable)
258000
- return;
258001
- // Refresh the accessibility permission alongside install status (independent).
258002
- refreshAccessibilityPermission();
258003
- try {
258004
- setState((prev) => ({ ...prev, status: 'checking' }));
258005
- const result = await invoke(GHOST_OS_TAURI_COMMANDS.CHECK_GHOST_OS_STATUS);
258006
- setStatus(result);
258007
- setState((prev) => ({
258008
- ...prev,
258009
- status: result.installed ? 'completed' : 'idle',
258010
- progress: result.installed ? 100 : prev.progress,
258011
- lastUpdated: new Date().toISOString(),
258012
- }));
258013
- }
258014
- catch (error) {
258015
- console.error('[useGhostOs] Failed to check status:', error);
258016
- setState((prev) => ({ ...prev, status: 'idle' }));
258017
- }
258018
- }, [isAvailable, invoke, setState, refreshAccessibilityPermission]);
258019
- /**
258020
- * Install and set up GhostOS. The host clones the repo + runs setup and emits
258021
- * progress/log/status events along the way.
258022
- */
258023
- const install = useCallback(async () => {
258024
- if (!isAvailable)
258025
- return;
258026
- try {
258027
- setState((prev) => ({
258028
- ...prev,
258029
- status: 'installing',
258030
- progress: 0,
258031
- message: 'Setting up your assistant…',
258032
- error: undefined,
258033
- logs: [],
258034
- }));
258035
- addLog({
258036
- timestamp: new Date().toISOString(),
258037
- level: 'info',
258038
- message: 'Starting setup…',
258039
- });
258040
- const result = await invoke(GHOST_OS_TAURI_COMMANDS.INSTALL_GHOST_OS);
258041
- addLog({
258042
- timestamp: new Date().toISOString(),
258043
- level: 'info',
258044
- message: typeof result === 'string' && result ? result : 'Setup complete',
258045
- });
258046
- setState((prev) => ({
258047
- ...prev,
258048
- status: 'completed',
258049
- progress: 100,
258050
- message: 'Your assistant is ready',
258051
- lastUpdated: new Date().toISOString(),
258052
- }));
258053
- await checkStatus();
258054
- }
258055
- catch (error) {
258056
- const message = error instanceof Error ? error.message : String(error);
258057
- setState((prev) => ({
258058
- ...prev,
258059
- status: 'error',
258060
- error: message,
258061
- message: 'Setup didn’t finish',
258062
- lastUpdated: new Date().toISOString(),
258063
- }));
258064
- addLog({
258065
- timestamp: new Date().toISOString(),
258066
- level: 'error',
258067
- message: `Install failed: ${message}`,
258068
- });
258069
- }
258070
- }, [isAvailable, invoke, addLog, checkStatus, setState]);
258071
- /**
258072
- * Stop the running GhostOS manager.
258073
- */
258074
- const stop = useCallback(async () => {
258075
- if (!isAvailable)
258076
- return;
258077
- try {
258078
- await invoke(GHOST_OS_TAURI_COMMANDS.STOP_GHOST_OS);
258079
- await checkStatus();
258080
- }
258081
- catch (error) {
258082
- console.error('[useGhostOs] Failed to stop GhostOS:', error);
258083
- }
258084
- }, [isAvailable, invoke, checkStatus]);
258085
- /**
258086
- * Reset the install state (for retrying after an error).
258087
- */
258088
- const resetState = useCallback(() => {
258089
- setState(initialGhostOsInstallState);
258090
- }, [setState]);
258091
- // Wire up host event listeners (progress / logs / status).
258092
- useEffect(() => {
258093
- if (!isAvailable)
258094
- return;
258095
- const setupListeners = async () => {
258096
- try {
258097
- const unlistenProgress = await listen(GHOST_OS_TAURI_EVENTS.INSTALL_PROGRESS, (payload) => {
258098
- const nextStatus = payload.status === 'completed'
258099
- ? 'completed'
258100
- : payload.status === 'cancelled'
258101
- ? 'cancelled'
258102
- : payload.status === 'error'
258103
- ? 'error'
258104
- : 'installing';
258105
- setState((prev) => ({
258106
- ...prev,
258107
- status: nextStatus,
258108
- progress: payload.percentage,
258109
- message: payload.message,
258110
- lastUpdated: new Date().toISOString(),
258111
- }));
258112
- });
258113
- unlistenRefs.current.push(unlistenProgress);
258114
- const unlistenLogs = await listen(GHOST_OS_TAURI_EVENTS.INSTALLATION_LOG, addLog);
258115
- unlistenRefs.current.push(unlistenLogs);
258116
- const unlistenStatus = await listen(GHOST_OS_TAURI_EVENTS.STATUS, setStatus);
258117
- unlistenRefs.current.push(unlistenStatus);
258118
- }
258119
- catch (error) {
258120
- console.error('[useGhostOs] Failed to setup event listeners:', error);
258121
- }
258122
- };
258123
- setupListeners();
258124
- return () => {
258125
- unlistenRefs.current.forEach((unlisten) => unlisten());
258126
- unlistenRefs.current = [];
258127
- };
258128
- }, [isAvailable, listen, setState, addLog]);
258129
- // Check status once on mount (in the desktop app).
258130
- useEffect(() => {
258131
- if (!isAvailable || hasCheckedStatus.current)
258132
- return;
258133
- hasCheckedStatus.current = true;
258134
- checkStatus();
258135
- }, [isAvailable, checkStatus]);
258136
- // Only expose the feature as available inside the desktop (Tauri) app.
258137
- const finalIsAvailable = isAvailable && isTauriApp$1();
258138
- return {
258139
- isAvailable: finalIsAvailable,
258140
- state,
258141
- status,
258142
- accessibilityPermission,
258143
- install,
258144
- stop,
258145
- checkStatus,
258146
- resetState,
258147
- requestAccessibilityPermission,
258148
- };
258149
- }
258150
-
258151
257836
  function UserProfileDropdown({ email, mainPlatformKey,
258152
257837
  // User data
258153
257838
  username, userIsAdmin = false, userIsStudent = false, userIsVisiting = false,
@@ -258171,11 +257856,9 @@ metadata, metadataLoaded = false,
258171
257856
  currentSPA = '', showMentorAIDisplayCheckbox = false, showLeaderboardDisplayCheckbox = false, showGradebookTab = false, showUsernameField = false, showPlatformName = false, enableCatalogInvite = false, authURL, onTenantUpdate, currentPlatformBaseDomain = '', rbacPermissions = {}, enableRbac = false, onLoadGroupPermissions, defaultSupportPhone = '', enableSupportPhone,
258172
257857
  // Local LLM props
258173
257858
  localLLMProps,
258174
- // System Control props
258175
- systemControlProps,
258176
257859
  // Controlled modal state
258177
257860
  isModalOpen, onModalOpenChange, defaultActiveTab, onAccountDeleted, enableMemoryTab, showBenchmarks = false, }) {
258178
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
257861
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
258179
257862
  const t = useT();
258180
257863
  // Use hooks to fetch user metadata
258181
257864
  const { data: userMetadata } = useGetUserMetadataQuery({
@@ -258203,17 +257886,6 @@ isModalOpen, onModalOpenChange, defaultActiveTab, onAccountDeleted, enableMemory
258203
257886
  const selectedFoundryModel = localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.selectedFoundryModel;
258204
257887
  const foundryStatus = localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.foundryStatus;
258205
257888
  const onSelectFoundryModel = localLLMProps === null || localLLMProps === void 0 ? void 0 : localLLMProps.onSelectFoundryModel;
258206
- // System Control (GhostOS) state from props (if provided) or from hook (fallback)
258207
- const ghostOsData = useGhostOs();
258208
- const isSystemControlAvailable = (_l = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.isAvailable) !== null && _l !== void 0 ? _l : ghostOsData.isAvailable;
258209
- const systemControlState = (_m = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.state) !== null && _m !== void 0 ? _m : ghostOsData.state;
258210
- const systemControlStatus = (_o = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.status) !== null && _o !== void 0 ? _o : ghostOsData.status;
258211
- const accessibilityPermission = (_p = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.accessibilityPermission) !== null && _p !== void 0 ? _p : ghostOsData.accessibilityPermission;
258212
- const installGhostOs = (_q = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.onInstall) !== null && _q !== void 0 ? _q : ghostOsData.install;
258213
- const stopGhostOs = (_r = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.onStop) !== null && _r !== void 0 ? _r : ghostOsData.stop;
258214
- const checkGhostOsStatus = (_s = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.onCheckStatus) !== null && _s !== void 0 ? _s : ghostOsData.checkStatus;
258215
- const resetGhostOsState = (_t = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.onResetState) !== null && _t !== void 0 ? _t : ghostOsData.resetState;
258216
- const requestAccessibilityPermission = (_u = systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.onRequestAccessibilityPermission) !== null && _u !== void 0 ? _u : ghostOsData.requestAccessibilityPermission;
258217
257889
  // Support controlled modal state
258218
257890
  const [internalIsUserProfileOpen, setInternalIsUserProfileOpen] = useState(false);
258219
257891
  const isUserProfileOpen = isModalOpen !== null && isModalOpen !== void 0 ? isModalOpen : internalIsUserProfileOpen;
@@ -258390,23 +258062,6 @@ isModalOpen, onModalOpenChange, defaultActiveTab, onAccountDeleted, enableMemory
258390
258062
  onResetState: resetState,
258391
258063
  onSelectFoundryModel,
258392
258064
  },
258393
- systemControlProps: {
258394
- isAvailable: isSystemControlAvailable,
258395
- state: systemControlState,
258396
- status: systemControlStatus,
258397
- requiredSizeGb: systemControlProps === null || systemControlProps === void 0 ? void 0 : systemControlProps.requiredSizeGb,
258398
- // Local-model status/memory + downloader, so the "Upgrade" action can
258399
- // check what's installed, keep the RAM/VRAM check, and pull a model.
258400
- ollamaStatus,
258401
- systemMemory,
258402
- accessibilityPermission,
258403
- onInstall: installGhostOs,
258404
- onStop: stopGhostOs,
258405
- onCheckStatus: checkGhostOsStatus,
258406
- onResetState: resetGhostOsState,
258407
- onRequestAccessibilityPermission: requestAccessibilityPermission,
258408
- onDownloadModel: startDownload,
258409
- },
258410
258065
  onAccountDeleted,
258411
258066
  enableMemoryTab,
258412
258067
  showBenchmarks,