Full Code of chatboxai/chatbox for AI

main 1577f1eda83d cached
617 files
4.0 MB
1.1M tokens
2029 symbols
1 requests
Download .txt
Showing preview only (4,272K chars total). Download the full file or copy to clipboard to get everything.
Repository: chatboxai/chatbox
Branch: main
Commit: 1577f1eda83d
Files: 617
Total size: 4.0 MB

Directory structure:
gitextract_f36dbven/

├── .erb/
│   ├── .vscode/
│   │   └── settings.json
│   ├── configs/
│   │   ├── .eslintrc
│   │   ├── webpack.config.base.ts
│   │   ├── webpack.config.eslint.ts
│   │   ├── webpack.config.main.prod.ts
│   │   ├── webpack.config.preload.dev.ts
│   │   ├── webpack.config.renderer.dev.dll.ts
│   │   ├── webpack.config.renderer.dev.ts
│   │   ├── webpack.config.renderer.prod.ts
│   │   └── webpack.paths.ts
│   ├── mocks/
│   │   └── fileMock.js
│   └── scripts/
│       ├── .eslintrc
│       ├── check-build-exists.ts
│       ├── check-native-dep.cjs
│       ├── check-native-dep.js
│       ├── check-node-env.js
│       ├── check-port-in-use.js
│       ├── clean.js
│       ├── delete-source-maps.js
│       ├── electron-rebuild.cjs
│       ├── electron-rebuild.js
│       ├── link-modules.cjs
│       ├── link-modules.ts
│       ├── notarize.js
│       ├── patch-libsql.cjs
│       └── postinstall.cjs
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── custom.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── config.yml
│   ├── dependabot.yml
│   └── stale.yml
├── .gitignore
├── .node-version
├── .npmrc
├── .prettierrc
├── ERROR_HANDLING.md
├── LICENSE
├── README.md
├── assets/
│   ├── assets.d.ts
│   ├── entitlements.mac.plist
│   ├── icon.icns
│   └── installer.nsh
├── biome.json
├── doc/
│   ├── FAQ-CN.md
│   ├── FAQ.md
│   └── README-CN.md
├── docs/
│   ├── adding-new-provider.md
│   ├── adding-provider.md
│   ├── dependency-reorg.md
│   ├── new-session-mechanism.md
│   ├── rag.md
│   ├── session-module-split-plan.md
│   ├── storage.md
│   ├── testing.md
│   └── token-estimation.md
├── electron-builder.yml
├── electron.vite.config.ts
├── i18next-parser.config.mjs
├── package.json
├── patches/
│   ├── libsql@0.5.22.patch
│   └── mdast-util-gfm-autolink-literal@2.0.1.patch
├── pnpm-workspace.yaml
├── postcss.config.js
├── release/
│   └── app/
│       └── package.json
├── script/
│   └── translate.mjs
├── scripts/
│   └── ralph/
│       ├── prompt-opencode.md
│       └── ralph.sh
├── src/
│   ├── __tests__/
│   │   └── App.test.tsx.bk
│   ├── main/
│   │   ├── adapters/
│   │   │   ├── index.ts
│   │   │   └── sentry.ts
│   │   ├── analystic-node.ts
│   │   ├── app-updater.ts
│   │   ├── autoLauncher.ts
│   │   ├── cache.ts
│   │   ├── deeplinks.ts
│   │   ├── file-parser.ts
│   │   ├── knowledge-base/
│   │   │   ├── db.ts
│   │   │   ├── file-loaders.ts
│   │   │   ├── index.ts
│   │   │   ├── ipc-handlers.ts
│   │   │   ├── model-providers.ts
│   │   │   ├── parsers/
│   │   │   │   ├── chatbox-parser.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── local-parser.ts
│   │   │   │   ├── mineru-parser.ts
│   │   │   │   └── types.ts
│   │   │   └── remote-file-parser.ts
│   │   ├── locales.ts
│   │   ├── main.ts
│   │   ├── mcp/
│   │   │   ├── ipc-stdio-transport.ts
│   │   │   ├── shell-env.cjs
│   │   │   └── shell-env.d.ts
│   │   ├── menu.ts
│   │   ├── proxy.ts
│   │   ├── readability.ts
│   │   ├── store-node.ts
│   │   ├── util.ts
│   │   └── window_state.ts
│   ├── preload/
│   │   └── index.ts
│   ├── renderer/
│   │   ├── Sidebar.tsx
│   │   ├── adapters/
│   │   │   ├── index.ts
│   │   │   └── sentry.ts
│   │   ├── components/
│   │   │   ├── Accordion.tsx
│   │   │   ├── ActionMenu.tsx
│   │   │   ├── AdaptiveSelect.tsx
│   │   │   ├── Artifact.tsx
│   │   │   ├── CustomProviderIcon.tsx
│   │   │   ├── EditableAvatar.tsx
│   │   │   ├── ErrorTestPannel.tsx
│   │   │   ├── FileIcon.tsx
│   │   │   ├── Image.tsx
│   │   │   ├── ImageCountSlider.tsx
│   │   │   ├── ImageModelSelect.tsx
│   │   │   ├── ImageStyleSelect.tsx
│   │   │   ├── InputBox/
│   │   │   │   ├── Attachments.tsx
│   │   │   │   ├── ImageUploadButton.tsx
│   │   │   │   ├── ImageUploadInput.tsx
│   │   │   │   ├── InputBox.tsx
│   │   │   │   ├── SessionSettingsButton.tsx
│   │   │   │   ├── TokenCountMenu.tsx
│   │   │   │   ├── WebBrowsingButton.tsx
│   │   │   │   ├── actionIconStyles.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── preprocessState.ts
│   │   │   ├── Markdown.tsx
│   │   │   ├── Mermaid.tsx
│   │   │   ├── ModelList.tsx
│   │   │   ├── ModelSelector/
│   │   │   │   ├── DesktopModelSelector.tsx
│   │   │   │   ├── MobileModelSelector.tsx
│   │   │   │   ├── ProviderHeader.tsx
│   │   │   │   ├── SimplePreview.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── shared.tsx
│   │   │   ├── Shortcut.tsx
│   │   │   ├── SortableItem.tsx
│   │   │   ├── SponsorChip.tsx
│   │   │   ├── StyledMenu.tsx
│   │   │   ├── UpdateAvailableButton.tsx
│   │   │   ├── chat/
│   │   │   │   ├── CompactionStatus.tsx
│   │   │   │   ├── Message.tsx
│   │   │   │   ├── MessageAttachmentGrid.tsx
│   │   │   │   ├── MessageErrTips.tsx
│   │   │   │   ├── MessageList.tsx
│   │   │   │   ├── MessageLoading.tsx
│   │   │   │   ├── MessageNavigation.tsx
│   │   │   │   └── SummaryMessage.tsx
│   │   │   ├── common/
│   │   │   │   ├── AdaptiveModal.tsx
│   │   │   │   ├── Avatar.tsx
│   │   │   │   ├── CompressionModal.tsx
│   │   │   │   ├── ConfirmDeleteButton.tsx
│   │   │   │   ├── CreatableSelect.tsx
│   │   │   │   ├── Divider.tsx
│   │   │   │   ├── ErrorBoundary.tsx
│   │   │   │   ├── LazyNumberInput.tsx
│   │   │   │   ├── LazySlider.tsx
│   │   │   │   ├── Link.tsx
│   │   │   │   ├── Mark.tsx
│   │   │   │   ├── MaxContextMessageCountSlider.tsx
│   │   │   │   ├── MiniButton.tsx
│   │   │   │   ├── PasswordTextField.tsx
│   │   │   │   ├── PopoverConfirm.tsx
│   │   │   │   ├── ScalableIcon.tsx
│   │   │   │   ├── SegmentedControl.tsx
│   │   │   │   ├── SliderWithInput.tsx
│   │   │   │   ├── TemperatureSlider.tsx
│   │   │   │   ├── TextFieldReset.tsx
│   │   │   │   ├── Toasts.tsx
│   │   │   │   └── TopPSlider.tsx
│   │   │   ├── dev/
│   │   │   │   ├── DevHeader.tsx
│   │   │   │   └── ThemeSwitchButton.tsx
│   │   │   ├── icons/
│   │   │   │   ├── ArrowRightIcon.tsx
│   │   │   │   ├── BrandGithub.tsx
│   │   │   │   ├── BrandRedNote.tsx
│   │   │   │   ├── BrandWechat.tsx
│   │   │   │   ├── BrandX.tsx
│   │   │   │   ├── Broom.tsx
│   │   │   │   ├── Dart.tsx
│   │   │   │   ├── FullscreenIcon.tsx
│   │   │   │   ├── HomepageIcon.tsx
│   │   │   │   ├── Java.tsx
│   │   │   │   ├── LayoutExpand.tsx
│   │   │   │   ├── LayoutShrink.tsx
│   │   │   │   ├── Loading.tsx
│   │   │   │   ├── ModelIcon.tsx
│   │   │   │   ├── ProviderIcon.tsx
│   │   │   │   ├── ProviderImageIcon.tsx
│   │   │   │   └── Robot.tsx
│   │   │   ├── knowledge-base/
│   │   │   │   ├── ChunksPreviewModal.tsx
│   │   │   │   ├── KnowledgeBase.tsx
│   │   │   │   ├── KnowledgeBaseDocuments.tsx
│   │   │   │   ├── KnowledgeBaseForm.tsx
│   │   │   │   ├── KnowledgeBaseMenu.tsx
│   │   │   │   └── RemoteRetryModal.tsx
│   │   │   ├── layout/
│   │   │   │   ├── ExitFullscreenButton.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── Overlay.tsx
│   │   │   │   ├── Page.tsx
│   │   │   │   ├── Toolbar.tsx
│   │   │   │   └── WindowControls.tsx
│   │   │   ├── mcp/
│   │   │   │   ├── MCPMenu.tsx
│   │   │   │   └── MCPStatus.tsx
│   │   │   ├── message-parts/
│   │   │   │   └── ToolCallPartUI.tsx
│   │   │   ├── session/
│   │   │   │   ├── SessionItem.tsx
│   │   │   │   ├── SessionList.tsx
│   │   │   │   └── ThreadHistoryDrawer.tsx
│   │   │   ├── settings/
│   │   │   │   ├── DocumentParserSettings.tsx
│   │   │   │   ├── mcp/
│   │   │   │   │   ├── BuiltinServersSection.tsx
│   │   │   │   │   ├── ConfigModal.tsx
│   │   │   │   │   ├── CustomServersSection.tsx
│   │   │   │   │   ├── ServerRegistrySpotlight.tsx
│   │   │   │   │   ├── registries.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   └── provider/
│   │   │   │       ├── AddProviderModal.tsx
│   │   │   │       ├── ImportProviderModal.tsx
│   │   │   │       └── ProviderList.tsx
│   │   │   └── ui/
│   │   │       ├── command.tsx
│   │   │       └── dialog.tsx
│   │   ├── dev/
│   │   │   └── devToolsConfig.ts
│   │   ├── hooks/
│   │   │   ├── dom.ts
│   │   │   ├── knowledge-base.ts
│   │   │   ├── mcp.ts
│   │   │   ├── useAppTheme.ts
│   │   │   ├── useChatboxAIModels.ts
│   │   │   ├── useChunksPreview.ts
│   │   │   ├── useCopied.ts
│   │   │   ├── useCopilots.ts
│   │   │   ├── useDefaultSystemLanguage.ts
│   │   │   ├── useI18nEffect.ts
│   │   │   ├── useInputBoxHistory.ts
│   │   │   ├── useKnowledgeBase.ts
│   │   │   ├── useMessageInput.ts
│   │   │   ├── useNeedRoomForWinControls.ts
│   │   │   ├── useProviderImport.ts
│   │   │   ├── useProviders.ts
│   │   │   ├── useScreenChange.ts
│   │   │   ├── useShortcut.tsx
│   │   │   ├── useThinkingTimer.ts
│   │   │   ├── useVersion.ts
│   │   │   └── useWindowMaximized.ts
│   │   ├── i18n/
│   │   │   ├── changelogs/
│   │   │   │   ├── changelog_en.ts
│   │   │   │   ├── changelog_zh_Hans.ts
│   │   │   │   └── changelog_zh_Hant.ts
│   │   │   ├── for-key-scan.ts
│   │   │   ├── index.ts
│   │   │   ├── locales/
│   │   │   │   ├── ar/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── de/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── en/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── es/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── fr/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── it-IT/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── ja/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── ko/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── nb-NO/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── pt-PT/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── ru/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── sv/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── zh-Hans/
│   │   │   │   │   └── translation.json
│   │   │   │   └── zh-Hant/
│   │   │   │       └── translation.json
│   │   │   ├── locales.ts
│   │   │   └── parser.ts
│   │   ├── index.ejs
│   │   ├── index.html
│   │   ├── index.tsx
│   │   ├── index.web.ejs
│   │   ├── lib/
│   │   │   ├── format-chat.tsx
│   │   │   └── utils.ts
│   │   ├── modals/
│   │   │   ├── AppStoreRating.tsx
│   │   │   ├── ArtifactPreview.tsx
│   │   │   ├── AttachLink.tsx
│   │   │   ├── ClearSessionList.tsx
│   │   │   ├── ContentViewer.tsx
│   │   │   ├── EdgeOneDeploySuccess.tsx
│   │   │   ├── ExportChat.tsx
│   │   │   ├── FileParseError.tsx
│   │   │   ├── JsonViewer.tsx
│   │   │   ├── MessageEdit.tsx
│   │   │   ├── ModelEdit.tsx
│   │   │   ├── ReportContent.tsx
│   │   │   ├── SessionSettings.tsx
│   │   │   ├── Settings.tsx
│   │   │   ├── ThreadNameEdit.tsx
│   │   │   ├── Welcome.tsx
│   │   │   └── index.tsx
│   │   ├── native/
│   │   │   └── stream-http.ts
│   │   ├── packages/
│   │   │   ├── apple_app_store.ts
│   │   │   ├── base64.test.ts
│   │   │   ├── base64.ts
│   │   │   ├── codeblock_state_recorder.ts
│   │   │   ├── context-management/
│   │   │   │   ├── attachment-payload.test.ts
│   │   │   │   ├── attachment-payload.ts
│   │   │   │   ├── compaction-detector.test.ts
│   │   │   │   ├── compaction-detector.ts
│   │   │   │   ├── compaction.ts
│   │   │   │   ├── context-builder.test.ts
│   │   │   │   ├── context-builder.ts
│   │   │   │   ├── context-tokens.hook.test.ts
│   │   │   │   ├── context-tokens.integration.test.ts
│   │   │   │   ├── context-tokens.ts
│   │   │   │   ├── context-tokens.unit.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── summary-generator.ts
│   │   │   │   ├── tool-cleanup.test.ts
│   │   │   │   └── tool-cleanup.ts
│   │   │   ├── edgeone.ts
│   │   │   ├── event.ts
│   │   │   ├── filetype.ts
│   │   │   ├── initial_data.ts
│   │   │   ├── keypairs.ts
│   │   │   ├── latex.test.ts
│   │   │   ├── latex.ts
│   │   │   ├── lemonsqueezy.ts
│   │   │   ├── local-parser.ts
│   │   │   ├── mcp/
│   │   │   │   ├── builtin.ts
│   │   │   │   ├── controller.ts
│   │   │   │   ├── ipc-stdio-transport.ts
│   │   │   │   └── types.ts
│   │   │   ├── model-calls/
│   │   │   │   ├── generate-image.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── message-utils.ts
│   │   │   │   ├── preprocess.ts
│   │   │   │   ├── stream-text.ts
│   │   │   │   ├── tools.ts
│   │   │   │   └── toolsets/
│   │   │   │       ├── file.ts
│   │   │   │       ├── knowledge-base.ts
│   │   │   │       └── web-search.ts
│   │   │   ├── model-context/
│   │   │   │   ├── builtin-data.ts
│   │   │   │   └── index.ts
│   │   │   ├── model-setting-utils/
│   │   │   │   ├── base-config.ts
│   │   │   │   ├── custom-provider-setting-util.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── interface.ts
│   │   │   │   ├── registry-setting-util.ts
│   │   │   │   └── util.ts
│   │   │   ├── navigator.ts
│   │   │   ├── pic_utils.ts
│   │   │   ├── prompts.ts
│   │   │   ├── remote.ts
│   │   │   ├── token-estimation/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── analyzer.test.ts
│   │   │   │   │   ├── cache-keys.test.ts
│   │   │   │   │   ├── computation-queue.test.ts
│   │   │   │   │   ├── result-persister.test.ts
│   │   │   │   │   ├── task-executor.test.ts
│   │   │   │   │   ├── tokenizer.test.ts
│   │   │   │   │   └── useTokenEstimation.test.ts
│   │   │   │   ├── analyzer.ts
│   │   │   │   ├── cache-keys.ts
│   │   │   │   ├── computation-queue.ts
│   │   │   │   ├── hooks/
│   │   │   │   │   └── useTokenEstimation.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── result-persister.ts
│   │   │   │   ├── task-executor.ts
│   │   │   │   ├── tokenizer.ts
│   │   │   │   └── types.ts
│   │   │   ├── token.test.ts
│   │   │   ├── token.tsx
│   │   │   ├── token_config.ts
│   │   │   ├── tools/
│   │   │   │   └── index.ts
│   │   │   ├── web-search/
│   │   │   │   ├── base.ts
│   │   │   │   ├── bing-news.ts
│   │   │   │   ├── bing.ts
│   │   │   │   ├── chatbox-search.ts
│   │   │   │   ├── duckduckgo.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── tavily.ts
│   │   │   └── word-count.ts
│   │   ├── pages/
│   │   │   ├── PictureDialog.tsx
│   │   │   ├── RemoteDialogWindow.tsx
│   │   │   ├── SearchDialog.tsx
│   │   │   └── SettingDialog/
│   │   │       └── AdvancedSettingTab.tsx
│   │   ├── platform/
│   │   │   ├── desktop_platform.ts
│   │   │   ├── index.ts
│   │   │   ├── interfaces.ts
│   │   │   ├── knowledge-base/
│   │   │   │   ├── desktop-controller.ts
│   │   │   │   └── interface.ts
│   │   │   ├── storages.ts
│   │   │   ├── test_platform.ts
│   │   │   ├── web_exporter.ts
│   │   │   ├── web_logger.ts
│   │   │   ├── web_platform.ts
│   │   │   └── web_platform_utils.ts
│   │   ├── preload.d.ts
│   │   ├── reportWebVitals.ts
│   │   ├── router.tsx
│   │   ├── routes/
│   │   │   ├── __root.tsx
│   │   │   ├── about.tsx
│   │   │   ├── copilots.tsx
│   │   │   ├── dev/
│   │   │   │   ├── context-generator.tsx
│   │   │   │   ├── css-var.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── model-selector.tsx
│   │   │   │   ├── route.tsx
│   │   │   │   └── storage.tsx
│   │   │   ├── image-creator/
│   │   │   │   ├── -components/
│   │   │   │   │   ├── EmptyState.tsx
│   │   │   │   │   ├── GeneratedImagesGallery.tsx
│   │   │   │   │   ├── HistoryItem.tsx
│   │   │   │   │   ├── HistoryPanel.tsx
│   │   │   │   │   ├── ImageGenerationErrorTips.tsx
│   │   │   │   │   ├── MobileDrawers.tsx
│   │   │   │   │   ├── PromptDisplay.tsx
│   │   │   │   │   ├── ReferenceImagesPreview.tsx
│   │   │   │   │   ├── Shimmer.tsx
│   │   │   │   │   └── constants.ts
│   │   │   │   └── index.tsx
│   │   │   ├── index.tsx
│   │   │   ├── session/
│   │   │   │   └── $sessionId.tsx
│   │   │   └── settings/
│   │   │       ├── chat.tsx
│   │   │       ├── chatbox-ai.tsx
│   │   │       ├── default-models.tsx
│   │   │       ├── document-parser.tsx
│   │   │       ├── general.tsx
│   │   │       ├── hotkeys.tsx
│   │   │       ├── index.tsx
│   │   │       ├── knowledge-base.tsx
│   │   │       ├── mcp.tsx
│   │   │       ├── provider/
│   │   │       │   ├── $providerId.tsx
│   │   │       │   ├── chatbox-ai/
│   │   │       │   │   ├── -components/
│   │   │       │   │   │   ├── LicenseDetailCard.tsx
│   │   │       │   │   │   ├── LicenseKeyView.tsx
│   │   │       │   │   │   ├── LicenseSelectionModal.tsx
│   │   │       │   │   │   ├── LoggedInView.tsx
│   │   │       │   │   │   ├── LoginView.tsx
│   │   │       │   │   │   ├── ModelManagement.tsx
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── types.ts
│   │   │       │   │   │   ├── useAuthTokens.ts
│   │   │       │   │   │   ├── useLicenseActivation.ts
│   │   │       │   │   │   ├── useLogin.ts
│   │   │       │   │   │   └── useUserLicenses.ts
│   │   │       │   │   └── index.tsx
│   │   │       │   ├── index.tsx
│   │   │       │   └── route.tsx
│   │   │       ├── route.tsx
│   │   │       └── web-search.tsx
│   │   ├── setup/
│   │   │   ├── ga_init.ts
│   │   │   ├── global_error_handler.ts
│   │   │   ├── init_data.ts
│   │   │   ├── load_polyfill.ts
│   │   │   ├── mcp_bootstrap.ts
│   │   │   ├── mobile_safe_area.ts
│   │   │   ├── protect.ts
│   │   │   ├── sentry_init.ts
│   │   │   ├── storage_clear.ts
│   │   │   └── token_estimation_init.ts
│   │   ├── setupTests.ts
│   │   ├── static/
│   │   │   ├── Block.css
│   │   │   ├── _headers
│   │   │   ├── globals.css
│   │   │   └── index.css
│   │   ├── storage/
│   │   │   ├── BaseStorage.ts
│   │   │   ├── ImageGenerationStorage.ts
│   │   │   ├── SQLiteImageGenerationStorage.ts
│   │   │   ├── StoreStorage.ts
│   │   │   └── index.ts
│   │   ├── stores/
│   │   │   ├── atoms/
│   │   │   │   ├── compactionAtoms.ts
│   │   │   │   ├── configAtoms.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── sessionAtoms.ts
│   │   │   │   ├── settingsAtoms.ts
│   │   │   │   ├── throttleWriteSessionAtom.ts
│   │   │   │   ├── uiAtoms.ts
│   │   │   │   └── utilAtoms.ts
│   │   │   ├── authInfoStore.ts
│   │   │   ├── chatStore.ts
│   │   │   ├── imageGenerationActions.ts
│   │   │   ├── imageGenerationStore.ts
│   │   │   ├── lastUsedModelStore.ts
│   │   │   ├── migration.test.ts
│   │   │   ├── migration.ts
│   │   │   ├── premiumActions.ts
│   │   │   ├── queryClient.ts
│   │   │   ├── safeStorage.ts
│   │   │   ├── scrollActions.ts
│   │   │   ├── session/
│   │   │   │   ├── crud.ts
│   │   │   │   ├── export.ts
│   │   │   │   ├── forks.ts
│   │   │   │   ├── generation.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── messages.ts
│   │   │   │   ├── naming.ts
│   │   │   │   ├── state.ts
│   │   │   │   ├── threads.ts
│   │   │   │   └── types.ts
│   │   │   ├── sessionActions.test.ts
│   │   │   ├── sessionActions.ts
│   │   │   ├── sessionHelpers.ts
│   │   │   ├── settingActions.ts
│   │   │   ├── settingsStore.ts
│   │   │   ├── toastActions.ts
│   │   │   ├── uiStore.ts
│   │   │   ├── updateQueue.test.ts
│   │   │   └── updateQueue.ts
│   │   ├── test.tsx
│   │   ├── utils/
│   │   │   ├── base64.ts
│   │   │   ├── error-testing.ts
│   │   │   ├── feature-flags.ts
│   │   │   ├── format.ts
│   │   │   ├── image.ts
│   │   │   ├── index.ts
│   │   │   ├── message.test.ts
│   │   │   ├── message.ts
│   │   │   ├── mobile-request.ts
│   │   │   ├── model-tester.ts
│   │   │   ├── modelLogo.tsx
│   │   │   ├── provider-config.test.ts
│   │   │   ├── provider-config.ts
│   │   │   ├── request.ts
│   │   │   ├── session-utils.ts
│   │   │   └── track.ts
│   │   └── variables.ts
│   └── shared/
│       ├── constants.ts
│       ├── defaults.ts
│       ├── electron-types.ts
│       ├── file-extensions.ts
│       ├── models/
│       │   ├── abstract-ai-sdk.ts
│       │   ├── errors.ts
│       │   ├── index.test.ts
│       │   ├── index.ts
│       │   ├── openai-compatible.ts
│       │   ├── rerank.ts
│       │   ├── types.ts
│       │   └── utils/
│       │       └── fetch-proxy.ts
│       ├── providers/
│       │   ├── definitions/
│       │   │   ├── azure.ts
│       │   │   ├── chatboxai.ts
│       │   │   ├── chatglm.ts
│       │   │   ├── claude.ts
│       │   │   ├── deepseek.ts
│       │   │   ├── gemini.ts
│       │   │   ├── groq.ts
│       │   │   ├── lmstudio.ts
│       │   │   ├── mistral-ai.ts
│       │   │   ├── models/
│       │   │   │   ├── azure.ts
│       │   │   │   ├── chatboxai.ts
│       │   │   │   ├── chatglm.ts
│       │   │   │   ├── claude.ts
│       │   │   │   ├── custom-claude.ts
│       │   │   │   ├── custom-gemini.ts
│       │   │   │   ├── custom-openai-responses.ts
│       │   │   │   ├── custom-openai.ts
│       │   │   │   ├── deepseek.ts
│       │   │   │   ├── gemini.ts
│       │   │   │   ├── groq.ts
│       │   │   │   ├── lmstudio.ts
│       │   │   │   ├── mistral-ai.ts
│       │   │   │   ├── ollama.ts
│       │   │   │   ├── openai-responses.ts
│       │   │   │   ├── openai.test.ts
│       │   │   │   ├── openai.ts
│       │   │   │   ├── openrouter.ts
│       │   │   │   ├── perplexity.ts
│       │   │   │   ├── siliconflow.ts
│       │   │   │   ├── volcengine.ts
│       │   │   │   └── xai.ts
│       │   │   ├── ollama.ts
│       │   │   ├── openai-responses.ts
│       │   │   ├── openai.ts
│       │   │   ├── openrouter.ts
│       │   │   ├── perplexity.ts
│       │   │   ├── siliconflow.ts
│       │   │   ├── volcengine.ts
│       │   │   └── xai.ts
│       │   ├── index.ts
│       │   ├── registry.test.ts
│       │   ├── registry.ts
│       │   ├── types.ts
│       │   └── utils.ts
│       ├── request/
│       │   ├── chatboxai_pool.ts
│       │   └── request.ts
│       ├── types/
│       │   ├── adapters.ts
│       │   ├── image-generation.ts
│       │   ├── mcp.ts
│       │   ├── provider.ts
│       │   ├── session.ts
│       │   └── settings.ts
│       ├── types.test.ts
│       ├── types.ts
│       └── utils/
│           ├── cache.ts
│           ├── index.ts
│           ├── json_utils.ts
│           ├── knowledge-base-model-parser.ts
│           ├── llm_utils.test.ts
│           ├── llm_utils.ts
│           ├── message.ts
│           ├── model_settings.ts
│           ├── network_utils.ts
│           ├── sentry_adapter.ts
│           └── word_count.ts
├── tailwind.config.js
├── tasks/
│   ├── prd-code-organization-optimization.md
│   ├── prd-compaction-ux-improvement.md
│   ├── prd-context-management.md
│   └── prd-provider-system-refactor.md
├── team-sharing/
│   ├── Caddyfile
│   ├── Dockerfile
│   ├── README-CN.md
│   ├── README.md
│   └── main.sh
├── test/
│   ├── cases/
│   │   ├── file-conversation/
│   │   │   ├── sample-large.txt
│   │   │   ├── sample.json
│   │   │   ├── sample.md
│   │   │   ├── sample.ts
│   │   │   └── sample.txt
│   │   └── provider-config-import-manual-test.md
│   └── integration/
│       ├── context-management/
│       │   ├── context-management.test.ts
│       │   └── setup.ts
│       ├── file-conversation/
│       │   ├── file-conversation.test.ts
│       │   ├── setup.ts
│       │   └── test-harness.ts
│       ├── mocks/
│       │   ├── model-dependencies.ts
│       │   └── sentry.ts
│       └── model-provider/
│           └── model-provider.test.ts
├── tsconfig.json
└── vitest.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .erb/.vscode/settings.json
================================================
{
  "cssVariables.lookupFiles": [
    "**/*.css",
    "**/*.scss",
    "**/*.sass",
    "**/*.less",
    "node_modules/@mantine/core/styles.css"
  ]
}


================================================
FILE: .erb/configs/.eslintrc
================================================
{
    "rules": {
        "no-console": "off",
        "global-require": "off",
        "import/no-dynamic-require": "off"
    }
}


================================================
FILE: .erb/configs/webpack.config.base.ts
================================================
/**
 * Base webpack config used across other specific configs
 */

import webpack from 'webpack'
import TsconfigPathsPlugins from 'tsconfig-paths-webpack-plugin'
import webpackPaths from './webpack.paths'
import { dependencies as externals } from '../../release/app/package.json'

const configuration: webpack.Configuration = {
  externals: [...Object.keys(externals || {})],

  stats: 'errors-only',

  module: {
    rules: [
      {
        test: /\.[jt]sx?$/,
        exclude: [/node_modules/, /\.d\.ts$/],
        use: {
          loader: 'ts-loader',
          options: {
            // Remove this line to enable type checking in webpack builds
            transpileOnly: true,
            compilerOptions: {
              module: 'esnext',
            },
          },
        },
      },
      // Special rule for mermaid to transpile static blocks
      {
        test: /\.m?js$/,
        include: /node_modules\/mermaid/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                targets: {
                  chrome: '58',
                  firefox: '60',
                  safari: '11',
                  edge: '16',
                  ios: '11',
                  android: '67'
                }
              }]
            ],
            plugins: [
              '@babel/plugin-transform-class-static-block'
            ]
          }
        }
      },
    ],
  },

  output: {
    path: webpackPaths.srcPath,
    // https://github.com/webpack/webpack/issues/1114
    library: {
      type: 'commonjs2',
    },
  },

  /**
   * Determine the array of extensions that should be used to resolve modules.
   */
  resolve: {
    extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
    modules: [webpackPaths.srcPath, 'node_modules'],
    // There is no need to add aliases here, the paths in tsconfig get mirrored
    plugins: [new TsconfigPathsPlugins()],
  },

  plugins: [
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'production',
      CHATBOX_BUILD_TARGET: 'unknown',
      CHATBOX_BUILD_PLATFORM: 'unknown',
      USE_LOCAL_API: '',
      USE_BETA_API: '',
      USE_LOCAL_CHATBOX: '',
      USE_BETA_CHATBOX: '',
    }),
  ],
}

export default configuration


================================================
FILE: .erb/configs/webpack.config.eslint.ts
================================================
/* eslint import/no-unresolved: off, import/no-self-import: off */

module.exports = require('./webpack.config.renderer.dev').default


================================================
FILE: .erb/configs/webpack.config.main.prod.ts
================================================
/**
 * Webpack config for production electron main process
 */

import path from 'path'
import TerserPlugin from 'terser-webpack-plugin'
import webpack from 'webpack'
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
import { merge } from 'webpack-merge'
import JavaScriptObfuscator from 'webpack-obfuscator'
import checkNodeEnv from '../scripts/check-node-env'
import baseConfig from './webpack.config.base'
import webpackPaths from './webpack.paths'

checkNodeEnv('production')

const configuration: webpack.Configuration = {
    devtool: false,

    mode: 'production',

    target: 'electron-main',

    entry: {
        main: path.join(webpackPaths.srcMainPath, 'main.ts'),
        preload: path.join(webpackPaths.srcMainPath, 'preload.ts'),
    },

    output: {
        path: webpackPaths.distMainPath,
        filename: '[name].js',
        library: {
            type: 'umd',
        },
    },

    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true,
            }),
        ],
    },

    plugins: [
        new BundleAnalyzerPlugin({
            analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
            analyzerPort: 8888,
        }),

        /**
         * Create global constants which can be configured at compile time.
         *
         * Useful for allowing different behaviour between development builds and
         * release builds
         *
         * NODE_ENV should be production so that modules do not perform certain
         * development checks
         */
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'production',
            DEBUG_PROD: false,
            START_MINIMIZED: false,
        }),

        new webpack.DefinePlugin({
            'process.type': '"browser"',
        }),

        new JavaScriptObfuscator({
            target: 'node',
            optionsPreset: 'default',
            // 默认的变量名混淆,可能被误报为恶意代码
            identifierNamesGenerator: 'mangled-shuffled',
        }),
    ],

    /**
     * Disables webpack processing of __dirname and __filename.
     * If you run the bundle in node.js it falls back to these values of node.js.
     * https://github.com/webpack/webpack/issues/2010
     */
    node: {
        __dirname: false,
        __filename: false,
    },
}

export default merge(baseConfig, configuration)


================================================
FILE: .erb/configs/webpack.config.preload.dev.ts
================================================
import path from 'path'
import webpack from 'webpack'
import { merge } from 'webpack-merge'
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
import baseConfig from './webpack.config.base'
import webpackPaths from './webpack.paths'
import checkNodeEnv from '../scripts/check-node-env'

// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
    checkNodeEnv('development')
}

const configuration: webpack.Configuration = {
    devtool: 'inline-source-map',

    mode: 'development',

    target: 'electron-preload',

    entry: path.join(webpackPaths.srcMainPath, 'preload.ts'),

    output: {
        path: webpackPaths.dllPath,
        filename: 'preload.js',
        library: {
            type: 'umd',
        },
    },

    plugins: [
        new BundleAnalyzerPlugin({
            analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
        }),

        /**
         * Create global constants which can be configured at compile time.
         *
         * Useful for allowing different behaviour between development builds and
         * release builds
         *
         * NODE_ENV should be production so that modules do not perform certain
         * development checks
         *
         * By default, use 'development' as NODE_ENV. This can be overriden with
         * 'staging', for example, by changing the ENV variables in the npm scripts
         */
        new webpack.EnvironmentPlugin({
            NODE_ENV: 'development',
        }),

        new webpack.LoaderOptionsPlugin({
            debug: true,
        }),
    ],

    /**
     * Disables webpack processing of __dirname and __filename.
     * If you run the bundle in node.js it falls back to these values of node.js.
     * https://github.com/webpack/webpack/issues/2010
     */
    node: {
        __dirname: false,
        __filename: false,
    },

    watch: true,
}

export default merge(baseConfig, configuration)


================================================
FILE: .erb/configs/webpack.config.renderer.dev.dll.ts
================================================
/**
 * Builds the DLL for development electron renderer process
 */

import webpack from 'webpack'
import path from 'path'
import { merge } from 'webpack-merge'
import baseConfig from './webpack.config.base'
import webpackPaths from './webpack.paths'
import { dependencies } from '../../package.json'
import checkNodeEnv from '../scripts/check-node-env'

checkNodeEnv('development')

const EXCLUDE_MODULES = new Set([
    '@modelcontextprotocol/sdk', // avoid `Package path . is not exported from package` error
    '@mastra/core',
    '@mastra/rag',
    '@libsql/client',
    'capacitor-stream-http', // local file dependency
  ])

const dist = webpackPaths.dllPath

const configuration: webpack.Configuration = {
  context: webpackPaths.rootPath,

  devtool: 'eval',

  mode: 'development',

  target: 'electron-renderer',

  externals: ['fsevents', 'crypto-browserify'],

  /**
   * Use `module` from `webpack.config.renderer.dev.js`
   */
  module: require('./webpack.config.renderer.dev').default.module,

  entry: {
    renderer: Object.keys(dependencies || {}).filter((dependency) => !EXCLUDE_MODULES.has(dependency)),
  },

  output: {
    path: dist,
    filename: '[name].dev.dll.js',
    library: {
      name: 'renderer',
      type: 'var',
    },
  },

  plugins: [
    new webpack.DllPlugin({
      path: path.join(dist, '[name].json'),
      name: '[name]',
    }),

    /**
     * Create global constants which can be configured at compile time.
     *
     * Useful for allowing different behaviour between development builds and
     * release builds
     *
     * NODE_ENV should be production so that modules do not perform certain
     * development checks
     */
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'development',
    }),

    new webpack.LoaderOptionsPlugin({
      debug: true,
      options: {
        context: webpackPaths.srcPath,
        output: {
          path: webpackPaths.dllPath,
        },
      },
    }),
  ],
}

export default merge(baseConfig, configuration)


================================================
FILE: .erb/configs/webpack.config.renderer.dev.ts
================================================
import 'webpack-dev-server'
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
import { TanStackRouterWebpack } from '@tanstack/router-plugin/webpack'
import chalk from 'chalk'
import { execSync, spawn } from 'child_process'
import fs from 'fs'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import path from 'path'
import webpack from 'webpack'
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
import { merge } from 'webpack-merge'
import checkNodeEnv from '../scripts/check-node-env'
import baseConfig from './webpack.config.base'
import webpackPaths from './webpack.paths'

// When an ESLint server is running, we can't set the NODE_ENV so we'll check if it's
// at the dev webpack config is not accidentally run in a production environment
if (process.env.NODE_ENV === 'production') {
  checkNodeEnv('development')
}

const port = process.env.PORT || 1212
const manifest = path.resolve(webpackPaths.dllPath, 'renderer.json')
const skipDLLs =
  module.parent?.filename.includes('webpack.config.renderer.dev.dll') ||
  module.parent?.filename.includes('webpack.config.eslint')

const DEV_WEB_ONLY = process.env.DEV_WEB_ONLY === 'true'

/**
 * Warn if the DLL is not built
 */
if (!skipDLLs && !(fs.existsSync(webpackPaths.dllPath) && fs.existsSync(manifest))) {
  console.log(
    chalk.black.bgYellow.bold(
      'The DLL files are missing. Sit back while we build them for you with "npm run build-dll"'
    )
  )
  execSync('npm run postinstall')
}

const configuration: webpack.Configuration = {
  devtool: 'inline-source-map',

  mode: 'development',

  target: ['web', 'electron-renderer'],

  entry: [
    `webpack-dev-server/client?http://localhost:${port}/dist`,
    'webpack/hot/only-dev-server',
    path.join(webpackPaths.srcRendererPath, 'index.tsx'),
  ],

  output: {
    path: webpackPaths.distRendererPath,
    publicPath: '/',
    filename: 'renderer.dev.js',
    library: {
      type: 'umd',
    },
  },

  module: {
    rules: [
      {
        test: /\.s?(c|a)ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              sourceMap: true,
              importLoaders: 1,
            },
          },
          'sass-loader',
        ],
        include: /\.module\.s?(c|a)ss$/,
      },
      {
        test: /\.s?css$/,
        use: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader', {
            loader: 'string-replace-loader',
            options: {
              search: /(\d+)dvh/g,
              replace: '$1vh',
            },
          }],
        exclude: /\.module\.s?(c|a)ss$/,
        sideEffects: true,
      },
      // Fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
      // Images
      {
        test: /\.(png|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      // SVG
      {
        test: /\.svg$/,
        use: [
          {
            loader: '@svgr/webpack',
            options: {
              prettier: false,
              svgo: false,
              svgoConfig: {
                plugins: [{ removeViewBox: false }],
              },
              titleProp: true,
              ref: true,
            },
          },
          'file-loader',
        ],
      },
    ],
  },
  plugins: [
    ...(skipDLLs
      ? []
      : [
          new webpack.DllReferencePlugin({
            context: webpackPaths.dllPath,
            manifest: require(manifest),
            sourceType: 'var',
          }),
        ]),

    new webpack.NoEmitOnErrorsPlugin(),

    TanStackRouterWebpack({
      target: 'react',
      autoCodeSplitting: true,
      routesDirectory: './src/renderer/routes',
      generatedRouteTree: './src/renderer/routeTree.gen.ts',
    }),

    /**
     * Create global constants which can be configured at compile time.
     *
     * Useful for allowing different behaviour between development builds and
     * release builds
     *
     * NODE_ENV should be production so that modules do not perform certain
     * development checks
     *
     * By default, use 'development' as NODE_ENV. This can be overriden with
     * 'staging', for example, by changing the ENV variables in the npm scripts
     */
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'development',
    }),

    new webpack.LoaderOptionsPlugin({
      debug: true,
    }),

    new BundleAnalyzerPlugin({
      analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
      analyzerPort: 8889,
    }),

    new ReactRefreshWebpackPlugin(),

    new HtmlWebpackPlugin({
      filename: path.join('index.html'),
      template: path.join(webpackPaths.srcRendererPath, 'index.ejs'),
      minify: {
        collapseWhitespace: true,
        removeAttributeQuotes: true,
        removeComments: true,
      },
      isBrowser: false,
      env: process.env.NODE_ENV,
      isDevelopment: process.env.NODE_ENV !== 'production',
      nodeModules: webpackPaths.appNodeModulesPath,
      favicon: path.join(webpackPaths.srcRendererPath, 'favicon.ico'),
    }),
  ],

  node: {
    __dirname: false,
    __filename: false,
  },

  devServer: {
    port,
    compress: true,
    hot: true,
    headers: { 'Access-Control-Allow-Origin': '*' },
    static: {
      publicPath: '/',
    },
    historyApiFallback: {
      verbose: true,
    },
    setupMiddlewares(middlewares) {
      console.log('Starting preload.js builder...')
      const preloadProcess = spawn('npm', ['run', 'start:preload'], {
        shell: true,
        stdio: 'inherit',
      })
        .on('close', (code: number) => process.exit(code!))
        .on('error', (spawnError) => console.error(spawnError))

      if (!DEV_WEB_ONLY) {
        console.log('Starting Main Process...')
        let args = ['run', 'start:main']
        if (process.env.MAIN_ARGS) {
          args = args.concat(['--', ...process.env.MAIN_ARGS.matchAll(/"[^"]+"|[^\s"]+/g)].flat())
        }
        spawn('npm', args, {
          shell: true,
          stdio: 'inherit',
        })
          .on('close', (code: number) => {
            preloadProcess.kill()
            process.exit(code!)
          })
          .on('error', (spawnError) => console.error(spawnError))
      }
      return middlewares
    },
  },
}

export default merge(baseConfig, configuration)


================================================
FILE: .erb/configs/webpack.config.renderer.prod.ts
================================================
/**
 * Build config for electron renderer process
 */

import { sentryWebpackPlugin } from '@sentry/webpack-plugin'
import { TanStackRouterWebpack } from '@tanstack/router-plugin/webpack'
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import path from 'path'
import TerserPlugin from 'terser-webpack-plugin'
import webpack from 'webpack'
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
import { merge } from 'webpack-merge'
import packageJson from '../../release/app/package.json'
import checkNodeEnv from '../scripts/check-node-env'
import baseConfig from './webpack.config.base'
import webpackPaths from './webpack.paths'

checkNodeEnv('production')

const inferredRelease = process.env.SENTRY_RELEASE || packageJson.version
const inferredDist = process.env.SENTRY_DIST || undefined

// Ensure downstream tooling sees consistent release/dist values
process.env.SENTRY_RELEASE = inferredRelease
if (inferredDist) {
  process.env.SENTRY_DIST = inferredDist
}

const configuration: webpack.Configuration = {
  devtool: 'source-map',

  mode: 'production',

  target: ['web', 'electron-renderer'],

  entry: [path.join(webpackPaths.srcRendererPath, 'index.tsx')],

  output: {
    path: webpackPaths.distRendererPath,
    publicPath: process.env.CHATBOX_BUILD_PLATFORM === 'web' ? '/' : './',
    filename: 'assets/js/[name].[contenthash].js', // JS文件放在assets/js目录下
    library: {
      type: 'umd',
    },
  },

  module: {
    rules: [
      {
        test: /\.s?(a|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              modules: true,
              sourceMap: true,
              importLoaders: 1,
            },
          },
          'sass-loader',
        ],
        include: /\.module\.s?(c|a)ss$/,
      },
      {
        test: /\.s?(a|c)ss$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', 'postcss-loader', {
            loader: 'string-replace-loader',
            options: {
              search: /(\d+)dvh/g,
              replace: '$1vh',
            },
          }],
        exclude: /\.module\.s?(c|a)ss$/,
        sideEffects: true,
      },
      // Fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'assets/fonts/[name].[hash][ext]', // 字体资源放在assets/fonts目录下
        },
      },
      // Images
      {
        test: /\.(png|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'assets/images/[name].[hash][ext]', // 图片资源放在assets/images目录下
        },
      },
      // SVG
      {
        test: /\.svg$/,
        use: [
          {
            loader: '@svgr/webpack',
            options: {
              prettier: false,
              svgo: false,
              svgoConfig: {
                plugins: [{ removeViewBox: false }],
              },
              titleProp: true,
              ref: true,
            },
          },
          'file-loader',
        ],
      },
    ],
  },

  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
  },

  plugins: [
    /**
     * Create global constants which can be configured at compile time.
     *
     * Useful for allowing different behaviour between development builds and
     * release builds
     *
     * NODE_ENV should be production so that modules do not perform certain
     * development checks
     */
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'production',
      DEBUG_PROD: false,
    }),

    TanStackRouterWebpack({
      target: 'react',
      autoCodeSplitting: process.env.CHATBOX_BUILD_PLATFORM === 'web' ? true : false,
      routesDirectory: './src/renderer/routes',
      generatedRouteTree: './src/renderer/routeTree.gen.ts',
    }),

    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css', // CSS文件放在assets/css目录下 - 又不放了,因为这样会导致非web端的字体文件引用路径出错
    }),

    new BundleAnalyzerPlugin({
      analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
      analyzerPort: 8889,
    }),

    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: path.join(
        webpackPaths.srcRendererPath,
        process.env.CHATBOX_BUILD_PLATFORM === 'web' ? 'index.web.ejs' : 'index.ejs'
      ),
      minify: {
        collapseWhitespace: true,
        removeAttributeQuotes: true,
        removeComments: true,
      },
      isBrowser: false,
      isDevelopment: false,
      favicon: path.join(webpackPaths.srcRendererPath, 'favicon.ico'),
    }),

    new webpack.DefinePlugin({
      'process.type': '"renderer"',
    }),
    // 禁用混淆,加快构建速度
    // new JavaScriptObfuscator({
    //   optionsPreset: 'default',
    //   // 太卡了
    //   // controlFlowFlattening: true,
    //   // controlFlowFlatteningThreshold: 0.1,

    //   // 默认的变量名混淆,可能被误报为恶意代码
    //   identifierNamesGenerator: 'mangled-shuffled',
    //   // 这些静态字符串混淆后,很可能被误报为恶意代码
    //   exclude: ['initial_data.ts', 'initial_data.js'],

    //   numbersToExpressions: true,
    //   // 保护前端代码不被偷到其他地方部署
    //   // 迁移过程中,暂时关闭保护
    //   // domainLock: ['localhost', ".chatboxai.app", ".chatboxai.com", ".chatboxapp.xyz", "chatbox-pro.pages.dev"],
    //   // domainLockRedirectUrl: 'https://chatboxai.app',
    //   sourceMap: true,
    // }),
    
    process.env.SENTRY_AUTH_TOKEN && sentryWebpackPlugin({
        authToken: process.env.SENTRY_AUTH_TOKEN,
        org: 'sentry',
        project: 'chatbox',
        url: 'https://sentry.midway.run/',
        release: {
          name: inferredRelease,
          ...(inferredDist ? { dist: inferredDist } : {}),
        },
      }),
  ],
}

export default merge(baseConfig, configuration)


================================================
FILE: .erb/configs/webpack.paths.ts
================================================
import path from 'path'

const rootPath = path.join(__dirname, '../..')

const dllPath = path.join(__dirname, '../dll')

const srcPath = path.join(rootPath, 'src')
const srcMainPath = path.join(srcPath, 'main')
const srcRendererPath = path.join(srcPath, 'renderer')

const releasePath = path.join(rootPath, 'release')
const appPath = path.join(releasePath, 'app')
const appPackagePath = path.join(appPath, 'package.json')
const appNodeModulesPath = path.join(appPath, 'node_modules')
const srcNodeModulesPath = path.join(srcPath, 'node_modules')

const distPath = path.join(appPath, 'dist')
const distMainPath = path.join(distPath, 'main')
const distRendererPath = path.join(distPath, 'renderer')

const buildPath = path.join(releasePath, 'build')

export default {
    rootPath,
    dllPath,
    srcPath,
    srcMainPath,
    srcRendererPath,
    releasePath,
    appPath,
    appPackagePath,
    appNodeModulesPath,
    srcNodeModulesPath,
    distPath,
    distMainPath,
    distRendererPath,
    buildPath,
}


================================================
FILE: .erb/mocks/fileMock.js
================================================
export default 'test-file-stub'


================================================
FILE: .erb/scripts/.eslintrc
================================================
{
    "rules": {
        "no-console": "off",
        "global-require": "off",
        "import/no-dynamic-require": "off",
        "import/no-extraneous-dependencies": "off"
    }
}


================================================
FILE: .erb/scripts/check-build-exists.ts
================================================
// Check if the renderer and main bundles are built
import path from 'path'
import chalk from 'chalk'
import fs from 'fs'
import webpackPaths from '../configs/webpack.paths'

const mainPath = path.join(webpackPaths.distMainPath, 'main.js')
const rendererPath = path.join(webpackPaths.distRendererPath, 'renderer.js')

if (!fs.existsSync(mainPath)) {
    throw new Error(
        chalk.whiteBright.bgRed.bold('The main process is not built yet. Build it by running "npm run build:main"')
    )
}

if (!fs.existsSync(rendererPath)) {
    throw new Error(
        chalk.whiteBright.bgRed.bold(
            'The renderer process is not built yet. Build it by running "npm run build:renderer"'
        )
    )
}


================================================
FILE: .erb/scripts/check-native-dep.cjs
================================================
const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')
const { dependencies } = require('../../package.json')

// Simple color helpers (chalk is ESM-only in newer versions)
const colors = {
    blue: (text) => `\x1b[34m${text}\x1b[0m`,
    gray: (text) => `\x1b[90m${text}\x1b[0m`,
    bold: (text) => `\x1b[1m${text}\x1b[0m`,
    bgYellow: (text) => `\x1b[43m\x1b[97m${text}\x1b[0m`,
    bgGreen: (text) => `\x1b[42m\x1b[97m${text}\x1b[0m`,
    bgRed: (text) => `\x1b[41m\x1b[97m${text}\x1b[0m`,
}

// Helper function to recursively find .node files in a directory
function findNodeFiles(dir) {
    const nodeFiles = []
    try {
        const entries = fs.readdirSync(dir, { withFileTypes: true })
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name)
            if (entry.isDirectory()) {
                // Only search in common subdirectories to avoid performance issues
                if (['build', 'prebuilds', 'lib', 'bin'].includes(entry.name)) {
                    nodeFiles.push(...findNodeFiles(fullPath))
                }
            } else if (entry.isFile() && entry.name.endsWith('.node')) {
                nodeFiles.push(fullPath)
            }
        }
    } catch (e) {
        // Ignore permission errors or missing directories
    }
    return nodeFiles
}

// Helper function to get all packages including scoped ones (@scope/pkg)
function getAllPackages(nodeModulesDir) {
    const packages = []
    try {
        const entries = fs.readdirSync(nodeModulesDir, { withFileTypes: true })
        for (const entry of entries) {
            if (!entry.isDirectory()) continue
            if (entry.name.startsWith('@')) {
                // Scoped package - read children
                const scopePath = path.join(nodeModulesDir, entry.name)
                try {
                    const scopedEntries = fs.readdirSync(scopePath, { withFileTypes: true })
                    for (const scopedEntry of scopedEntries) {
                        if (scopedEntry.isDirectory()) {
                            packages.push(`${entry.name}/${scopedEntry.name}`)
                        }
                    }
                } catch (e) {
                    // Ignore errors reading scoped directory
                }
            } else {
                packages.push(entry.name)
            }
        }
    } catch (e) {
        // Ignore errors reading node_modules
    }
    return packages
}

if (dependencies) {
    const dependenciesKeys = Object.keys(dependencies)
    
    // Packages to exclude from native dependency check:
    // These packages have transitive native dependencies but are correctly handled by
    // electron-vite (externalized for main process) and electron-builder (bundled from
    // release/app/node_modules). This check is designed for webpack bundling issues
    // which don't apply to electron-vite's architecture.
    //
    // - capacitor-stream-http: Capacitor plugin, not an Electron native dep
    // - epub: Optional zipfile dep, used in renderer for parsing
    // - @libsql/client, @mastra/libsql: Native bindings for SQLite, externalized by electron-vite
    // - @mastra/core, @mastra/rag: Type imports in shared + runtime in main, externalized
    // - officeparser: Uses pdfjs-dist with native canvas, externalized by electron-vite
    const excludePackages = [
        'capacitor-stream-http',
        'epub',
        '@libsql/client',
        '@mastra/libsql',
        '@mastra/core',
        '@mastra/rag',
        'officeparser',
    ]
    
    // Get all packages including scoped ones (@scope/pkg)
    const allPackages = getAllPackages('node_modules')
    
    // Check for packages with binding.gyp (source-based native modules)
    const nativeDepsByBindingGyp = allPackages.filter((pkg) => {
        if (excludePackages.includes(pkg)) return false
        return fs.existsSync(`node_modules/${pkg}/binding.gyp`)
    })
    
    // Check for packages with .node files (precompiled native modules)
    const nativeDepsByNodeFiles = allPackages.filter((pkg) => {
        if (excludePackages.includes(pkg)) return false
        const nodeFiles = findNodeFiles(`node_modules/${pkg}`)
        return nodeFiles.length > 0
    })
    
    // Combine both types of native dependencies
    const allNativeDeps = [...new Set([...nativeDepsByBindingGyp, ...nativeDepsByNodeFiles])]
    
    if (allNativeDeps.length === 0) {
        process.exit(0)
    }
    
    console.debug(colors.blue(`Found native dependencies: ${allNativeDeps.join(', ')}`))
    console.debug(colors.gray(`- With binding.gyp: ${nativeDepsByBindingGyp.join(', ') || 'none'}`))
    console.debug(colors.gray(`- With .node files: ${nativeDepsByNodeFiles.join(', ') || 'none'}`))
    
    try {
        // Find the reason for why the dependency is installed. If it is installed
        // because of a devDependency then that is okay. Warn when it is installed
        // because of a dependency
        // Note: pnpm ls --json returns an array (one entry per workspace package)
        const lsResult = JSON.parse(
            execSync(`pnpm ls ${allNativeDeps.join(' ')} --json`).toString()
        )
        const rootResult = Array.isArray(lsResult)
            ? lsResult.find((item) => item.path === process.cwd()) ?? lsResult[0]
            : lsResult
        const dependenciesObject = rootResult?.dependencies ?? {}
        const rootDependencies = Object.keys(dependenciesObject)
        const filteredRootDependencies = rootDependencies.filter((rootDependency) =>
            dependenciesKeys.includes(rootDependency) && !excludePackages.includes(rootDependency)
        )
        if (filteredRootDependencies.length > 0) {
            const plural = filteredRootDependencies.length > 1
            console.log(`
 ${colors.bgYellow(colors.bold('Webpack does not work with native dependencies.'))}
${colors.bold(filteredRootDependencies.join(', '))} ${
                plural ? 'are native dependencies' : 'is a native dependency'
            } and should be installed inside of the "./release/app" folder.
 First, uninstall the packages from "./package.json":
${colors.bgGreen(colors.bold('pnpm remove your-package'))}
 ${colors.bold('Then, instead of installing the package to the root "./package.json":')}
${colors.bgRed(colors.bold('pnpm add your-package'))}
 ${colors.bold('Install the package to "./release/app/package.json"')}
${colors.bgGreen(colors.bold('cd ./release/app && pnpm add your-package'))}
 Read more about native dependencies at:
${colors.bold('https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure')}
 `)
            process.exit(1)
        }
    } catch (e) {
        console.log('Native dependencies could not be checked:', e.message)
    }
}


================================================
FILE: .erb/scripts/check-native-dep.js
================================================
import chalk from 'chalk'
import { execSync } from 'child_process'
import fs from 'fs'
import path from 'path'
import { dependencies } from '../../package.json'

// Helper function to recursively find .node files in a directory
function findNodeFiles(dir) {
    const nodeFiles = []
    try {
        const entries = fs.readdirSync(dir, { withFileTypes: true })
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name)
            if (entry.isDirectory()) {
                // Only search in common subdirectories to avoid performance issues
                if (['build', 'prebuilds', 'lib', 'bin'].includes(entry.name)) {
                    nodeFiles.push(...findNodeFiles(fullPath))
                }
            } else if (entry.isFile() && entry.name.endsWith('.node')) {
                nodeFiles.push(fullPath)
            }
        }
    } catch (e) {
        // Ignore permission errors or missing directories
    }
    return nodeFiles
}

// Helper function to get all packages including scoped ones (@scope/pkg)
function getAllPackages(nodeModulesDir) {
    const packages = []
    try {
        const entries = fs.readdirSync(nodeModulesDir, { withFileTypes: true })
        for (const entry of entries) {
            if (!entry.isDirectory()) continue
            if (entry.name.startsWith('@')) {
                // Scoped package - read children
                const scopePath = path.join(nodeModulesDir, entry.name)
                try {
                    const scopedEntries = fs.readdirSync(scopePath, { withFileTypes: true })
                    for (const scopedEntry of scopedEntries) {
                        if (scopedEntry.isDirectory()) {
                            packages.push(`${entry.name}/${scopedEntry.name}`)
                        }
                    }
                } catch (e) {
                    // Ignore errors reading scoped directory
                }
            } else {
                packages.push(entry.name)
            }
        }
    } catch (e) {
        // Ignore errors reading node_modules
    }
    return packages
}

if (dependencies) {
    const dependenciesKeys = Object.keys(dependencies)
    
    // Packages to exclude from native dependency check:
    // These packages have transitive native dependencies but are correctly handled by
    // electron-vite (externalized for main process) and electron-builder (bundled from
    // release/app/node_modules). This check is designed for webpack bundling issues
    // which don't apply to electron-vite's architecture.
    //
    // - capacitor-stream-http: Capacitor plugin, not an Electron native dep
    // - epub: Optional zipfile dep, used in renderer for parsing
    // - @libsql/client, @mastra/libsql: Native bindings for SQLite, externalized by electron-vite
    // - @mastra/core, @mastra/rag: Type imports in shared + runtime in main, externalized
    // - officeparser: Uses pdfjs-dist with native canvas, externalized by electron-vite
    const excludePackages = [
        'capacitor-stream-http',
        'epub',
        '@libsql/client',
        '@mastra/libsql',
        '@mastra/core',
        '@mastra/rag',
        'officeparser',
    ]
    
    // Get all packages including scoped ones (@scope/pkg)
    const allPackages = getAllPackages('node_modules')
    
    // Check for packages with binding.gyp (source-based native modules)
    const nativeDepsByBindingGyp = allPackages.filter((pkg) => {
        if (excludePackages.includes(pkg)) return false
        return fs.existsSync(`node_modules/${pkg}/binding.gyp`)
    })
    
    // Check for packages with .node files (precompiled native modules)
    const nativeDepsByNodeFiles = allPackages.filter((pkg) => {
        if (excludePackages.includes(pkg)) return false
        const nodeFiles = findNodeFiles(`node_modules/${pkg}`)
        return nodeFiles.length > 0
    })
    
    // Combine both types of native dependencies
    const allNativeDeps = [...new Set([...nativeDepsByBindingGyp, ...nativeDepsByNodeFiles])]
    
    if (allNativeDeps.length === 0) {
        process.exit(0)
    }
    
    console.debug(chalk.blue(`Found native dependencies: ${allNativeDeps.join(', ')}`))
    console.debug(chalk.gray(`- With binding.gyp: ${nativeDepsByBindingGyp.join(', ') || 'none'}`))
    console.debug(chalk.gray(`- With .node files: ${nativeDepsByNodeFiles.join(', ') || 'none'}`))
    
    try {
        // Find the reason for why the dependency is installed. If it is installed
        // because of a devDependency then that is okay. Warn when it is installed
        // because of a dependency
        // Note: pnpm ls --json returns an array (one entry per workspace package)
        const lsResult = JSON.parse(
            execSync(`pnpm ls ${allNativeDeps.join(' ')} --json`).toString()
        )
        const rootResult = Array.isArray(lsResult)
            ? lsResult.find((item) => item.path === process.cwd()) ?? lsResult[0]
            : lsResult
        const dependenciesObject = rootResult?.dependencies ?? {}
        const rootDependencies = Object.keys(dependenciesObject)
        const filteredRootDependencies = rootDependencies.filter((rootDependency) =>
            dependenciesKeys.includes(rootDependency) && !excludePackages.includes(rootDependency)
        )
        if (filteredRootDependencies.length > 0) {
            const plural = filteredRootDependencies.length > 1
            console.log(`
 ${chalk.whiteBright.bgYellow.bold('Webpack does not work with native dependencies.')}
${chalk.bold(filteredRootDependencies.join(', '))} ${
                plural ? 'are native dependencies' : 'is a native dependency'
            } and should be installed inside of the "./release/app" folder.
 First, uninstall the packages from "./package.json":
${chalk.whiteBright.bgGreen.bold('pnpm remove your-package')}
 ${chalk.bold('Then, instead of installing the package to the root "./package.json":')}
${chalk.whiteBright.bgRed.bold('pnpm add your-package')}
 ${chalk.bold('Install the package to "./release/app/package.json"')}
${chalk.whiteBright.bgGreen.bold('cd ./release/app && pnpm add your-package')}
 Read more about native dependencies at:
${chalk.bold('https://electron-react-boilerplate.js.org/docs/adding-dependencies/#module-structure')}
 `)
            process.exit(1)
        }
    } catch (e) {
        console.log('Native dependencies could not be checked:', e.message)
    }
}


================================================
FILE: .erb/scripts/check-node-env.js
================================================
import chalk from 'chalk'

export default function checkNodeEnv(expectedEnv) {
    if (!expectedEnv) {
        throw new Error('"expectedEnv" not set')
    }

    if (process.env.NODE_ENV !== expectedEnv) {
        console.log(
            chalk.whiteBright.bgRed.bold(`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`)
        )
        process.exit(2)
    }
}


================================================
FILE: .erb/scripts/check-port-in-use.js
================================================
import chalk from 'chalk'
import detectPort from 'detect-port'

const port = process.env.PORT || '1212'

detectPort(port, (err, availablePort) => {
    if (port !== String(availablePort)) {
        throw new Error(
            chalk.whiteBright.bgRed.bold(
                `Port "${port}" on "localhost" is already in use. Please use another port. ex: PORT=4343 npm start`
            )
        )
    } else {
        process.exit(0)
    }
})


================================================
FILE: .erb/scripts/clean.js
================================================
import { rimrafSync } from 'rimraf'
import fs from 'fs'
import webpackPaths from '../configs/webpack.paths'

const foldersToRemove = [webpackPaths.distPath, webpackPaths.appNodeModulesPath, webpackPaths.buildPath, webpackPaths.dllPath]

foldersToRemove.forEach((folder) => {
    if (fs.existsSync(folder)) rimrafSync(folder)
})


================================================
FILE: .erb/scripts/delete-source-maps.js
================================================
import fs from 'fs'
import path from 'path'
import { rimrafSync } from 'rimraf'
import webpackPaths from '../configs/webpack.paths'

export default function deleteSourceMaps() {
    if (fs.existsSync(webpackPaths.distMainPath))
        rimrafSync(path.join(webpackPaths.distMainPath, '*.js.map'), {
            glob: true,
        })
    if (fs.existsSync(webpackPaths.distRendererPath))
        rimrafSync(path.join(webpackPaths.distRendererPath, '*.js.map'), {
            glob: true,
        })
}


================================================
FILE: .erb/scripts/electron-rebuild.cjs
================================================
const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')

// Inline the paths instead of importing from webpack.paths.ts
const rootPath = path.join(__dirname, '../..')
const appPath = path.join(rootPath, 'release/app')
const appNodeModulesPath = path.join(appPath, 'node_modules')

// Read dependencies from release/app/package.json
const appPackageJson = require('../../release/app/package.json')
const dependencies = appPackageJson.dependencies || {}

if (Object.keys(dependencies).length > 0 && fs.existsSync(appNodeModulesPath)) {
    const electronRebuildCmd =
        '../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .'
    const cmd = process.platform === 'win32' ? electronRebuildCmd.replace(/\//g, '\\') : electronRebuildCmd
    execSync(cmd, {
        cwd: appPath,
        stdio: 'inherit',
    })
}


================================================
FILE: .erb/scripts/electron-rebuild.js
================================================
import { execSync } from 'child_process'
import fs from 'fs'
import { dependencies } from '../../release/app/package.json'
import webpackPaths from '../configs/webpack.paths'

if (Object.keys(dependencies || {}).length > 0 && fs.existsSync(webpackPaths.appNodeModulesPath)) {
    const electronRebuildCmd =
        '../../node_modules/.bin/electron-rebuild --force --types prod,dev,optional --module-dir .'
    const cmd = process.platform === 'win32' ? electronRebuildCmd.replace(/\//g, '\\') : electronRebuildCmd
    execSync(cmd, {
        cwd: webpackPaths.appPath,
        stdio: 'inherit',
    })
}


================================================
FILE: .erb/scripts/link-modules.cjs
================================================
const fs = require('fs')
const path = require('path')

// Inline the paths
const rootPath = path.join(__dirname, '../..')
const srcNodeModulesPath = path.join(rootPath, 'src/node_modules')
const appNodeModulesPath = path.join(rootPath, 'release/app/node_modules')

if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) {
    fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction')
}


================================================
FILE: .erb/scripts/link-modules.ts
================================================
import fs from 'fs'
import webpackPaths from '../configs/webpack.paths'

const { srcNodeModulesPath } = webpackPaths
const { appNodeModulesPath } = webpackPaths

if (!fs.existsSync(srcNodeModulesPath) && fs.existsSync(appNodeModulesPath)) {
    fs.symlinkSync(appNodeModulesPath, srcNodeModulesPath, 'junction')
}


================================================
FILE: .erb/scripts/notarize.js
================================================
const { notarize } = require('@electron/notarize')

exports.default = async function notarizeMacos(context) {
    const { electronPlatformName, appOutDir } = context
    if (electronPlatformName !== 'darwin') {
        return
    }

    if (!('APPLE_ID' in process.env && 'APPLE_ID_PASS' in process.env && 'APPLE_TEAM_ID' in process.env)) {
        console.warn('Skipping notarizing step. APPLE_ID, APPLE_ID_PASS and APPLE_TEAM_ID env variables must be set')
        return
    }

    const appName = context.packager.appInfo.productFilename

    console.log('[Notarize] start macOS notarization: notarize.js running with notarytool')

    await notarize({
        tool: 'notarytool',
        appBundleId: 'xyz.chatboxapp.app',
        appPath: `${appOutDir}/${appName}.app`,
        appleId: process.env.APPLE_ID,
        appleIdPassword: process.env.APPLE_ID_PASS,
        teamId: process.env.APPLE_TEAM_ID,
    })
}


================================================
FILE: .erb/scripts/patch-libsql.cjs
================================================
const fs = require('fs')
const path = require('path')

const muslTargetBlock = `  // @neon-rs/load doesn't detect arm musl
  if (target === "linux-arm-gnueabihf" && familySync() == MUSL) {
      target = "linux-arm-musleabihf";
  }
`

const winArm64GuardBlock = `  if (target === "win32-arm64-msvc") {
    console.log("[libsql] Windows ARM64 detected - native module not available");
    return {};
  }
`

const directRequireBlock = `  return require(\`@libsql/\${target}\`);
`

const tryCatchRequireBlock = `  try {
    return require(\`@libsql/\${target}\`);
  } catch (e) {
    const isMissingTarget =
      e?.code === "MODULE_NOT_FOUND" &&
      typeof e?.message === "string" &&
      (e.message.includes(\`@libsql/\${target}\`) || e.message.includes(\`@libsql\\\\\${target}\`));
    if (!isMissingTarget) throw e;
    console.error(\`[libsql] Native module @libsql/\${target} not found\`);
    return {};
  }
`

const oldIncludeLine = '      e.message.includes(`@libsql/${target}`);'
const newIncludeLine =
  '      (e.message.includes(`@libsql/${target}`) || e.message.includes(`@libsql\\${target}`));'

function patchLibsqlFile(filePath) {
  if (!fs.existsSync(filePath)) {
    return 'missing'
  }

  const source = fs.readFileSync(filePath, 'utf8')
  let patched = source

  if (patched.includes(oldIncludeLine)) {
    patched = patched.replaceAll(oldIncludeLine, newIncludeLine)
  }

  if (!patched.includes('if (target === "win32-arm64-msvc") {') && patched.includes(muslTargetBlock)) {
    patched = patched.replace(muslTargetBlock, `${muslTargetBlock}${winArm64GuardBlock}`)
  }

  if (patched.includes(directRequireBlock)) {
    patched = patched.replace(directRequireBlock, tryCatchRequireBlock)
  }

  const isFullyPatched =
    patched.includes('if (target === "win32-arm64-msvc") {') &&
    patched.includes('Native module @libsql/${target} not found') &&
    patched.includes('e.message.includes(`@libsql\\\\${target}`)')

  if (!isFullyPatched) {
    return 'skip-unknown'
  }

  if (patched === source) {
    return 'already-patched'
  }

  fs.writeFileSync(filePath, patched, 'utf8')
  return 'patched'
}

function getCandidateLibsqlDirs(context) {
  const candidates = []
  const appDir =
    context.appDir ||
    context.packager?.appDir ||
    (context.packager?.projectDir && path.join(context.packager.projectDir, 'release', 'app'))

  if (appDir) {
    candidates.push(path.join(appDir, 'node_modules', 'libsql'))
  }

  if (context.appOutDir) {
    const productFilename = context.packager?.appInfo?.productFilename
    if (productFilename) {
      candidates.push(
        path.join(
          context.appOutDir,
          `${productFilename}.app`,
          'Contents',
          'Resources',
          'app.asar.unpacked',
          'node_modules',
          'libsql',
        ),
      )
    }
    candidates.push(path.join(context.appOutDir, 'resources', 'app.asar.unpacked', 'node_modules', 'libsql'))
  }

  return [...new Set(candidates)]
}

exports.default = async function patchLibsql(context) {
  let touched = false

  for (const libsqlDir of getCandidateLibsqlDirs(context)) {
    for (const file of ['index.js', 'promise.js']) {
      const filePath = path.join(libsqlDir, file)
      const state = patchLibsqlFile(filePath)
      if (state === 'patched') {
        touched = true
        console.log(`[patch-libsql] patched ${filePath}`)
      } else if (state === 'already-patched') {
        touched = true
        console.log(`[patch-libsql] already patched ${filePath}`)
      } else if (state === 'skip-unknown') {
        console.warn(`[patch-libsql] skip unknown structure: ${filePath}`)
      }
    }
  }

  if (!touched) {
    console.warn('[patch-libsql] no libsql files patched')
  }
}


================================================
FILE: .erb/scripts/postinstall.cjs
================================================
/**
 * Root postinstall script.
 * 
 * NOTE: We intentionally do NOT run electron-builder install-app-deps here.
 * With pnpm workspaces, electron-builder install-app-deps corrupts the shared
 * node_modules by running pnpm install --production in release/app.
 * 
 * Native module rebuilding is handled by:
 * 1. release/app/postinstall runs electron-rebuild for native deps in release/app
 * 2. The build process handles the rest
 */
const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')

// Run native dependency check
try {
    require('./check-native-dep.cjs')
} catch (e) {
    if (e.code === 'MODULE_NOT_FOUND') {
        console.log('Native dependency check skipped: module not found')
    } else {
        throw e
    }
}

console.log('Postinstall complete (skipping electron-builder install-app-deps for pnpm compatibility)')


================================================
FILE: .eslintignore
================================================
# 暂时关掉 eslint
* 

# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Coverage directory used by tools like istanbul
coverage
.eslintcache

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# OSX
.DS_Store

release/app/dist
release/build
.erb/dll

.idea
npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts

# eslint ignores hidden directories by default:
# https://github.com/eslint/eslint/issues/8429
!.erb


================================================
FILE: .eslintrc.js
================================================
module.exports = {
    extends: 'erb',
    plugins: ['@typescript-eslint'],
    rules: {
        // A temporary hack related to IDE not resolving correct package.json
        'import/no-extraneous-dependencies': 'off',
        'react/react-in-jsx-scope': 'off',
        'react/jsx-filename-extension': 'off',
        'import/extensions': 'off',
        'import/no-unresolved': 'off',
        'import/no-import-module-exports': 'off',
        'no-shadow': 'off',
        '@typescript-eslint/no-shadow': 'error',
        'no-unused-vars': 'off',
        '@typescript-eslint/no-unused-vars': 'error',
    },
    parserOptions: {
        ecmaVersion: 2020,
        sourceType: 'module',
        project: './tsconfig.json',
        tsconfigRootDir: __dirname,
        createDefaultProgram: true,
    },
    settings: {
        'import/resolver': {
            // See https://github.com/benmosher/eslint-plugin-import/issues/1396#issuecomment-575727774 for line below
            node: {},
            webpack: {
                config: require.resolve('./.erb/configs/webpack.config.eslint.ts'),
            },
            typescript: {},
        },
        'import/parsers': {
            '@typescript-eslint/parser': ['.ts', '.tsx'],
        },
    },
}


================================================
FILE: .gitattributes
================================================
*       text    eol=lf
*.exe   binary
*.png   binary
*.jpg   binary
*.jpeg  binary
*.ico   binary
*.icns  binary
*.eot   binary
*.otf   binary
*.ttf   binary
*.woff  binary
*.woff2 binary


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: Bin-Huang
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve / BUG 反馈(提交前请搜索是否存在重复issues)
title: '[BUG]'
labels: ''
assignees: ''
---

**Bug Description**
Please provide a clear and concise description of what the bug is.

**Steps to Reproduce**
Please provide the steps to reproduce the bug:

1. Go to "..."
2. Click on "..."
3. Scroll down to "..."
4. Observe the bug.

**Expected Results**
Please provide a clear and concise description of what you expected to happen.

**Actual Results**
Please provide a clear and concise description of what actually happened.

**Screenshots**
If possible, please add screenshots to help explain the issue.

**Desktop (please complete the following information):**

-   Operating System: [e.g. macOS]
-   Application Version: [e.g. 2.0.1]

**Additional Context**
Please provide any additional context about the issue, such as interactions with other software or applications.

---

**Bug 描述**
清晰简洁地描述这个 bug 是什么。

**重现步骤**
请提供能够让我们重现这个 bug 的步骤:

1. 前往 "......"
2. 点击 "......"
3. 滚动到 "......"
4. 发现了这个 bug。

**期望结果**
请清晰简洁地描述预期的行为。

**实际结果**
请清晰简洁地描述实际的行为。

**截图**
如果可行,添加截图以帮助解释问题。

**桌面端(请填写以下信息):**

-   操作系统:[例如 macOS]
-   应用程序版本:[例如 2.0.1]

**其他上下文**
在这里提供关于问题的任何其他上下文,例如与其他软件或应用程序的交互等。


================================================
FILE: .github/ISSUE_TEMPLATE/custom.md
================================================
---
name: Custom issue template
about: Describe this issue template's purpose here. / 其他建设性意见与讨论
title: '[Other]'
labels: ''
assignees: ''
---


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project / 新功能新特性的想法(提交前请检查是否有重复 issues)
title: '[Feature]'
labels: ''
assignees: ''
---

**Problem Description**
Please describe the issue or difficulty you are experiencing and why it makes using the software difficult or frustrating.

**Proposed Solution**
Please provide a clear and concise description of what you would like to see in terms of a function or solution.

**Additional Context**
Please provide any additional context or information that would help better understanding your feature request, such as screenshots, examples, or use cases.

---

**问题描述**
请描述您遇到的问题或难题,以及为什么这使得使用软件变得困难或令人沮丧。

**解决思路**
请提供一个清晰、简洁的描述,说明您希望看到的功能或解决方案。

**附加上下文**
请提供任何其他上下文或信息,以便更好地理解您的功能请求,例如截图、示例或用例。


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
### Description

[Please provide a detailed description of your contribution, including the main changes and their purpose]

### Additional Notes

[If you have any additional comments or notes, please add them here]

### Screenshots

[Optional: Include screenshots that help explain your PR]

### Contributor Agreement

By submitting this Pull Request, I confirm that I have read and agree to the following terms:

- I agree to contribute all code submitted in this PR to the open-source community edition licensed under GPLv3 and the proprietary official edition without compensation.
- I grant the official edition development team the rights to freely use, modify, and distribute this code, including for commercial purposes.
- I confirm that this code is my original work, or I have obtained the appropriate authorization from the copyright holder to submit this code under these terms.
- I understand that the submitted code will be publicly released under the GPLv3 license, and may also be used in the proprietary official edition.

**Please check the box below to confirm:**

[ ] I have read and agree with the above statement.


================================================
FILE: .github/config.yml
================================================
requiredHeaders:
    - Prerequisites
    - Expected Behavior
    - Current Behavior
    - Possible Solution
    - Your Environment


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"


================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
    - discussion
    - security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
    This issue has been automatically marked as stale because it has not had
    recent activity. It will be closed if no further activity occurs. Thank you
    for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock
.DS_Store

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# Webpack
.webpack/

# Electron-Forge
out/

publish.sh
build.sh
build-web.sh

# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage
/test/output

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# IDE / Editor
.idea
.dir-locals.el

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

tmp

# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Coverage directory used by tools like istanbul
coverage
.eslintcache

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# OSX
.DS_Store

release/app/dist
release/build
.erb/dll

.idea
npm-debug.log.*
*.css.d.ts
*.sass.d.ts
*.scss.d.ts

dist

electron-builder.env

# Share VS Code settings
# .vscode


**/routeTree.gen.ts

.env.sentry-build-plugin
.sisyphus/

================================================
FILE: .node-version
================================================
v22.7.0


================================================
FILE: .npmrc
================================================
# Electron compatibility - use flat node_modules
node-linker=hoisted

# Auto-install peer dependencies
auto-install-peers=true

# Preserved from original npm config
node-options=--max-old-space-size=4096
update-notifier=false
engine-strict=true


================================================
FILE: .prettierrc
================================================
{
    "tabWidth": 2,
    "singleQuote": true,
    "printWidth": 120,
    "semi": false,
    "overrides": [
        {
            "files": [
                ".prettierrc",
                ".eslintrc"
            ],
            "options": {
                "parser": "json"
            }
        }
    ]
}

================================================
FILE: ERROR_HANDLING.md
================================================
# Error Handling Improvements

This document describes the error handling improvements made to Chatbox to address the issue where some users experienced "something went wrong!" errors with "cannot read properties of undefined" that were not being reported to Sentry.

## Changes Made

### 1. React Error Boundary (`src/renderer/components/ErrorBoundary.tsx`)

Created a comprehensive React Error Boundary component that:
- Catches React component rendering errors
- Automatically reports errors to Sentry with detailed context
- Displays a user-friendly error UI with retry options
- Shows detailed error information when requested
- Provides both custom and Sentry-wrapped error boundary variants

### 2. Global Error Handlers (`src/renderer/setup/global_error_handler.ts`)

Added global error handlers for:
- **Window errors**: Catches unhandled JavaScript errors
- **Unhandled promise rejections**: Catches async errors that weren't handled
- **Console error interception**: Monitors console errors for specific patterns like "cannot read properties of undefined"

### 3. Application Integration

Updated the main application files:
- `src/renderer/index.tsx`: Wrapped both initialization and main app with ErrorBoundary
- `src/renderer/routes/__root.tsx`: Added error boundary at the route level
- Added global error handler initialization

### 4. Error Testing Utilities (`src/renderer/utils/error-testing.ts`)

Created testing utilities for development mode:
- Test React error boundaries
- Test global error handlers
- Test unhandled promise rejections
- Test Sentry integration
- Available at `window.errorTestingUtils` in development

## Error Catching Strategy

The solution implements a multi-layered error catching approach:

1. **React Error Boundaries**: Catch component rendering errors
2. **Global Window Handlers**: Catch unhandled JavaScript errors
3. **Promise Rejection Handlers**: Catch unhandled async errors
4. **Console Error Monitoring**: Detect specific error patterns
5. **Existing Try-Catch Blocks**: Already handling API and model errors

## Testing

In development mode, you can test error handling using:

```javascript
// Test React error boundary
window.errorTestingUtils.triggerReactError()

// Test global error handler
window.errorTestingUtils.triggerGlobalError()

// Test unhandled promise rejection
window.errorTestingUtils.triggerUnhandledRejection()

// Test property access error
window.errorTestingUtils.triggerPropertyError()

// Test Sentry integration
window.errorTestingUtils.testSentryCapture()

// Test console error interception
window.errorTestingUtils.triggerConsoleError()
```

## User Experience

When errors occur, users will see:
- A clean error UI instead of broken components
- Options to retry or reload the application
- Ability to view error details if needed
- Automatic error reporting to Sentry for debugging

## Benefits

1. **Better Error Reporting**: All errors are now captured and sent to Sentry
2. **Improved User Experience**: Users see helpful error messages instead of broken UI
3. **Easier Debugging**: Detailed error context is provided to developers
4. **Graceful Recovery**: Users can retry operations or reload the app
5. **Comprehensive Coverage**: Multiple layers catch different types of errors

## Sentry Integration

All caught errors are reported to Sentry with:
- Error type tags (React, global, promise rejection, etc.)
- Detailed context about the error
- Component stack traces (for React errors)
- Browser and application information
- User session data

This ensures that developers can identify and fix issues that users encounter in production. 

================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

================================================
FILE: README.md
================================================
<p align="right">
  <a href="README.md">English</a> |
  <a href="./doc/README-CN.md">简体中文</a>
</p>

This is the repository for the Chatbox Community Edition, open-sourced under the GPLv3 license.

[Chatbox is going open-source Again!](https://github.com/chatboxai/chatbox/issues/2266)

We regularly sync code from the pro repo to this repo, and vice versa.

### Download for Desktop

<table style="width: 100%">
  <tr>
    <td width="25%" align="center">
      <b>Windows</b>
    </td>
    <td width="25%" align="center" colspan="2">
      <b>MacOS</b>
    </td>
    <td width="25%" align="center">
      <b>Linux</b>
    </td>
  </tr>
  <tr style="text-align: center">
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-windows'>
        <img src='./doc/statics/windows.png' style="height:24px; width: 24px" />
        <br />
        <b>Setup.exe</b>
      </a>
    </td>
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-mac-intel'>
        <img src='./doc/statics/mac.png' style="height:24px; width: 24px" />
        <br />
        <b>Intel</b>
      </a>
    </td>
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-mac-aarch'>
        <img src='./doc/statics/mac.png' style="height:24px; width: 24px" />
        <br />
        <b style="white-space: nowrap;">Apple Silicon</b>
      </a>
    </td>
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-linux'>
        <img src='./doc/statics/linux.png' style="height:24px; width: 24px" />
        <br />
        <b>AppImage</b>
      </a>
    </td>
  </tr>
</table>

### Download for iOS/Android

<a href='https://apps.apple.com/app/chatbox-ai/id6471368056' style='margin-right: 4px'>
<img src='./doc/statics/app_store.webp' style="height:38px;" />
</a>
<a href='https://play.google.com/store/apps/details?id=xyz.chatboxapp.chatbox' style='margin-right: 4px'>
<img src='./doc/statics/google_play.png' style="height:38px;" />
</a>
<a href='https://chatboxai.app/install?download=android_apk' style='margin-right: 4px; display: inline-flex; justify-content: center'>
<img src='./doc/statics/android.png' style="height:28px; display: inline-block" />
.APK
</a>

For more information: [chatboxai.app](https://chatboxai.app/)

---
<div align="center" markdown="1">
  <a href="https://go.warp.dev/chatbox">
    <img alt="Warp sponsorship" width="400" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-02.png">
  </a>

### [Warp, built for coding with multiple AI agents.](https://go.warp.dev/chatbox)
[Available for MacOS, Linux, & Windows](https://go.warp.dev/chatbox)<br>
</div>

<hr>

<h1 align="center">
<img src='./doc/statics/icon.png' width='30'>
<span>
    Chatbox
    <span style="font-size:8px; font-weight: normal;">(Community Edition)</span>
</span>
</h1>
<p align="center">
    <em>Your Ultimate AI Copilot on the Desktop. <br />Chatbox is a desktop client for ChatGPT, Claude and other LLMs, available on Windows, Mac, Linux</em>
</p>

<p align="center">
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="macOS" src="https://img.shields.io/badge/-macOS-black?style=flat-square&logo=apple&logoColor=white" />
</a>
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="Windows" src="https://img.shields.io/badge/-Windows-blue?style=flat-square&logo=windows&logoColor=white" />
</a>
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="Linux" src="https://img.shields.io/badge/-Linux-yellow?style=flat-square&logo=linux&logoColor=white" />
</a>
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="Downloads" src="https://img.shields.io/github/downloads/chatboxai/chatbox/total.svg?style=flat" />
</a>
</p>

<a href="https://www.producthunt.com/posts/chatbox?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-chatbox" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=429547&theme=light" alt="Chatbox - Better&#0032;UI&#0032;&#0038;&#0032;Desktop&#0032;App&#0032;for&#0032;ChatGPT&#0044;&#0032;Claude&#0032;and&#0032;other&#0032;LLMs&#0046; | Product Hunt" style="width: 150px; height: 30px;" width="100" height="40" /></a>

<a href="./doc/statics/snapshot_light.png">
<img src="./doc/statics/snapshot_light.png" width="400"/>
</a>
<a href="./doc/statics/snapshot_dark.png">
<img src="./doc/statics/snapshot_dark.png" width="400"/>
</a>

<!-- <table>
<tr>
<td>
<img src="./dec/../doc/demo_mobile_1.png" alt="App Screenshot" style="box-shadow: 2px 2px 10px rgba(0,0,0,0.1); border: 1px solid #ddd; border-radius: 8px; height: 300px" />
</td>
<td>
<img src="./dec/../doc/demo_mobile_2.png" alt="App Screenshot" style="box-shadow: 2px 2px 10px rgba(0,0,0,0.1); border: 1px solid #ddd; border-radius: 8px; height: 300px" />
</td>
</tr>
</table> -->

## Features

-   **Local Data Storage**  
    :floppy_disk: Your data remains on your device, ensuring it never gets lost and maintains your privacy.

-   **No-Deployment Installation Packages**  
    :package: Get started quickly with downloadable installation packages. No complex setup necessary!

-   **Support for Multiple LLM Providers**  
    :gear: Seamlessly integrate with a variety of cutting-edge language models:

    -   OpenAI (ChatGPT)
    -   Azure OpenAI
    -   Claude
    -   Google Gemini Pro
    -   Ollama (enable access to local models like llama2, Mistral, Mixtral, codellama, vicuna, yi, and solar)
    -   ChatGLM-6B

-   **Image Generation with Dall-E-3**  
    :art: Create the images of your imagination with Dall-E-3.

-   **Enhanced Prompting**  
    :speech_balloon: Advanced prompting features to refine and focus your queries for better responses.

-   **Keyboard Shortcuts**  
    :keyboard: Stay productive with shortcuts that speed up your workflow.

-   **Markdown, Latex & Code Highlighting**  
    :scroll: Generate messages with the full power of Markdown and Latex formatting, coupled with syntax highlighting for various programming languages, enhancing readability and presentation.

-   **Prompt Library & Message Quoting**  
    :books: Save and organize prompts for reuse, and quote messages for context in discussions.

-   **Streaming Reply**  
    :arrow_forward: Provide rapid responses to your interactions with immediate, progressive replies.

-   **Ergonomic UI & Dark Theme**  
    :new_moon: A user-friendly interface with a night mode option for reduced eye strain during extended use.

-   **Team Collaboration**  
    :busts_in_silhouette: Collaborate with ease and share OpenAI API resources among your team. [Learn More](./team-sharing/README.md)

-   **Cross-Platform Availability**  
    :computer: Chatbox is ready for Windows, Mac, Linux users.

-   **Access Anywhere with the Web Version**  
    :globe_with_meridians: Use the web application on any device with a browser, anywhere.

-   **iOS & Android**  
    :phone: Use the mobile applications that will bring this power to your fingertips on the go.

-   **Multilingual Support**  
    :earth_americas: Catering to a global audience by offering support in multiple languages:

    -   English
    -   简体中文 (Simplified Chinese)
    -   繁體中文 (Traditional Chinese)
    -   日本語 (Japanese)
    -   한국어 (Korean)
    -   Français (French)
    -   Deutsch (German)
    -   Русский (Russian)
    -   Español (Spanish)

-   **And More...**  
    :sparkles: Constantly enhancing the experience with new features!

## FAQ

-   [Frequently Asked Questions](./doc/FAQ.md)

## Why I made Chatbox?

I developed Chatbox initially because I was debugging some prompts and found myself in need of a simple and easy-to-use prompt and API debugging tool. I thought there might be more people who needed such a tool, so I open-sourced it.

At first, I didn't know that it would be so popular. I listened to the feedback from the open-source community and continued to develop and improve it. Now, it has become a very useful AI desktop application. There are many users who love Chatbox, and they not only use it for developing and debugging prompts, but also for daily chatting, and even to do some more interesting things like using well-designed prompts to make AI play various professional roles to assist them in everyday work...

## How to Contribute

Any form of contribution is welcome, including but not limited to:

-   Submitting issues
-   Submitting pull requests
-   Submitting feature requests
-   Submitting bug reports
-   Submitting documentation revisions
-   Submitting translations
-   Submitting any other forms of contribution

## Prerequisites

- Node.js (v20.x – v22.x)
- npm (required – pnpm is not supported)

## Build Instructions

1. Clone the repository from Github

```bash
git clone https://github.com/chatboxai/chatbox.git
```

2. Install the required dependencies

```bash
npm install
```

3. Start the application (in development mode)

```bash
npm run dev
```

4. Build the application, package the installer for current platform

```bash
npm run package
```

5. Build the application, package the installer for all platforms

```bash
npm run package:all
```

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=chatboxai/chatbox&type=Date)](https://star-history.com/#chatboxai/chatbox&Date)

## Contact

[Twitter](https://x.com/ChatboxAI_HQ) | [Email](mailto:hi@chatboxai.com)

## License

[LICENSE](./LICENSE)


================================================
FILE: assets/assets.d.ts
================================================
type Styles = Record<string, string>

declare module '*.svg' {
    import React = require('react')

    export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>

    const content: string
    export default content
}

declare module '*.png' {
    const content: string
    export default content
}

declare module '*.jpg' {
    const content: string
    export default content
}

declare module '*.scss' {
    const content: Styles
    export default content
}

declare module '*.sass' {
    const content: Styles
    export default content
}

declare module '*.css' {
    const content: Styles
    export default content
}


================================================
FILE: assets/entitlements.mac.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
  </dict>
</plist>


================================================
FILE: assets/installer.nsh
================================================
!include LogicLib.nsh

!macro customInit
  ; Check for x64 VC++ Redistributable (skip ARM64 check for now)
  ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed"
  ${If} $0 != "1"
    MessageBox MB_YESNO|MB_ICONQUESTION "\
      ${PRODUCT_NAME} requires Microsoft Visual C++ Redistributable 2015-2022 (x64).$\r$\n$\r$\n\
      Would you like to download and install it now?" IDYES InstallVCRedist IDNO SkipVCRedist
    
    InstallVCRedist:
      ; Download using inetc plugin with visual progress
      inetc::get /CAPTION " " /BANNER "Downloading Microsoft Visual C++ Redistributable..." "https://aka.ms/vs/17/release/vc_redist.x64.exe" "$TEMP\vc_redist.x64.exe"
      Pop $1
      ${If} $1 != "OK"
        MessageBox MB_OK|MB_ICONSTOP "Failed to download Visual C++ Redistributable.$\r$\n$\r$\nPlease install it manually from:$\r$\nhttps://aka.ms/vs/17/release/vc_redist.x64.exe"
        Abort
      ${EndIf}
      
      ; Install VC++ Redistributable
      DetailPrint "Installing Microsoft Visual C++ Redistributable..."
      ExecWait '"$TEMP\vc_redist.x64.exe" /install /quiet /norestart' $2
      
      ; Clean up
      Delete "$TEMP\vc_redist.x64.exe"
      
      ; Check if installation was successful
      ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed"
      ${If} $0 != "1"
        MessageBox MB_OK|MB_ICONSTOP "Failed to install Visual C++ Redistributable.$\r$\n$\r$\nThe installation cannot continue."
        Abort
      ${EndIf}
      
      DetailPrint "Visual C++ Redistributable installed successfully!"
      Goto Done
    
    SkipVCRedist:
      MessageBox MB_OK|MB_ICONEXCLAMATION "Visual C++ Redistributable is required for ${PRODUCT_NAME} to run properly.$\r$\n$\r$\nPlease install it manually from:$\r$\nhttps://aka.ms/vs/17/release/vc_redist.x64.exe"
      Abort
  ${EndIf}
  
  Done:
!macroend

================================================
FILE: biome.json
================================================
{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
  "files": { "ignoreUnknown": true },
  "formatter": {
    "enabled": true,
    "formatWithErrors": false,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineEnding": "lf",
    "lineWidth": 120,
    "attributePosition": "auto",
    "bracketSameLine": false,
    "bracketSpacing": true,
    "expand": "auto",
    "useEditorconfig": true,
    "includes": [
      "src/**",
      "test/integration/**/*.ts",
      "!src/main/mcp/shell-env.cjs",
      "!src/renderer/components/icons/**",
      "biome.json",
      "*.config.js",
      "*.config.ts",
      "*.config.mjs",
      "!erb/**"
    ]
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "a11y": "off",
      "correctness": {
        "useExhaustiveDependencies": "warn"
      },
      "suspicious": {
        "useAwait": "warn",
        "noArrayIndexKey": "warn"
      },
      "nursery": {
        "noFloatingPromises": "warn"
      }
    },
    "includes": [
      "src/**",
      "!src/main/mcp/shell-env.cjs",
      "!src/renderer/components/icons/**",
      "biome.json",
      "*.config.js",
      "*.config.ts",
      "*.config.mjs",
      "!erb/**"
    ]
  },
  "javascript": {
    "formatter": {
      "jsxQuoteStyle": "double",
      "quoteProperties": "asNeeded",
      "trailingCommas": "es5",
      "semicolons": "asNeeded",
      "arrowParentheses": "always",
      "bracketSameLine": false,
      "quoteStyle": "single",
      "attributePosition": "auto",
      "bracketSpacing": true
    }
  },
  "assist": {
    "enabled": true,
    "actions": { "source": { "organizeImports": "on" } }
  }
}


================================================
FILE: doc/FAQ-CN.md
================================================
# 常见问题

<p align="center">
    <a href="./FAQ.md">English</a> | 中文
</p>

这里列举了一些最常见的问题和解决方案。如果你依然没有找到答案,也可以提交一个 [Issue](https://github.com/Bin-Huang/chatbox/issues/new/choose)。

### 1001

#### 消息发送失败,提示 `Failed to fetch`?

这是因为 Chatbox 无法连接到你设置的 AI 模型服务器,请检查你当前的网络环境,确保可以正常连接到 AI 模型服务器。

对于 OpenAI API 的用户,如果你选择了 OpenAI API 作为 AI 模型提供方(即设置页的 AI Provider 中选择了 `OpenAI API`),那么一般是 Chatbox 无法访问设置的 `API HOST`。在默认设置下,Chatbox 会使用 `https://api.openai.com` 作为 API HOST,请确保你的当前网络可以访问这个服务。注意,在某些国家和地区是无法直接访问的。

### 1002

#### 以前用的好好的,突然报错 `{"error":{"message":"You exceeded your current quota, please check your plan and billing details.`?

如果你以前使用一切正常,某天之后突然无法使用过,并且每次发送消息都报错:

```
{"error":{"message":"You exceeded your current quota, please check your plan and billing details.","type":"insufficient_quota","param":null,"code":null}}
```

请注意,这个问题和 Chatbox 没有任何关系。这个情况中往往是因为你正在使用自己的 OpenAI API 账户,而你账户中的免费额度已经全部用完或过期了(一般都是因为过期导致的)。你需要自行登录 OpenAI 账户的控制台,绑定一张海外信用卡才能继续使用。OpenAI API 账户对信用卡有很多要求,如果你的信用卡不符合要求,那么你需要自行解决(非常折腾)。

**更推荐使用 `Chatbox AI`:** 如果你不想折腾这些问题,也可以使用 Chatbox 内置的 `Chatbox AI` 服务。这个服务可以让你无需折腾、什么都不用管、轻松使用 AI 能力。前往配置页,将 AI Provider 设置为 `Chatbox AI`,你将看到相应的设置。

### 1003

#### 无法使用 GPT-4?

如果你选择 GPT-4,然后发送消息时得到类似的报错:

```
{"error":{"message":"The model: gpt-4-32k does not exist","type":"invalid_request_error","param":null,"code":"model_not_found"}}
```

这个情况往往是因为你正在使用自己的 OpenAI 账户,你在模型中选择了 GPT-4,但 OpenAI API 账户不支持 GPT-4。截止到 2023 年 07 月 04 日,所有 OpenAI API 账户都需要向 OpenAI 填写申请后才能使用 GPT-4 模型。这里是申请链接: https://openai.com/waitlist/gpt-4-api 。请注意,即使你是 ChatGPT Plus 用户,你也需要申请后才能使用 GPT-4 的 API 模型。


================================================
FILE: doc/FAQ.md
================================================
# Frequently Asked Questions

<p align="center">
    English | <a href="./FAQ-CN.md">中文</a>
</p>

If you still haven't found the answer you're looking for, feel free to submit an [Issue](https://github.com/Bin-Huang/chatbox/issues/new/choose) as well.

### 1001

#### Message sending failed, showing `Failed to fetch`?

This issue occurs when Chatbox cannot connect to the AI model server you've set up. Please check your current network environment and make sure it can connect properly to the AI model server.

For OpenAI API users, if you've chosen OpenAI API as the AI model provider (meaning you've selected `OpenAI API` in the AI Provider settings), it's typically because Chatbox cannot access the `API HOST` you've set. By default, Chatbox uses `https://api.openai.com` as the API HOST. Please make sure your current network can access this service.

### 1002

#### Everything was working fine before, but now I keep getting an error: `{"error":{"message":"You exceeded your current quota, please check your plan and billing details`?

If everything was working fine before and now you're unable to use the service, with each message sending attempt resulting in the following error:

```
{"error":{"message":"You exceeded your current quota, please check your plan and billing details.","type":"insufficient_quota","param":null,"code":null}}
```

Please note that this issue is not related to Chatbox. In this situation, it's likely that you're using your own OpenAI API account and your free quota has either been used up or expired (usually due to expiration). You need to log in to your OpenAI account's dashboard and link a credit card to continue using the service. The OpenAI API account has many requirements for credit cards. If your card doesn't meet these requirements, you'll need to resolve this issue yourself (it can be quite frustrating).

**Consider using `Chatbox AI`:** If you don't want to deal with these issues, you can also use Chatbox's built-in `Chatbox AI` service. This service allows you to enjoy AI capabilities without any hassle. Go to the settings page and set the AI Provider to `Chatbox AI`, and you'll see the corresponding options.

### 1003

#### Unable to use GPT-4?

If you select GPT-4 and receive a similar error message when sending messages:

```
{"error":{"message":"The model: gpt-4-32k does not exist","type":"invalid_request_error","param":null,"code":"model_not_found"}}
```

This issue often occurs when you're using your own OpenAI account and have selected the GPT-4 model, but your OpenAI API account does not support GPT-4. As of July 4, 2023, all OpenAI API accounts require a request to be submitted to OpenAI before the GPT-4 model can be used. Here's the application link: https://openai.com/waitlist/gpt-4-api. Please note that even if you're a ChatGPT Plus user, you still need to apply for access to use the GPT-4 API model.


================================================
FILE: doc/README-CN.md
================================================
<p align="right">
  <a href="../README.md">English</a> |
  <a href="README-CN.md">简体中文</a>
</p>

这里是 Chatbox 社区版的代码仓库,以 GPLv3 许可证开源。

[Chatbox 再次开源!](https://github.com/chatboxai/chatbox/issues/2266)

我们定期从专业版仓库同步代码到这个仓库,反之亦然。

### 下载电脑端

<table style="width: 100%">
  <tr>
    <td width="25%" align="center">
      <b>Windows</b>
    </td>
    <td width="25%" align="center" colspan="2">
      <b>MacOS</b>
    </td>
    <td width="25%" align="center">
      <b>Linux</b>
    </td>
  </tr>
  <tr style="text-align: center">
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-windows'>
        <img src='./statics/windows.png' style="height:24px; width: 24px" />
        <br />
        <b>Setup.exe</b>
      </a>
    </td>
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-mac-intel'>
        <img src='./statics/mac.png' style="height:24px; width: 24px" />
        <br />
        <b>Intel</b>
      </a>
    </td>
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-mac-aarch'>
        <img src='./statics/mac.png' style="height:24px; width: 24px" />
        <br />
        <b style="white-space: nowrap;">Apple Silicon</b>
      </a>
    </td>
    <td align="center" valign="middle">
      <a href='https://chatboxai.app/?c=download-linux'>
        <img src='./statics/linux.png' style="height:24px; width: 24px" />
        <br />
        <b>AppImage</b>
      </a>
    </td>
  </tr>
</table>

### 下载移动端

<a href='https://apps.apple.com/app/chatbox-ai/id6471368056' style='margin-right: 4px'>
<img src='./statics/app_store.webp' style="height:38px;" />
</a>
<a href='https://play.google.com/store/apps/details?id=xyz.chatboxapp.chatbox' style='margin-right: 4px'>
<img src='./statics/google_play.png' style="height:38px;" />
</a>
<a href='https://chatboxai.app/zh/install?download=android_apk' style='margin-right: 4px; display: inline-flex; justify-content: center'>
<img src='./statics/android.png' style="height:28px; display: inline-block" />
.APK
</a>

更多信息请访问: [chatboxai.app](https://chatboxai.app/)

---


<h1 align="center">
<img src='./statics/icon.png' width='30'>
<span>
    Chatbox
    <span style="font-size:8px; font-weight: normal;">(Community Edition)</span>
</span>
</h1>
<p align="center">
    <em>Chatbox 是一个 AI 模型桌面客户端,支持 ChatGPT、Claude、Google Gemini、Ollama 等主流模型,适用于 Windows、Mac、Linux、Web、Android 和 iOS 全平台</em>
</p>

<p align="center">
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="macOS" src="https://img.shields.io/badge/-macOS-black?style=flat-square&logo=apple&logoColor=white" />
</a>
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="Windows" src="https://img.shields.io/badge/-Windows-blue?style=flat-square&logo=windows&logoColor=white" />
</a>
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="Linux" src="https://img.shields.io/badge/-Linux-yellow?style=flat-square&logo=linux&logoColor=white" />
</a>
<a href="https://github.com/chatboxai/chatbox/releases" target="_blank">
<img alt="下载量" src="https://img.shields.io/github/downloads/chatboxai/chatbox/total.svg?style=flat" />
</a>
<a href="https://twitter.com/benn_huang" target="_blank">
<img alt="Twitter" src="https://img.shields.io/badge/关注-benn_huang-blue?style=flat&logo=Twitter" />
</a>
</p>

<a href="https://www.producthunt.com/posts/chatbox?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-chatbox" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=429547&theme=light" alt="Chatbox - Better&#0032;UI&#0032;&#0038;&#0032;Desktop&#0032;App&#0032;for&#0032;ChatGPT&#0044;&#0032;Claude&#0032;and&#0032;other&#0032;LLMs&#0046; | Product Hunt" style="width: 150px; height: 30px;" width="100" height="40" /></a>

<img src="./statics/demo_desktop_1.jpg" alt="应用截图" style="box-shadow: 2px 2px 10px rgba(0,0,0,0.1); border: 1px solid #ddd; border-radius: 8px; width: 700px" />

<img src="./statics/demo_desktop_2.jpg" alt="应用截图" style="box-shadow: 2px 2px 10px rgba(0,0,0,0.1); border: 1px solid #ddd; border-radius: 8px; width: 700px" />

## 特性

-   **本地数据存储**  
    :floppy_disk: 您的数据保留在您的设备上,确保数据永不丢失并保护您的隐私。

-   **无需部署、直接安装的安装包**  
    :package: 通过可下载的安装包快速开始使用。无需复杂设置!

-   **支持多个 LLM 提供商**  
    :gear: 无缝集成多种 AI 模型:

    -   OpenAI (ChatGPT)
    -   Azure OpenAI
    -   Claude
    -   Google Gemini Pro
    -   Ollama (启用对本地模型的访问,如 llama2、Mistral、Mixtral、codellama、vicuna、yi 和 solar)
    -   ChatGLM-6B

-   **使用 Dall-E-3 生成图像**  
    :art: 使用 Dall-E-3 创建您想象中的图像。

-   **增强提示**  
    :speech_balloon: 高级提示功能,精炼并聚焦您的查询以获得更好的响应。

-   **键盘快捷键**  
    :keyboard: 使用加速您工作流程的快捷键保持高效。

-   **Markdown、Latex 和代码高亮**  
    :scroll: 使用 Markdown 和 Latex 的全部功能生成消息,并结合各种编程语言的语法高亮,提高可读性和呈现效果。

-   **提示库和消息引用**  
    :books: 保存和组织提示以供重复使用,并引用消息以在讨论中提供上下文。

-   **流式回复**  
    :arrow_forward: 通过即时、渐进式回复快速响应您的互动。

-   **人体工程学 UI 和深色主题**  
    :new_moon: 用户友好的界面,带有夜间模式选项,减少长时间使用时的眼睛疲劳。

-   **团队协作**  
    :busts_in_silhouette: 轻松协作并在团队中共享 OpenAI API 资源。[了解更多](../team-sharing/README.md)

-   **跨平台可用性**  
    :computer: 聊天盒已为 Windows、Mac、Linux 用户准备就绪。

-   **通过 Web 版本随处访问**  
    :globe_with_meridians: 在任何设备上使用带有浏览器的 Web 应用程序,随时随地。

-   **iOS 和 Android**  
    :phone: 使用移动应用程序,随时随地在您的指尖上带来这种能力。

-   **多语言支持**  
    :earth_americas: 通过提供多种语言的支持,迎合全球受众:

    -   English
    -   简体中文 (Simplified Chinese)
    -   繁體中文 (Traditional Chinese)
    -   日本語 (Japanese)
    -   한국어 (Korean)
    -   Français (French)
    -   Deutsch (German)
    -   Русский (Russian)

-   **更多...**  
    :sparkles: 不断增强体验,加入新功能!

## 常见问题解答

-   [常见问题](./FAQ-CN.md)

## 如何贡献

欢迎任何形式的贡献,包括但不限于:

-   提交问题
-   提交拉取请求
-   提交功能请求
-   提交错误报告
-   提交文档修订
-   提交翻译
-   提交任何其他形式的贡献

## 构建指南

1. 从 Github 克隆仓库

```bash
git clone https://github.com/chatboxai/chatbox.git
```

2. 安装所需的依赖

```bash
npm install
```

3. 启动应用程序(开发模式)

```bash
npm run dev
```

4. 构建应用程序,为当前平台打包安装程序

```bash
npm run package
```

5. 构建应用程序,为所有平台打包安装程序

```bash
npm run package:all
```

## Star History

[![星星历史图表](https://api.star-history.com/svg?repos=chatboxai/chatbox&type=Date)](https://star-history.com/#chatboxai/chatbox&Date)

## 联系方式

[Twitter](https://x.com/ChatboxAI_HQ) | [电子邮件](mailto:hi@chatboxai.com)


================================================
FILE: docs/adding-new-provider.md
================================================
# Adding a New Provider (Registry Architecture)

This guide documents how to add a new AI provider to Chatbox using the **registry-based architecture**.

## Overview

The provider system uses a centralized registry. Adding a new provider requires:

1. **One definition file** - Registers the provider with `defineProvider()`
2. **One model class file** - Implements the AI SDK interface
3. **One enum entry** - Adds the provider ID to `ModelProviderEnum`
4. **One import** - Side-effect import in `providers/index.ts`

That's it. No more scattered switch statements or setting-util files.

## Step-by-Step Guide

### Step 1: Add Provider to Enum

**File:** `src/shared/types.ts`

Add your provider to `ModelProviderEnum`:

```typescript
export enum ModelProviderEnum {
  // ... existing providers
  YourProvider = 'your-provider',
}
```

### Step 2: Create the Model Class

**File:** `src/shared/providers/definitions/models/your-provider.ts`

For **OpenAI-compatible APIs**, extend `OpenAICompatible`:

```typescript
import type { ModelDependencies } from '@shared/types/adapters'
import type { ProviderModelInfo } from '@shared/types'
import { OpenAICompatible } from '@shared/models/openai-compatible'

export interface YourProviderConfig {
  apiKey: string
  model: ProviderModelInfo
  temperature: number
  topP: number
  maxOutputTokens: number | undefined
  stream: boolean | undefined
}

export default class YourProvider extends OpenAICompatible {
  public name = 'YourProvider'

  constructor(options: YourProviderConfig, dependencies: ModelDependencies) {
    super(
      {
        apiKey: options.apiKey,
        apiHost: 'https://api.yourprovider.com/v1', // Your API base URL
        model: options.model,
        temperature: options.temperature,
        topP: options.topP,
        maxOutputTokens: options.maxOutputTokens,
        stream: options.stream,
      },
      dependencies
    )
  }
}
```

For **custom APIs** (non-OpenAI compatible), extend `AbstractAISDKModel` and implement:
- `streamText()` - Streaming chat completion
- `callChatCompletion()` - Non-streaming chat completion
- Optionally: `isSupportToolUse()`, `isSupportVision()`, `isSupportReasoning()`

See `definitions/models/claude.ts` or `definitions/models/gemini.ts` for examples.

### Step 3: Create the Provider Definition

**File:** `src/shared/providers/definitions/your-provider.ts`

```typescript
import { ModelProviderEnum, ModelProviderType } from '../../types'
import { defineProvider } from '../registry'
import YourProvider from './models/your-provider'

export const yourProviderProvider = defineProvider({
  // Required: Unique ID from ModelProviderEnum
  id: ModelProviderEnum.YourProvider,
  
  // Required: Display name shown in UI
  name: 'Your Provider',
  
  // Required: API type (affects model class behavior)
  type: ModelProviderType.OpenAI, // OpenAI | Claude | Gemini
  
  // Optional: Description for UI
  description: 'Your provider description',
  
  // Optional: Related URLs for settings page
  urls: {
    website: 'https://yourprovider.com',
    apiKey: 'https://yourprovider.com/api-keys',
    docs: 'https://yourprovider.com/docs',
  },
  
  // Required: Default configuration
  defaultSettings: {
    apiHost: 'https://api.yourprovider.com',
    models: [
      {
        modelId: 'your-model-v1',
        contextWindow: 128_000,
        maxOutput: 4_096,
        capabilities: ['vision', 'tool_use'], // Optional: vision, tool_use, reasoning
      },
      {
        modelId: 'your-model-v2',
        contextWindow: 200_000,
        maxOutput: 8_192,
      },
    ],
  },
  
  // Required: Factory function to create model instances
  createModel: (config) => {
    return new YourProvider(
      {
        apiKey: config.providerSetting.apiKey || '',
        model: config.model,
        temperature: config.settings.temperature,
        topP: config.settings.topP,
        maxOutputTokens: config.settings.maxTokens,
        stream: config.settings.stream,
      },
      config.dependencies
    )
  },
  
  // Optional: Custom display name for message headers
  getDisplayName: (modelId, providerSettings) => {
    const nickname = providerSettings?.models?.find((m) => m.modelId === modelId)?.nickname
    return `Your Provider (${nickname || modelId})`
  },
})
```

### Step 4: Register the Provider

**File:** `src/shared/providers/index.ts`

Add a side-effect import at the top of the file:

```typescript
import './definitions/your-provider'
```

This import triggers `defineProvider()` which registers the provider in the registry.

### Step 5: Add Provider Icon (Optional but Recommended)

**File:** `src/renderer/components/icons/ProviderIcon.tsx`

Add an SVG icon case:

```typescript
case ModelProviderEnum.YourProvider:
  return (
    <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
      {/* Your SVG path data */}
    </svg>
  )
```

## ProviderDefinition Field Reference

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | `string` | Yes | Unique identifier from `ModelProviderEnum` |
| `name` | `string` | Yes | Display name in UI |
| `type` | `ModelProviderType` | Yes | API type: `OpenAI`, `Claude`, or `Gemini` |
| `description` | `string` | No | Provider description for UI |
| `urls` | `object` | No | Related URLs (website, apiKey, docs, models) |
| `defaultSettings` | `ProviderSettings` | No | Default apiHost and models list |
| `createModel` | `function` | Yes | Factory function that creates model instances |
| `getDisplayName` | `function` | No | Custom display name for message headers |

### CreateModelConfig (passed to createModel)

| Field | Type | Description |
|-------|------|-------------|
| `settings` | `SessionSettings` | Session-level settings (temperature, topP, etc.) |
| `globalSettings` | `Settings` | Global application settings |
| `config` | `Config` | App configuration (uuid, etc.) |
| `dependencies` | `ModelDependencies` | Platform dependencies (fetch, etc.) |
| `providerSetting` | `ProviderSettings` | Provider-specific settings (apiKey, apiHost, models) |
| `formattedApiHost` | `string` | Pre-formatted API host URL |
| `model` | `ProviderModelInfo` | Selected model configuration |

### Model Capabilities

In `defaultSettings.models[].capabilities`, you can specify:

| Capability | Description |
|------------|-------------|
| `vision` | Model supports image inputs |
| `tool_use` | Model supports function/tool calling |
| `reasoning` | Model is a reasoning/thinking model (o1, o3, etc.) |

## Complete Example: Groq Provider

**File:** `src/shared/providers/definitions/groq.ts`

```typescript
import { ModelProviderEnum, ModelProviderType } from '../../types'
import { defineProvider } from '../registry'
import Groq from './models/groq'

export const groqProvider = defineProvider({
  id: ModelProviderEnum.Groq,
  name: 'Groq',
  type: ModelProviderType.OpenAI,
  urls: {
    website: 'https://groq.com/',
  },
  defaultSettings: {
    apiHost: 'https://api.groq.com/openai',
    models: [
      {
        modelId: 'llama-3.3-70b-versatile',
        contextWindow: 131_072,
        maxOutput: 32_768,
        capabilities: ['tool_use'],
      },
    ],
  },
  createModel: (config) => {
    return new Groq(
      {
        apiKey: config.providerSetting.apiKey || '',
        model: config.model,
        temperature: config.settings.temperature,
        topP: config.settings.topP,
        maxOutputTokens: config.settings.maxTokens,
        stream: config.settings.stream,
      },
      config.dependencies
    )
  },
  getDisplayName: (modelId, providerSettings) => {
    return `Groq API (${providerSettings?.models?.find((m) => m.modelId === modelId)?.nickname || modelId})`
  },
})
```

## Testing Your Implementation

1. **TypeScript check:**
   ```bash
   npm run check
   ```

2. **Lint check:**
   ```bash
   npm run lint
   ```

3. **Run development mode:**
   ```bash
   npm run dev
   ```

4. **Verify in the app:**
   - Provider appears in Settings > Provider
   - API key can be configured
   - Models are listed in model selector
   - Chat functionality works

## Migration Notes

The registry-based architecture replaces the previous scattered approach:

| Old Location | New Location |
|--------------|--------------|
| `src/shared/models/your-provider.ts` | `src/shared/providers/definitions/models/your-provider.ts` |
| `src/shared/models/index.ts` switch case | `defineProvider()` in definition file |
| `src/shared/defaults.ts` SystemProviders entry | `defaultSettings` in definition file |
| `src/renderer/packages/model-setting-utils/*-setting-util.ts` | `getDisplayName` in definition file |

All provider information is now consolidated in a single `defineProvider()` call.


================================================
FILE: docs/adding-provider.md
================================================
# Adding a New Provider to Chatbox

This guide documents all the steps and files that need to be modified when adding a new AI provider to the Chatbox application.

## Overview

Adding a new provider involves modifying approximately 7-8 files across the codebase. The implementation follows a consistent pattern, making it straightforward to add support for new AI models.

## Step-by-Step Implementation

### 1. Add Provider to Enum

**File:** `/src/shared/types.ts`

Add your provider to the `ModelProviderEnum`:

```typescript
export enum ModelProviderEnum {
  // ... existing providers
  YourProvider = 'your-provider',
}
```

### 2. Create Provider Implementation

**File:** `/src/shared/models/your-provider.ts`

Create a new file implementing your provider's API. Most providers can extend the base OpenAI-compatible class:

```typescript
import { OpenAICompatible } from './openai-compatible'

export class YourProvider extends OpenAICompatible {
  name = 'YourProvider'

  constructor(apiKey: string, apiHost: string) {
    super(apiKey, apiHost)
  }
}
```

For providers with custom APIs, extend `AbstractAISdk` directly and implement required methods.

### 3. Register Provider in Factory

**File:** `/src/shared/models/index.ts`

Add three entries:

1. Import your provider:
```typescript
import { YourProvider } from './your-provider'
```

2. Add case in `getModel()` function:
```typescript
case ModelProviderEnum.YourProvider:
  return new YourProvider(apiKey, apiHost)
```

3. Add to `aiProviderNameHash`:
```typescript
export const aiProviderNameHash = {
  // ... existing entries
  [ModelProviderEnum.YourProvider]: 'Your Provider Name',
}
```

4. (Optional) Add to `AIModelProviderMenuOptionList` if it should appear in selection menus:
```typescript
export const AIModelProviderMenuOptionList = [
  // ... existing entries
  { value: ModelProviderEnum.YourProvider, label: 'Your Provider' },
]
```

### 4. Configure Default Settings

**File:** `/src/shared/defaults.ts`

Add your provider configuration to the `SystemProviders` array:

```typescript
{
  id: ModelProviderEnum.YourProvider,
  name: 'Your Provider',
  type: ModelProviderType.OpenAI,  // OpenAI | Gemini | Claude
  defaultSettings: {
    apiHost: 'https://api.yourprovider.com',
    models: [
      {
        modelId: 'model-1',
        capabilities: ['vision', 'tool_use'],  // optional
        contextWindow: 128_000,  // optional
      },
    ],
  },
}
```

**Note:** See existing providers in `defaults.ts` for more examples.

### 5. Create Settings Utility

**File:** `/src/renderer/packages/model-setting-utils/your-provider-setting-util.ts`

Create a settings utility class:

```typescript
import { BaseModelSettingUtil } from './base-model-setting-util'
import { ModelProviderEnum } from '@/shared/types'

export class YourProviderSettingUtil extends BaseModelSettingUtil {
  provider = ModelProviderEnum.YourProvider
  
  // Add any provider-specific validation or configuration methods
}
```

### 6. Register Settings Utility

**File:** `/src/renderer/packages/model-setting-utils/index.ts`

Add your utility to the `getModelSettingUtil()` function:

```typescript
import { YourProviderSettingUtil } from './your-provider-setting-util'

export function getModelSettingUtil(provider: ModelProviderEnum): BaseModelSettingUtil {
  const hash = {
    // ... existing entries
    [ModelProviderEnum.YourProvider]: new YourProviderSettingUtil(),
  }
  return hash[provider] || new BaseModelSettingUtil()
}
```

### 7. Add Provider Icons

**SVG Icon - File:** `/src/renderer/components/icons/ProviderIcon.tsx`

Add an SVG icon component in the switch statement:

```typescript
case ModelProviderEnum.YourProvider:
  return (
    <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
      {/* Your SVG path data */}
    </svg>
  )
```

**PNG Icon - File:** `/src/renderer/static/icons/providers/your-provider.png`

Add a 36x36 PNG icon for the provider list display.

## Optional Steps

### Translations

If your provider requires custom UI text, add translations to the appropriate locale files in `/src/renderer/i18n/locales/`.

### Testing

Create test files for your provider implementation:
- `/src/shared/models/your-provider.test.ts`
- `/src/renderer/packages/model-setting-utils/your-provider-setting-util.test.ts`

### Custom Settings UI

If your provider needs custom settings beyond API key and host, you may need to create a custom component in `/src/renderer/routes/settings/provider/`. However, most providers work with the default settings page.

## Example: Recent VolcEngine Implementation

The VolcEngine provider was recently added following this pattern:

1. Added enum value in `types.ts`
2. Created `/src/shared/models/volcengine.ts` extending OpenAICompatible
3. Added entries in `/src/shared/models/index.ts`
4. Added configuration in `defaults.ts` with chat models
5. Created settings utility
6. Registered utility in index

## Testing Your Implementation

After implementing:

1. Run `npm run lint:fix` to ensure code style consistency
2. Run `npm run check` for TypeScript validation
3. Test the provider in development mode: `npm run dev`
4. Verify:
   - Provider appears in settings
   - API key/host can be configured
   - Models are selectable
   - Chat functionality works

## Common Patterns

- Most providers extend `OpenAICompatible` if they follow OpenAI's API format
- Use `supportContinuous: true` for streaming support
- Set `functionCall: true` on models that support function calling
- The `apiHost` in defaults should not include `/v1` suffix (added automatically)

================================================
FILE: docs/dependency-reorg.md
================================================
# Dependency split for Electron Vite

依据 electron-vite 的建议,本次调整将所有仅用于 renderer(前端打包)的依赖移动到 `devDependencies`,仅保留主进程(`src/main`)和 preload(`src/preload`)在运行时需要的依赖在 `dependencies` 中。

## Runtime dependencies(保留在 `dependencies`)

- @libsql/client
- @mastra/libsql
- @mastra/rag
- @modelcontextprotocol/sdk
- @mozilla/readability
- @sentry/node
- ai
- auto-launch
- chardet
- cohere-ai
- electron
- electron-debug
- electron-devtools-installer
- electron-log
- electron-store
- electron-updater
- epub
- fs-extra
- iconv-lite
- linkedom
- lodash
- ofetch
- officeparser
- sanitize-filename
- uuid

## 主要变动

- 新增 `@libsql/client` 到 `dependencies`(主进程知识库类型定义及运行时需求)。
- 将 `electron`、`electron-debug`、`electron-devtools-installer` 从 `devDependencies` 挪到 `dependencies`(主进程运行时直接使用)。
- 其余原本在 `dependencies` 中、仅被 renderer 使用的依赖全部移动到 `devDependencies`,以符合 electron-vite 关于依赖归类的最佳实践。

## 后续操作

- 运行 `npm install` 以更新本地安装目录和锁文件。
- 如需验证,可执行 `npm run build`/`npm start` 确认依赖拆分未影响构建与运行。


================================================
FILE: docs/new-session-mechanism.md
================================================
# 首页新会话机制文档

## 概述

Chatbox 的首页(`/`路由)是用户创建新对话的入口。本文档详细说明了新会话的创建机制,特别是临时状态的管理和转移过程。

## 核心概念

### 1. 临时会话 ID:"new"

在用户真正发送第一条消息之前,首页使用一个特殊的会话 ID `"new"` 来标识这是一个尚未创建的临时会话。

```typescript
const [session, setSession] = useState<Session>({
  id: 'new',
  ...initEmptyChatSession(),
})
```

### 2. 临时状态管理:newSessionStateAtom

为了管理新会话的临时状态(如知识库选择、网页浏览模式等),系统使用了专门的 atom:

```typescript
// src/renderer/stores/atoms/uiAtoms.ts
export const newSessionStateAtom = atom<{
  knowledgeBase?: Pick<KnowledgeBase, 'id' | 'name'>
  webBrowsing?: boolean
}>({})
```

这个 atom 专门存储用户在发送第一条消息前的各种选择和设置。

## 工作流程

### 1. 用户交互阶段

当用户在首页进行以下操作时,状态都保存在临时存储中:

- **选择知识库**:存储在 `newSessionStateAtom.knowledgeBase`
- **选择模型**:存储在组件的 `session` state 中
- **选择 Copilot**:同样存储在组件的 `session` state 中

### 2. InputBox 组件的智能处理

InputBox 组件会根据 sessionId 智能选择存储位置:

```typescript
const isNewSession = currentSessionId === 'new'

const knowledgeBase = isNewSession 
  ? newSessionState.knowledgeBase 
  : sessionKnowledgeBaseMap[currentSessionId]

const setKnowledgeBase = useCallback((value) => {
  if (isNewSession) {
    setNewSessionState(prev => ({ ...prev, knowledgeBase: value }))
  } else {
    // 更新实际会话的知识库映射
    setSessionKnowledgeBaseMap(prev => ({
      ...prev,
      [currentSessionId]: value
    }))
  }
}, [currentSessionId, isNewSession])
```

### 3. 会话创建和状态转移

当用户发送第一条消息时,`handleSubmit` 函数执行以下步骤:

```typescript
const handleSubmit = async (payload: InputBoxPayload) => {
  // 1. 创建真正的会话
  const newSession = await createSession({
    name: session.name,
    type: 'chat',
    picUrl: session.picUrl,
    messages: session.messages,
    copilotId: session.copilotId,
    settings: session.settings,
  })

  // 2. 转移临时状态到新会话
  if (newSessionState.knowledgeBase) {
    setSessionKnowledgeBaseMap({
      ...sessionKnowledgeBaseMap,
      [newSession.id]: newSessionState.knowledgeBase,
    })
    // 清空临时状态
    setNewSessionState({})
  }

  // 3. 切换到新会话
  sessionActions.switchCurrentSession(newSession.id)

  // 4. 发送消息
  // ...
}
```

## 关键设计决策

### 1. 为什么使用 "new" 作为临时 ID?

- 简单明了,易于识别
- 避免与真实的 UUID 冲突
- 便于在代码中进行特殊处理

### 2. 为什么需要 newSessionStateAtom?

- **职责分离**:临时状态和持久状态分开管理
- **避免污染**:不会在 sessionKnowledgeBaseMap 中留下无效数据
- **易于扩展**:未来可以轻松添加更多临时状态字段

### 3. 状态转移时机

状态转移发生在会话创建成功后、切换会话之前。这确保了:
- 用户选择的设置不会丢失
- 新会话立即拥有正确的配置
- 避免了异步操作的竞态条件

## 数据流图

```
用户操作 → newSessionStateAtom (临时存储)
    ↓
发送消息 → 创建会话
    ↓
状态转移 → sessionKnowledgeBaseMap[newSessionId] (持久存储)
    ↓
清空临时状态 → newSessionStateAtom = {}
```

## 注意事项

1. **内存管理**:newSessionStateAtom 在会话创建后会被清空,避免内存泄漏
2. **并发安全**:状态转移是同步操作,避免了并发问题
3. **用户体验**:整个过程对用户透明,选择的设置会无缝延续到新会话

## 相关文件

- `/src/renderer/routes/index.tsx` - 首页组件
- `/src/renderer/components/InputBox.tsx` - 输入框组件
- `/src/renderer/stores/atoms/uiAtoms.ts` - UI 状态定义
- `/src/renderer/stores/sessionActions.ts` - 会话相关操作

================================================
FILE: docs/rag.md
================================================
技术方案

## 数据流

1. 上传文件到目录
2. 在数据库创建对每个文件预处理 task
3. 触发 worker 处理 task,work 处理完自动停止,直到下次触发
4. worker 根据后缀名或 mime type,找到 loader,如果找不到 loader 任务失败,loader 加载文件内容
5. 根据文件内容调用 embedding,写入 vector store。(vector store 因为需要操作 db 文件,需要在 electron main 层执行,在 renderer 层通过 ipc 调用)

## 文件读取

根据文件格式,采用不同的 loader

- Mastra MDocument
  - 文本,markdown,html,json
- officeparser(免费)
  - office 类
- unstructured api(付费用户可用)
  - 其他

## embedding

- vercel ai sdk

## rerank (TODO)

- 接入 cohere, voyage, jina

## vector store

- libsql

## UI

在设置页面添加知识库管理页面,可以列出和创建知识库,每个知识库中可以添加文件,添加后进入待处理、之后存在处理中、处理完或处理失败状态。

## AI 调用知识库

提供一系列 tool 让 AI 来访问知识库,用户可以选中一个知识库,AI 可以使用

# 和目前的系统结合

- settings/provider 页面,对 ProviderModelInfo 类型,增加模型分类:`embedding`,之前的默认分类为 `chat`,只有 `chat` 模型可以选择 `capabilities`
- 知识库页面可以设置使用的 embedding 和 reranker model,默认使用模型提供方设置里找到第一个可用的
- main 层的 rag 服务,需要使用 renderer 层的 provider 参数(baseURL 和 apikey、modelId),所以需要在 renderer 层通过 ipc 来初始化 ai sdk 的 model,每次进行知识库操作需要保证初始化已经进行过


================================================
FILE: docs/session-module-split-plan.md
================================================
# Session Module Split Plan

**Purpose**: Document the dependency analysis and proposed module split for `src/renderer/stores/sessionActions.ts` (1799 lines) to enable safe refactoring without circular imports.

## Current State

The `sessionActions.ts` file has grown to 1799 lines and handles multiple responsibilities:
- Session CRUD operations
- Message operations
- Thread/history management
- Fork (message branching) operations
- AI generation orchestration
- Session/thread naming
- Export functionality

## Module-Level State

The file contains two shared state objects that must be moved to a central location:

```typescript
// Line 1054-1055
const pendingNameGenerations = new Map<string, ReturnType<typeof setTimeout>>()
const activeNameGenerations = new Set<string>()
```

**Purpose**: Debounce and deduplicate name generation requests
**Used by**: `scheduleGenerateNameAndThreadName`, `scheduleGenerateThreadName`
**Strategy**: Move to `stores/session/state.ts` and import where needed

## Dependency Graph

### Call Chains (Critical Paths)

```
submitNewUserMessage
  └── insertMessage
  └── insertMessageAfter  
  └── modifyMessage
  └── generate (internal)
        └── genMessageContext
        └── streamText (external)
        └── generateImage (external)
        └── modifyMessage
        └── trackGenerateEvent (internal)

generateMore
  └── insertMessageAfter
  └── generate (internal)

generateMoreInNewFork
  └── createNewFork
  └── generateMore

regenerateInNewFork  
  └── findMessageLocation (internal)
  └── createNewFork
  └── generateMore (or passed in runGenerateMore)
  └── generate (internal, fallback)

scheduleGenerateNameAndThreadName
  └── generateNameAndThreadName (internal)
        └── _generateName (internal)
              └── modifyNameAndThreadName

scheduleGenerateThreadName
  └── generateThreadName (internal)
        └── _generateName (internal)
              └── modifyThreadName

createNewFork / switchFork / deleteFork / expandFork
  └── buildCreateForkPatch / buildSwitchForkPatch / buildDeleteForkPatch / buildExpandForkPatch (internal)
        └── applyForkTransform (internal)
              └── switchForkInMessages (internal, for switchFork only)
              └── computeNextMessageForksHash (internal)

startNewThread
  └── refreshContextAndCreateNewThread

moveThreadToConversations
  └── copySession (internal)
  └── removeThread
  └── switchCurrentSession

moveCurrentThreadToConversations
  └── copySession (internal)
  └── removeCurrentThread
  └── switchCurrentSession
```

### External Dependencies (imports)

| Import | Used By |
|--------|---------|
| `@dnd-kit/sortable` (arrayMove) | reorderSessions |
| `@sentry/react` | submitNewUserMessage, generate, _generateName |
| `@shared/defaults` | refreshContextAndCreateNewThread, compressAndCreateThread |
| `@shared/models` (getModel) | submitNewUserMessage, generate, _generateName |
| `jotai` (getDefaultStore) | switchCurrentSession, switchToNext |
| `lodash` (identity, omit, pickBy) | copySession, generate |
| `uuid` (uuidv4) | refreshContextAndCreateNewThread, switchThread, compressAndCreateThread, fork operations |
| `@/adapters` (createModelDependencies) | submitNewUserMessage, generate, _generateName |
| `@/hooks/dom` | startNewThread, compressAndCreateThread |
| `@/i18n/locales` | _generateName |
| `@/packages/apple_app_store` | generate |
| `@/packages/context-management` | submitNewUserMessage, genMessageContext |
| `@/packages/model-calls` | generate, _generateName |
| `@/packages/model-setting-utils` | submitNewUserMessage, generate |
| `@/packages/token` | insertMessage, insertMessageAfter, modifyMessage, genMessageContext |
| `@/router` | switchCurrentSession |
| `@/storage/StoreStorage` | generate |
| `@/utils/session-utils` | reorderSessions |
| `@/utils/track` | trackGenerateEvent |
| `@shared/models/errors` | submitNewUserMessage, generate |
| `@shared/types` | Various (type imports) |
| `@shared/utils/message` | Various message operations |
| `../packages/prompts` | _generateName |
| `../platform` | submitNewUserMessage, generate, _generateName |
| `../storage` | generate, genMessageContext |
| `./atoms` | switchCurrentSession, switchToNext |
| `./chatStore` | Most operations |
| `./scrollActions` | switchCurrentSession, startNewThread, compressAndCreateThread, switchThread |
| `./sessionHelpers` | createEmpty, refreshContextAndCreateNewThread, compressAndCreateThread, exportSessionChat |
| `./settingActions` | submitNewUserMessage |
| `./settingsStore` | generate, _generateName |
| `./uiStore` | getSessionWebBrowsing, generate |

## Proposed Module Assignments

### `stores/session/state.ts`
Shared module state (no dependencies on other session modules):
```typescript
export const pendingNameGenerations = new Map<string, ReturnType<typeof setTimeout>>()
export const activeNameGenerations = new Set<string>()
```

### `stores/session/types.ts`
Internal types used across modules:
```typescript
export type MessageForkEntry = NonNullable<Session['messageForksHash']>[string]
export type MessageLocation = { list: Message[]; index: number }
```

### `stores/session/crud.ts` (~150 lines)
Session lifecycle operations:
- `createEmpty` - creates new chat/picture session
- `copyAndSwitchSession` - duplicates session
- `switchCurrentSession` - changes active session  
- `switchToIndex` - switch by index
- `switchToNext` - switch to next/prev
- `reorderSessions` - drag-drop reorder
- `clearConversationList` - bulk delete sessions
- `clear` - clear messages in session

**Internal**: `create`, `copySession`, `clearSessionList`

**Dependencies**: chatStore, atoms, scrollActions, router, sessionHelpers

### `stores/session/messages.ts` (~200 lines)
Message CRUD operations:
- `insertMessage` - add message to session
- `insertMessageAfter` - insert after specific message
- `modifyMessage` - update message
- `removeMessage` - delete message
- `submitNewUserMessage` - handle user input with AI response

**Dependencies**: chatStore, settingActions, settingsStore, generation.ts (imports `generate`)

**Note**: `submitNewUserMessage` calls `generate` - will need to import from generation.ts

### `stores/session/threads.ts` (~250 lines)
Thread/history management:
- `editThread` - rename thread
- `removeThread` - delete thread
- `switchThread` - change active thread
- `refreshContextAndCreateNewThread` - archive current, start fresh
- `startNewThread` - wrapper with scroll/focus
- `removeCurrentThread` - delete current thread
- `compressAndCreateThread` - compress with summary
- `moveThreadToConversations` - promote thread to session
- `moveCurrentThreadToConversations` - promote current thread

**Dependencies**: chatStore, scrollActions, dom, sessionHelpers, crud.ts (for switchCurrentSession, copySession)

### `stores/session/forks.ts` (~400 lines)
Message fork/branch operations:
- `createNewFork` - create branch point
- `switchFork` - navigate branches
- `deleteFork` - remove current branch
- `expandFork` - flatten all branches

**Internal helpers**:
- `buildCreateForkPatch`
- `buildSwitchForkPatch`
- `buildDeleteForkPatch`
- `buildExpandForkPatch`
- `switchForkInMessages`
- `applyForkTransform`
- `computeNextMessageForksHash`

**Dependencies**: chatStore, types.ts

### `stores/session/generation.ts` (~450 lines)
AI generation orchestration:
- `generate` (internal, but used by messages.ts) - core generation logic
- `generateMore` - continue generation
- `generateMoreInNewFork` - new branch + generate
- `regenerateInNewFork` - regenerate in new branch
- `createLoadingPictures` - placeholder images
- `genMessageContext` - build prompt context
- `getMessageThreadContext` - get thread messages

**Internal helpers**:
- `trackGenerateEvent`
- `getSessionWebBrowsing`
- `findMessageLocation`

**Dependencies**: chatStore, settingsStore, uiStore, platform, storage, model-calls, messages.ts (circular - see below)

**Circular Dependency Issue**: 
- `generation.ts` exports `generate` which is called by `submitNewUserMessage` in `messages.ts`
- `generate` doesn't call anything from messages.ts directly (it calls `modifyMessage` but that can be imported directly)
- **Solution**: `messages.ts` imports `generate` from `generation.ts`. No circular dependency.

### `stores/session/naming.ts` (~150 lines)
Session/thread naming:
- `modifyNameAndThreadName` - update session + thread name
- `modifyThreadName` - update thread name only
- `scheduleGenerateNameAndThreadName` - debounced auto-naming
- `scheduleGenerateThreadName` - debounced thread naming

**Internal helpers**:
- `_generateName` - core name generation
- `generateNameAndThreadName` - wrapper
- `generateThreadName` - wrapper

**Dependencies**: chatStore, settingsStore, platform, state.ts, model-calls, prompts

### `stores/session/export.ts` (~20 lines)
Export functionality:
- `exportSessionChat` - export session to file

**Dependencies**: chatStore, sessionHelpers

### `stores/session/index.ts`
Re-exports all public functions (37 total):

```typescript
// CRUD (7)
export { createEmpty, copyAndSwitchSession, switchCurrentSession } from './crud'
export { switchToIndex, switchToNext, reorderSessions, clearConversationList, clear } from './crud'

// Messages (5)
export { insertMessage, insertMessageAfter, modifyMessage, removeMessage } from './messages'
export { submitNewUserMessage } from './messages'

// Threads (9)
export { editThread, removeThread, switchThread } from './threads'
export { refreshContextAndCreateNewThread, startNewThread, removeCurrentThread } from './threads'
export { compressAndCreateThread, moveThreadToConversations, moveCurrentThreadToConversations } from './threads'

// Forks (4)
export { createNewFork, switchFork, deleteFork, expandFork } from './forks'

// Generation (6)
export { generateMore, generateMoreInNewFork, regenerateInNewFork } from './generation'
export { createLoadingPictures, genMessageContext, getMessageThreadContext } from './generation'

// Naming (4)
export { modifyNameAndThreadName, modifyThreadName } from './naming'
export { scheduleGenerateNameAndThreadName, scheduleGenerateThreadName } from './naming'

// Export (1)
export { exportSessionChat } from './export'
```

**Total exported: 36 functions** (Note: `clear` is included in CRUD = 37)

## Shared State Handling Strategy

**Decision: Centralized State Module**

The `pendingNameGenerations` and `activeNameGenerations` Maps will be moved to `stores/session/state.ts` and imported by `naming.ts`.

**Rationale**:
1. These are simple, isolated state containers
2. Only used by naming operations
3. No complex initialization or cleanup needed
4. Easy to import without circular dependencies

**Alternative considered**: Zustand store
- Rejected because the state is only used internally for debouncing
- No need for reactivity or persistence

## Migration Order

1. **US-001**: Create directory structure + state.ts + types.ts
2. **US-002**: Extract crud.ts (no dependencies on other new modules)
3. **US-003**: Extract messages.ts (depends on generation.ts - stub import initially)
4. **US-004**: Extract threads.ts (depends on crud.ts)
5. **US-005**: Extract forks.ts (independent)
6. **US-006**: Extract generation.ts (provides `generate` to messages.ts)
7. **US-007**: Extract naming.ts (uses state.ts)
8. **US-008**: Extract export.ts (independent)
9. **US-009**: Clean up sessionActions.ts to be re-export facade
10. **US-010**: Finalize index.ts with all exports

## Verification Checklist

- [ ] No circular dependencies (`npx madge --circular src/renderer/stores/`)
- [ ] TypeScript compiles (`npm run check`)
- [ ] All 37 exports accessible from sessionActions.ts
- [ ] Internal helpers (prefixed with `_`) not exported
- [ ] Module state properly isolated in state.ts

## File Size Targets

| Module | Estimated Lines |
|--------|-----------------|
| state.ts | ~10 |
| types.ts | ~20 |
| crud.ts | ~150 |
| messages.ts | ~200 |
| threads.ts | ~250 |
| forks.ts | ~400 |
| generation.ts | ~450 |
| naming.ts | ~150 |
| export.ts | ~20 |
| index.ts | ~50 |
| **sessionActions.ts (facade)** | **<100** |

## Risk Mitigation

1. **Circular imports**: The main risk is between `messages.ts` and `generation.ts`. Analysis shows `generate` is called by `submitNewUserMessage`, but `generate` only calls `modifyMessage` which can be a direct chatStore call. No circular dependency.

2. **Missing exports**: Use TypeScript to ensure all 37 exports are available after split.

3. **Broken imports**: Update all imports in codebase to use `sessionActions.ts` facade (re-exports maintain compatibility).

4. **State synchronization**: pendingNameGenerations/activeNameGenerations are simple Maps/Sets - no sync issues expected.


================================================
FILE: docs/storage.md
================================================
# 存储架构文档

Chatbox 跨平台存储方案和版本迁移机制说明。

## 跨平台存储方案

### 存储类型

- **DESKTOP_FILE**: 桌面端文件存储(通过 IPC)
- **INDEXEDDB**: IndexedDB(通过 localforage)
- **LOCAL_STORAGE**: localStorage(已弃用)
- **MOBILE_SQLITE**: SQLite 数据库(通过 Capacitor)

### 当前方案(v1.17.0)

| 平台 | Settings/Configs | Sessions | 原因 |
|------|-----------------|----------|------|
| **Desktop** | 文件存储 | IndexedDB | 配置便于备份,会话需要大容量 |
| **Mobile** | SQLite | SQLite | 统一存储,性能更好 |
| **Web** | IndexedDB | IndexedDB | 大容量,异步访问 |

## 版本历史

| 版本 | Config Version | Desktop | Mobile | 主要变化 |
|------|---------------|---------|--------|---------|
| v1.9.8-v1.9.10 | 0-5 | 全部 File | localStorage | 初始版本 |
| v1.9.11 | 6-7 | - | **→ SQLite** | Mobile 解决容量限制 |
| v1.12.0 | 7-8 | - | - | 数据格式:sessions → session-list |
| v1.13.1 | 9-10 | - | - | Provider/Session 设置重构 |
| v1.16.1 | 11-12 | **Sessions → IndexedDB**<br/>Configs 保持 File | **→ IndexedDB** | Desktop 分离存储<br/>Mobile 统一到 IndexedDB |
| **v1.17.0** | **12-13** | Sessions 保持 IndexedDB<br/>Configs 保持 File | **→ SQLite** | Desktop 无变化<br/>Mobile 性能优化 |

**关键历史事实**:
- Desktop 的 `configVersion`/`settings`/`configs` **从未** 存储在 IndexedDB 中
- Desktop 从 v1.16.1 开始只将 **sessions** 移到 IndexedDB
- v1.16.1 → v1.17.0,Desktop 存储策略 **完全未变**
- Mobile 的完整演进:localStorage → SQLite (v1.9.11) → IndexedDB (v1.16.1) → SQLite (v1.17.0)

## 迁移机制

### 核心逻辑

```typescript
// 1. 找到最新的旧存储
const [oldConfigVersion, oldStorage] = await findNewestStorage(getOldVersionStorages())

// 2. 判断是否需要迁移数据
if (
  (oldConfigVersion > configVersion || platform.type === 'desktop') &&
  oldStorage &&
  oldStorage.getStorageType() !== storage.getStorageType()  // 存储类型不同
) {
  await doMigrateStorage(oldStorage)  // 迁移数据
}

// 3. 增量升级数据格式
for (; configVersion < CurrentVersion; configVersion++) {
  await migrateFunctions[configVersion]?.(dataStore)
  await setConfigVersion(configVersion + 1)
}
```

### 迁移策略差异

| 平台 | 策略 | 说明 |
|------|------|------|
| **Mobile** | 复制所有 key | 所有数据在同一存储 |
| **Desktop** | 只复制会话数据 | Settings/Configs 保留在文件中 |

## 关键设计决策

### 1. 同类型存储共享数据源

**原则**: 旧存储和当前存储类型相同时,无需迁移数据。

**示例**: Mobile v1.9.11 (SQLite v7) → v1.17.0 (SQLite v13)
- 都用 SQLite,数据已经在那里
- 只需升级数据格式,无需复制数据

### 2. 多个旧存储选最新

**原则**: 存在多个旧存储时,选择 configVersion 最大的。

**示例**: localStorage v5 + IndexedDB v12 → 选择 IndexedDB v12
- 避免迁移过时数据
- 确保用户获得最新状态

### 3. 桌面端混合存储

**原则**: 配置文件便于备份,会话数据用 IndexedDB。

**历史演进**:
- v1.9.x: 所有数据在 config.json 文件中
- v1.16.1: 会话数据移到 IndexedDB,配置保持在文件中
- v1.17.0: 与 v1.16.1 完全相同(无变化)

**关键事实**: 
- `configVersion`/`settings`/`configs` 从未在 IndexedDB 中存储过
- 只有会话数据(`chat-sessions-list`、`session:*`)在 IndexedDB

**特殊处理**: 
- 迁移时只复制会话数据到 IndexedDB
- Settings/Configs 保留在文件存储

### 4. 增量数据格式升级

**原则**: 数据格式升级按版本逐步执行。

**优势**:
- 从任意版本升级都能正确迁移
- 中断后可继续
- 便于维护

## 测试要点

### 覆盖场景

1. ✅ 首次运行(无旧数据)
2. ✅ 版本已是最新(跳过迁移)
3. ✅ 同类型存储(数据已可访问)
4. ✅ 跨存储迁移(File → IndexedDB, localStorage → SQLite)
5. ✅ 多个旧存储共存(选择最新版本)
6. ✅ 历史版本直接升级(跳过中间版本)

### 关键洞察

**1. 同类型存储共享数据源**
```typescript
// 测试 mock 体现
if (type === 'MOBILE_SQLITE') {
  sqliteData = { ...data }  // 共享同一数据容器
}
```

**2. 最新版本优先**
```typescript
// localStorage v5 + IndexedDB v12 → 选择 v12
```

**3. 桌面端部分迁移**
```typescript
// 只迁移 sessions,不迁移 settings/configs
```

**4. 使用真实 Platform 实例**
```typescript
beforeAll(async () => {
  const { default: DesktopPlatformClass } = await import('@/platform/desktop_platform')
  desktopPlatform = new DesktopPlatformClass(window.electronAPI)
})
```

## 常见问题

**Q: 为什么回退到 SQLite?**  
A: IndexedDB 在某些 WebView 环境存在数据被清理问题,SQLite 更稳定。

**Q: 迁移失败会怎样?**  
A: 捕获异常并记录,应用仍可运行(初始化默认数据)。

**Q: 如何添加新版本?**  
A: 增加 `CurrentVersion`,在 `migrateFunctions` 添加迁移函数,更新文档。

## 参考

- [Migration 源码](../src/renderer/stores/migration.ts)
- [Migration 测试](../src/renderer/stores/migration.test.ts)
- [测试文档](./testing.md)

---

**最后更新**: 2025-10-25 | **当前版本**: v1.17.0 (Config Version 13)



================================================
FILE: docs/testing.md
================================================
# Testing Strategy and Implementation

## Current Testing Infrastructure

### Test Framework
- **Vitest** - Modern, ESM-first test runner with excellent TypeScript support
- **@ai-sdk/provider-utils/test** - Mock server utilities for AI provider testing
- **Testing Library** - Component testing utilities

### Test Configuration
```typescript
// vitest.config.ts
export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    env: {
      NODE_ENV: 'test',
    },
    include: ['src/**/*.{test,spec}.{ts,tsx}'],
    exclude: ['node_modules', 'dist', 'release', '.erb'],
  }
})
```

### Test Commands
- `npm run test` - Run all tests once
- `npm run test:watch` - Run tests in watch mode
- `npm run test:ui` - Launch Vitest UI for interactive testing
- `npm run test:coverage` - Run tests with coverage report

## Existing Test Coverage

### ✅ Completed Tests

1. **AI Provider Adapters** (`src/shared/models/`)
   - OpenAI streaming and tool calls
   - Error handling (rate limits, network errors)
   - Message format conversion
   - Stream parsing and response handling

2. **Utility Functions** (`src/shared/utils/`)
   - API URL normalization (`llm_utils.test.ts`)
   - Message sequencing and merging
   - ContentParts array handling

3. **Content Processing** (`src/renderer/`)
   - Base64 image parsing (`base64.test.ts`)
   - LaTeX rendering (`latex.test.ts`)
   - Provider configuration parsing (`provider-config.test.ts`)

4. **Message Handling** (`src/renderer/utils/`)
   - Message role sequencing
   - Empty message filtering
   - Multi-part content merging
   - Image content handling

## Testing Patterns and Best Practices

### Mock Server Pattern
For AI provider testing, use `createTestServer` from `@ai-sdk/provider-utils/test`:

```typescript
import { createTestServer } from '@ai-sdk/provider-utils/test'

const server = createTestServer({
  'https://api.openai.com/v1/chat/completions': {
    headers: { 'Content-Type': 'text/event-stream' },
    chunks: [
      'data: {"id":"1","object":"chat.completion.chunk","choices":[{"delta":{"content":"Hello"}}]}\n\n',
      'data: [DONE]\n\n',
    ]
  }
})
```

### Handling Dynamic Responses
Use `callNumber` parameter for different responses per call:

```typescript
const server = createTestServer({
  'https://api.openai.com/v1/chat/completions': ({ callNumber }) => ({
    chunks: callNumber === 0 
      ? ['data: {"choices":[{"delta":{"tool_calls":[...]}}]}\n\n']
      : ['data: {"choices":[{"delta":{"content":"Result"}}]}\n\n']
  })
})
```

### Environment-Aware Code
Suppress console output in tests:

```typescript
if (process.env.NODE_ENV !== 'test') {
  console.error('Error message')
}
```

## Test Coverage Goals

### High Priority (Core Functionality)
- [x] AI provider adapters - Basic streaming and error handling
- [x] Message processing core logic
- [ ] Data storage layer (BaseStorage)
- [ ] Session management
- [ ] Settings management

### Medium Priority (Features)
- [x] Content rendering (LaTeX, Markdown basics)
- [x] Provider configuration
- [ ] File processing and uploads
- [ ] Knowledge base integration
- [ ] MCP server communication

### Low Priority (Extensions)
- [ ] UI component testing
- [ ] Electron main process testing
- [ ] Platform-specific features
- [ ] Performance benchmarks

## Implementation Guidelines

### 1. Test Structure
- Place test files alongside source files with `.test.ts` extension
- Use descriptive test names that explain the expected behavior
- Group related tests using `describe` blocks

### 2. Mock Strategy
- Use real fetch with mock servers for API testing
- Avoid mocking internal modules unless necessary
- Create reusable test fixtures for common data

### 3. Async Testing
- Always await async operations
- Use proper cleanup in afterEach hooks
- Handle streaming responses correctly

### 4. Type Safety
- Never use `any` type in tests
- Ensure all mocks match actual type signatures
- Use type assertions sparingly and correctly

## Migration from Jest

The project has been successfully migrated from Jest to Vitest for better ESM support and modern tooling:

1. **Key Changes**
   - Replaced Jest configuration with Vitest config
   - Updated test scripts in package.json
   - Fixed import issues with `@ai-sdk/provider-utils/test`
   - Updated test expectations for new data structures

2. **Benefits**
   - Native ESM support
   - Faster test execution
   - Better TypeScript integration
   - Interactive UI for test debugging

## Next Steps

1. **Immediate Actions**
   - Add tests for data storage layer
   - Test session lifecycle management
   - Verify settings persistence

2. **Short-term Goals**
   - Achieve 70% code coverage for core modules
   - Add integration tests for critical user flows
   - Set up automated test runs in CI/CD

3. **Long-term Vision**
   - Comprehensive E2E testing with Playwright
   - Performance regression testing
   - Cross-platform compatibility testing

## Resources

- [Vitest Documentation](https://vitest.dev/)
- [AI SDK Testing Guide](https://sdk.vercel.ai/docs/testing)
- [Testing Library](https://testing-library.com/)

================================================
FILE: docs/token-estimation.md
================================================
# Token Estimation System

Token 预估系统用于异步计算聊天消息和附件的 token 数量,在不阻塞 UI 的情况下提供实时的 token 统计。

## 架构概览

```text
┌─────────────────────────────────────────────────────────────────────┐
│  React UI (InputBox, TokenCountMenu)                                │
│    └── useTokenEstimation hook                                      │
│           ├── 返回: { totalTokens, isCalculating, breakdown }       │
│           └── 订阅 computationQueue 状态变化                         │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  analyzer.ts                                                        │
│    ├── 检查消息的 tokenCountMap 缓存                                 │
│    ├── 已缓存 → 直接返回 token 数                                    │
│    └── 未缓存 → 生成 pendingTasks                                   │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  computation-queue.ts (Singleton)                                   │
│    ├── 优先级队列 (priority: 0=当前输入, 10+=历史消息)               │
│    ├── 任务去重 (by taskId)                                         │
│    ├── 并发控制 (maxConcurrency=1)                                  │
│    └── Session 级别取消                                             │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  task-executor.ts                                                   │
│    ├── 读取消息/附件内容                                             │
│    ├── 调用 tokenizer 计算 token                                    │
│    └── 将结果发送到 resultPersister                                 │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  result-persister.ts                                                │
│    ├── 累积计算结果                                                  │
│    ├── Throttle 机制 (1000ms) - 保证每秒至少 flush 一次             │
│    └── 调用 chatStore.updateMessages() 持久化                       │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  chatStore.ts                                                       │
│    ├── 更新 storage (IndexedDB)                                     │
│    └── setQueryData() 更新 React Query 缓存 → UI 重新渲染           │
└─────────────────────────────────────────────────────────────────────┘
```

## 文件结构

```text
src/renderer/packages/token-estimation/
├── index.ts                 # 公共 API 导出
├── types.ts                 # 类型定义 (ComputationTask, TaskResult, etc.)
├── hooks/
│   └── useTokenEstimation.ts  # React Hook - UI 入口
├── analyzer.ts              # 分析哪些消息需要计算
├── computation-queue.ts     # 任务队列管理
├── task-executor.ts         # 任务执行逻辑
├── result-persister.ts      # 结果持久化 (throttle)
├── tokenizer.ts             # Token 计算逻辑 (tiktoken/deepseek)
├── cache-keys.ts            # 缓存 key 生成工具
└── __tests__/               # 单元测试
```

## 核心组件

### 1. useTokenEstimation Hook

**位置**: `hooks/useTokenEstimation.ts`

React 组件的入口点,负责:
- 调用 `analyzeTokenRequirements()` 分析需要计算的任务
- 将任务入队到 `computationQueue`
- 订阅队列状态变化,返回 `isCalculating`
- 当 session 切换时取消旧 session 的任务

```typescript
const {
  totalTokens,      // 总 token 数
  contextTokens,    // 上下文消息 token 数
  currentInputTokens, // 当前输入 token 数
  isCalculating,    // 是否正在计算
  pendingTasks,     // 待处理任务数
  breakdown,        // 详细分解
} = useTokenEstimation({
  sessionId,
  constructedMessage,  // 当前输入(未发送)
  contextMessages,     // 历史消息
  model,
  modelSupportToolUseForFile,
})
```

### 2. Analyzer

**位置**: `analyzer.ts`

分析消息列表,确定哪些需要计算:
- 检查每条消息的 `tokenCountMap` 缓存
- 已缓存 → 直接累加到结果
- 未缓存 → 生成 `ComputationTask`

### 3. Computation Queue

**位置**: `computation-queue.ts`

优先级任务队列,特性:
- **优先级调度**: 当前输入 (0) > 附件 (1) > 历史消息 (10+)
- **去重**: 通过 taskId 防止重复计算
- **并发控制**: 最多 1 个任务同时执行
- **Session 取消**: 切换会话时取消旧会话的任务

```typescript
// 优先级常量
PRIORITY = {
  CURRENT_INPUT_TEXT: 0,      // 最高优先级
  CURRENT_INPUT_ATTACHMENT: 1,
  CONTEXT_TEXT: 10,           // 历史消息基础优先级
  CONTEXT_ATTACHMENT: 11,
}
```

### 4. Task Executor

**位置**: `task-executor.ts`

执行具体的 token 计算:
- 读取消息文本或附件内容
- 调用 tokenizer 计算 token 数
- 将结果发送到 `resultPersister`

### 5. Result Persister

**位置**: `result-persister.ts`

批量持久化计算结果,使用 **throttle** 机制:

```typescript
// Throttle 而非 Debounce
// - Debounce: 每次调用重置计时器,可能导致长时间不 flush
// - Throttle: 保证每 1000ms 至少 flush 一次

private throttleMs = 1000
private lastFlushTime = 0

private scheduleFlush(): void {
  const now = Date.now()
  const timeSinceLastFlush = now - this.lastFlushTime

  if (timeSinceLastFlush >= this.throttleMs) {
    // 距离上次 flush 已超过 1s,立即 flush
    this.doFlush()
  } else if (!this.flushTimer) {
    // 安排在剩余时间后 flush
    this.flushTimer = setTimeout(() => {
      this.doFlush()
    }, this.throttleMs - timeSinceLastFlush)
  }
  // 如果已有计时器,不做任何事(throttle 行为)
}
```

**为什么用 Throttle?**
- 计算 100 条消息时,任务会连续完成
- Debounce 会不断重置计时器,直到所有任务完成才 flush
- Throttle 保证用户每秒都能看到中间进度

### 6. Tokenizer

**位置**: `tokenizer.ts`

实际的 token 计算逻辑,支持:
- **Tiktoken**: OpenAI 模型 (cl100k_base, o200k_base)
- **DeepSeek**: DeepSeek 模型专用 tokenizer

## 缓存机制

Token 计算结果缓存在消息对象的 `tokenCountMap` 字段:

```typescript
interface Message {
  // ...
  tokenCountMap?: {
    tiktoken?: number           // 文本 token (tiktoken)
    tiktoken_preview?: number   // 预览模式 token
    deepseek?: number           // 文本 token (deepseek)
    deepseek_preview?: number   // 预览模式 token
  }
  tokenCalculatedAt?: {
    tiktoken?: number           // 计算时间戳
    // ...
  }
}
```

附件也有类似的缓存结构:

```typescript
interface MessageFile {
  // ...
  tokenCountMap?: TokenCountMap
  tokenCalculatedAt?: Record<string, number>
  lineCount?: number
  byteLength?: number
}
```

## React Query 集成

系统通过 `chatStore` 与 React Query 集成:

```typescript
// result-persister.ts
await chatStore.updateMessages(sessionId, (messages) => {
  return messages.map((msg) => {
    const update = sessionUpdates.find((u) => u.messageId === msg.id)
    if (!update) return msg
    return applyUpdates(msg, update.updates)
  })
})

// chatStore.ts - updateMessages 内部
queryClient.setQueryData(QueryKeys.ChatSession(sessionId), updated)
// ↑ 直接更新缓存,触发 UI 重新渲染
// 不使用 invalidateQueries,避免不必要的重新获取
```

## 初始化

系统在应用启动时初始化:

```typescript
// src/renderer/setup/token_estimation_init.ts
import { initializeExecutor, setResultPersister } from '@/packages/token-estimation/task-executor'
import { resultPersister } from '@/packages/token-estimation/result-persister'
import { computationQueue } from '@/packages/token-estimation/computation-queue'

// 连接 persister 到 executor
setResultPersister(resultPersister)

// 初始化 executor (连接到 queue)
initializeExecutor()

// 启动定期清理
computationQueue.startCleanup()
```

## 调试工具

开发环境下可通过 `window.__tokenEstimation` 访问:

```javascript
// 查看队列状态
window.__tokenEstimation.getStatus()
// { pending: 0, running: 0 }

// 查看待处理任务
window.__tokenEstimation.getPendingTasks()

// 手动触发 flush
window.__tokenEstimation.flushNow()
```

## 性能考虑

1. **并发限制**: 最多 1 个任务同时执行,防止 CPU 过载
2. **优先级调度**: 当前输入优先计算,用户体验更好
3. **Throttle 持久化**: 每秒最多写入一次,减少 I/O
4. **去重**: 相同任务不会重复计算
5. **Session 取消**: 切换会话时取消旧任务,节省资源
6. **内存清理**: 定期清理已完成任务 ID,防止内存泄漏

## 常见问题

### Q: 为什么 token 数显示为 0?
检查:
1. `initializeExecutor()` 是否被调用
2. `setResultPersister()` 是否被调用
3. 控制台是否有错误日志

### Q: 为什么计算很慢?
可能原因:
1. 大量历史消息需要计算
2. 附件文件较大
3. 可以通过 `window.__tokenEstimation.getStatus()` 查看队列状态

### Q: 如何添加新的 tokenizer?
1. 在 `tokenizer.ts` 添加新的计算逻辑
2. 在 `types.ts` 更新 `TokenizerType` 类型
3. 在 `cache-keys.ts` 更新缓存 key 生成逻辑

### Q: 切换 session 后 isCalculating 状态不正确?

**问题**(已修复):切换 session 后,InputBox 和 TokenCountMenu 仍显示上一个 session 的计算状态。

**原因**:
1. `InputBox` 使用 `key` 导致组件重新挂载,原有的 `prevSessionIdRef` 取消逻辑失效
2. `computationQueue.getStatus()` 返回全局队列状态,而非当前 session 的状态

**解决方案**:
1. 使用 effect cleanup function 在组件卸载时取消任务(替代 `useRef` 方案)
2. 添加 `getStatusForSession(sessionId)` 方法返回指定 session 的状态
3. `useTokenEstimation` hook 订阅当前 session 的状态变化

**相关代码**:
- `computation-queue.ts`: `getStatusForSession()`
- `useTokenEstimation.ts`: cleanup function + session-scoped status


================================================
FILE: electron-builder.yml
================================================
productName: Chatbox
appId: xyz.chatboxapp.app
asar: true
asarUnpack:
  - "**\\*.{node,dll}"
  - "**/node_modules/libsql/**"
files:
  - "dist"
  # - "!dist/**/*.map"
  # - "!dist/**/stats.html"
  - "node_modules"
  - "package.json"

afterSign: .erb/scripts/notarize.js
afterPack: .erb/scripts/patch-libsql.cjs

# releaseInfo:
#   releaseNotes: See the changelog for details

mac:
  notarize: false
  category: public.app-category.developer-tools
  target:
    target: default
    arch:
      - arm64
      - x64
  type: distribution
  hardenedRuntime: true
  entitlements: assets/entitlements.mac.plist
  entitlementsInherit: assets/entitlements.mac.plist
  gatekeeperAssess: false

dmg:
  contents:
    - x: 130
      y: 220
    - x: 410
      y: 220
      type: link
      path: /Applications

win:
  target:
    - target: nsis
      arch:
        - x64
        - arm64
  verifyUpdateCodeSignature: false
  artifactName: ${productName}-${version}-Setup.${ext}
  sign: ./custom_win_sign.js
  signingHashAlgorithms:
    - sha256

nsis:
  oneClick: false
  allowToChangeInstallationDirectory: true
  include: assets/installer.nsh

linux:
  target:
    - target: AppImage
      arch:
        - x64
        - arm64
    - target: deb
      arch:
        - x64
        - arm64
  category: Development
  artifactName: ${productName}-${version}-${arch}.${ext}

directories:
  app: release/app
  buildResources: assets
  output: release/build

extraResources:
  - ./assets/**

publish:
  - provider: s3
    bucket: chatbox
    endpoint: https://208624959c9d215edea0720162a740c1.r2.cloudflarestorage.com
    path: /releases
    channel: ${env.UPDATE_CHANNEL}


================================================
FILE: electron.vite.config.ts
================================================
import { sentryVitePlugin } from '@sentry/vite-plugin'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import react from '@vitejs/plugin-react'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import path, { resolve } from 'path'
import { visualizer } from 'rollup-plugin-visualizer'
import type { Plugin } from 'vite'
import packageJson from './release/app/package.json'
/**
 * Vite plugin to inject <base href="/"> for web builds
 * This ensures relative paths resolve correctly for SPA routes like /session/xxx
 */
export function injectBaseTag(): Plugin {
  return {
    name: 'inject-base-tag',
    transformIndexHtml() {
      return [
        {
          tag: 'base',
          attrs: { href: '/' },
          injectTo: 'head-prepend', // Inject at the beginning of <head>
        },
      ]
    },
  }
}

/**
 * Vite plugin to replace dvh units with vh units
 * This replaces the webpack string-replace-loader functionality
 */
export function dvhToVh(): Plugin {
  return {
    name: 'dvh-to-vh',
    transform(code, id) {
      if (id.endsWith('.css') || id.endsWith('.scss') || id.endsWith('.sass')) {
        return {
          code: code.replace(/(\d+)dvh/g, '$1vh'),
          map: null,
        }
      }
      return null
    },
  }
}

const inferredRelease = process.env.SENTRY_RELEASE || packageJson.version
const inferredDist = process.env.SENTRY_DIST || undefined

process.env.SENTRY_RELEASE = inferredRelease
if (inferredDist) {
  process.env.SENTRY_DIST = inferredDist
}

export default defineConfig(({ mode }) => {
  const isProduction = mode === 'production'
  const isWeb = process.env.CHATBOX_BUILD_PLATFORM === 'web'

  return {
    main: {
      plugins: [
        ...(isProduction
          ? [
              visualizer({
                filename: 'release/app/dist/main/stats.html',
                open: false,
                title: 'Main Process Dependency Analysis',
              }),
            ]
          : [externalizeDepsPlugin()]),
        process.env.SENTRY_AUTH_TOKEN
          ? sentryVitePlugin({
              authToken: process.env.SENTRY_AUTH_TOKEN,
              org: 'sentry',
              project: 'chatbox',
              url: 'https://sentry.midway.run/',
              release: {
                name: inferredRelease,
                ...(inferredDist ? { dist: inferredDist } : {}),
              },
              sourcemaps: {
                assets: isProduction ? 'release/app/dist/main/**' : 'output/main/**',
              },
              telemetry: false,
            })
          : undefined,
      ].filter(Boolean),
      build: {
        outDir: isProduction ? 'release/app/dist/main' : undefined,
        lib: {
          entry: resolve(__dirname, 'src/main/main.ts'),
        },
        sourcemap: isProduction ? 'hidden' : true,
        minify: isProduction,
        rollupOptions: {
          external: Object.keys(packageJson.dependencies || {}),
          output: {
            entryFileNames: '[name].js',
            inlineDynamicImports: true,
          },
        },
      },
      resolve: {
        alias: {
          '@': path.resolve(__dirname, './src/renderer'),
          'src/shared': path.resolve(__dirname, './src/shared'),
        },
      },
      define: {
        'process.type': '"browser"',
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
        'process.env.CHATBOX_BUILD_TARGET': JSON.stringify(process.env.CHATBOX_BUILD_TARGET || 'unknown'),
        'process.env.CHATBOX_BUILD_PLATFORM': JSON.stringify(process.env.CHATBOX_BUILD_PLATFORM || 'unknown'),
        'process.env.USE_LOCAL_API': JSON.stringify(process.env.USE_LOCAL_API || ''),
        'process.env.USE_BETA_API': JSON.stringify(process.env.USE_BETA_API || ''),
      },
    },
    preload: {
      plugins: [
        visualizer({
          filename: 'release/app/dist/preload/stats.html',
          open: false,
          title: 'Preload Process Dependency Analysis',
        }),
      ],
      build: {
        outDir: isProduction ? 'release/app/dist/preload' : undefined,
        lib: {
          entry: resolve(__dirname, 'src/preload/index.ts'),
        },
        sourcemap: isProduction ? 'hidden' : true,
        minify: isProduction,
      },
      resolve: {
        alias: {
          '@': path.resolve(__dirname, './src/renderer'),
          'src/shared': path.resolve(__dirname, './src/shared'),
        },
      },
    },
    renderer: {
      resolve: {
        alias: {
          '@': path.resolve(__dirname, 'src/renderer'),
          '@shared': path.resolve(__dirname, 'src/shared'),
        },
      },
      plugins: [
        TanStackRouterVite({
          target: 'react',
          autoCodeSplitting: true,
          routesDirectory: './src/renderer/routes',
          generatedRouteTree: './src/renderer/routeTree.gen.ts',
        }),
        react({}),
        dvhToVh(),
        isWeb ? injectBaseTag() : undefined,
        visualizer({
          filename: 'release/app/dist/renderer/stats.html',
          open: false,
          title: 'Renderer Process Dependency Analysis',
        }),
        process.env.SENTRY_AUTH_TOKEN
          ? sentryVitePlugin({
              authToken: process.env.SENTRY_AUTH_TOKEN,
              org: 'sentry',
              project: 'chatbox',
              url: 'https://sentry.midway.run/',
              release: {
                name: inferredRelease,
                ...(inferredDist ? { dist: inferredDist } : {}),
              },
              sourcemaps: {
                assets: isProduction ? 'release/app/dist/renderer/**' : 'output/renderer/**',
              },
              telemetry: false,
            })
          : undefined,
      ].filter(Boolean),
      build: {
        outDir: isProduction ? 'release/app/dist/renderer' : undefined,
        target: 'es2020', // Avoid static initialization blocks for browser compatibility
        sourcemap: isProduction ? 'hidden' : true,
        minify: isProduction ? 'esbuild' : false, // Use esbuild for faster, less memory-intensive minification
        rollupOptions: {
          output: {
            entryFileNames: 'js/[name].[hash].js',
            chunkFileNames: 'js/[name].[hash].js',
            assetFileNames: (assetInfo) => {
              if (assetInfo.name?.endsWith('.css')) {
                return 'styles/[name].[hash][extname]'
              }
              if (/\.(woff|woff2|eot|ttf|otf)$/i.test(assetInfo.name || '')) {
                return 'fonts/[name].[hash][extname]'
              }
              if (/\.(png|jpg|jpeg|gif|svg|webp|ico)$/i.test(assetInfo.name || '')) {
                return 'images/[name].[hash][extname]'
              }
              return 'assets/[name].[hash][extname]'
            },
            // Optimize chunk splitting to reduce memory usage during build
            manualChunks(id) {
              if (id.includes('node_modules')) {
                // Split large vendor chunks
                if (id.includes('@ai-sdk') || id.includes('ai/')) {
                  return 'vendor-ai'
                }
                if (id.includes('@mantine') || id.includes('@tabler')) {
                  return 'vendor-ui'
                }
                if (id.includes('mermaid') || id.includes('d3')) {
                  return 'vendor-charts'
                }
              }
            },
          },
        },
      },
      css: {
        modules: {
          generateScopedName: '[name]__[local]___[hash:base64:5]',
        },
        postcss: './postcss.config.cjs',
      },
      server: {
        port: 1212,
        strictPort: true,
      },
      define: {
        'process.type': '"renderer"',
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
        'process.env.CHATBOX_BUILD_TARGET': JSON.stringify(process.env.CHATBOX_BUILD_TARGET || 'unknown'),
        'process.env.CHATBOX_BUILD_PLATFORM': JSON.stringify(process.env.CHATBOX_BUILD_PLATFORM || 'unknown'),
        'process.env.USE_LOCAL_API': JSON.stringify(process.env.USE_LOCAL_API || ''),
        'process.env.USE_BETA_API': JSON.stringify(process.env.USE_BETA_API || ''),
      },
      optimizeDeps: {
        include: ['mermaid'],
        esbuildOptions: {
          target: 'es2015',
        },
      },
    },
  }
})


================================================
FILE: i18next-parser.config.mjs
================================================
export default {
  input: ['src/renderer/**/*.{js,jsx,ts,tsx}'],
  output: 'src/renderer/i18n/locales/$LOCALE/$NAMESPACE.json',
  locales: ['en', 'ar', 'de', 'es', 'fr', 'it-IT', 'ja', 'ko', 'nb-NO', 'pt-PT', 'ru', 'sv', 'zh-Hans', 'zh-Hant'],
  createOldCatalogs: false,
  keepRemoved: true,
  pluralSeparator: false,
  keySeparator: false,
  namespaceSeparator: false,
  sort: true,
}


================================================
FILE: package.json
================================================
{
  "name": "xyz.chatboxapp.ce",
  "productName": "xyz.chatboxapp.ce",
  "version": "0.0.1",
  "engines": {
    "node": ">=20.0.0 <23.0.0",
    "pnpm": ">=10.0.0"
  },
  "private": true,
  "description": "A desktop client for multiple cutting-edge AI models",
  "main": "out/main/main.js",
  "scripts": {
    "build": "electron-vite build",
    "build:main": "electron-vite build",
    "build:preload": "electron-vite build",
    "build:renderer": "electron-vite build",
    "build:web": "cross-env CHATBOX_BUILD_PLATFORM=web electron-vite build && pnpm run delete-sourcemaps",
    "delete-sourcemaps": "ts-node ./.erb/scripts/delete-source-maps-runner.js",
    "postinstall": "node .erb/scripts/postinstall.cjs",
    "package": "ts-node ./.erb/scripts/clean.js && pnpm run build && electron-builder build --publish never",
    "package:all": "ts-node ./.erb/scripts/clean.js && pnpm run build && electron-builder build --publish never --win --mac --linux",
    "release:web": "bash release-web.sh",
    "release:mac": "bash release-mac.sh",
    "release:linux": "bash release-linux.sh",
    "release:win": "bash release-win.sh",
    "electron:publish-mac": "pnpm run build && electron-builder build --publish always --mac",
    "electron:publish-linux": "pnpm run build && electron-builder build --publish always --linux",
    "electron:publish-win": "pnpm run build && electron-builder build --publish always --win",
    "rebuild": "electron-rebuild --parallel --types prod,dev,optional --module-dir release/app",
    "dev": "pnpm start",
    "dev:local": "cross-env USE_LOCAL_API=true pnpm start",
    "dev:web": "cross-env DEV_WEB_ONLY=true CHATBOX_BUILD_PLATFORM=web pnpm start",
    "dev:debug": "cross-env MAIN_ARGS=\"--inspect=5858\" pnpm start",
    "start": "electron-vite dev",
    "start:main": "cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only .",
    "start:preload": "electron-vite build --preload --watch",
    "start:renderer": "electron-vite",
    "serve:web": "npx serve ./release/app/dist/renderer",
    "test": "vitest run",
    "test:ui": "vitest --ui",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:integration": "vitest run test/integration --testTimeout=300000",
    "test:file-conversation": "vitest run test/integration/file-conversation --testTimeout=120000",
    "test:model-provider": "vitest run test/integration/model-provider --testTimeout=120000",
    "lint": "biome lint .",
    "lint:fix": "biome lint . --write",
    "check": "npx tsc --noEmit",
    "format": "biome format --write",
    "check:biome": "biome check",
    "check:ci": "biome ci",
    "mobile:sync": "pnpm run mobile:sync:ios && pnpm run mobile:sync:android",
    "mobile:sync:ios": "cross-env CHATBOX_BUILD_TARGET=mobile_app CHATBOX_BUILD_PLATFORM=ios electron-vite build && pnpm run delete-sourcemaps && npx cap sync ios",
    "mobile:sync:android": "cross-env CHATBOX_BUILD_TARGET=mobile_app CHATBOX_BUILD_PLATFORM=android electron-vite build && pnpm run delete-sourcemaps && npx cap sync android",
    "mobile:ios": "pnpm run mobile:sync:ios && npx cap open ios",
    "mobile:android": "pnpm run mobile:sync:android && npx cap open android",
    "mobile:assets": "npx capacitor-assets generate --ios --android",
    "prepare": "node -e \"try { require('husky') } catch(e) { process.exit(0) }\" && husky || true",
    "translate": "i18next && node script/translate.mjs"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/chatboxai/chatbox.git"
  },
  "keywords": [],
  "author": {
    "name": "bennhuang",
    "email": "tohuangbin@gmail.com"
  },
  "devDependencies": {
    "@ai-sdk/anthropic": "^3.0.6",
    "@ai-sdk/azure": "^3.0.4",
    "@ai-sdk/deepseek": "^2.0.3",
    "@ai-sdk/google": "^3.0.3",
    "@ai-sdk/mcp": "^1.0.3",
    "@ai-sdk/mistral": "^3.0.4",
    "@ai-sdk/openai": "^3.0.4",
    "@ai-sdk/openai-compatible": "^2.0.3",
    "@ai-sdk/perplexity": "^3.0.3",
    "@ai-sdk/provider": "^3.0.1",
    "@babel/core": "^7.28.0",
    "@babel/plugin-transform-class-static-block": "^7.27.1",
    "@babel/preset-env": "^7.28.0",
    "@biomejs/biome": "2.0.0",
    "@braintree/sanitize-url": "^6.0.4",
    "@capacitor-community/sqlite": "^7.0.2",
    "@capacitor/android": "^7.0.0",
    "@capacitor/app": "^7.0.0",
    "@capacitor/assets": "^3.0.5",
    "@capacitor/browser": "^7.0.2",
    "@capacitor/cli": "^7.0.0",
    "@capacitor/core": "^7.0.0",
    "@capacitor/device": "^7.0.2",
    "@capacitor/filesystem": "^7.0.0",
    "@capacitor/ios": "^7.0.0",
    "@capacitor/keyboard": "^7.0.0",
    "@capacitor/share": "^7.0.0",
    "@capacitor/splash-screen": "^7.0.0",
    "@capacitor/toast": "^7.0.0",
    "@dnd-kit/core": "^6.0.8",
    "@dnd-kit/modifiers": "^6.0.1",
    "@dnd-kit/sortable": "^7.0.2",
    "@dnd-kit/utilities": "^3.2.1",
    "@ebay/nice-modal-react": "^1.2.13",
    "@electron/notarize": "^2.0.0",
    "@electron/rebuild": "^3.2.13",
    "@emotion/babel-plugin": "^11.13.5",
    "@emotion/babel-preset-css-prop": "^11.12.0",
    "@emotion/css": "^11.13.5",
    "@emotion/react": "^11.14.0",
    "@emotion/styled": "^11.14.0",
    "@epic-web/cachified": "^5.5.1",
    "@faker-js/faker": "^8.0.2",
    "@mantine/core": "^7.17.7",
    "@mantine/form": "^7.17.7",
    "@mantine/hooks": "^7.17.7",
    "@mantine/modals": "^7.17.7",
    "@mantine/spotlight": "^7.17.7",
    "@mui/icons-material": "^5.11.11",
    "@mui/material": "^5.11.11",
    "@openrouter/ai-sdk-provider": "^2.0.0",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
    "@radix-ui/react-dialog": "^1.0.5",
    "@sentry/react": "^10.12.0",
    "@sentry/vite-plugin": "^4.6.1",
    "@sentry/webpack-plugin": "^4.3.0",
    "@svgr/webpack": "^8.0.1",
    "@tabler/icons-react": "^3.31.0",
    "@tanstack/react-query": "^5.74.4",
    "@tanstack/react-router": "^1.114.23",
    "@tanstack/router-devtools": "^1.114.23",
    "@tanstack/router-plugin": "^1.120.15",
    "@tanstack/zod-adapter": "^1.127.3",
    "@teamsupercell/typings-for-css-modules-loader": "^2.5.2",
    "@testing-library/react": "^14.0.0",
    "@types/auto-launch": "^5.0.5",
    "@types/autosize": "^4.0.3",
    "@types/big.js": "^6.2.2",
    "@types/canvas-confetti": "^1.9.0",
    "canvas-confetti": "^1.9.4",
    "@types/d3": "^7.4.3",
    "@types/epub": "^0.0.11",
    "@types/gtag.js": "^0.0.13",
    "@types/highlight.js": "^10.1.0",
    "@types/katex": "^0.16.2",
    "@types/lodash": "^4.14.197",
    "@types/mark.js": "^8.11.12",
    "@types/markdown-it": "^12.2.3",
    "@types/markdown-it-link-attributes": "^3.0.1",
    "@types/node": "20.2.5",
    "@types/react": "^18.2.8",
    "@types/react-dom": "^18.2.4",
    "@types/react-swipeable-views": "^0.13.6",
    "@types/react-syntax-highlighter": "^15.5.9",
    "@types/react-test-renderer": "^18.0.0",
    "@types/shell-quote": "^1.7.5",
    "@types/store": "^2.0.2",
    "@types/terser-webpack-plugin": "^5.0.4",
    "@types/uuid": "^9.0.1",
    "@types/webpack-bundle-analyzer": "^4.6.0",
    "@vitejs/plugin-react": "^5.1.1",
    "@vitest/coverage-v8": "^4.0.16",
    "@vitest/ui": "^4.0.16",
    "ai": "^6.0.11",
    "ai-retry": "^1.0.1",
    "autoprefixer": "^10.4.14",
    "autosize": "^6.0.1",
    "axios": "^1.3.4",
    "babel-loader": "^10.0.0",
    "big.js": "^7.0.1",
    "browserslist-config-erb": "^0.0.3",
    "capacitor-plugin-safe-area": "^4.0.0",
    "capacitor-stream-http": "^0.1.0",
    "chalk": "^4.1.2",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.0.0",
    "cmdk": "^0.2.0",
    "compare-versions": "^6.1.1",
    "concurrently": "^8.1.0",
    "copy-to-clipboard": "^3.3.3",
    "core-js": "^3.34.0",
    "cross-env": "^7.0.3",
    "css-loader": "^6.8.1",
    "css-minimizer-webpack-plugin": "^5.0.0",
    "dayjs": "^1.11.13",
    "deepmerge": "^4.3.1",
    "detect-port": "^1.5.1",
    "dotenv": "^16.3.1",
    "electron": "^26.6.10",
    "electron-builder": "^24.2.1",
    "electron-vite": "^4.0.1",
    "electronmon": "^2.0.2",
    "emittery": "^1.1.0",
    "file-loader": "^6.2.0",
    "fork-ts-checker-webpack-plugin": "^7.2.13",
    "form-data": "^4.0.0",
    "highlight.js": "^11.7.0",
    "html-webpack-plugin": "^5.5.1",
    "husky": "^9.0.11",
    "i18next": "^22.4.13",
    "i18next-parser": "^9.3.0",
    "identity-obj-proxy": "^3.0.0",
    "immer": "^10.1.1",
    "javascript-obfuscator": "^4.0.2",
    "jotai": "^2.1.0",
    "jotai-immer": "^0.4.1",
    "jotai-optics": "^0.3.0",
    "js-base64": "^3.7.7",
    "js-tiktoken": "^1.0.7",
    "jsdom": "^26.1.0",
    "lint-staged": "^16.1.2",
    "localforage": "^1.10.0",
    "lucide-react": "^0.419.0",
    "mark.js": "^8.11.1",
    "material-ui-popup-state": "^5.0.4",
    "mermaid": "^11.4.0",
    "mini-css-extract-plugin": "^2.7.6",
    "msw": "^2.10.5",
    "node-loader": "^2.0.0",
    "optics-ts": "^2.4.0",
    "p-map": "^7.0.3",
    "p-timeout": "^6.1.4",
    "photoswipe": "^5.4.4",
    "postcss": "^8.5.3",
    "postcss-loader": "^7.3.3",
    "postcss-preset-mantine": "^1.17.0",
    "postcss-simple-vars": "^7.0.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-dropzone": "14.2.9",
    "react-hotkeys-hook": "^4.6.1",
    "react-i18next": "^12.2.0",
    "react-markdown": "^9.0.0",
    "react-photoswipe-gallery": "^3.1.1",
    "react-refresh": "^0.14.0",
    "react-router-dom": "^6.11.2",
    "react-swipeable-views": "^0.14.1",
    "react-syntax-highlighter": "^15.5.0",
    "react-test-renderer": "^18.2.0",
    "react-virtuoso": "^4.10.4",
    "react-zoom-pan-pinch": "^3.4.4",
    "rehype-katex": "^7.0.0",
    "remark-breaks": "^4.0.0",
    "remark-gfm": "^4.0.0",
    "remark-math": "^6.0.0",
    "rimraf": "^5.0.1",
    "rollup-plugin-visualizer": "^6.0.5",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.1",
    "shell-env": "^4.0.1",
    "shell-quote": "^1.8.2",
    "sonner": "^2.0.3",
    "store": "^2.0.12",
    "string-replace-loader": "^3.2.0",
    "style-loader": "^3.3.3",
    "swr": "^2.1.5",
    "tailwind-merge": "^1.14.0",
    "tailwind-scrollbar": "^3.1.0",
    "tailwindcss": "^3.4.0",
    "tailwindcss-animate": "^1.0.7",
    "terser-webpack-plugin": "^5.3.9",
    "ts-loader": "^9.4.3",
    "ts-node": "^10.9.1",
    "tsconfig-paths-webpack-plugin": "^4.0.1",
    "typescript": "^5.8.3",
    "unist-util-visit": "^5.0.0",
    "url-loader": "^4.1.1",
    "vaul": "^1.1.2",
    "vite": "^7.2.6",
    "vite-plugin-babel": "^1.3.2",
    "vite-plugin-svgr": "^4.5.0",
    "vite-tsconfig-paths": "^5.1.4",
    "vitest": "^4.0.16",
    "web-vitals": "^2.1.4",
    "webpack": "^5.85.0",
    "webpack-bundle-analyzer": "^4.9.0",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0",
    "webpack-merge": "^5.9.0",
    "webpack-obfuscator": "^3.5.1",
    "zod": "^4.0.17",
    "zustand": "^5.0.6"
  },
  "dependencies": {
    "@libsql/client": "^0.15.6",
    "@lobehub/icons": "^4.0.2",
    "@mastra/core": "^0.13.2",
    "@mastra/libsql": "^0.13.2",
    "@mastra/rag": "^1.0.8",
    "@modelcontextprotocol/sdk": "^1.15.1",
    "@mozilla/readability": "^0.5.0",
    "@sentry/node": "^9.28.1",
    "auto-launch": "^5.0.6",
    "chardet": "^2.1.0",
    "cohere-ai": "^7.17.1",
    "electron-debug": "^3.2.0",
    "electron-devtools-installer": "^3.2.0",
    "electron-log": "^5.3.4",
    "electron-store": "^8.1.0",
    "electron-updater": "^6.3.9",
    "epub": "^1.3.0",
    "es-toolkit": "^1.43.0",
    "fs-extra": "^11.1.1",
    "iconv-lite": "^0.6.3",
    "linkedom": "^0.18.5",
    "lodash": "^4.17.21",
    "ofetch": "^1.0.1",
    "officeparser": "5.0.0",
    "sanitize-filename": "^1.6.3",
    "uuid": "^9.0.0"
  },
  "browserslist": [],
  "electronmon": {
    "patterns": [
      "!**/**",
      "src/main/**"
    ],
    "logLevel": "quiet"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": "biome format --write"
  },
  "pnpm": {
    "onlyBuiltDependencies": [
      "@parcel/watcher",
      "@sentry/cli",
      "core-js",
      "core-js-pure",
      "electron",
      "esbuild",
      "msw",
      "protobufjs",
      "sharp",
      "zipfile"
    ],
    "overrides": {
      "@tanstack/router-generator": "1.120.15",
      "@tanstack/router-plugin": "1.120.15",
      "@mastra/libsql": "0.13.2",
      "@mastra/rag": "1.0.8"
    },
    "patchedD
Download .txt
gitextract_f36dbven/

├── .erb/
│   ├── .vscode/
│   │   └── settings.json
│   ├── configs/
│   │   ├── .eslintrc
│   │   ├── webpack.config.base.ts
│   │   ├── webpack.config.eslint.ts
│   │   ├── webpack.config.main.prod.ts
│   │   ├── webpack.config.preload.dev.ts
│   │   ├── webpack.config.renderer.dev.dll.ts
│   │   ├── webpack.config.renderer.dev.ts
│   │   ├── webpack.config.renderer.prod.ts
│   │   └── webpack.paths.ts
│   ├── mocks/
│   │   └── fileMock.js
│   └── scripts/
│       ├── .eslintrc
│       ├── check-build-exists.ts
│       ├── check-native-dep.cjs
│       ├── check-native-dep.js
│       ├── check-node-env.js
│       ├── check-port-in-use.js
│       ├── clean.js
│       ├── delete-source-maps.js
│       ├── electron-rebuild.cjs
│       ├── electron-rebuild.js
│       ├── link-modules.cjs
│       ├── link-modules.ts
│       ├── notarize.js
│       ├── patch-libsql.cjs
│       └── postinstall.cjs
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── custom.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── config.yml
│   ├── dependabot.yml
│   └── stale.yml
├── .gitignore
├── .node-version
├── .npmrc
├── .prettierrc
├── ERROR_HANDLING.md
├── LICENSE
├── README.md
├── assets/
│   ├── assets.d.ts
│   ├── entitlements.mac.plist
│   ├── icon.icns
│   └── installer.nsh
├── biome.json
├── doc/
│   ├── FAQ-CN.md
│   ├── FAQ.md
│   └── README-CN.md
├── docs/
│   ├── adding-new-provider.md
│   ├── adding-provider.md
│   ├── dependency-reorg.md
│   ├── new-session-mechanism.md
│   ├── rag.md
│   ├── session-module-split-plan.md
│   ├── storage.md
│   ├── testing.md
│   └── token-estimation.md
├── electron-builder.yml
├── electron.vite.config.ts
├── i18next-parser.config.mjs
├── package.json
├── patches/
│   ├── libsql@0.5.22.patch
│   └── mdast-util-gfm-autolink-literal@2.0.1.patch
├── pnpm-workspace.yaml
├── postcss.config.js
├── release/
│   └── app/
│       └── package.json
├── script/
│   └── translate.mjs
├── scripts/
│   └── ralph/
│       ├── prompt-opencode.md
│       └── ralph.sh
├── src/
│   ├── __tests__/
│   │   └── App.test.tsx.bk
│   ├── main/
│   │   ├── adapters/
│   │   │   ├── index.ts
│   │   │   └── sentry.ts
│   │   ├── analystic-node.ts
│   │   ├── app-updater.ts
│   │   ├── autoLauncher.ts
│   │   ├── cache.ts
│   │   ├── deeplinks.ts
│   │   ├── file-parser.ts
│   │   ├── knowledge-base/
│   │   │   ├── db.ts
│   │   │   ├── file-loaders.ts
│   │   │   ├── index.ts
│   │   │   ├── ipc-handlers.ts
│   │   │   ├── model-providers.ts
│   │   │   ├── parsers/
│   │   │   │   ├── chatbox-parser.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── local-parser.ts
│   │   │   │   ├── mineru-parser.ts
│   │   │   │   └── types.ts
│   │   │   └── remote-file-parser.ts
│   │   ├── locales.ts
│   │   ├── main.ts
│   │   ├── mcp/
│   │   │   ├── ipc-stdio-transport.ts
│   │   │   ├── shell-env.cjs
│   │   │   └── shell-env.d.ts
│   │   ├── menu.ts
│   │   ├── proxy.ts
│   │   ├── readability.ts
│   │   ├── store-node.ts
│   │   ├── util.ts
│   │   └── window_state.ts
│   ├── preload/
│   │   └── index.ts
│   ├── renderer/
│   │   ├── Sidebar.tsx
│   │   ├── adapters/
│   │   │   ├── index.ts
│   │   │   └── sentry.ts
│   │   ├── components/
│   │   │   ├── Accordion.tsx
│   │   │   ├── ActionMenu.tsx
│   │   │   ├── AdaptiveSelect.tsx
│   │   │   ├── Artifact.tsx
│   │   │   ├── CustomProviderIcon.tsx
│   │   │   ├── EditableAvatar.tsx
│   │   │   ├── ErrorTestPannel.tsx
│   │   │   ├── FileIcon.tsx
│   │   │   ├── Image.tsx
│   │   │   ├── ImageCountSlider.tsx
│   │   │   ├── ImageModelSelect.tsx
│   │   │   ├── ImageStyleSelect.tsx
│   │   │   ├── InputBox/
│   │   │   │   ├── Attachments.tsx
│   │   │   │   ├── ImageUploadButton.tsx
│   │   │   │   ├── ImageUploadInput.tsx
│   │   │   │   ├── InputBox.tsx
│   │   │   │   ├── SessionSettingsButton.tsx
│   │   │   │   ├── TokenCountMenu.tsx
│   │   │   │   ├── WebBrowsingButton.tsx
│   │   │   │   ├── actionIconStyles.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── preprocessState.ts
│   │   │   ├── Markdown.tsx
│   │   │   ├── Mermaid.tsx
│   │   │   ├── ModelList.tsx
│   │   │   ├── ModelSelector/
│   │   │   │   ├── DesktopModelSelector.tsx
│   │   │   │   ├── MobileModelSelector.tsx
│   │   │   │   ├── ProviderHeader.tsx
│   │   │   │   ├── SimplePreview.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── shared.tsx
│   │   │   ├── Shortcut.tsx
│   │   │   ├── SortableItem.tsx
│   │   │   ├── SponsorChip.tsx
│   │   │   ├── StyledMenu.tsx
│   │   │   ├── UpdateAvailableButton.tsx
│   │   │   ├── chat/
│   │   │   │   ├── CompactionStatus.tsx
│   │   │   │   ├── Message.tsx
│   │   │   │   ├── MessageAttachmentGrid.tsx
│   │   │   │   ├── MessageErrTips.tsx
│   │   │   │   ├── MessageList.tsx
│   │   │   │   ├── MessageLoading.tsx
│   │   │   │   ├── MessageNavigation.tsx
│   │   │   │   └── SummaryMessage.tsx
│   │   │   ├── common/
│   │   │   │   ├── AdaptiveModal.tsx
│   │   │   │   ├── Avatar.tsx
│   │   │   │   ├── CompressionModal.tsx
│   │   │   │   ├── ConfirmDeleteButton.tsx
│   │   │   │   ├── CreatableSelect.tsx
│   │   │   │   ├── Divider.tsx
│   │   │   │   ├── ErrorBoundary.tsx
│   │   │   │   ├── LazyNumberInput.tsx
│   │   │   │   ├── LazySlider.tsx
│   │   │   │   ├── Link.tsx
│   │   │   │   ├── Mark.tsx
│   │   │   │   ├── MaxContextMessageCountSlider.tsx
│   │   │   │   ├── MiniButton.tsx
│   │   │   │   ├── PasswordTextField.tsx
│   │   │   │   ├── PopoverConfirm.tsx
│   │   │   │   ├── ScalableIcon.tsx
│   │   │   │   ├── SegmentedControl.tsx
│   │   │   │   ├── SliderWithInput.tsx
│   │   │   │   ├── TemperatureSlider.tsx
│   │   │   │   ├── TextFieldReset.tsx
│   │   │   │   ├── Toasts.tsx
│   │   │   │   └── TopPSlider.tsx
│   │   │   ├── dev/
│   │   │   │   ├── DevHeader.tsx
│   │   │   │   └── ThemeSwitchButton.tsx
│   │   │   ├── icons/
│   │   │   │   ├── ArrowRightIcon.tsx
│   │   │   │   ├── BrandGithub.tsx
│   │   │   │   ├── BrandRedNote.tsx
│   │   │   │   ├── BrandWechat.tsx
│   │   │   │   ├── BrandX.tsx
│   │   │   │   ├── Broom.tsx
│   │   │   │   ├── Dart.tsx
│   │   │   │   ├── FullscreenIcon.tsx
│   │   │   │   ├── HomepageIcon.tsx
│   │   │   │   ├── Java.tsx
│   │   │   │   ├── LayoutExpand.tsx
│   │   │   │   ├── LayoutShrink.tsx
│   │   │   │   ├── Loading.tsx
│   │   │   │   ├── ModelIcon.tsx
│   │   │   │   ├── ProviderIcon.tsx
│   │   │   │   ├── ProviderImageIcon.tsx
│   │   │   │   └── Robot.tsx
│   │   │   ├── knowledge-base/
│   │   │   │   ├── ChunksPreviewModal.tsx
│   │   │   │   ├── KnowledgeBase.tsx
│   │   │   │   ├── KnowledgeBaseDocuments.tsx
│   │   │   │   ├── KnowledgeBaseForm.tsx
│   │   │   │   ├── KnowledgeBaseMenu.tsx
│   │   │   │   └── RemoteRetryModal.tsx
│   │   │   ├── layout/
│   │   │   │   ├── ExitFullscreenButton.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── Overlay.tsx
│   │   │   │   ├── Page.tsx
│   │   │   │   ├── Toolbar.tsx
│   │   │   │   └── WindowControls.tsx
│   │   │   ├── mcp/
│   │   │   │   ├── MCPMenu.tsx
│   │   │   │   └── MCPStatus.tsx
│   │   │   ├── message-parts/
│   │   │   │   └── ToolCallPartUI.tsx
│   │   │   ├── session/
│   │   │   │   ├── SessionItem.tsx
│   │   │   │   ├── SessionList.tsx
│   │   │   │   └── ThreadHistoryDrawer.tsx
│   │   │   ├── settings/
│   │   │   │   ├── DocumentParserSettings.tsx
│   │   │   │   ├── mcp/
│   │   │   │   │   ├── BuiltinServersSection.tsx
│   │   │   │   │   ├── ConfigModal.tsx
│   │   │   │   │   ├── CustomServersSection.tsx
│   │   │   │   │   ├── ServerRegistrySpotlight.tsx
│   │   │   │   │   ├── registries.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   └── provider/
│   │   │   │       ├── AddProviderModal.tsx
│   │   │   │       ├── ImportProviderModal.tsx
│   │   │   │       └── ProviderList.tsx
│   │   │   └── ui/
│   │   │       ├── command.tsx
│   │   │       └── dialog.tsx
│   │   ├── dev/
│   │   │   └── devToolsConfig.ts
│   │   ├── hooks/
│   │   │   ├── dom.ts
│   │   │   ├── knowledge-base.ts
│   │   │   ├── mcp.ts
│   │   │   ├── useAppTheme.ts
│   │   │   ├── useChatboxAIModels.ts
│   │   │   ├── useChunksPreview.ts
│   │   │   ├── useCopied.ts
│   │   │   ├── useCopilots.ts
│   │   │   ├── useDefaultSystemLanguage.ts
│   │   │   ├── useI18nEffect.ts
│   │   │   ├── useInputBoxHistory.ts
│   │   │   ├── useKnowledgeBase.ts
│   │   │   ├── useMessageInput.ts
│   │   │   ├── useNeedRoomForWinControls.ts
│   │   │   ├── useProviderImport.ts
│   │   │   ├── useProviders.ts
│   │   │   ├── useScreenChange.ts
│   │   │   ├── useShortcut.tsx
│   │   │   ├── useThinkingTimer.ts
│   │   │   ├── useVersion.ts
│   │   │   └── useWindowMaximized.ts
│   │   ├── i18n/
│   │   │   ├── changelogs/
│   │   │   │   ├── changelog_en.ts
│   │   │   │   ├── changelog_zh_Hans.ts
│   │   │   │   └── changelog_zh_Hant.ts
│   │   │   ├── for-key-scan.ts
│   │   │   ├── index.ts
│   │   │   ├── locales/
│   │   │   │   ├── ar/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── de/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── en/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── es/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── fr/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── it-IT/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── ja/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── ko/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── nb-NO/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── pt-PT/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── ru/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── sv/
│   │   │   │   │   └── translation.json
│   │   │   │   ├── zh-Hans/
│   │   │   │   │   └── translation.json
│   │   │   │   └── zh-Hant/
│   │   │   │       └── translation.json
│   │   │   ├── locales.ts
│   │   │   └── parser.ts
│   │   ├── index.ejs
│   │   ├── index.html
│   │   ├── index.tsx
│   │   ├── index.web.ejs
│   │   ├── lib/
│   │   │   ├── format-chat.tsx
│   │   │   └── utils.ts
│   │   ├── modals/
│   │   │   ├── AppStoreRating.tsx
│   │   │   ├── ArtifactPreview.tsx
│   │   │   ├── AttachLink.tsx
│   │   │   ├── ClearSessionList.tsx
│   │   │   ├── ContentViewer.tsx
│   │   │   ├── EdgeOneDeploySuccess.tsx
│   │   │   ├── ExportChat.tsx
│   │   │   ├── FileParseError.tsx
│   │   │   ├── JsonViewer.tsx
│   │   │   ├── MessageEdit.tsx
│   │   │   ├── ModelEdit.tsx
│   │   │   ├── ReportContent.tsx
│   │   │   ├── SessionSettings.tsx
│   │   │   ├── Settings.tsx
│   │   │   ├── ThreadNameEdit.tsx
│   │   │   ├── Welcome.tsx
│   │   │   └── index.tsx
│   │   ├── native/
│   │   │   └── stream-http.ts
│   │   ├── packages/
│   │   │   ├── apple_app_store.ts
│   │   │   ├── base64.test.ts
│   │   │   ├── base64.ts
│   │   │   ├── codeblock_state_recorder.ts
│   │   │   ├── context-management/
│   │   │   │   ├── attachment-payload.test.ts
│   │   │   │   ├── attachment-payload.ts
│   │   │   │   ├── compaction-detector.test.ts
│   │   │   │   ├── compaction-detector.ts
│   │   │   │   ├── compaction.ts
│   │   │   │   ├── context-builder.test.ts
│   │   │   │   ├── context-builder.ts
│   │   │   │   ├── context-tokens.hook.test.ts
│   │   │   │   ├── context-tokens.integration.test.ts
│   │   │   │   ├── context-tokens.ts
│   │   │   │   ├── context-tokens.unit.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── summary-generator.ts
│   │   │   │   ├── tool-cleanup.test.ts
│   │   │   │   └── tool-cleanup.ts
│   │   │   ├── edgeone.ts
│   │   │   ├── event.ts
│   │   │   ├── filetype.ts
│   │   │   ├── initial_data.ts
│   │   │   ├── keypairs.ts
│   │   │   ├── latex.test.ts
│   │   │   ├── latex.ts
│   │   │   ├── lemonsqueezy.ts
│   │   │   ├── local-parser.ts
│   │   │   ├── mcp/
│   │   │   │   ├── builtin.ts
│   │   │   │   ├── controller.ts
│   │   │   │   ├── ipc-stdio-transport.ts
│   │   │   │   └── types.ts
│   │   │   ├── model-calls/
│   │   │   │   ├── generate-image.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── message-utils.ts
│   │   │   │   ├── preprocess.ts
│   │   │   │   ├── stream-text.ts
│   │   │   │   ├── tools.ts
│   │   │   │   └── toolsets/
│   │   │   │       ├── file.ts
│   │   │   │       ├── knowledge-base.ts
│   │   │   │       └── web-search.ts
│   │   │   ├── model-context/
│   │   │   │   ├── builtin-data.ts
│   │   │   │   └── index.ts
│   │   │   ├── model-setting-utils/
│   │   │   │   ├── base-config.ts
│   │   │   │   ├── custom-provider-setting-util.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── interface.ts
│   │   │   │   ├── registry-setting-util.ts
│   │   │   │   └── util.ts
│   │   │   ├── navigator.ts
│   │   │   ├── pic_utils.ts
│   │   │   ├── prompts.ts
│   │   │   ├── remote.ts
│   │   │   ├── token-estimation/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── analyzer.test.ts
│   │   │   │   │   ├── cache-keys.test.ts
│   │   │   │   │   ├── computation-queue.test.ts
│   │   │   │   │   ├── result-persister.test.ts
│   │   │   │   │   ├── task-executor.test.ts
│   │   │   │   │   ├── tokenizer.test.ts
│   │   │   │   │   └── useTokenEstimation.test.ts
│   │   │   │   ├── analyzer.ts
│   │   │   │   ├── cache-keys.ts
│   │   │   │   ├── computation-queue.ts
│   │   │   │   ├── hooks/
│   │   │   │   │   └── useTokenEstimation.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── result-persister.ts
│   │   │   │   ├── task-executor.ts
│   │   │   │   ├── tokenizer.ts
│   │   │   │   └── types.ts
│   │   │   ├── token.test.ts
│   │   │   ├── token.tsx
│   │   │   ├── token_config.ts
│   │   │   ├── tools/
│   │   │   │   └── index.ts
│   │   │   ├── web-search/
│   │   │   │   ├── base.ts
│   │   │   │   ├── bing-news.ts
│   │   │   │   ├── bing.ts
│   │   │   │   ├── chatbox-search.ts
│   │   │   │   ├── duckduckgo.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── tavily.ts
│   │   │   └── word-count.ts
│   │   ├── pages/
│   │   │   ├── PictureDialog.tsx
│   │   │   ├── RemoteDialogWindow.tsx
│   │   │   ├── SearchDialog.tsx
│   │   │   └── SettingDialog/
│   │   │       └── AdvancedSettingTab.tsx
│   │   ├── platform/
│   │   │   ├── desktop_platform.ts
│   │   │   ├── index.ts
│   │   │   ├── interfaces.ts
│   │   │   ├── knowledge-base/
│   │   │   │   ├── desktop-controller.ts
│   │   │   │   └── interface.ts
│   │   │   ├── storages.ts
│   │   │   ├── test_platform.ts
│   │   │   ├── web_exporter.ts
│   │   │   ├── web_logger.ts
│   │   │   ├── web_platform.ts
│   │   │   └── web_platform_utils.ts
│   │   ├── preload.d.ts
│   │   ├── reportWebVitals.ts
│   │   ├── router.tsx
│   │   ├── routes/
│   │   │   ├── __root.tsx
│   │   │   ├── about.tsx
│   │   │   ├── copilots.tsx
│   │   │   ├── dev/
│   │   │   │   ├── context-generator.tsx
│   │   │   │   ├── css-var.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── model-selector.tsx
│   │   │   │   ├── route.tsx
│   │   │   │   └── storage.tsx
│   │   │   ├── image-creator/
│   │   │   │   ├── -components/
│   │   │   │   │   ├── EmptyState.tsx
│   │   │   │   │   ├── GeneratedImagesGallery.tsx
│   │   │   │   │   ├── HistoryItem.tsx
│   │   │   │   │   ├── HistoryPanel.tsx
│   │   │   │   │   ├── ImageGenerationErrorTips.tsx
│   │   │   │   │   ├── MobileDrawers.tsx
│   │   │   │   │   ├── PromptDisplay.tsx
│   │   │   │   │   ├── ReferenceImagesPreview.tsx
│   │   │   │   │   ├── Shimmer.tsx
│   │   │   │   │   └── constants.ts
│   │   │   │   └── index.tsx
│   │   │   ├── index.tsx
│   │   │   ├── session/
│   │   │   │   └── $sessionId.tsx
│   │   │   └── settings/
│   │   │       ├── chat.tsx
│   │   │       ├── chatbox-ai.tsx
│   │   │       ├── default-models.tsx
│   │   │       ├── document-parser.tsx
│   │   │       ├── general.tsx
│   │   │       ├── hotkeys.tsx
│   │   │       ├── index.tsx
│   │   │       ├── knowledge-base.tsx
│   │   │       ├── mcp.tsx
│   │   │       ├── provider/
│   │   │       │   ├── $providerId.tsx
│   │   │       │   ├── chatbox-ai/
│   │   │       │   │   ├── -components/
│   │   │       │   │   │   ├── LicenseDetailCard.tsx
│   │   │       │   │   │   ├── LicenseKeyView.tsx
│   │   │       │   │   │   ├── LicenseSelectionModal.tsx
│   │   │       │   │   │   ├── LoggedInView.tsx
│   │   │       │   │   │   ├── LoginView.tsx
│   │   │       │   │   │   ├── ModelManagement.tsx
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── types.ts
│   │   │       │   │   │   ├── useAuthTokens.ts
│   │   │       │   │   │   ├── useLicenseActivation.ts
│   │   │       │   │   │   ├── useLogin.ts
│   │   │       │   │   │   └── useUserLicenses.ts
│   │   │       │   │   └── index.tsx
│   │   │       │   ├── index.tsx
│   │   │       │   └── route.tsx
│   │   │       ├── route.tsx
│   │   │       └── web-search.tsx
│   │   ├── setup/
│   │   │   ├── ga_init.ts
│   │   │   ├── global_error_handler.ts
│   │   │   ├── init_data.ts
│   │   │   ├── load_polyfill.ts
│   │   │   ├── mcp_bootstrap.ts
│   │   │   ├── mobile_safe_area.ts
│   │   │   ├── protect.ts
│   │   │   ├── sentry_init.ts
│   │   │   ├── storage_clear.ts
│   │   │   └── token_estimation_init.ts
│   │   ├── setupTests.ts
│   │   ├── static/
│   │   │   ├── Block.css
│   │   │   ├── _headers
│   │   │   ├── globals.css
│   │   │   └── index.css
│   │   ├── storage/
│   │   │   ├── BaseStorage.ts
│   │   │   ├── ImageGenerationStorage.ts
│   │   │   ├── SQLiteImageGenerationStorage.ts
│   │   │   ├── StoreStorage.ts
│   │   │   └── index.ts
│   │   ├── stores/
│   │   │   ├── atoms/
│   │   │   │   ├── compactionAtoms.ts
│   │   │   │   ├── configAtoms.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── sessionAtoms.ts
│   │   │   │   ├── settingsAtoms.ts
│   │   │   │   ├── throttleWriteSessionAtom.ts
│   │   │   │   ├── uiAtoms.ts
│   │   │   │   └── utilAtoms.ts
│   │   │   ├── authInfoStore.ts
│   │   │   ├── chatStore.ts
│   │   │   ├── imageGenerationActions.ts
│   │   │   ├── imageGenerationStore.ts
│   │   │   ├── lastUsedModelStore.ts
│   │   │   ├── migration.test.ts
│   │   │   ├── migration.ts
│   │   │   ├── premiumActions.ts
│   │   │   ├── queryClient.ts
│   │   │   ├── safeStorage.ts
│   │   │   ├── scrollActions.ts
│   │   │   ├── session/
│   │   │   │   ├── crud.ts
│   │   │   │   ├── export.ts
│   │   │   │   ├── forks.ts
│   │   │   │   ├── generation.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── messages.ts
│   │   │   │   ├── naming.ts
│   │   │   │   ├── state.ts
│   │   │   │   ├── threads.ts
│   │   │   │   └── types.ts
│   │   │   ├── sessionActions.test.ts
│   │   │   ├── sessionActions.ts
│   │   │   ├── sessionHelpers.ts
│   │   │   ├── settingActions.ts
│   │   │   ├── settingsStore.ts
│   │   │   ├── toastActions.ts
│   │   │   ├── uiStore.ts
│   │   │   ├── updateQueue.test.ts
│   │   │   └── updateQueue.ts
│   │   ├── test.tsx
│   │   ├── utils/
│   │   │   ├── base64.ts
│   │   │   ├── error-testing.ts
│   │   │   ├── feature-flags.ts
│   │   │   ├── format.ts
│   │   │   ├── image.ts
│   │   │   ├── index.ts
│   │   │   ├── message.test.ts
│   │   │   ├── message.ts
│   │   │   ├── mobile-request.ts
│   │   │   ├── model-tester.ts
│   │   │   ├── modelLogo.tsx
│   │   │   ├── provider-config.test.ts
│   │   │   ├── provider-config.ts
│   │   │   ├── request.ts
│   │   │   ├── session-utils.ts
│   │   │   └── track.ts
│   │   └── variables.ts
│   └── shared/
│       ├── constants.ts
│       ├── defaults.ts
│       ├── electron-types.ts
│       ├── file-extensions.ts
│       ├── models/
│       │   ├── abstract-ai-sdk.ts
│       │   ├── errors.ts
│       │   ├── index.test.ts
│       │   ├── index.ts
│       │   ├── openai-compatible.ts
│       │   ├── rerank.ts
│       │   ├── types.ts
│       │   └── utils/
│       │       └── fetch-proxy.ts
│       ├── providers/
│       │   ├── definitions/
│       │   │   ├── azure.ts
│       │   │   ├── chatboxai.ts
│       │   │   ├── chatglm.ts
│       │   │   ├── claude.ts
│       │   │   ├── deepseek.ts
│       │   │   ├── gemini.ts
│       │   │   ├── groq.ts
│       │   │   ├── lmstudio.ts
│       │   │   ├── mistral-ai.ts
│       │   │   ├── models/
│       │   │   │   ├── azure.ts
│       │   │   │   ├── chatboxai.ts
│       │   │   │   ├── chatglm.ts
│       │   │   │   ├── claude.ts
│       │   │   │   ├── custom-claude.ts
│       │   │   │   ├── custom-gemini.ts
│       │   │   │   ├── custom-openai-responses.ts
│       │   │   │   ├── custom-openai.ts
│       │   │   │   ├── deepseek.ts
│       │   │   │   ├── gemini.ts
│       │   │   │   ├── groq.ts
│       │   │   │   ├── lmstudio.ts
│       │   │   │   ├── mistral-ai.ts
│       │   │   │   ├── ollama.ts
│       │   │   │   ├── openai-responses.ts
│       │   │   │   ├── openai.test.ts
│       │   │   │   ├── openai.ts
│       │   │   │   ├── openrouter.ts
│       │   │   │   ├── perplexity.ts
│       │   │   │   ├── siliconflow.ts
│       │   │   │   ├── volcengine.ts
│       │   │   │   └── xai.ts
│       │   │   ├── ollama.ts
│       │   │   ├── openai-responses.ts
│       │   │   ├── openai.ts
│       │   │   ├── openrouter.ts
│       │   │   ├── perplexity.ts
│       │   │   ├── siliconflow.ts
│       │   │   ├── volcengine.ts
│       │   │   └── xai.ts
│       │   ├── index.ts
│       │   ├── registry.test.ts
│       │   ├── registry.ts
│       │   ├── types.ts
│       │   └── utils.ts
│       ├── request/
│       │   ├── chatboxai_pool.ts
│       │   └── request.ts
│       ├── types/
│       │   ├── adapters.ts
│       │   ├── image-generation.ts
│       │   ├── mcp.ts
│       │   ├── provider.ts
│       │   ├── session.ts
│       │   └── settings.ts
│       ├── types.test.ts
│       ├── types.ts
│       └── utils/
│           ├── cache.ts
│           ├── index.ts
│           ├── json_utils.ts
│           ├── knowledge-base-model-parser.ts
│           ├── llm_utils.test.ts
│           ├── llm_utils.ts
│           ├── message.ts
│           ├── model_settings.ts
│           ├── network_utils.ts
│           ├── sentry_adapter.ts
│           └── word_count.ts
├── tailwind.config.js
├── tasks/
│   ├── prd-code-organization-optimization.md
│   ├── prd-compaction-ux-improvement.md
│   ├── prd-context-management.md
│   └── prd-provider-system-refactor.md
├── team-sharing/
│   ├── Caddyfile
│   ├── Dockerfile
│   ├── README-CN.md
│   ├── README.md
│   └── main.sh
├── test/
│   ├── cases/
│   │   ├── file-conversation/
│   │   │   ├── sample-large.txt
│   │   │   ├── sample.json
│   │   │   ├── sample.md
│   │   │   ├── sample.ts
│   │   │   └── sample.txt
│   │   └── provider-config-import-manual-test.md
│   └── integration/
│       ├── context-management/
│       │   ├── context-management.test.ts
│       │   └── setup.ts
│       ├── file-conversation/
│       │   ├── file-conversation.test.ts
│       │   ├── setup.ts
│       │   └── test-harness.ts
│       ├── mocks/
│       │   ├── model-dependencies.ts
│       │   └── sentry.ts
│       └── model-provider/
│           └── model-provider.test.ts
├── tsconfig.json
└── vitest.config.ts
Download .txt
SYMBOL INDEX (2029 symbols across 418 files)

FILE: .erb/configs/webpack.config.renderer.dev.dll.ts
  constant EXCLUDE_MODULES (line 15) | const EXCLUDE_MODULES = new Set([

FILE: .erb/configs/webpack.config.renderer.dev.ts
  constant DEV_WEB_ONLY (line 28) | const DEV_WEB_ONLY = process.env.DEV_WEB_ONLY === 'true'
  method setupMiddlewares (line 204) | setupMiddlewares(middlewares) {

FILE: .erb/scripts/check-native-dep.cjs
  function findNodeFiles (line 17) | function findNodeFiles(dir) {
  function getAllPackages (line 39) | function getAllPackages(nodeModulesDir) {

FILE: .erb/scripts/check-native-dep.js
  function findNodeFiles (line 8) | function findNodeFiles(dir) {
  function getAllPackages (line 30) | function getAllPackages(nodeModulesDir) {

FILE: .erb/scripts/check-node-env.js
  function checkNodeEnv (line 3) | function checkNodeEnv(expectedEnv) {

FILE: .erb/scripts/delete-source-maps.js
  function deleteSourceMaps (line 6) | function deleteSourceMaps() {

FILE: .erb/scripts/patch-libsql.cjs
  function patchLibsqlFile (line 36) | function patchLibsqlFile(filePath) {
  function getCandidateLibsqlDirs (line 73) | function getCandidateLibsqlDirs(context) {

FILE: assets/assets.d.ts
  type Styles (line 1) | type Styles = Record<string, string>

FILE: electron.vite.config.ts
  function injectBaseTag (line 13) | function injectBaseTag(): Plugin {
  function dvhToVh (line 32) | function dvhToVh(): Plugin {
  method manualChunks (line 202) | manualChunks(id) {

FILE: script/translate.mjs
  function translateMessage (line 6) | async function translateMessage(message, target, keysToTrans, instructio...
  function translateFile (line 28) | async function translateFile(locale, instruction) {

FILE: src/main/adapters/index.ts
  function createModelDependencies (line 9) | async function createModelDependencies(): Promise<ModelDependencies> {

FILE: src/main/adapters/sentry.ts
  function initSentry (line 6) | function initSentry() {
  class MainSentryAdapter (line 49) | class MainSentryAdapter implements SentryAdapter {
    method captureException (line 50) | captureException(error: any): void {
    method withScope (line 54) | withScope(callback: (scope: SentryScope) => void): void {

FILE: src/main/analystic-node.ts
  function event (line 14) | async function event(name: string, params: any = {}) {

FILE: src/main/app-updater.ts
  class AppUpdater (line 7) | class AppUpdater {
    method constructor (line 8) | constructor(onUpdateDownloaded: () => void) {
    method tryUpdate (line 33) | async tryUpdate() {

FILE: src/main/autoLauncher.ts
  function get (line 7) | function get() {
  function sync (line 14) | async function sync() {
  function ensure (line 28) | async function ensure(enable: boolean) {

FILE: src/main/cache.ts
  type CacheItem (line 1) | interface CacheItem<T> {
  function cache (line 9) | async function cache<T>(

FILE: src/main/deeplinks.ts
  function handleDeepLink (line 4) | function handleDeepLink(mainWindow: BrowserWindow, link: string) {

FILE: src/main/file-parser.ts
  function decodeHtmlEntities (line 11) | function decodeHtmlEntities(text: string): string {
  function concurrentMap (line 42) | async function concurrentMap<T, R>(
  function parseFile (line 69) | async function parseFile(filePath: string) {
  function parseEpub (line 113) | async function parseEpub(filePath: string): Promise<string> {

FILE: src/main/knowledge-base/db.ts
  function initDB (line 28) | async function initDB(db: Client) {
  function initializeDatabase (line 114) | async function initializeDatabase() {
  function getDatabase (line 138) | function getDatabase(): Client {
  function getVectorStore (line 152) | function getVectorStore(): LibSQLVector {
  function parseSQLiteTimestamp (line 167) | function parseSQLiteTimestamp(sqliteTimestamp: string): number {
  function withTransaction (line 193) | async function withTransaction<T>(operation: () => Promise<T>): Promise<...
  function cleanupProcessingFiles (line 233) | async function cleanupProcessingFiles() {
  function checkProcessingTimeouts (line 255) | async function checkProcessingTimeouts() {

FILE: src/main/knowledge-base/file-loaders.ts
  function parseErrorMessage (line 20) | function parseErrorMessage(errorMessage: string): string {
  function parseFileToDocumentWithRouter (line 51) | async function parseFileToDocumentWithRouter(
  function processFileWithMastra (line 69) | async function processFileWithMastra(
  function processPendingFiles (line 263) | async function processPendingFiles() {
  function startWorkerLoop (line 358) | async function startWorkerLoop() {
  function searchKnowledgeBase (line 380) | async function searchKnowledgeBase(kbId: number, query: string) {
  function readChunks (line 444) | async function readChunks(kbId: number, chunks: { fileId: number; chunkI...

FILE: src/main/knowledge-base/index.ts
  function initializeKnowledgeBase (line 11) | async function initializeKnowledgeBase() {
  function getInitPromise (line 47) | function getInitPromise() {

FILE: src/main/knowledge-base/ipc-handlers.ts
  function registerKnowledgeBaseHandlers (line 16) | function registerKnowledgeBaseHandlers() {

FILE: src/main/knowledge-base/model-providers.ts
  function getMergedSettings (line 15) | function getMergedSettings(providerId: string, modelId: string) {
  function getEmbeddingProvider (line 53) | async function getEmbeddingProvider(kbId: number) {
  function getVisionProvider (line 128) | async function getVisionProvider(kbId: number) {
  function getRerankProvider (line 185) | async function getRerankProvider(kbId: number) {

FILE: src/main/knowledge-base/parsers/chatbox-parser.ts
  class ChatboxParser (line 10) | class ChatboxParser implements DocumentParser {
    method parse (line 13) | async parse(filePath: string, meta: ParserFileMeta): Promise<string> {

FILE: src/main/knowledge-base/parsers/index.ts
  function createParser (line 19) | function createParser(config: DocumentParserConfig, kbId?: number): Docu...
  function getEffectiveParserConfig (line 40) | function getEffectiveParserConfig(
  function parseFileWithRouter (line 63) | async function parseFileWithRouter(
  function getParserDisplayName (line 87) | function getParserDisplayName(type: DocumentParserType): string {

FILE: src/main/knowledge-base/parsers/local-parser.ts
  class LocalParser (line 19) | class LocalParser implements DocumentParser {
    method constructor (line 22) | constructor(private kbId?: number) {}
    method parse (line 24) | async parse(filePath: string, meta: ParserFileMeta): Promise<string> {
    method parseImage (line 53) | private async parseImage(filePath: string, meta: ParserFileMeta): Prom...

FILE: src/main/knowledge-base/parsers/mineru-parser.ts
  constant MINERU_API_BASE (line 19) | const MINERU_API_BASE = 'https://mineru.net/api/v4'
  constant POLL_INTERVAL_MS (line 20) | const POLL_INTERVAL_MS = 10000 // 10 seconds
  constant MAX_POLL_ATTEMPTS (line 21) | const MAX_POLL_ATTEMPTS = 30 // 5 minutes total timeout
  constant MAX_FILE_SIZE (line 22) | const MAX_FILE_SIZE = 200 * 1024 * 1024 // 200MB
  function mapErrorCode (line 27) | function mapErrorCode(code: string | number): MineruErrorCode {
  function sleep (line 47) | function sleep(ms: number, signal?: AbortSignal): Promise<void> {
  class MineruParser (line 78) | class MineruParser implements DocumentParser {
    method constructor (line 81) | constructor(private apiToken: string) {}
    method parse (line 83) | async parse(filePath: string, meta: ParserFileMeta, signal?: AbortSign...
    method getBatchUploadUrl (line 133) | private async getBatchUploadUrl(filename: string, dataId: string): Pro...
    method uploadFile (line 163) | private async uploadFile(filePath: string, uploadUrl: string): Promise...
    method pollBatchResult (line 180) | private async pollBatchResult(batchId: string, dataId: string, signal?...
    method downloadAndExtract (line 233) | private async downloadAndExtract(zipUrl: string): Promise<string> {
    method findMarkdownFiles (line 272) | private async findMarkdownFiles(dir: string): Promise<string[]> {
  function testMineruConnection (line 294) | async function testMineruConnection(apiToken: string): Promise<{ success...

FILE: src/main/knowledge-base/parsers/types.ts
  type ParserFileMeta (line 6) | interface ParserFileMeta {
  type DocumentParser (line 15) | interface DocumentParser {
  type ParserResult (line 30) | interface ParserResult {
  type MineruErrorCode (line 38) | type MineruErrorCode =
  class MineruError (line 51) | class MineruError extends Error {
    method constructor (line 52) | constructor(
  type MineruBatchUploadResponse (line 64) | interface MineruBatchUploadResponse {
  type MineruExtractResult (line 73) | interface MineruExtractResult {
  type MineruBatchResultResponse (line 86) | interface MineruBatchResultResponse {

FILE: src/main/knowledge-base/remote-file-parser.ts
  constant MAX_FILE_SIZE (line 12) | const MAX_FILE_SIZE = 50 * 1024 * 1024
  function getPlatformInfo (line 15) | function getPlatformInfo() {
  function getAfetch (line 25) | function getAfetch() {
  function getChatboxHeaders (line 30) | function getChatboxHeaders() {
  function getLicenseKey (line 43) | function getLicenseKey(): string | undefined {
  function generateUploadUrl (line 50) | async function generateUploadUrl(licenseKey: string, filename: string): ...
  function uploadFileFromPath (line 80) | async function uploadFileFromPath(filePath: string, uploadUrl: string, m...
  function createAndParseFile (line 100) | async function createAndParseFile(
  function parseFileRemotely (line 144) | async function parseFileRemotely(filePath: string, filename: string, mim...

FILE: src/main/locales.ts
  class Locale (line 3) | class Locale {
    method constructor (line 6) | constructor() {
    method isCN (line 14) | isCN(): boolean {
    method t (line 18) | t(key: TranslationKey): string {
  type TranslationKey (line 23) | type TranslationKey = keyof typeof translations

FILE: src/main/main.ts
  constant RESOURCES_PATH (line 52) | const RESOURCES_PATH = app.isPackaged
  constant PROTOCOL_SCHEME (line 61) | const PROTOCOL_SCHEME = process.defaultApp ? 'chatbox-dev' : 'chatbox'
  function normalizeShortcut (line 85) | function normalizeShortcut(shortcut: string) {
  function isValidShortcut (line 110) | function isValidShortcut(shortcut: string): boolean {
  function registerShortcuts (line 134) | function registerShortcuts(shortcutSetting?: ShortcutSetting) {
  function unregisterShortcuts (line 151) | function unregisterShortcuts() {
  function createTray (line 157) | function createTray() {
  function ensureTray (line 188) | function ensureTray() {
  function destroyTray (line 201) | function destroyTray() {
  function createWindow (line 242) | async function createWindow() {
  function showOrHideWindow (line 355) | async function showOrHideWindow() {

FILE: src/main/mcp/ipc-stdio-transport.ts
  function enhanceEnv (line 13) | async function enhanceEnv(configEnv?: Record<string, string>) {
  function getTransport (line 28) | function getTransport(transportId: string) {
  function closeAllTransports (line 101) | function closeAllTransports() {

FILE: src/main/mcp/shell-env.cjs
  function checkPathExt (line 48) | function checkPathExt(path, options) {
  function checkStat (line 65) | function checkStat(stat, path, options) {
  function isexe (line 71) | function isexe(path, options, cb) {
  function sync (line 76) | function sync(path, options) {
  function isexe (line 86) | function isexe(path, options, cb) {
  function sync (line 91) | function sync(path, options) {
  function checkStat (line 94) | function checkStat(stat, options) {
  function checkMode (line 97) | function checkMode(stat, options) {
  function isexe (line 123) | function isexe(path, options, cb) {
  function sync (line 152) | function sync(path, options) {
  function resolveCommandAttempt (line 275) | function resolveCommandAttempt(parsed, withoutPathExt) {
  function resolveCommand (line 301) | function resolveCommand(parsed) {
  function escapeCommand (line 310) | function escapeCommand(arg) {
  function escapeArgument (line 314) | function escapeArgument(arg, doubleEscapeMetaChars) {
  function readShebang (line 355) | function readShebang(command) {
  function detectShebang (line 378) | function detectShebang(parsed) {
  function parseNonShell (line 388) | function parseNonShell(parsed) {
  function parse (line 406) | function parse(command, args, options) {
  function notFoundError (line 431) | function notFoundError(original, syscall) {
  function hookChildProcess (line 440) | function hookChildProcess(cp, parsed) {
  function verifyENOENT (line 455) | function verifyENOENT(status, parsed) {
  function verifyENOENTSync (line 461) | function verifyENOENTSync(status, parsed) {
  function spawn (line 480) | function spawn(command, args, options) {
  function spawnSync (line 486) | function spawnSync(command, args, options) {
  class MaxBufferError (line 1418) | class MaxBufferError extends Error {
    method constructor (line 1419) | constructor() {
  function getStream (line 1424) | async function getStream(inputStream, options) {
  function add (line 1475) | function add(source) {
  function isEmpty (line 1486) | function isEmpty() {
  function remove (line 1489) | function remove(source) {
  function ansiRegex (line 1885) | function ansiRegex({ onlyFirst = false } = {}) {
  function stripAnsi (line 1896) | function stripAnsi(string) {
  function shellEnv (line 1943) | async function shellEnv(shell) {
  function shellEnvSync (line 1958) | function shellEnvSync(shell) {

FILE: src/main/menu.ts
  type DarwinMenuItemConstructorOptions (line 4) | interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOp...
  class MenuBuilder (line 9) | class MenuBuilder {
    method constructor (line 12) | constructor(mainWindow: BrowserWindow) {
    method buildMenu (line 16) | buildMenu(): Menu {
    method buildDarwinTemplate (line 57) | buildDarwinTemplate(): MenuItemConstructorOptions[] {
    method buildDefaultTemplate (line 231) | buildDefaultTemplate() {

FILE: src/main/proxy.ts
  function init (line 4) | function init() {
  function ensure (line 11) | function ensure(proxy?: string) {

FILE: src/main/store-node.ts
  type StoreType (line 33) | interface StoreType {
  function autoBackup (line 54) | async function autoBackup() {
  function getSettings (line 68) | function getSettings(): Settings {
  function getConfig (line 73) | function getConfig(): Config {
  function backup (line 85) | async function backup() {
  function getBackups (line 110) | function getBackups() {
  function needBackup (line 134) | function needBackup() {
  function clearBackups (line 146) | async function clearBackups() {
  function checkConfigValid (line 212) | function checkConfigValid(filepath: string) {
  function getStoreBlob (line 221) | async function getStoreBlob(key: string) {
  function setStoreBlob (line 230) | async function setStoreBlob(key: string, value: string) {
  function delStoreBlob (line 236) | async function delStoreBlob(key: string) {
  function listStoreBlobKeys (line 245) | async function listStoreBlobKeys() {

FILE: src/main/util.ts
  function resolveHtmlPath (line 5) | function resolveHtmlPath(htmlFileName: string) {
  function sliceTextWithEllipsis (line 12) | function sliceTextWithEllipsis(text: string, maxLength: number) {
  function getLogger (line 28) | function getLogger(logId: string) {

FILE: src/main/window_state.ts
  type IWindowState (line 7) | interface IWindowState {
  type WindowMode (line 16) | enum WindowMode {
  function defaultWindowState (line 27) | function defaultWindowState(mode = WindowMode.Normal): IWindowState {
  function getState (line 37) | function getState(): [IWindowState, boolean? /* has multiple displays */] {
  function saveState (line 42) | function saveState(win: Electron.BrowserWindow): void {
  function restoreWindowState (line 68) | function restoreWindowState(state?: IWindowState): [IWindowState, boolea...
  function validateWindowState (line 84) | function validateWindowState(state: IWindowState, displays: Display[]): ...
  function getWorkingArea (line 216) | function getWorkingArea(display: Display): Rectangle | undefined {
  function setCache (line 233) | function setCache(state: IWindowState) {
  function getCache (line 237) | function getCache(): IWindowState {

FILE: src/renderer/Sidebar.tsx
  function Sidebar (line 32) | function Sidebar() {

FILE: src/renderer/adapters/index.ts
  function createModelDependencies (line 11) | async function createModelDependencies(): Promise<ModelDependencies> {

FILE: src/renderer/adapters/sentry.ts
  class RendererSentryAdapter (line 7) | class RendererSentryAdapter implements SentryAdapter {
    method captureException (line 8) | captureException(error: any): void {
    method withScope (line 12) | withScope(callback: (scope: SentryScope) => void): void {

FILE: src/renderer/components/ActionMenu.tsx
  type ActionMenuItemProps (line 10) | type ActionMenuItemProps =
  type ActionMenuProps (line 30) | type ActionMenuProps = {

FILE: src/renderer/components/AdaptiveSelect.tsx
  type AdaptiveSelectProps (line 7) | interface AdaptiveSelectProps extends Omit<MantineSelectProps, 'onChange...
  function AdaptiveSelect (line 11) | function AdaptiveSelect(props: AdaptiveSelectProps) {

FILE: src/renderer/components/Artifact.tsx
  constant RENDERABLE_CODE_LANGUAGES (line 16) | const RENDERABLE_CODE_LANGUAGES = ['html'] as const
  type RenderableCodeLanguage (line 17) | type RenderableCodeLanguage = (typeof RENDERABLE_CODE_LANGUAGES)[number]
  constant CODE_BLOCK_LANGUAGES (line 19) | const CODE_BLOCK_LANGUAGES = [...RENDERABLE_CODE_LANGUAGES, 'js', 'javas...
  type CodeBlockLanguage (line 20) | type CodeBlockLanguage = (typeof CODE_BLOCK_LANGUAGES)[number]
  function isContainRenderableCode (line 22) | function isContainRenderableCode(markdown: string): boolean {
  function isRenderableCodeLanguage (line 32) | function isRenderableCodeLanguage(language: string): boolean {
  function MessageArtifact (line 36) | function MessageArtifact(props: {
  function ArtifactWithButtons (line 69) | function ArtifactWithButtons(props: {
  function Artifact (line 171) | function Artifact(props: { htmlCode: string; reloadSign?: number; classN...
  function generateHtml (line 210) | function generateHtml(markdowns: string[]): string {

FILE: src/renderer/components/CustomProviderIcon.tsx
  type CustomProviderIconProps (line 4) | type CustomProviderIconProps = {
  constant BG_COLORS (line 10) | const BG_COLORS = [
  constant DEFAULT_SIZE (line 29) | const DEFAULT_SIZE = 32

FILE: src/renderer/components/EditableAvatar.tsx
  type Props (line 9) | interface Props {
  function EditableAvatar (line 17) | function EditableAvatar(props: Props) {

FILE: src/renderer/components/ErrorTestPannel.tsx
  function ErrorTestPanel (line 4) | function ErrorTestPanel() {

FILE: src/renderer/components/FileIcon.tsx
  function FileIcon (line 26) | function FileIcon(props: { filename: string; className?: string }) {

FILE: src/renderer/components/Image.tsx
  function Img (line 51) | function Img(props: {
  function handleImageInputAndSave (line 59) | function handleImageInputAndSave(file: File, key: string, updateKey?: (k...

FILE: src/renderer/components/ImageCountSlider.tsx
  type Props (line 4) | interface Props {
  function ImageCountSlider (line 10) | function ImageCountSlider(props: Props) {

FILE: src/renderer/components/ImageModelSelect.tsx
  type ImageModel (line 6) | interface ImageModel {
  constant CHATBOXAI_IMAGE_MODEL_IDS (line 12) | const CHATBOXAI_IMAGE_MODEL_IDS = [
  constant OPENAI_IMAGE_MODEL_IDS (line 19) | const OPENAI_IMAGE_MODEL_IDS = ['gpt-image-1', 'gpt-image-1.5']
  constant GEMINI_IMAGE_MODEL_IDS (line 20) | const GEMINI_IMAGE_MODEL_IDS = [
  constant CHATBOXAI_DEFAULT_IMAGE_MODEL (line 28) | const CHATBOXAI_DEFAULT_IMAGE_MODEL: ImageModel = {
  constant IMAGE_MODEL_FALLBACK_NAMES (line 33) | const IMAGE_MODEL_FALLBACK_NAMES: Record<string, string> = {
  function getAvailableImageModels (line 44) | function getAvailableImageModels(provider: ProviderInfo, imageModelIds: ...
  type ImageModelSelectProps (line 61) | type ImageModelSelectProps = PropsWithChildren<

FILE: src/renderer/components/ImageStyleSelect.tsx
  type Props (line 5) | interface Props {
  function ImageStyleSelect (line 11) | function ImageStyleSelect(props: Props) {

FILE: src/renderer/components/InputBox/Attachments.tsx
  function getTranslatedErrorMessage (line 11) | function getTranslatedErrorMessage(errorCode: string | undefined, t: (ke...
  function ImageMiniCard (line 23) | function ImageMiniCard(props: { storageKey: string; onDelete: () => void...
  function FileMiniCard (line 47) | function FileMiniCard(props: {
  function formatFileSize (line 106) | function formatFileSize(bytes: number | undefined): string {
  function getFileTypeLabel (line 113) | function getFileTypeLabel(filename: string, fileType?: string): string {
  function MessageAttachment (line 120) | function MessageAttachment(props: {
  function LinkMiniCard (line 185) | function LinkMiniCard(props: {

FILE: src/renderer/components/InputBox/ImageUploadButton.tsx
  type ImageUploadButtonProps (line 6) | interface ImageUploadButtonProps {

FILE: src/renderer/components/InputBox/ImageUploadInput.tsx
  type ImageUploadInputProps (line 4) | interface ImageUploadInputProps {

FILE: src/renderer/components/InputBox/InputBox.tsx
  type InputBoxPayload (line 110) | type InputBoxPayload = {
  type InputBoxRef (line 116) | type InputBoxRef = {
  type InputBoxProps (line 120) | type InputBoxProps = {

FILE: src/renderer/components/InputBox/SessionSettingsButton.tsx
  type SessionSettingsButtonProps (line 7) | interface SessionSettingsButtonProps {

FILE: src/renderer/components/InputBox/TokenCountMenu.tsx
  type Props (line 9) | type Props = {

FILE: src/renderer/components/InputBox/WebBrowsingButton.tsx
  type WebBrowsingButtonProps (line 7) | interface WebBrowsingButtonProps {

FILE: src/renderer/components/InputBox/preprocessState.ts
  function markLinkProcessing (line 7) | function markLinkProcessing(prev: PreConstructedMessageState, url: strin...
  function storeLinkPromise (line 21) | function storeLinkPromise(
  function onLinkProcessed (line 38) | function onLinkProcessed(
  function cleanupLink (line 67) | function cleanupLink(prev: PreConstructedMessageState, url: string): Pre...
  function markFileProcessing (line 91) | function markFileProcessing(prev: PreConstructedMessageState, file: File...
  function storeFilePromise (line 105) | function storeFilePromise(
  function onFileProcessed (line 122) | function onFileProcessed(
  function cleanupFile (line 151) | function cleanupFile(prev: PreConstructedMessageState, file: File): PreC...

FILE: src/renderer/components/Markdown.tsx
  constant CODE_BLOCK_COLLAPSE_LINE_THRESHOLD (line 68) | const CODE_BLOCK_COLLAPSE_LINE_THRESHOLD = 7
  function remarkAddCodeIndex (line 70) | function remarkAddCodeIndex() {
  function Markdown (line 82) | function Markdown(props: {
  type BlockCodeCollapsedStateContextType (line 212) | interface BlockCodeCollapsedStateContextType {
  type BlockCodeCollapsedStateProviderProps (line 224) | interface BlockCodeCollapsedStateProviderProps {
  type BlockCodeProps (line 299) | type BlockCodeProps = {

FILE: src/renderer/components/Mermaid.tsx
  function MessageMermaid (line 15) | function MessageMermaid(props: { source: string; theme: 'light' | 'dark'...
  function Loading (line 43) | function Loading() {
  function MermaidSVGPreviewDangerous (line 57) | function MermaidSVGPreviewDangerous(props: {
  function SVGPreview (line 103) | function SVGPreview(props: { xmlCode: string; className?: string; genera...
  function mermaidCodeToSvgCode (line 188) | async function mermaidCodeToSvgCode(source: string, theme: 'light' | 'da...

FILE: src/renderer/components/ModelList.tsx
  type ModelListProps (line 20) | interface ModelListProps {
  function ModelList (line 32) | function ModelList({

FILE: src/renderer/components/ModelSelector/DesktopModelSelector.tsx
  type FilteredProvider (line 26) | type FilteredProvider = {
  type DesktopModelSelectorProps (line 33) | interface DesktopModelSelectorProps {

FILE: src/renderer/components/ModelSelector/MobileModelSelector.tsx
  type FilteredProvider (line 16) | type FilteredProvider = {
  type MobileModelSelectorProps (line 23) | interface MobileModelSelectorProps {

FILE: src/renderer/components/ModelSelector/ProviderHeader.tsx
  type ProviderHeaderProps (line 8) | interface ProviderHeaderProps {

FILE: src/renderer/components/ModelSelector/SimplePreview.tsx
  function SimplePreview (line 12) | function SimplePreview() {

FILE: src/renderer/components/ModelSelector/index.tsx
  type ModelSelectorProps (line 13) | type ModelSelectorProps = PropsWithChildren<

FILE: src/renderer/components/ModelSelector/shared.tsx
  constant SELECTED_BG_CLASS (line 10) | const SELECTED_BG_CLASS = '!bg-chatbox-background-brand-secondary'
  constant TRANSITION_DURATION (line 11) | const TRANSITION_DURATION = 200
  type FavoriteModel (line 14) | type FavoriteModel = { provider?: { id: string; name: string; isCustom?:...

FILE: src/renderer/components/Shortcut.tsx
  function formatKey (line 16) | function formatKey(key: string) {
  function Keys (line 69) | function Keys(props: {
  type ShortcutDataItem (line 91) | type ShortcutDataItem = {
  function ShortcutConfig (line 98) | function ShortcutConfig(props: {
  function ShortcutText (line 238) | function ShortcutText(props: { shortcut: string; isConflict?: boolean; c...
  function ShortcutSelect (line 252) | function ShortcutSelect({

FILE: src/renderer/components/SponsorChip.tsx
  function SponsorChip (line 10) | function SponsorChip(props: {}) {

FILE: src/renderer/components/chat/CompactionStatus.tsx
  constant MAX_CHARS (line 19) | const MAX_CHARS = 200
  constant MAX_LINES (line 20) | const MAX_LINES = 3
  function shouldTruncate (line 22) | function shouldTruncate(text: string): boolean {
  function getTruncatedText (line 28) | function getTruncatedText(text: string): string {
  type CompactionStatusProps (line 39) | interface CompactionStatusProps {

FILE: src/renderer/components/chat/Message.tsx
  type Props (line 55) | interface Props {
  function getBase64ImageSize (line 595) | function getBase64ImageSize(base64: string): Promise<{ width: number; he...
  type PictureGalleryProps (line 608) | type PictureGalleryProps = {

FILE: src/renderer/components/chat/MessageAttachmentGrid.tsx
  constant COLLAPSED_MAX (line 7) | const COLLAPSED_MAX = 4
  type MessageAttachmentGridProps (line 9) | interface MessageAttachmentGridProps {
  function MessageAttachmentGrid (line 14) | function MessageAttachmentGrid({ files, links }: MessageAttachmentGridPr...

FILE: src/renderer/components/chat/MessageErrTips.tsx
  constant MAX_CHARS (line 18) | const MAX_CHARS = 200
  constant MAX_LINES (line 19) | const MAX_LINES = 3
  function shouldTruncate (line 21) | function shouldTruncate(text: string): boolean {
  function getTruncatedText (line 27) | function getTruncatedText(text: string): string {
  function isContextLengthError (line 41) | function isContextLengthError(errorText: string | null | undefined): boo...
  function MessageErrTips (line 55) | function MessageErrTips(props: { msg: Message }) {

FILE: src/renderer/components/chat/MessageList.tsx
  constant MAX_SCROLL_CACHE_SIZE (line 59) | const MAX_SCROLL_CACHE_SIZE = 100
  function setScrollPosition (line 62) | function setScrollPosition(sessionId: string, snapshot: StateSnapshot) {
  function clearScrollPositionCache (line 77) | function clearScrollPositionCache(sessionId: string) {
  type MessageListRef (line 81) | interface MessageListRef {
  type MessageListProps (line 86) | interface MessageListProps {
  function ForkNav (line 399) | function ForkNav(props: { sessionId: string; msgId: string; forks: NonNu...
  type ThreadLabelProps (line 461) | type ThreadLabelProps = {

FILE: src/renderer/components/chat/MessageLoading.tsx
  function MessageStatuses (line 9) | function MessageStatuses(props: { statuses: Message['status'] }) {
  function MessageStatus (line 23) | function MessageStatus(props: { status: NonNullable<Message['status']>[n...
  function RetryingIndicator (line 89) | function RetryingIndicator(props: { attempt: number; maxAttempts: number...
  function LoadingBubble (line 100) | function LoadingBubble(props: { children: React.ReactNode }) {

FILE: src/renderer/components/chat/MessageNavigation.tsx
  type MessageNavigationProps (line 6) | type MessageNavigationProps = {

FILE: src/renderer/components/chat/SummaryMessage.tsx
  type SummaryMessageProps (line 14) | interface SummaryMessageProps {

FILE: src/renderer/components/common/AdaptiveModal.tsx
  type AdaptiveModalProps (line 9) | interface AdaptiveModalProps extends Omit<MantineModalProps, 'opened' | ...
  function AdaptiveModal (line 14) | function AdaptiveModal({ opened, onClose, children, title, ...props }: A...
  function AdaptiveModalActions (line 47) | function AdaptiveModalActions({ children }: { children: ReactNode }) {
  function AdaptiveModalCloseButton (line 67) | function AdaptiveModalCloseButton(props: ButtonProps & HTMLAttributes<HT...

FILE: src/renderer/components/common/Avatar.tsx
  type SystemAvatarProps (line 10) | type SystemAvatarProps = {
  type UserAvatarProps (line 36) | type UserAvatarProps = {
  type AssistantAvatarProps (line 66) | type AssistantAvatarProps = {

FILE: src/renderer/components/common/CompressionModal.tsx
  type CompressionModalProps (line 7) | interface CompressionModalProps {
  function CompressionModal (line 13) | function CompressionModal({ opened, onClose, session }: CompressionModal...

FILE: src/renderer/components/common/ConfirmDeleteButton.tsx
  type Props (line 9) | interface Props {
  function ConfirmDeleteMenuItem (line 16) | function ConfirmDeleteMenuItem({ onDelete, label, color = 'error', icon ...
  function ConfirmDeleteButton (line 84) | function ConfirmDeleteButton({ onDelete, icon, label, color = 'error' }:...

FILE: src/renderer/components/common/CreatableSelect.tsx
  function CreatableSelect (line 9) | function CreatableSelect(props: {

FILE: src/renderer/components/common/Divider.tsx
  type DividerProps (line 5) | interface DividerProps {

FILE: src/renderer/components/common/ErrorBoundary.tsx
  type ErrorBoundaryProps (line 8) | interface ErrorBoundaryProps {
  function ErrorBoundary (line 23) | function ErrorBoundary({ children, fallback: CustomFallback, name = 'Err...
  type DefaultErrorFallbackProps (line 63) | interface DefaultErrorFallbackProps {
  function DefaultErrorFallback (line 68) | function DefaultErrorFallback({ error, retry }: DefaultErrorFallbackProp...

FILE: src/renderer/components/common/LazyNumberInput.tsx
  type Props (line 7) | type Props = {
  function LazyNumberInput (line 23) | function LazyNumberInput({

FILE: src/renderer/components/common/LazySlider.tsx
  type LazySliderProps (line 4) | type LazySliderProps = SliderProps

FILE: src/renderer/components/common/Link.tsx
  function LinkTargetBlank (line 4) | function LinkTargetBlank(props: {

FILE: src/renderer/components/common/Mark.tsx
  function Mark (line 4) | function Mark(props: { children: string | ReactElement; marks: string[] ...

FILE: src/renderer/components/common/MaxContextMessageCountSlider.tsx
  function toBeRemoved_getContextMessageCount (line 7) | function toBeRemoved_getContextMessageCount(
  type Props (line 18) | interface Props {
  constant MESSAGE_COUNT_OPTIONS (line 26) | const MESSAGE_COUNT_OPTIONS = [0, 2, 4, 6, 8, 10, 20, 50, 100, 200, 500,...
  function MaxContextMessageCountSlider (line 27) | function MaxContextMessageCountSlider({ value, onChange, className, wrap...

FILE: src/renderer/components/common/MiniButton.tsx
  type MiniButtonProps (line 6) | interface MiniButtonProps {

FILE: src/renderer/components/common/PasswordTextField.tsx
  function PasswordTextField (line 7) | function PasswordTextField(props: {

FILE: src/renderer/components/common/PopoverConfirm.tsx
  type PopoverConfirmProps (line 5) | type PopoverConfirmProps = PropsWithChildren<

FILE: src/renderer/components/common/ScalableIcon.tsx
  type Props (line 5) | type Props = Omit<IconProps, 'size'> & {

FILE: src/renderer/components/common/SegmentedControl.tsx
  function SegmentedControl (line 3) | function SegmentedControl({

FILE: src/renderer/components/common/SliderWithInput.tsx
  type Props (line 6) | type Props = {
  function SliderWithInput (line 16) | function SliderWithInput({ value, onChange, min = 0, max = 1, step = 0.0...

FILE: src/renderer/components/common/TemperatureSlider.tsx
  type Props (line 5) | interface Props {
  function TemperatureSlider (line 11) | function TemperatureSlider(props: Props) {

FILE: src/renderer/components/common/TextFieldReset.tsx
  function TextFieldReset (line 5) | function TextFieldReset(

FILE: src/renderer/components/common/Toasts.tsx
  function Toasts (line 7) | function Toasts() {

FILE: src/renderer/components/common/TopPSlider.tsx
  type Props (line 5) | interface Props {
  function TopPSlider (line 11) | function TopPSlider(props: Props) {

FILE: src/renderer/components/dev/DevHeader.tsx
  type DevHeaderProps (line 7) | interface DevHeaderProps {
  function DevHeader (line 11) | function DevHeader({ title }: DevHeaderProps) {

FILE: src/renderer/components/icons/ArrowRightIcon.tsx
  function ArrowRightIcon (line 3) | function ArrowRightIcon(props: { className?: string; onClick?: MouseEven...

FILE: src/renderer/components/icons/BrandGithub.tsx
  function BrandGithub (line 3) | function BrandGithub(props: React.SVGProps<SVGSVGElement>) {

FILE: src/renderer/components/icons/BrandRedNote.tsx
  function BrandRedNote (line 3) | function BrandRedNote(props: { className?: string; onClick?: MouseEventH...

FILE: src/renderer/components/icons/BrandWechat.tsx
  function BrandWechat (line 3) | function BrandWechat(props: { className?: string; onClick?: MouseEventHa...

FILE: src/renderer/components/icons/BrandX.tsx
  function BrandX (line 3) | function BrandX(props: { className?: string; onClick?: MouseEventHandler...

FILE: src/renderer/components/icons/Broom.tsx
  function Broom (line 3) | function Broom(props: IconProps) {

FILE: src/renderer/components/icons/Dart.tsx
  function Dart (line 3) | function Dart(props: IconProps) {

FILE: src/renderer/components/icons/FullscreenIcon.tsx
  function FullscreenIcon (line 3) | function FullscreenIcon(props: { className?: string; onClick?: MouseEven...

FILE: src/renderer/components/icons/HomepageIcon.tsx
  function HomepageIcon (line 4) | function HomepageIcon(props: React.SVGProps<SVGSVGElement>) {

FILE: src/renderer/components/icons/Java.tsx
  function Java (line 3) | function Java(props: IconProps) {

FILE: src/renderer/components/icons/LayoutExpand.tsx
  function LayoutExpand (line 3) | function LayoutExpand(props: IconProps) {

FILE: src/renderer/components/icons/LayoutShrink.tsx
  function LayoutShrink (line 3) | function LayoutShrink(props: IconProps) {

FILE: src/renderer/components/icons/Loading.tsx
  function Loading (line 3) | function Loading(props: React.SVGProps<SVGSVGElement>) {

FILE: src/renderer/components/icons/ModelIcon.tsx
  type ModelIconProps (line 6) | interface ModelIconProps {
  function ModelIcon (line 22) | function ModelIcon({ modelId, providerId, size = 16, className }: ModelI...

FILE: src/renderer/components/icons/ProviderIcon.tsx
  function ProviderIcon (line 3) | function ProviderIcon(props: { className?: string; size?: number; provid...

FILE: src/renderer/components/icons/ProviderImageIcon.tsx
  function ProviderImageIcon (line 22) | function ProviderImageIcon(props: {

FILE: src/renderer/components/icons/Robot.tsx
  function Robot (line 3) | function Robot(props: IconProps) {

FILE: src/renderer/components/knowledge-base/ChunksPreviewModal.tsx
  type FileChunk (line 8) | interface FileChunk {
  type ChunksPreviewModalProps (line 15) | interface ChunksPreviewModalProps {
  type ChunkCardProps (line 111) | interface ChunkCardProps {

FILE: src/renderer/components/knowledge-base/KnowledgeBase.tsx
  type ModelPillProps (line 31) | interface ModelPillProps {
  function formatModelName (line 215) | function formatModelName(model: string) {
  function formatParserType (line 224) | function formatParserType(parserType?: DocumentParserType): string {

FILE: src/renderer/components/knowledge-base/KnowledgeBaseDocuments.tsx
  type KnowledgeBaseDocumentsProps (line 49) | interface KnowledgeBaseDocumentsProps {

FILE: src/renderer/components/knowledge-base/KnowledgeBaseForm.tsx
  type ModelSelectorsProps (line 11) | interface ModelSelectorsProps {
  type KnowledgeBaseChatboxAIInfoProps (line 80) | interface KnowledgeBaseChatboxAIInfoProps {
  type KnowledgeBaseProviderModeSelectProps (line 110) | interface KnowledgeBaseProviderModeSelectProps {
  type KnowledgeBaseFormActionsProps (line 137) | interface KnowledgeBaseFormActionsProps {
  type KnowledgeBaseNameInputProps (line 186) | interface KnowledgeBaseNameInputProps {
  constant PARSER_OPTIONS (line 215) | const PARSER_OPTIONS: { value: DocumentParserType; label: string; descri...
  type DocumentParserSelectorProps (line 235) | interface DocumentParserSelectorProps {
  type DocumentParserDisplayProps (line 374) | interface DocumentParserDisplayProps {

FILE: src/renderer/components/knowledge-base/KnowledgeBaseMenu.tsx
  type Props (line 10) | type Props = {

FILE: src/renderer/components/knowledge-base/RemoteRetryModal.tsx
  function parseErrorMessage (line 16) | function parseErrorMessage(errorMessage: string): string {
  type RemoteRetryModalProps (line 44) | interface RemoteRetryModalProps {
  function RemoteRetryModal (line 51) | function RemoteRetryModal({ opened, onClose, failedFiles, onSuccess }: R...

FILE: src/renderer/components/layout/ExitFullscreenButton.tsx
  function ExitFullscreenButton (line 9) | function ExitFullscreenButton() {

FILE: src/renderer/components/layout/Header.tsx
  function Header (line 18) | function Header(props: { session: Session }) {

FILE: src/renderer/components/layout/Overlay.tsx
  function withOverlayManager (line 28) | function withOverlayManager<P extends { opened?: boolean; closeOnEscape?...

FILE: src/renderer/components/layout/Page.tsx
  type PageProps (line 11) | type PageProps = {

FILE: src/renderer/components/layout/Toolbar.tsx
  function Toolbar (line 33) | function Toolbar({ sessionId }: { sessionId: string }) {

FILE: src/renderer/components/layout/WindowControls.tsx
  type ControlButtonProps (line 36) | type ControlButtonProps = {

FILE: src/renderer/components/mcp/MCPMenu.tsx
  type ServerItem (line 14) | interface ServerItem {

FILE: src/renderer/components/message-parts/ToolCallPartUI.tsx
  type WebBrowsingToolCallPart (line 70) | type WebBrowsingToolCallPart = MessageToolCallPart<

FILE: src/renderer/components/session/SessionItem.tsx
  type Props (line 21) | interface Props {
  function SessionItem (line 26) | function SessionItem(props: Props) {

FILE: src/renderer/components/session/SessionList.tsx
  type Props (line 31) | interface Props {
  function SessionList (line 35) | function SessionList(props: Props) {
  function SortableItem (line 135) | function SortableItem(props: { id: string; children?: React.ReactNode }) {

FILE: src/renderer/components/session/ThreadHistoryDrawer.tsx
  function ThreadHistoryDrawer (line 19) | function ThreadHistoryDrawer({ session }: { session: Session }) {
  function ThreadItem (line 101) | function ThreadItem(props: {

FILE: src/renderer/components/settings/DocumentParserSettings.tsx
  constant ALL_PARSER_OPTIONS (line 9) | const ALL_PARSER_OPTIONS: {
  constant PARSER_DESCRIPTIONS (line 21) | const PARSER_DESCRIPTIONS: Record<DocumentParserType, string> = {
  type DocumentParserSettingsProps (line 30) | interface DocumentParserSettingsProps {
  function DocumentParserSettings (line 34) | function DocumentParserSettings({ showTitle = true }: DocumentParserSett...

FILE: src/renderer/components/settings/mcp/ConfigModal.tsx
  type ConnectionTestingResult (line 25) | interface ConnectionTestingResult {
  type Props (line 201) | interface Props {

FILE: src/renderer/components/settings/mcp/CustomServersSection.tsx
  type Props (line 50) | type Props = {

FILE: src/renderer/components/settings/mcp/registries.ts
  function getIcon (line 19) | function getIcon(filename: string): string {
  type MCPRegistryEntry (line 23) | interface MCPRegistryEntry {
  constant MCP_ENTRIES_OFFICIAL (line 36) | const MCP_ENTRIES_OFFICIAL: MCPRegistryEntry[] = [
  constant MCP_ENTRIES_COMMUNITY (line 433) | const MCP_ENTRIES_COMMUNITY: MCPRegistryEntry[] = [

FILE: src/renderer/components/settings/mcp/utils.ts
  type MCPServerConfigFormValues (line 28) | type MCPServerConfigFormValues = MCPServerConfig<
  function getConfigFromFormValues (line 41) | function getConfigFromFormValues(values: MCPServerConfigFormValues): MCP...
  function getFormValuesFromConfig (line 66) | function getFormValuesFromConfig(config: MCPServerConfig): MCPServerConf...
  function parseServerFromJson (line 107) | function parseServerFromJson(text: string): MCPServerConfig | undefined {
  function parseServersFromJson (line 118) | function parseServersFromJson(text: string): MCPServerConfig[] {

FILE: src/renderer/components/settings/provider/AddProviderModal.tsx
  type AddProviderModalProps (line 11) | interface AddProviderModalProps {
  function AddProviderModal (line 16) | function AddProviderModal({ opened, onClose }: AddProviderModalProps) {

FILE: src/renderer/components/settings/provider/ImportProviderModal.tsx
  type ImportProviderModalProps (line 14) | interface ImportProviderModalProps {
  function ImportProviderModal (line 40) | function ImportProviderModal({ opened, onClose, importedConfig, existing...

FILE: src/renderer/components/settings/provider/ProviderList.tsx
  type ProviderListProps (line 31) | interface ProviderListProps {
  function ProviderList (line 38) | function ProviderList({ providers, onAddProvider, onImportProvider, isIm...

FILE: src/renderer/components/ui/command.tsx
  type CommandDialogProps (line 24) | interface CommandDialogProps extends DialogProps {}

FILE: src/renderer/dev/devToolsConfig.ts
  constant FORCE_ENABLE_DEV_PAGES (line 3) | const FORCE_ENABLE_DEV_PAGES = process.env.NODE_ENV === 'development'

FILE: src/renderer/hooks/dom.ts
  function getInputBoxHeight (line 7) | function getInputBoxHeight(): number {
  function setMessageInputCursorToEnd (line 24) | function setMessageInputCursorToEnd() {

FILE: src/renderer/hooks/mcp.ts
  function useMCPServerStatus (line 9) | function useMCPServerStatus(id: string) {
  function useToggleMCPServer (line 17) | function useToggleMCPServer() {

FILE: src/renderer/hooks/useAppTheme.ts
  function useAppTheme (line 25) | function useAppTheme() {
  function getThemeDesign (line 60) | function getThemeDesign(realTheme: 'light' | 'dark', fontSize: number, l...

FILE: src/renderer/hooks/useCopilots.ts
  function useMyCopilots (line 11) | function useMyCopilots() {
  function useRemoteCopilots (line 46) | function useRemoteCopilots() {

FILE: src/renderer/hooks/useDefaultSystemLanguage.ts
  function useSystemLanguageWhenInit (line 5) | function useSystemLanguageWhenInit() {

FILE: src/renderer/hooks/useI18nEffect.ts
  function useI18nEffect (line 5) | function useI18nEffect() {

FILE: src/renderer/hooks/useInputBoxHistory.ts
  constant MAX_HISTORY_LENGTH (line 6) | const MAX_HISTORY_LENGTH = 20

FILE: src/renderer/hooks/useKnowledgeBase.ts
  function useKnowledgeBase (line 7) | function useKnowledgeBase({ isNewSession }: { isNewSession: boolean }) {

FILE: src/renderer/hooks/useMessageInput.ts
  constant DEFAULT_OPTIONS (line 6) | const DEFAULT_OPTIONS = { saveDraft: true, timeout: 300, isNewSession: f...
  type Options (line 7) | type Options = typeof DEFAULT_OPTIONS
  function useMessageInput (line 8) | function useMessageInput(initialMessage = '', _options: Partial<Options>...

FILE: src/renderer/hooks/useProviderImport.ts
  function useProviderImport (line 6) | function useProviderImport(providers: ProviderInfo[]) {

FILE: src/renderer/hooks/useScreenChange.ts
  function useScreenChange (line 6) | function useScreenChange() {
  function useIsSmallScreen (line 14) | function useIsSmallScreen() {
  function useScreenDownToMD (line 20) | function useScreenDownToMD() {
  function useIsLargeScreen (line 25) | function useIsLargeScreen() {
  function useSidebarWidth (line 30) | function useSidebarWidth() {
  function useInputBoxHeight (line 61) | function useInputBoxHeight(): { min: number; max: number } {

FILE: src/renderer/hooks/useShortcut.tsx
  function useShortcut (line 13) | function useShortcut() {

FILE: src/renderer/hooks/useThinkingTimer.ts
  function useThinkingTimer (line 10) | function useThinkingTimer(startTime: number | undefined, isActive: boole...
  function formatElapsedTime (line 42) | function formatElapsedTime(milliseconds: number): string {

FILE: src/renderer/hooks/useVersion.ts
  function getInitialTime (line 10) | function getInitialTime() {
  function isFirstDay (line 20) | function isFirstDay(): boolean {
  function useVersion (line 31) | function useVersion() {

FILE: src/renderer/i18n/for-key-scan.ts
  function _errorI18nKeys (line 11) | function _errorI18nKeys(t: (key: string) => string) {

FILE: src/renderer/i18n/index.ts
  function changelog (line 81) | function changelog() {

FILE: src/renderer/i18n/parser.ts
  function parseLocale (line 4) | function parseLocale(locale: string): Language {

FILE: src/renderer/index.tsx
  function initializeApp (line 57) | async function initializeApp() {
  function InitPage (line 78) | function InitPage() {

FILE: src/renderer/lib/format-chat.tsx
  type ToolCallSummary (line 10) | type ToolCallSummary = {
  function collectToolCallSummaries (line 18) | function collectToolCallSummaries(message: Message): Map<string, ToolCal...
  function tryParseJsonString (line 45) | function tryParseJsonString(value: string): unknown {
  function stringifyDataForExport (line 60) | function stringifyDataForExport(value: unknown): string | null {
  function indentMultiline (line 75) | function indentMultiline(text: string, indent: string): string {
  function getAttachmentNames (line 82) | function getAttachmentNames(message: Message): string[] {
  function renderToolCallMarkdown (line 86) | function renderToolCallMarkdown(summary: ToolCallSummary): string {
  function renderToolCallTxt (line 99) | function renderToolCallTxt(summary: ToolCallSummary): string {
  function renderToolCallHtml (line 112) | function renderToolCallHtml(summary: ToolCallSummary): string {
  function formatChatAsMarkdown (line 129) | function formatChatAsMarkdown(sessionName: string, threads: SessionThrea...
  function formatChatAsTxt (line 200) | function formatChatAsTxt(sessionName: string, threads: SessionThread[]) {
  function formatChatAsHtml (line 265) | async function formatChatAsHtml(sessionName: string, threads: SessionThr...

FILE: src/renderer/lib/utils.ts
  function cn (line 11) | function cn(...inputs: ClassValue[]) {
  function getLogger (line 15) | function getLogger(logId: string) {

FILE: src/renderer/modals/ArtifactPreview.tsx
  type ArtifactPreviewProps (line 11) | interface ArtifactPreviewProps {

FILE: src/renderer/modals/ContentViewer.tsx
  type ContentViewerProps (line 11) | interface ContentViewerProps {

FILE: src/renderer/modals/EdgeOneDeploySuccess.tsx
  type EdgeOneDeploySuccessProps (line 9) | interface EdgeOneDeploySuccessProps {

FILE: src/renderer/modals/FileParseError.tsx
  type FileParseErrorProps (line 13) | interface FileParseErrorProps {

FILE: src/renderer/modals/JsonViewer.tsx
  type JsonViewerProps (line 10) | interface JsonViewerProps {

FILE: src/renderer/modals/SessionSettings.tsx
  type ThinkingBudgetConfigProps (line 285) | interface ThinkingBudgetConfigProps {
  function ThinkingBudgetConfig (line 294) | function ThinkingBudgetConfig({
  function ClaudeProviderConfig (line 421) | function ClaudeProviderConfig({
  function OpenAIProviderConfig (line 456) | function OpenAIProviderConfig({
  function GoogleProviderConfig (line 523) | function GoogleProviderConfig({
  function ChatConfig (line 553) | function ChatConfig({
  function PictureConfig (line 673) | function PictureConfig(props: { dataEdit: Session; setDataEdit: (data: S...

FILE: src/renderer/modals/Settings.tsx
  type SettingsModalProps (line 36) | type SettingsModalProps = {}
  function navigateToSettings (line 110) | function navigateToSettings(path?: string) {

FILE: src/renderer/native/stream-http.ts
  function createNativeReadableStream (line 6) | function createNativeReadableStream(options: StartStreamOptions): Readab...

FILE: src/renderer/packages/apple_app_store.ts
  function tryOpenAppStoreReviewPage (line 9) | async function tryOpenAppStoreReviewPage() {
  function recordAppStoreRatingClick (line 33) | async function recordAppStoreRatingClick() {
  function tickAfterMessageGenerated (line 38) | function tickAfterMessageGenerated() {

FILE: src/renderer/packages/base64.ts
  function parseImage (line 1) | function parseImage(base64: string) {

FILE: src/renderer/packages/codeblock_state_recorder.ts
  type CodeblockState (line 1) | interface CodeblockState {
  constant MAX_CACHE_SIZE (line 8) | const MAX_CACHE_SIZE = 500
  function limitCacheSize (line 11) | function limitCacheSize() {
  function getID (line 22) | function getID(content: string, language: string) {
  type Options (line 33) | interface Options {
  function needCollapse (line 40) | function needCollapse(options: Options): CodeblockState {
  function saveState (line 55) | function saveState(options: Options & { collapsed: boolean }) {
  function calculateState (line 64) | function calculateState(options: Options): CodeblockState {

FILE: src/renderer/packages/context-management/attachment-payload.ts
  constant MAX_INLINE_FILE_LINES (line 3) | const MAX_INLINE_FILE_LINES = 500
  constant PREVIEW_LINES (line 4) | const PREVIEW_LINES = 100
  type AttachmentWrapperPrefixParams (line 6) | interface AttachmentWrapperPrefixParams {
  type AttachmentWrapperSuffixParams (line 14) | interface AttachmentWrapperSuffixParams {
  type SelectMessagesForSendContextParams (line 21) | interface SelectMessagesForSendContextParams {
  function buildAttachmentWrapperPrefix (line 29) | function buildAttachmentWrapperPrefix(params: AttachmentWrapperPrefixPar...
  function buildAttachmentWrapperSuffix (line 43) | function buildAttachmentWrapperSuffix(params: AttachmentWrapperSuffixPar...
  function selectMessagesForSendContext (line 57) | function selectMessagesForSendContext(params: SelectMessagesForSendConte...

FILE: src/renderer/packages/context-management/compaction-detector.ts
  constant OUTPUT_RESERVE_TOKENS (line 4) | const OUTPUT_RESERVE_TOKENS = 32_000
  constant DEFAULT_COMPACTION_THRESHOLD (line 5) | const DEFAULT_COMPACTION_THRESHOLD = 0.6
  type OverflowCheckOptions (line 7) | interface OverflowCheckOptions {
  type OverflowCheckResult (line 19) | interface OverflowCheckResult {
  function checkOverflow (line 31) | function checkOverflow(options: OverflowCheckOptions): OverflowCheckResu...
  function isOverflow (line 60) | function isOverflow(options: OverflowCheckOptions): boolean {
  function getCompactionThresholdTokens (line 64) | function getCompactionThresholdTokens(

FILE: src/renderer/packages/context-management/compaction.ts
  function getModelContextWindowFromSettings (line 19) | function getModelContextWindowFromSettings(
  type CompactionOptions (line 32) | interface CompactionOptions {
  type CompactionResult (line 36) | interface CompactionResult {
  function isAutoCompactionEnabled (line 43) | function isAutoCompactionEnabled(sessionSettings?: SessionSettings, glob...
  function isCompactionInProgress (line 50) | function isCompactionInProgress(sessionId: string): boolean {
  function needsCompaction (line 54) | async function needsCompaction(sessionId: string): Promise<boolean> {
  function runCompactionWithUIState (line 124) | async function runCompactionWithUIState(
  function runCompactionWithStreaming (line 152) | async function runCompactionWithStreaming(sessionId: string): Promise<Co...

FILE: src/renderer/packages/context-management/context-builder.test.ts
  function createMessage (line 11) | function createMessage(id: string, role: (typeof MessageRoleEnum)[keyof ...
  function createSummaryMessage (line 19) | function createSummaryMessage(id: string, text = 'Summary of conversatio...
  function createCompactionPoint (line 28) | function createCompactionPoint(

FILE: src/renderer/packages/context-management/context-builder.ts
  type BuildContextOptions (line 4) | interface BuildContextOptions {
  function buildContextForAI (line 19) | function buildContextForAI(options: BuildContextOptions): Message[] {
  function buildContextForSession (line 58) | function buildContextForSession(
  function buildContextForThread (line 84) | function buildContextForThread(
  function getContextMessageIds (line 103) | function getContextMessageIds(session: Session, maxCount?: number): stri...
  function findLatestCompactionPoint (line 114) | function findLatestCompactionPoint(compactionPoints?: CompactionPoint[])...
  function findMessageIndex (line 124) | function findMessageIndex(messages: Message[], messageId: string): number {
  function findMessage (line 128) | function findMessage(messages: Message[], messageId: string): Message | ...

FILE: src/renderer/packages/context-management/context-tokens.hook.test.ts
  function createTestSession (line 41) | function createTestSession(overrides?: Partial<Session>): Session {
  function createWrapper (line 57) | function createWrapper() {

FILE: src/renderer/packages/context-management/context-tokens.integration.test.ts
  function createSessionWithoutTokenCache (line 22) | function createSessionWithoutTokenCache(): Session {
  function createWrapper (line 37) | function createWrapper() {

FILE: src/renderer/packages/context-management/context-tokens.ts
  type UseContextTokensOptions (line 18) | interface UseContextTokensOptions {
  type UseContextTokensResult (line 30) | interface UseContextTokensResult {
  type ContextTokensCacheKeyParams (line 43) | interface ContextTokensCacheKeyParams {
  type ContextTokensCacheValue (line 54) | interface ContextTokensCacheValue {
  type GetContextMessagesForTokenEstimationOptions (line 63) | interface GetContextMessagesForTokenEstimationOptions {
  function getContextMessagesForTokenEstimation (line 83) | function getContextMessagesForTokenEstimation(
  function getContextTokensCacheKey (line 117) | function getContextTokensCacheKey(params: ContextTokensCacheKeyParams): ...
  function getLatestCompactionBoundaryId (line 137) | function getLatestCompactionBoundaryId(compactionPoints?: CompactionPoin...
  function useContextTokens (line 161) | function useContextTokens(options: UseContextTokensOptions): UseContextT...

FILE: src/renderer/packages/context-management/context-tokens.unit.test.ts
  function createMessage (line 43) | function createMessage(id: string, role: (typeof MessageRoleEnum)[keyof ...
  function createTestSession (line 51) | function createTestSession(overrides?: Partial<Session>): Session {

FILE: src/renderer/packages/context-management/summary-generator.ts
  type SummaryGeneratorOptions (line 14) | interface SummaryGeneratorOptions {
  type SummaryResult (line 20) | interface SummaryResult {
  function generateSummary (line 26) | async function generateSummary(options: SummaryGeneratorOptions): Promis...
  function buildModelSettings (line 68) | function buildModelSettings(globalSettings: Settings, sessionSettings?: ...
  function isSummaryGenerationAvailable (line 103) | function isSummaryGenerationAvailable(): boolean {
  type StreamingSummaryOptions (line 123) | interface StreamingSummaryOptions extends SummaryGeneratorOptions {
  function generateSummaryWithStream (line 127) | async function generateSummaryWithStream(options: StreamingSummaryOption...

FILE: src/renderer/packages/context-management/tool-cleanup.test.ts
  function createMessage (line 6) | function createMessage(
  function createTextPart (line 17) | function createTextPart(text: string) {
  function createToolCallPart (line 21) | function createToolCallPart(toolName: string, state: 'call' | 'result' |...

FILE: src/renderer/packages/context-management/tool-cleanup.ts
  function cleanToolCalls (line 8) | function cleanToolCalls(messages: Message[], keepRounds = 2): Message[] {
  function findRoundBoundaryIndex (line 23) | function findRoundBoundaryIndex(messages: Message[], keepRounds: number)...
  function removeToolCallParts (line 49) | function removeToolCallParts(message: Message): Message {

FILE: src/renderer/packages/edgeone.ts
  constant EDGEONE_BASE_URL_ENDPOINT (line 5) | const EDGEONE_BASE_URL_ENDPOINT = 'https://mcp.edgeone.site/get_base_url'
  constant BASE_URL_TTL (line 6) | const BASE_URL_TTL = 60 * 1000
  function generateInstallationId (line 10) | function generateInstallationId(length = 8): string {
  function httpGet (line 22) | async function httpGet(url: string): Promise<unknown> {
  function httpPost (line 31) | async function httpPost(
  function fetchBaseUrl (line 44) | async function fetchBaseUrl(): Promise<string> {
  function getEdgeOneBaseUrl (line 57) | function getEdgeOneBaseUrl(force = false): Promise<string> {
  function deployHtmlToEdgeOne (line 64) | async function deployHtmlToEdgeOne(value: string): Promise<string> {

FILE: src/renderer/packages/event.ts
  function trackingEvent (line 4) | function trackingEvent(name: string, params: { [key: string]: string } =...

FILE: src/renderer/packages/filetype.ts
  function isTextFile (line 4) | function isTextFile(file: File) {
  function isPdf (line 17) | function isPdf(file: File) {
  function isWord (line 21) | function isWord(file: File) {
  function isPPT (line 28) | function isPPT(file: File) {
  function isExcel (line 35) | function isExcel(file: File) {

FILE: src/renderer/packages/latex.ts
  function processLaTeX (line 9) | function processLaTeX(content: string): string {
  function escapeBrackets (line 40) | function escapeBrackets(text: string): string {
  function escapeMhchem (line 62) | function escapeMhchem(text: string) {

FILE: src/renderer/packages/lemonsqueezy.ts
  type ActivateResponse (line 4) | type ActivateResponse =
  function activateLicense (line 26) | async function activateLicense(
  function deactivateLicense (line 63) | async function deactivateLicense(key: string, instanceId: string) {
  type ValidateLicenseKeyResponse (line 74) | type ValidateLicenseKeyResponse = {
  function validateLicense (line 78) | async function validateLicense(key: string, instanceId: string): Promise...

FILE: src/renderer/packages/local-parser.ts
  function parseTextFile (line 5) | async function parseTextFile(file: File, options: { maxLength?: number }...
  function parseUrl (line 15) | async function parseUrl(url: string) {

FILE: src/renderer/packages/mcp/builtin.ts
  type BuildinMCPServerConfig (line 5) | interface BuildinMCPServerConfig {
  constant BUILTIN_MCP_SERVERS (line 12) | const BUILTIN_MCP_SERVERS: BuildinMCPServerConfig[] = [
  function getBuiltinServerConfig (line 49) | function getBuiltinServerConfig(id: string, licenseKey?: string): MCPSer...

FILE: src/renderer/packages/mcp/controller.ts
  type TransportConfig (line 9) | type TransportConfig = MCPServerConfig['transport']
  type MCPClient (line 10) | type MCPClient = Awaited<ReturnType<typeof createMCPClient>>
  function createClient (line 12) | async function createClient(transportConfig: TransportConfig, name = 'ch...
  class MCPServer (line 64) | class MCPServer extends Emittery<{ status: MCPServerStatus }> {
    method constructor (line 69) | constructor(private readonly transportConfig: TransportConfig) {
    method status (line 73) | get status() {
    method status (line 77) | set status(status: MCPServerStatus) {
    method start (line 82) | async start() {
    method stop (line 98) | async stop() {
    method getAvailableTools (line 108) | getAvailableTools(): ToolSet {
  method bootstrap (line 121) | bootstrap(serverConfigs: MCPServerConfig[]) {
  method startServer (line 129) | async startServer(serverConfig: MCPServerConfig) {
  method stopServer (line 147) | async stopServer(id: string) {
  method updateServer (line 154) | async updateServer(serverConfig: MCPServerConfig) {
  method getServer (line 172) | getServer(id: string): MCPServer | undefined {
  method subscribeToServerStatus (line 177) | subscribeToServerStatus(id: string, callback: (status: MCPServerStatus) ...
  method getAvailableTools (line 197) | getAvailableTools(): ToolSet {
  constant SERVER_NAME_REGEX (line 220) | const SERVER_NAME_REGEX = /^[A-Za-z0-9_-]+$/
  function normalizeToolName (line 222) | function normalizeToolName(serverName: string, toolName: string) {

FILE: src/renderer/packages/mcp/ipc-stdio-transport.ts
  class IPCStdioTransport (line 7) | class IPCStdioTransport implements Transport {
    method create (line 8) | static async create(serverParams: StdioServerParameters) {
    method constructor (line 17) | constructor(private readonly ipcTransportId: string) {
    method start (line 36) | async start(): Promise<void> {
    method send (line 40) | async send(message: JSONRPCMessage): Promise<void> {
    method close (line 44) | async close(): Promise<void> {

FILE: src/renderer/packages/model-calls/generate-image.ts
  function generateImage (line 6) | async function generateImage(

FILE: src/renderer/packages/model-calls/index.ts
  function generateText (line 8) | async function generateText(model: ModelInterface, messages: Message[]) {

FILE: src/renderer/packages/model-calls/message-utils.ts
  function convertContentParts (line 9) | async function convertContentParts<T extends TextPart | ImagePart | File...
  function convertUserContentParts (line 57) | async function convertUserContentParts(
  function convertAssistantContentParts (line 65) | async function convertAssistantContentParts(
  function convertToModelMessages (line 72) | async function convertToModelMessages(
  function injectModelSystemPrompt (line 119) | function injectModelSystemPrompt(

FILE: src/renderer/packages/model-calls/preprocess.ts
  function imageOCR (line 7) | async function imageOCR(ocrModel: ModelInterface, messages: Message[]) {
  function doOCR (line 28) | async function doOCR(model: ModelInterface, imageData: string) {

FILE: src/renderer/packages/model-calls/stream-text.ts
  function handleSearchResult (line 43) | async function handleSearchResult(
  function ocrMessages (line 92) | async function ocrMessages(messages: Message[]) {
  function streamText (line 125) | async function streamText(

FILE: src/renderer/packages/model-calls/tools.ts
  function extractSearchActionFromResult (line 16) | function extractSearchActionFromResult<T = any>(result: {
  function searchByPromptEngineering (line 41) | async function searchByPromptEngineering(model: ModelInterface, messages...
  function knowledgeBaseSearchByPromptEngineering (line 69) | async function knowledgeBaseSearchByPromptEngineering(
  function combinedSearchByPromptEngineering (line 102) | async function combinedSearchByPromptEngineering(
  function constructMessagesWithSearchResults (line 142) | function constructMessagesWithSearchResults(
  function constructMessagesWithKnowledgeBaseResults (line 179) | function constructMessagesWithKnowledgeBaseResults(

FILE: src/renderer/packages/model-calls/toolsets/file.ts
  constant DEFAULT_LINES (line 6) | const DEFAULT_LINES = 200
  constant MAX_LINES (line 7) | const MAX_LINES = MAX_INLINE_FILE_LINES
  constant MAX_LINE_LENGTH (line 8) | const MAX_LINE_LENGTH = 2000
  constant GREP_MAX_RESULTS (line 27) | const GREP_MAX_RESULTS = 100

FILE: src/renderer/packages/model-calls/toolsets/knowledge-base.ts
  function getFilesMetaTool (line 25) | function getFilesMetaTool(knowledgeBaseId: number) {
  function readFileChunksTool (line 41) | function readFileChunksTool(knowledgeBaseId: number) {
  function listFilesTool (line 64) | function listFilesTool(knowledgeBaseId: number) {
  function getToolSetDescription (line 84) | async function getToolSetDescription(knowledgeBaseId: number, knowledgeB...
  function getToolSet (line 113) | async function getToolSet(knowledgeBaseId: number, knowledgeBaseName: st...

FILE: src/renderer/packages/model-calls/toolsets/web-search.ts
  constant DEFAULT_PARSE_LINK_MAX_CHARS (line 30) | const DEFAULT_PARSE_LINK_MAX_CHARS = 12_000

FILE: src/renderer/packages/model-context/builtin-data.ts
  constant BUILTIN_MODEL_CONTEXT (line 17) | const BUILTIN_MODEL_CONTEXT: Record<string, number> = {

FILE: src/renderer/packages/model-context/index.ts
  constant CACHE_KEY (line 3) | const CACHE_KEY = 'model-context-cache'
  constant CACHE_EXPIRY_MS (line 4) | const CACHE_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000
  constant API_URL (line 5) | const API_URL = 'https://models.dev/api.json'
  constant DEFAULT_CONTEXT_WINDOW (line 6) | const DEFAULT_CONTEXT_WINDOW = 96_000
  type CacheEntry (line 8) | interface CacheEntry {
  type ModelsDevResponse (line 13) | interface ModelsDevResponse {
  function getCache (line 28) | function getCache(): CacheEntry | null {
  function setCache (line 38) | function setCache(data: Record<string, number>): void {
  function isCacheValid (line 50) | function isCacheValid(entry: CacheEntry): boolean {
  function parseModelsDevResponse (line 54) | function parseModelsDevResponse(response: ModelsDevResponse): Record<str...
  function fetchModelContextData (line 73) | async function fetchModelContextData(): Promise<Record<string, number>> {
  function getModelContextData (line 82) | function getModelContextData(): Promise<Record<string, number>> {
  function findExactMatch (line 112) | function findExactMatch(modelId: string, data: Record<string, number>): ...
  function findPrefixMatch (line 122) | function findPrefixMatch(modelId: string, data: Record<string, number>):...
  function getModelContextWindow (line 138) | async function getModelContextWindow(modelId: string): Promise<number | ...
  function getModelContextWindowSync (line 158) | function getModelContextWindowSync(modelId: string): number | null {
  function getModelContextWindowWithDefault (line 180) | function getModelContextWindowWithDefault(modelId: string): number {
  function prefetchModelContextData (line 184) | function prefetchModelContextData(): void {

FILE: src/renderer/packages/model-setting-utils/base-config.ts
  method listRemoteProviderModels (line 23) | private async listRemoteProviderModels(): Promise<ProviderModelInfo[]> {
  method getMergeOptionGroups (line 37) | public async getMergeOptionGroups(providerSettings: ProviderSettings): P...
  method mergeOptionGroups (line 67) | protected mergeOptionGroups(localOptionGroups: ProviderModelInfo[], remo...
  method enrichModelsWithInfo (line 95) | private async enrichModelsWithInfo(models: ProviderModelInfo[]): Promise...

FILE: src/renderer/packages/model-setting-utils/custom-provider-setting-util.ts
  class CustomProviderSettingUtil (line 21) | class CustomProviderSettingUtil extends BaseConfig implements ModelSetti...
    method constructor (line 25) | constructor(provider: ModelProvider, customProviderType?: ModelProvide...
    method getCurrentModelDisplayName (line 31) | async getCurrentModelDisplayName(
    method getDefaultProviderName (line 42) | private getDefaultProviderName(): string {
    method getDefaultModelId (line 56) | private getDefaultModelId(): string {
    method listProviderModels (line 69) | protected async listProviderModels(settings: ProviderSettings): Promis...

FILE: src/renderer/packages/model-setting-utils/index.ts
  function getModelSettingUtil (line 14) | function getModelSettingUtil(
  function getModelDisplayName (line 24) | function getModelDisplayName(settings: SessionSettings, globalSettings: ...

FILE: src/renderer/packages/model-setting-utils/interface.ts
  type ModelSettingUtil (line 3) | interface ModelSettingUtil {

FILE: src/renderer/packages/model-setting-utils/registry-setting-util.ts
  class RegistrySettingUtil (line 7) | class RegistrySettingUtil extends BaseConfig implements ModelSettingUtil {
    method constructor (line 10) | constructor(provider: ModelProvider) {
    method getCurrentModelDisplayName (line 15) | async getCurrentModelDisplayName(
    method listProviderModels (line 32) | protected async listProviderModels(settings: ProviderSettings): Promis...

FILE: src/renderer/packages/model-setting-utils/util.ts
  function updateModelInfo (line 6) | function updateModelInfo(localModel: ProviderModelInfo, newModelInfo: Pr...
  function updateLocalModels (line 13) | function updateLocalModels(providerId: string, latestModels: ProviderMod...
  function updateAllLocalModels (line 38) | function updateAllLocalModels() {

FILE: src/renderer/packages/navigator.ts
  function copyToClipboard (line 4) | function copyToClipboard(text: string) {

FILE: src/renderer/packages/pic_utils.ts
  function getImageBase64AndResize (line 6) | async function getImageBase64AndResize(file: File) {
  function svgCodeToBase64 (line 66) | function svgCodeToBase64(svgCode: string) {
  function svgToPngBase64 (line 70) | async function svgToPngBase64(svgBase64: string): Promise<string> {

FILE: src/renderer/packages/prompts.ts
  function nameConversation (line 4) | function nameConversation(msgs: Message[], language: string): Message[] {
  function answerWithSearchResults (line 37) | function answerWithSearchResults(): string {
  function contructSearchAction (line 64) | function contructSearchAction(language: string) {
  function constructKnowledgeBaseSearchAction (line 80) | function constructKnowledgeBaseSearchAction(language: string) {
  function constructCombinedSearchAction (line 95) | function constructCombinedSearchAction(language: string, hasKnowledgeBas...
  function answerWithKnowledgeBaseResults (line 119) | function answerWithKnowledgeBaseResults(): string {
  function summarizeConversation (line 146) | function summarizeConversation(msgs: Message[], language: string): Messa...

FILE: src/renderer/packages/remote.ts
  function initAfetch (line 26) | async function initAfetch(): Promise<ReturnType<typeof createAfetch>> {
  function getAfetch (line 42) | async function getAfetch() {
  function initAuthenticatedAfetch (line 54) | async function initAuthenticatedAfetch(): Promise<ReturnType<typeof crea...
  function getAuthenticatedAfetch (line 84) | async function getAuthenticatedAfetch() {
  function getAPIOrigin (line 94) | function getAPIOrigin() {
  function getChatboxOrigin (line 102) | function getChatboxOrigin() {
  function checkNeedUpdate (line 123) | async function checkNeedUpdate(version: string, os: string, config: Conf...
  function listCopilots (line 162) | async function listCopilots(lang: string) {
  function recordCopilotShare (line 174) | async function recordCopilotShare(detail: CopilotDetail) {
  function getPremiumPrice (line 183) | async function getPremiumPrice() {
  function getRemoteConfig (line 197) | async function getRemoteConfig(config: keyof RemoteConfig) {
  type DialogConfig (line 208) | interface DialogConfig {
  function getDialogConfig (line 213) | async function getDialogConfig(params: { uuid: string; language: string;...
  function getLicenseDetail (line 226) | async function getLicenseDetail(params: { licenseKey: string }) {
  type LicenseDetailError (line 240) | interface LicenseDetailError {
  type LicenseDetailResponse (line 247) | interface LicenseDetailResponse {
  function getLicenseDetailRealtime (line 252) | async function getLicenseDetailRealtime(params: { licenseKey: string }):...
  function generateUploadUrl (line 285) | async function generateUploadUrl(params: { licenseKey: string; filename:...
  function createUserFile (line 310) | async function createUserFile<T extends boolean>(params: {
  function uploadAndCreateUserFile (line 340) | async function uploadAndCreateUserFile(licenseKey: string, file: File) {
  function parseUserLinkPro (line 361) | async function parseUserLinkPro(params: { licenseKey: string; url: strin...
  function parseUserLinkFree (line 401) | async function parseUserLinkFree(params: { url: string }) {
  function webBrowsing (line 418) | async function webBrowsing(params: { licenseKey: string; query: string }) {
  function activateLicense (line 451) | async function activateLicense(params: { licenseKey: string; instanceNam...
  function deactivateLicense (line 479) | async function deactivateLicense(params: { licenseKey: string; instanceI...
  function validateLicense (line 497) | async function validateLicense(params: { licenseKey: string; instanceId:...
  function getModelManifest (line 541) | async function getModelManifest(params: { aiProvider: ModelProvider; lic...
  function reportContent (line 570) | async function reportContent(params: { id: string; type: string; details...
  function getProviderModelsInfo (line 587) | async function getProviderModelsInfo(params: { modelIds: string[] }) {
  function requestLoginTicketId (line 608) | async function requestLoginTicketId() {
  function checkLoginStatus (line 653) | async function checkLoginStatus(ticketId: string) {
  function refreshAccessToken (line 697) | async function refreshAccessToken(params: { refreshToken: string }) {
  function getUserProfile (line 738) | async function getUserProfile() {
  type UserLicense (line 765) | interface UserLicense {
  function listLicensesByUser (line 788) | async function listLicensesByUser(): Promise<UserLicense[]> {

FILE: src/renderer/packages/token-estimation/__tests__/analyzer.test.ts
  function createMessage (line 6) | function createMessage(overrides: Partial<Message> = {}): Message {
  function createFile (line 15) | function createFile(overrides: Partial<MessageFile> = {}): MessageFile {
  function createLink (line 27) | function createLink(overrides: Partial<MessageLink> = {}): MessageLink {

FILE: src/renderer/packages/token-estimation/__tests__/computation-queue.test.ts
  type TaskInput (line 5) | type TaskInput = Omit<ComputationTask, 'id' | 'createdAt'>
  function createMessageTextTask (line 7) | function createMessageTextTask(overrides: Partial<TaskInput> = {}): Task...
  function createAttachmentTask (line 18) | function createAttachmentTask(overrides: Partial<TaskInput> = {}): TaskI...

FILE: src/renderer/packages/token-estimation/__tests__/result-persister.test.ts
  function createMessageTextResult (line 21) | function createMessageTextResult(
  function createAttachmentResult (line 35) | function createAttachmentResult(

FILE: src/renderer/packages/token-estimation/__tests__/task-executor.test.ts
  function createMessage (line 23) | function createMessage(overrides: Partial<Message> = {}): Message {
  function createSession (line 32) | function createSession(overrides: Partial<Session> = {}): Session {
  function createMessageTextTask (line 42) | function createMessageTextTask(overrides: Partial<ComputationTask> = {})...
  function createAttachmentTask (line 55) | function createAttachmentTask(overrides: Partial<ComputationTask> = {}):...

FILE: src/renderer/packages/token-estimation/__tests__/useTokenEstimation.test.ts
  function createMessage (line 10) | function createMessage(overrides: Partial<Message> = {}): Message {

FILE: src/renderer/packages/token-estimation/analyzer.ts
  type AnalyzeTokenRequirementsOptions (line 23) | interface AnalyzeTokenRequirementsOptions {
  type AnalysisResult (line 37) | interface AnalysisResult {
  type MessageTextAnalysisResult (line 54) | interface MessageTextAnalysisResult {
  type MessageAttachmentsAnalysisResult (line 66) | interface MessageAttachmentsAnalysisResult {
  function analyzeTokenRequirements (line 88) | function analyzeTokenRequirements(options: AnalyzeTokenRequirementsOptio...
  function analyzeMessageText (line 165) | function analyzeMessageText(
  function getTokenModel (line 201) | function getTokenModel(tokenizerType: TokenizerType): { provider: string...
  function analyzeMessageAttachments (line 218) | function analyzeMessageAttachments(

FILE: src/renderer/packages/token-estimation/cache-keys.ts
  function getTokenCacheKey (line 22) | function getTokenCacheKey(params: { tokenizerType: TokenizerType; conten...
  function isMessageTextCacheValid (line 58) | function isMessageTextCacheValid(
  function isAttachmentCacheValid (line 95) | function isAttachmentCacheValid(attachment: MessageFile | MessageLink, c...

FILE: src/renderer/packages/token-estimation/computation-queue.ts
  function generateTaskId (line 25) | function generateTaskId(task: Omit<ComputationTask, 'id' | 'createdAt'>)...
  constant PRIORITY (line 40) | const PRIORITY = {
  function getPriority (line 59) | function getPriority(
  class ComputationQueue (line 85) | class ComputationQueue {
    method setExecutor (line 105) | setExecutor(executor: (task: ComputationTask) => Promise<TaskResult>):...
    method enqueue (line 116) | enqueue(task: Omit<ComputationTask, 'id' | 'createdAt'>): void {
    method enqueueBatch (line 142) | enqueueBatch(tasks: Omit<ComputationTask, 'id' | 'createdAt'>[]): void {
    method cancelBySession (line 196) | cancelBySession(sessionId: string): void {
    method retainOnlyMessages (line 214) | retainOnlyMessages(sessionId: string, allowedMessageIds: Set<string>):...
    method retainOnlyTokenizerType (line 229) | retainOnlyTokenizerType(sessionId: string, tokenizerType: string): void {
    method isSessionCancelled (line 244) | isSessionCancelled(sessionId: string): boolean {
    method getStatus (line 251) | getStatus(): { pending: number; running: number } {
    method getStatusForSession (line 261) | getStatusForSession(sessionId: string): { pending: number; running: nu...
    method getPendingTasks (line 275) | getPendingTasks(): ComputationTask[] {
    method invalidateCompletedTasks (line 282) | invalidateCompletedTasks(taskIds: string[]): void {
    method subscribe (line 299) | subscribe(listener: () => void): () => void {
    method clearCompletedBySession (line 310) | clearCompletedBySession(sessionId: string): void {
    method processQueue (line 328) | private processQueue(): void {
    method executeTask (line 347) | private async executeTask(task: ComputationTask): Promise<void> {
    method sortPending (line 376) | private sortPending(): void {
    method notifyListeners (line 388) | private notifyListeners(): void {
    method startCleanup (line 402) | startCleanup(): void {
    method stopCleanup (line 418) | stopCleanup(): void {
    method _reset (line 433) | _reset(): void {
    method _getState (line 447) | _getState(): QueueState {

FILE: src/renderer/packages/token-estimation/hooks/useTokenEstimation.ts
  type UseTokenEstimationOptions (line 8) | interface UseTokenEstimationOptions {
  function useTokenEstimation (line 16) | function useTokenEstimation(options: UseTokenEstimationOptions): TokenEs...

FILE: src/renderer/packages/token-estimation/result-persister.ts
  type AttachmentUpdate (line 9) | interface AttachmentUpdate {
  type PendingUpdate (line 18) | interface PendingUpdate {
  function applyAttachmentUpdate (line 28) | function applyAttachmentUpdate<T extends MessageFile | MessageLink>(atta...
  function applyUpdates (line 44) | function applyUpdates(msg: Message, updates: PendingUpdate['updates']): ...
  function buildCacheKey (line 74) | function buildCacheKey(tokenizerType: TokenizerType, contentMode?: Conte...
  class ResultPersister (line 81) | class ResultPersister {
    method addResult (line 89) | addResult(result: NonNullable<TaskResult['result']>): void {
    method getPendingCount (line 147) | getPendingCount(): number {
    method subscribe (line 151) | subscribe(listener: () => void): () => void {
    method flushNow (line 158) | async flushNow(): Promise<void> {
    method cancel (line 173) | cancel(): void {
    method scheduleFlush (line 181) | private scheduleFlush(): void {
    method doFlush (line 198) | private doFlush(): void {
    method flush (line 206) | private async flush(): Promise<void> {
    method notifyListeners (line 241) | private notifyListeners(): void {

FILE: src/renderer/packages/token-estimation/task-executor.ts
  type ResultPersister (line 16) | interface ResultPersister {
  function setResultPersister (line 22) | function setResultPersister(persister: ResultPersister): void {
  function executeTask (line 26) | async function executeTask(task: ComputationTask): Promise<TaskResult> {
  function executeMessageTextTask (line 40) | async function executeMessageTextTask(task: ComputationTask): Promise<Ta...
  function executeAttachmentTask (line 80) | async function executeAttachmentTask(task: ComputationTask): Promise<Tas...
  function getTokenModel (line 219) | function getTokenModel(tokenizerType: TokenizerType): { provider: string...
  function initializeExecutor (line 226) | function initializeExecutor(): void {

FILE: src/renderer/packages/token-estimation/tokenizer.ts
  type TokenModel (line 32) | type TokenModel =
  function isDeepSeekModel (line 51) | function isDeepSeekModel(model?: TokenModel): boolean {
  function getTokenizerType (line 63) | function getTokenizerType(model?: TokenModel): TokenizerType {
  function estimateDeepSeekTokens (line 84) | function estimateDeepSeekTokens(text: string): number {
  function estimateTokens (line 135) | function estimateTokens(str: string, model?: TokenModel): number {

FILE: src/renderer/packages/token-estimation/types.ts
  type TokenizerType (line 17) | type TokenizerType = 'default' | 'deepseek'
  type ContentMode (line 24) | type ContentMode = 'full' | 'preview'
  type TokenCacheKey (line 30) | type TokenCacheKey = 'default' | 'deepseek' | 'default_preview' | 'deeps...
  type ComputationTaskType (line 41) | type ComputationTaskType = 'message-text' | 'attachment'
  type AttachmentType (line 48) | type AttachmentType = 'file' | 'link'
  type ComputationTask (line 53) | interface ComputationTask {
  type TaskResult (line 83) | interface TaskResult {
  type QueueState (line 124) | interface QueueState {
  type TokenBreakdown (line 140) | interface TokenBreakdown {
  type TokenEstimationResult (line 150) | interface TokenEstimationResult {
  type TokenCacheEntry (line 177) | interface TokenCacheEntry {
  type TokenCache (line 189) | type TokenCache = Partial<Record<TokenCacheKey, TokenCacheEntry>>
  type TokenEstimationConfig (line 198) | interface TokenEstimationConfig {

FILE: src/renderer/packages/token.test.ts
  function createMessage (line 17) | function createMessage(overrides: Partial<Message> & { text?: string } =...

FILE: src/renderer/packages/token.tsx
  function getTokenCacheKey (line 20) | function getTokenCacheKey(model?: TokenModel): TokenCacheKey {
  function getTokenCountForModel (line 27) | function getTokenCountForModel(item: { tokenCountMap?: Record<string, nu...
  function estimateTokensFromMessages (line 39) | function estimateTokensFromMessages(
  function sumCachedTokensFromMessages (line 96) | function sumCachedTokensFromMessages(messages: Message[], model?: TokenM...
  function sliceTextByTokenLimit (line 144) | function sliceTextByTokenLimit(text: string, limit: number, model?: Toke...
  type PreviewTokenCacheKey (line 161) | type PreviewTokenCacheKey = 'default_preview' | 'deepseek_preview'
  function getAttachmentTokenCacheKey (line 163) | function getAttachmentTokenCacheKey(params: {
  constant FALLBACK_WRAPPER_SAFETY_MARGIN_TOKENS (line 176) | const FALLBACK_WRAPPER_SAFETY_MARGIN_TOKENS = 50
  function computeAttachmentTokens (line 178) | function computeAttachmentTokens(
  type EstimateTokensForSendPayloadOptions (line 242) | interface EstimateTokensForSendPayloadOptions {
  function estimateTokensFromMessagesForSendPayload (line 248) | function estimateTokensFromMessagesForSendPayload(

FILE: src/renderer/packages/tools/index.ts
  function getToolName (line 3) | function getToolName(toolName: string): string {

FILE: src/renderer/packages/web-search/base.ts
  method fetch (line 9) | async fetch(url: string, options: FetchOptions) {

FILE: src/renderer/packages/web-search/bing-news.ts
  class BingNewsSearch (line 4) | class BingNewsSearch extends WebSearch {
    method search (line 5) | async search(query: string, signal?: AbortSignal): Promise<SearchResul...
    method fetchSerp (line 11) | private async fetchSerp(query: string, signal?: AbortSignal) {
    method extractItems (line 20) | private extractItems(html: string) {

FILE: src/renderer/packages/web-search/bing.ts
  class BingSearch (line 4) | class BingSearch extends WebSearch {
    method search (line 5) | async search(query: string, signal?: AbortSignal): Promise<SearchResul...
    method fetchSerp (line 11) | private async fetchSerp(query: string, signal?: AbortSignal) {
    method extractItems (line 20) | private extractItems(html: string) {

FILE: src/renderer/packages/web-search/chatbox-search.ts
  class ChatboxSearch (line 5) | class ChatboxSearch extends WebSearch {
    method constructor (line 8) | constructor(licenseKey: string) {
    method search (line 13) | async search(query: string): Promise<SearchResult> {

FILE: src/renderer/packages/web-search/duckduckgo.ts
  class DuckDuckGoSearch (line 4) | class DuckDuckGoSearch extends WebSearch {
    method search (line 5) | async search(query: string, signal?: AbortSignal): Promise<SearchResul...
    method fetchSerp (line 11) | private async fetchSerp(query: string, signal?: AbortSignal) {
    method extractItems (line 20) | private extractItems(html: string) {

FILE: src/renderer/packages/web-search/index.ts
  constant MAX_CONTEXT_ITEMS (line 12) | const MAX_CONTEXT_ITEMS = 10
  function getSearchProviders (line 15) | function getSearchProviders() {
  function _searchRelatedResults (line 60) | async function _searchRelatedResults(query: string, signal?: AbortSignal) {

FILE: src/renderer/packages/web-search/tavily.ts
  class TavilySearch (line 5) | class TavilySearch extends WebSearch {
    method constructor (line 14) | constructor(
    method search (line 29) | async search(query: string, signal?: AbortSignal): Promise<SearchResul...
    method buildRequestBody (line 56) | private buildRequestBody(query: string): any {
    method isNullOrNone (line 76) | private isNullOrNone(value: string | null): boolean {

FILE: src/renderer/packages/word-count.ts
  function countWord (line 7) | function countWord(data: string): number {

FILE: src/renderer/pages/PictureDialog.tsx
  function PictureDialog (line 12) | function PictureDialog(props: {}) {
  function _PictureDialog (line 25) | function _PictureDialog(props: {

FILE: src/renderer/pages/RemoteDialogWindow.tsx
  function RemoteDialogWindow (line 12) | function RemoteDialogWindow() {

FILE: src/renderer/pages/SearchDialog.tsx
  type Props (line 20) | type Props = {}
  function SearchDialog (line 22) | function SearchDialog(props: Props) {

FILE: src/renderer/pages/SettingDialog/AdvancedSettingTab.tsx
  type Props (line 26) | interface Props {
  function AdvancedSettingTab (line 32) | function AdvancedSettingTab(props: Props) {
  type ExportDataItem (line 145) | enum ExportDataItem {
  function ExportAndImport (line 152) | function ExportAndImport(props: { onCancel: () => void }) {
  function AnalyticsSetting (line 322) | function AnalyticsSetting() {
  function AllowReportingAndTrackingCheckbox (line 340) | function AllowReportingAndTrackingCheckbox(props: { className?: string }) {

FILE: src/renderer/platform/desktop_platform.ts
  class DesktopPlatform (line 21) | class DesktopPlatform implements Platform {
    method constructor (line 30) | constructor(ipc: ElectronIPC) {
    method getStorageType (line 34) | public getStorageType(): string {
    method getVersion (line 38) | public async getVersion() {
    method getPlatform (line 41) | public async getPlatform() {
    method getArch (line 44) | public async getArch() {
    method shouldUseDarkColors (line 47) | public async shouldUseDarkColors(): Promise<boolean> {
    method onSystemThemeChange (line 50) | public onSystemThemeChange(callback: () => void): () => void {
    method onWindowShow (line 53) | public onWindowShow(callback: () => void): () => void {
    method onWindowFocused (line 56) | public onWindowFocused(callback: () => void): () => void {
    method onUpdateDownloaded (line 59) | public onUpdateDownloaded(callback: () => void): () => void {
    method onNavigate (line 62) | public onNavigate(callback: (path: string) => void): () => void {
    method openLink (line 65) | public async openLink(url: string): Promise<void> {
    method getDeviceName (line 68) | public async getDeviceName(): Promise<string> {
    method getInstanceName (line 74) | public async getInstanceName(): Promise<string> {
    method getLocale (line 78) | public async getLocale() {
    method ensureShortcutConfig (line 82) | public async ensureShortcutConfig(config: ShortcutSetting): Promise<vo...
    method ensureProxyConfig (line 85) | public async ensureProxyConfig(config: { proxy?: string }): Promise<vo...
    method relaunch (line 88) | public async relaunch(): Promise<void> {
    method getConfig (line 92) | public async getConfig(): Promise<Config> {
    method getSettings (line 95) | public async getSettings(): Promise<Settings> {
    method needStoreInFile (line 99) | private needStoreInFile(key: string): boolean {
    method setStoreValue (line 103) | public async setStoreValue(key: string, value: any) {
    method getStoreValue (line 118) | public async getStoreValue(key: string) {
    method delStoreValue (line 132) | public async delStoreValue(key: string) {
    method getAllStoreValues (line 139) | public async getAllStoreValues(): Promise<{ [key: string]: any }> {
    method getAllStoreKeys (line 153) | public async getAllStoreKeys(): Promise<string[]> {
    method setAllStoreValues (line 158) | public async setAllStoreValues(data: { [key: string]: any }): Promise<...
    method getStoreBlob (line 164) | public async getStoreBlob(key: string): Promise<string | null> {
    method setStoreBlob (line 167) | public async setStoreBlob(key: string, value: string) {
    method delStoreBlob (line 170) | public async delStoreBlob(key: string) {
    method listStoreBlobKeys (line 173) | public async listStoreBlobKeys(): Promise<string[]> {
    method initTracking (line 177) | public initTracking(): void {
    method trackingEvent (line 182) | public trackingEvent(name: string, params: { [key: string]: string }) {
    method shouldShowAboutDialogWhenStartUp (line 187) | public async shouldShowAboutDialogWhenStartUp(): Promise<boolean> {
    method appLog (line 193) | public async appLog(level: string, message: string) {
    method exportLogs (line 197) | public async exportLogs(): Promise<string> {
    method clearLogs (line 201) | public async clearLogs(): Promise<void> {
    method ensureAutoLaunch (line 205) | public async ensureAutoLaunch(enable: boolean) {
    method parseFileLocally (line 209) | async parseFileLocally(file: File): Promise<{ key?: string; isSupporte...
    method parseFileWithMineru (line 227) | async parseFileWithMineru(
    method cancelMineruParse (line 244) | async cancelMineruParse(filePath: string): Promise<{ success: boolean;...
    method parseUrl (line 248) | public async parseUrl(url: string): Promise<{ key: string; title: stri...
    method isFullscreen (line 253) | public async isFullscreen() {
    method setFullscreen (line 257) | public async setFullscreen(enabled: boolean) {
    method installUpdate (line 261) | public async installUpdate() {
    method switchTheme (line 265) | public async switchTheme(theme: 'dark' | 'light') {
    method getKnowledgeBaseController (line 269) | public getKnowledgeBaseController() {
    method getImageGenerationStorage (line 276) | public getImageGenerationStorage(): ImageGenerationStorage {
    method minimize (line 283) | public minimize() {
    method maximize (line 287) | public maximize() {
    method unmaximize (line 291) | public unmaximize() {
    method closeWindow (line 295) | public closeWindow() {
    method isMaximized (line 299) | public isMaximized() {
    method onMaximizedChange (line 303) | public onMaximizedChange(callback: (isMaximized: boolean) => void): ()...

FILE: src/renderer/platform/index.ts
  function initPlatform (line 7) | function initPlatform(): Platform {

FILE: src/renderer/platform/interfaces.ts
  type PlatformType (line 6) | type PlatformType = 'web' | 'desktop' | 'mobile'
  type Storage (line 8) | interface Storage {
  type Platform (line 18) | interface Platform extends Storage {
  type Exporter (line 105) | interface Exporter {

FILE: src/renderer/platform/knowledge-base/desktop-controller.ts
  class DesktopKnowledgeBaseController (line 6) | class DesktopKnowledgeBaseController implements KnowledgeBaseController {
    method constructor (line 7) | constructor(private ipc: ElectronIPC) {}
    method list (line 9) | async list() {
    method create (line 14) | async create(createParams: {
    method delete (line 25) | async delete(id: number) {
    method listFiles (line 29) | async listFiles(kbId: number) {
    method countFiles (line 34) | async countFiles(kbId: number) {
    method listFilesPaginated (line 38) | async listFilesPaginated(kbId: number, offset = 0, limit = 20) {
    method uploadFile (line 42) | async uploadFile(kbId: number, file: FileMeta) {
    method deleteFile (line 46) | async deleteFile(fileId: number) {
    method retryFile (line 50) | async retryFile(fileId: number, useRemoteParsing = false) {
    method pauseFile (line 54) | async pauseFile(fileId: number) {
    method resumeFile (line 58) | async resumeFile(fileId: number) {
    method search (line 62) | async search(kbId: number, query: string) {
    method update (line 67) | async update(updateParams: { id: number; name?: string; rerankModel?: ...
    method getFilesMeta (line 71) | async getFilesMeta(kbId: number, fileIds: number[]) {
    method readFileChunks (line 75) | async readFileChunks(kbId: number, chunks: { fileId: number; chunkInde...
    method testMineruConnection (line 79) | async testMineruConnection(apiToken: string): Promise<{ success: boole...

FILE: src/renderer/platform/knowledge-base/interface.ts
  type KnowledgeBaseController (line 10) | interface KnowledgeBaseController {

FILE: src/renderer/platform/storages.ts
  class DesktopFileStorage (line 7) | class DesktopFileStorage implements Storage {
    method getStorageType (line 10) | public getStorageType(): string {
    method setStoreValue (line 14) | public async setStoreValue(key: string, value: any) {
    method getStoreValue (line 23) | public async getStoreValue(key: string) {
    method delStoreValue (line 26) | public delStoreValue(key: string) {
    method getAllStoreValues (line 29) | public async getAllStoreValues(): Promise<{ [key: string]: any }> {
    method getAllStoreKeys (line 33) | public async getAllStoreKeys(): Promise<string[]> {
    method setAllStoreValues (line 36) | public async setAllStoreValues(data: { [key: string]: any }) {
  class LocalStorage (line 41) | class LocalStorage implements Storage {
    method getStorageType (line 51) | public getStorageType(): string {
    method setStoreValue (line 55) | public async setStoreValue(key: string, value: any) {
    method getStoreValue (line 60) | public async getStoreValue(key: string) {
    method delStoreValue (line 64) | public async delStoreValue(key: string) {
    method getAllStoreValues (line 67) | public async getAllStoreValues(): Promise<{ [key: string]: any }> {
    method getAllStoreKeys (line 84) | public async getAllStoreKeys(): Promise<string[]> {
    method setAllStoreValues (line 88) | public async setAllStoreValues(data: { [key: string]: any }): Promise<...
  class SQLiteStorage (line 95) | class SQLiteStorage {
    method constructor (line 100) | constructor() {
    method initialize (line 106) | private async initialize(): Promise<void> {
    method ensureInitialized (line 128) | private async ensureInitialized(): Promise<void> {
    method setItem (line 133) | async setItem(key: string, value: string): Promise<void> {
    method getItem (line 149) | async getItem(key: string): Promise<string | null> {
    method removeItem (line 166) | async removeItem(key: string): Promise<void> {
    method getAllItems (line 182) | async getAllItems(): Promise<{ [key: string]: any }> {
    method getAllKeys (line 205) | async getAllKeys(): Promise<string[]> {
    method closeDatabase (line 228) | async closeDatabase(): Promise<void> {
  class MobileSQLiteStorage (line 237) | class MobileSQLiteStorage implements Storage {
    method getStorageType (line 238) | public getStorageType(): string {
    method setStoreValue (line 243) | public async setStoreValue(key: string, value: any) {
    method getStoreValue (line 246) | public async getStoreValue(key: string) {
    method delStoreValue (line 250) | public async delStoreValue(key: string) {
    method getAllStoreValues (line 253) | public async getAllStoreValues(): Promise<{ [key: string]: any }> {
    method getAllStoreKeys (line 266) | public async getAllStoreKeys(): Promise<string[]> {
    method setAllStoreValues (line 269) | public async setAllStoreValues(data: { [key: string]: any }): Promise<...
  class IndexedDBStorage (line 276) | class IndexedDBStorage implements Storage {
    method getStorageType (line 279) | public getStorageType(): string {
    method setStoreValue (line 283) | public async setStoreValue(key: string, value: any) {
    method getStoreValue (line 292) | public async getStoreValue(key: string) {
    method delStoreValue (line 302) | public async delStoreValue(key: string) {
    method getAllStoreValues (line 305) | public async getAllStoreValues(): Promise<{ [key: string]: any }> {
    method getAllStoreKeys (line 321) | public async getAllStoreKeys(): Promise<string[]> {
    method setAllStoreValues (line 324) | public async setAllStoreValues(data: { [key: string]: any }): Promise<...
  function getOldVersionStorages (line 331) | function getOldVersionStorages(): Storage[] {

FILE: src/renderer/platform/test_platform.ts
  class InMemoryStorage (line 20) | class InMemoryStorage implements Storage {
    method getStorageType (line 23) | public getStorageType(): string {
    method setStoreValue (line 27) | public async setStoreValue(key: string, value: any): Promise<void> {
    method getStoreValue (line 31) | public async getStoreValue(key: string): Promise<any> {
    method delStoreValue (line 36) | public async delStoreValue(key: string): Promise<void> {
    method getAllStoreValues (line 40) | public async getAllStoreValues(): Promise<{ [key: string]: any }> {
    method getAllStoreKeys (line 48) | public async getAllStoreKeys(): Promise<string[]> {
    method setAllStoreValues (line 52) | public async setAllStoreValues(data: { [key: string]: any }): Promise<...
    method clear (line 58) | public clear(): void {
  class TestExporter (line 66) | class TestExporter implements Exporter {
    method exportBlob (line 69) | async exportBlob(filename: string, blob: Blob, encoding?: 'utf8' | 'as...
    method exportTextFile (line 74) | async exportTextFile(filename: string, content: string): Promise<void> {
    method exportImageFile (line 78) | async exportImageFile(basename: string, base64: string): Promise<void> {
    method exportByUrl (line 82) | async exportByUrl(filename: string, url: string): Promise<void> {
    method exportStreamingJson (line 86) | async exportStreamingJson(
    method getExport (line 97) | getExport(filename: string): any {
    method getAllExports (line 101) | getAllExports(): Map<string, any> {
    method clear (line 105) | clear(): void {
  class TestPlatform (line 113) | class TestPlatform implements Platform {
    method constructor (line 122) | constructor() {
    method getStorageType (line 130) | public getStorageType(): string {
    method setStoreValue (line 134) | public async setStoreValue(key: string, value: any): Promise<void> {
    method getStoreValue (line 138) | public async getStoreValue(key: string): Promise<any> {
    method delStoreValue (line 142) | public async delStoreValue(key: string): Promise<void> {
    method getAllStoreValues (line 146) | public async getAllStoreValues(): Promise<{ [key: string]: any }> {
    method getAllStoreKeys (line 150) | public async getAllStoreKeys(): Promise<string[]> {
    method setAllStoreValues (line 154) | public async setAllStoreValues(data: { [key: string]: any }): Promise<...
    method getStoreBlob (line 160) | public async getStoreBlob(key: string): Promise<string | null> {
    method setStoreBlob (line 164) | public async setStoreBlob(key: string, value: string): Promise<void> {
    method delStoreBlob (line 168) | public async delStoreBlob(key: string): Promise<void> {
    method listStoreBlobKeys (line 172) | public async listStoreBlobKeys(): Promise<string[]> {
    method getVersion (line 178) | public async getVersion(): Promise<string> {
    method getPlatform (line 182) | public async getPlatform(): Promise<string> {
    method getArch (line 186) | public async getArch(): Promise<string> {
    method shouldUseDarkColors (line 190) | public async shouldUseDarkColors(): Promise<boolean> {
    method onSystemThemeChange (line 194) | public onSystemThemeChange(callback: () => void): () => void {
    method onWindowShow (line 198) | public onWindowShow(callback: () => void): () => void {
    method onWindowFocused (line 202) | public onWindowFocused(callback: () => void): () => void {
    method onUpdateDownloaded (line 206) | public onUpdateDownloaded(callback: () => void): () => void {
    method openLink (line 210) | public async openLink(url: string): Promise<void> {
    method getDeviceName (line 214) | public async getDeviceName(): Promise<string> {
    method getInstanceName (line 218) | public async getInstanceName(): Promise<string> {
    method getLocale (line 222) | public async getLocale(): Promise<Language> {
    method ensureShortcutConfig (line 226) | public async ensureShortcutConfig(config: ShortcutSetting): Promise<vo...
    method ensureProxyConfig (line 230) | public async ensureProxyConfig(config: { proxy?: string }): Promise<vo...
    method relaunch (line 234) | public async relaunch(): Promise<void> {
    method getConfig (line 240) | public async getConfig(): Promise<Config> {
    method getSettings (line 247) | public async getSettings(): Promise<Settings> {
    method initTracking (line 256) | public initTracking(): void {
    method trackingEvent (line 260) | public trackingEvent(name: string, params: { [key: string]: string }):...
    method shouldShowAboutDialogWhenStartUp (line 266) | public async shouldShowAboutDialogWhenStartUp(): Promise<boolean> {
    method appLog (line 270) | public async appLog(level: string, message: string): Promise<void> {
    method exportLogs (line 274) | public async exportLogs(): Promise<string> {
    method clearLogs (line 278) | public async clearLogs(): Promise<void> {
    method ensureAutoLaunch (line 282) | public async ensureAutoLaunch(enable: boolean): Promise<void> {
    method parseFileLocally (line 286) | public async parseFileLocally(file: File): Promise<{ key?: string; isS...
    method isFullscreen (line 298) | public async isFullscreen(): Promise<boolean> {
    method setFullscreen (line 302) | public async setFullscreen(enabled: boolean): Promise<void> {
    method installUpdate (line 306) | public async installUpdate(): Promise<void> {
    method getKnowledgeBaseController (line 310) | public getKnowledgeBaseController(): KnowledgeBaseController {
    method getImageGenerationStorage (line 314) | public getImageGenerationStorage(): ImageGenerationStorage {
    method minimize (line 318) | public async minimize(): Promise<void> {
    method maximize (line 322) | public async maximize(): Promise<void> {
    method unmaximize (line 326) | public async unmaximize(): Promise<void> {
    method closeWindow (line 330) | public async closeWindow(): Promise<void> {
    method isMaximized (line 334) | public async isMaximized(): Promise<boolean> {
    method onMaximizedChange (line 338) | public onMaximizedChange(callback: (isMaximized: boolean) => void): ()...
    method loadFile (line 349) | public loadFile(storageKey: string, content: string): void {
    method loadFiles (line 357) | public loadFiles(files: Record<string, string>): void {
    method getAllBlobs (line 366) | public getAllBlobs(): Record<string, string> {
    method clear (line 377) | public clear(): void {
    method setSettings (line 388) | public setSettings(settings: Partial<Settings>): void {
    method setConfig (line 395) | public setConfig(config: Partial<Config>): void {
    method getInternalStorage (line 402) | public getInternalStorage(): InMemoryStorage {

FILE: src/renderer/platform/web_exporter.ts
  class WebExporter (line 4) | class WebExporter implements Exporter {
    method constructor (line 5) | constructor() {}
    method exportBlob (line 7) | async exportBlob(filename: string, blob: Blob, encoding?: 'utf8' | 'as...
    method exportTextFile (line 17) | async exportTextFile(filename: string, content: string) {
    method exportImageFile (line 28) | async exportImageFile(basename: string, base64Data: string) {
    method exportByUrl (line 54) | async exportByUrl(filename: string, url: string) {
    method exportStreamingJson (line 64) | async exportStreamingJson(filename: string, dataCallback: () => AsyncG...

FILE: src/renderer/platform/web_logger.ts
  constant LOG_STORAGE_KEY (line 4) | const LOG_STORAGE_KEY = 'chatbox-app-logs'
  constant MAX_LOG_ENTRIES (line 5) | const MAX_LOG_ENTRIES = 1000 // 最大日志条数
  constant MAX_LOG_AGE_DAYS (line 6) | const MAX_LOG_AGE_DAYS = 30 // 日志保留天数
  type LogEntry (line 8) | interface LogEntry {
  class WebLogger (line 18) | class WebLogger {
    method constructor (line 24) | private constructor() {}
    method getInstance (line 26) | public static getInstance(): WebLogger {
    method init (line 36) | public async init(): Promise<void> {
    method cleanupOldLogs (line 51) | private async cleanupOldLogs(): Promise<void> {
    method getStoredLogs (line 76) | private async getStoredLogs(): Promise<LogEntry[]> {
    method log (line 88) | public log(level: string, message: string): void {
    method scheduleFlush (line 104) | private scheduleFlush(): void {
    method flush (line 116) | private async flush(): Promise<void> {
    method flushNow (line 140) | public async flushNow(): Promise<void> {
    method exportLogs (line 152) | public async exportLogs(): Promise<string> {
    method clearLogs (line 168) | public async clearLogs(): Promise<void> {

FILE: src/renderer/platform/web_platform.ts
  class WebPlatform (line 15) | class WebPlatform extends IndexedDBStorage implements Platform {
    method constructor (line 22) | constructor() {
    method getVersion (line 27) | public async getVersion(): Promise<string> {
    method getPlatform (line 30) | public async getPlatform(): Promise<string> {
    method getArch (line 33) | public async getArch(): Promise<string> {
    method shouldUseDarkColors (line 36) | public async shouldUseDarkColors(): Promise<boolean> {
    method onSystemThemeChange (line 39) | public onSystemThemeChange(callback: () => void): () => void {
    method onWindowShow (line 45) | public onWindowShow(callback: () => void): () => void {
    method onWindowFocused (line 48) | public onWindowFocused(callback: () => void): () => void {
    method onUpdateDownloaded (line 51) | public onUpdateDownloaded(callback: () => void): () => void {
    method openLink (line 54) | public async openLink(url: string): Promise<void> {
    method getDeviceName (line 57) | public async getDeviceName(): Promise<string> {
    method getInstanceName (line 61) | public async getInstanceName(): Promise<string> {
    method getLocale (line 64) | public async getLocale() {
    method ensureShortcutConfig (line 68) | public async ensureShortcutConfig(config: ShortcutSetting): Promise<vo...
    method ensureProxyConfig (line 71) | public async ensureProxyConfig(config: { proxy?: string }): Promise<vo...
    method relaunch (line 74) | public async relaunch(): Promise<void> {
    method getConfig (line 78) | public async getConfig(): Promise<Config> {
    method getSettings (line 86) | public async getSettings(): Promise<Settings> {
    method getStoreBlob (line 95) | public async getStoreBlob(key: string): Promise<string | null> {
    method setStoreBlob (line 98) | public async setStoreBlob(key: string, value: string): Promise<void> {
    method delStoreBlob (line 101) | public async delStoreBlob(key: string) {
    method listStoreBlobKeys (line 104) | public async listStoreBlobKeys(): Promise<string[]> {
    method initTracking (line 108) | public async initTracking() {
    method trackingEvent (line 128) | public trackingEvent(name: string, params: { [key: string]: string }) {
    method shouldShowAboutDialogWhenStartUp (line 132) | public async shouldShowAboutDialogWhenStartUp(): Promise<boolean> {
    method appLog (line 136) | public async appLog(level: string, message: string): Promise<void> {
    method exportLogs (line 140) | public async exportLogs(): Promise<string> {
    method clearLogs (line 144) | public async clearLogs(): Promise<void> {
    method ensureAutoLaunch (line 148) | public async ensureAutoLaunch(enable: boolean) {
    method parseFileLocally (line 152) | async parseFileLocally(file: File): Promise<{ key?: string; isSupporte...
    method parseUrl (line 162) | public async parseUrl(url: string): Promise<{ key: string; title: stri...
    method isFullscreen (line 166) | public async isFullscreen() {
    method setFullscreen (line 170) | public async setFullscreen(enabled: boolean): Promise<void> {
    method installUpdate (line 174) | installUpdate(): Promise<void> {
    method getKnowledgeBaseController (line 178) | public getKnowledgeBaseController(): KnowledgeBaseController {
    method getImageGenerationStorage (line 182) | public getImageGenerationStorage(): ImageGenerationStorage {
    method minimize (line 189) | public minimize() {
    method maximize (line 193) | public maximize() {
    method unmaximize (line 197) | public unmaximize() {
    method closeWindow (line 201) | public closeWindow() {
    method isMaximized (line 205) | public isMaximized() {
    method onMaximizedChange (line 209) | public onMaximizedChange() {

FILE: src/renderer/platform/web_platform_utils.ts
  function parseTextFileLocally (line 6) | async function parseTextFileLocally(file: File): Promise<{ text: string;...
  function parseUrlContentFree (line 15) | async function parseUrlContentFree(url: string) {

FILE: src/renderer/preload.d.ts
  type Window (line 5) | interface Window {

FILE: src/renderer/router.tsx
  type Register (line 23) | interface Register {

FILE: src/renderer/routes/__root.tsx
  function Root (line 60) | function Root() {
  type ExtendedCustomColors (line 504) | type ExtendedCustomColors =
  type MantineThemeColorsOverride (line 516) | interface MantineThemeColorsOverride {

FILE: src/renderer/routes/about.tsx
  function RouteComponent (line 32) | function RouteComponent() {
  function WechatQRCode (line 168) | function WechatQRCode() {
  function List (line 185) | function List(props: { children: ReactElement[] }) {
  function ListItem (line 198) | function ListItem({

FILE: src/renderer/routes/copilots.tsx
  function Copilots (line 42) | function Copilots() {
  type MiniItemProps (line 190) | type MiniItemProps =
  function MiniItem (line 205) | function MiniItem(props: MiniItemProps) {
  type CopilotFormProps (line 359) | interface CopilotFormProps {
  function CopilotForm (line 367) | function CopilotForm(props: CopilotFormProps) {
  function getEmptyCopilot (line 471) | async function getEmptyCopilot(): Promise<CopilotDetail> {

FILE: src/renderer/routes/dev/context-generator.tsx
  type GeneratedMessage (line 32) | type GeneratedMessage = Message & {
  type MessageLengthPreset (line 36) | type MessageLengthPreset = 'short' | 'medium' | 'long' | 'mixed'
  constant MESSAGE_LENGTH_PRESETS (line 38) | const MESSAGE_LENGTH_PRESETS: Record<MessageLengthPreset, { min: number;...
  function generateFakeMessage (line 45) | function generateFakeMessage(role: MessageRole, lengthPreset: MessageLen...
  function generateConversation (line 68) | function generateConversation(
  function generateToTargetTokens (line 96) | function generateToTargetTokens(
  function stripEstimatedTokens (line 132) | function stripEstimatedTokens(messages: GeneratedMessage[]): Message[] {
  function ContextGeneratorPage (line 136) | function ContextGeneratorPage() {

FILE: src/renderer/routes/dev/css-var.tsx
  function RouteComponent (line 10) | function RouteComponent() {

FILE: src/renderer/routes/dev/index.tsx
  function DevIndexPage (line 37) | function DevIndexPage() {

FILE: src/renderer/routes/dev/route.tsx
  function DevLayout (line 11) | function DevLayout() {

FILE: src/renderer/routes/dev/storage.tsx
  type StorageEntry (line 30) | type StorageEntry = {
  type DetailState (line 38) | type DetailState =
  function StorageViewerPage (line 42) | function StorageViewerPage() {
  function detectType (line 322) | function detectType(value: unknown): string {
  function safeStringify (line 330) | function safeStringify(value: unknown): string {
  function buildPreview (line 341) | function buildPreview(text: string): string {
  function formatSize (line 347) | function formatSize(length: number): string {
  function renderModalTitle (line 353) | function renderModalTitle(detail: DetailState): string {

FILE: src/renderer/routes/image-creator/-components/EmptyState.tsx
  type EmptyStateProps (line 5) | interface EmptyStateProps {
  function EmptyState (line 9) | function EmptyState({ onPromptSelect }: EmptyStateProps) {

FILE: src/renderer/routes/image-creator/-components/GeneratedImagesGallery.tsx
  type GeneratedImagesGalleryProps (line 14) | interface GeneratedImagesGalleryProps {
  type GeneratedImageGalleryItemProps (line 71) | interface GeneratedImageGalleryItemProps {
  constant MAX_HEIGHT (line 79) | const MAX_HEIGHT = 600
  constant MAX_WIDTH (line 80) | const MAX_WIDTH = 840
  constant MIN_WIDTH (line 81) | const MIN_WIDTH = 320
  constant MOBILE_SIZE (line 82) | const MOBILE_SIZE = 320 // Fixed 1:1 size for mobile
  function calculateDisplaySize (line 84) | function calculateDisplaySize(width: number, height: number): { displayW...
  function GeneratedImageGalleryItem (line 106) | function GeneratedImageGalleryItem({ storageKey, onUseAsReference, isSma...

FILE: src/renderer/routes/image-creator/-components/HistoryItem.tsx
  type HistoryItemProps (line 10) | interface HistoryItemProps {
  function HistoryItem (line 18) | function HistoryItem({ record, isActive, isMobile, onClick, onDelete }: ...
  type HistoryThumbnailProps (line 150) | interface HistoryThumbnailProps {
  function HistoryThumbnail (line 155) | function HistoryThumbnail({ storageKey, size = 48 }: HistoryThumbnailPro...

FILE: src/renderer/routes/image-creator/-components/HistoryPanel.tsx
  type HistoryListContentProps (line 11) | interface HistoryListContentProps {
  function HistoryListContent (line 23) | function HistoryListContent({
  type HistoryPanelProps (line 87) | interface HistoryPanelProps {
  function HistoryPanel (line 102) | function HistoryPanel({

FILE: src/renderer/routes/image-creator/-components/ImageGenerationErrorTips.tsx
  type ImageGenerationErrorTipsProps (line 11) | interface ImageGenerationErrorTipsProps {
  function ImageGenerationErrorTips (line 17) | function ImageGenerationErrorTips({ record, onRetry, isRetrying }: Image...

FILE: src/renderer/routes/image-creator/-components/MobileDrawers.tsx
  type MobileHistoryDrawerProps (line 12) | interface MobileHistoryDrawerProps {
  function MobileHistoryDrawer (line 26) | function MobileHistoryDrawer({
  type MobileModelDrawerProps (line 98) | interface MobileModelDrawerProps {
  function MobileModelDrawer (line 107) | function MobileModelDrawer({
  type MobileRatioDrawerProps (line 181) | interface MobileRatioDrawerProps {
  function MobileRatioDrawer (line 189) | function MobileRatioDrawer({ open, onOpenChange, options, selectedRatio,...

FILE: src/renderer/routes/image-creator/-components/PromptDisplay.tsx
  type PromptDisplayProps (line 5) | interface PromptDisplayProps {
  function PromptDisplay (line 11) | function PromptDisplay({ prompt, modelDisplayName, referenceImageCount }...

FILE: src/renderer/routes/image-creator/-components/ReferenceImagesPreview.tsx
  type ReferenceImagesPreviewProps (line 6) | interface ReferenceImagesPreviewProps {
  function ReferenceImagesPreview (line 12) | function ReferenceImagesPreview({ images, onRemove, onAddClick }: Refere...

FILE: src/renderer/routes/image-creator/-components/Shimmer.tsx
  function LoadingShimmer (line 3) | function LoadingShimmer() {

FILE: src/renderer/routes/image-creator/-components/constants.ts
  constant MAX_REFERENCE_IMAGES (line 1) | const MAX_REFERENCE_IMAGES = 14
  constant HISTORY_PANEL_WIDTH (line 3) | const HISTORY_PANEL_WIDTH = 280
  constant IMAGE_MODEL_FALLBACK_NAMES (line 5) | const IMAGE_MODEL_FALLBACK_NAMES: Record<string, string> = {
  constant CHATBOXAI_IMAGE_MODEL_IDS (line 14) | const CHATBOXAI_IMAGE_MODEL_IDS = [
  constant OPENAI_IMAGE_MODEL_IDS (line 21) | const OPENAI_IMAGE_MODEL_IDS = ['gpt-image-1', 'gpt-image-1.5']
  constant GEMINI_IMAGE_MODEL_IDS (line 22) | const GEMINI_IMAGE_MODEL_IDS = [
  type ImageModelFamily (line 30) | type ImageModelFamily = 'gpt' | 'gemini' | 'default'
  constant RATIO_OPTIONS (line 32) | const RATIO_OPTIONS: Record<ImageModelFamily, string[]> = {
  function getRatioOptionsForModel (line 38) | function getRatioOptionsForModel(modelId: string): string[] {
  function blobToDataUrl (line 61) | function blobToDataUrl(blob: string): string {
  function getBase64ImageSize (line 69) | function getBase64ImageSize(base64: string): Promise<{ width: number; he...

FILE: src/renderer/routes/image-creator/index.tsx
  type InputToolbarProps (line 77) | interface InputToolbarProps {
  function InputToolbar (line 90) | function InputToolbar({
  function ImageCreatorPage (line 207) | function ImageCreatorPage() {

FILE: src/renderer/routes/index.tsx
  function Index (line 35) | function Index() {
  constant MAX_COPILOTS_TO_SHOW (line 270) | const MAX_COPILOTS_TO_SHOW = 10

FILE: src/renderer/routes/session/$sessionId.tsx
  function RouteComponent (line 23) | function RouteComponent() {

FILE: src/renderer/routes/settings/chat.tsx
  constant MAX_IMAGE_SIZE (line 17) | const MAX_IMAGE_SIZE = 5 * 1024 * 1024 // 5MB
  function RouteComponent (line 23) | function RouteComponent() {
  function ContextManagementSection (line 385) | function ContextManagementSection() {

FILE: src/renderer/routes/settings/chatbox-ai.tsx
  function RouteComponent (line 20) | function RouteComponent() {

FILE: src/renderer/routes/settings/default-models.tsx
  function RouteComponent (line 16) | function RouteComponent() {

FILE: src/renderer/routes/settings/document-parser.tsx
  function RouteComponent (line 8) | function RouteComponent() {

FILE: src/renderer/routes/settings/general.tsx
  function RouteComponent (line 37) | function RouteComponent() {
  type ExportDataItem (line 493) | enum ExportDataItem {

FILE: src/renderer/routes/settings/hotkeys.tsx
  function RouteComponent (line 10) | function RouteComponent() {

FILE: src/renderer/routes/settings/index.tsx
  function RouteComponent (line 16) | function RouteComponent() {

FILE: src/renderer/routes/settings/mcp.tsx
  function RouteComponent (line 22) | function RouteComponent() {

FILE: src/renderer/routes/settings/provider/$providerId.tsx
  type ModelTestResult (line 56) | type ModelTestResult = ModelTestState & {
  function normalizeAPIHost (line 61) | function normalizeAPIHost(
  function RouteComponent (line 87) | function RouteComponent() {
  function ProviderSettings (line 92) | function ProviderSettings({ providerId }: { providerId: string }) {

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/LicenseDetailCard.tsx
  type LicenseDetailCardProps (line 9) | interface LicenseDetailCardProps {
  function LicenseDetailCard (line 15) | function LicenseDetailCard({ licenseDetail, language, utmContent }: Lice...

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/LicenseKeyView.tsx
  type LicenseKeyViewProps (line 19) | interface LicenseKeyViewProps {

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/LicenseSelectionModal.tsx
  type LicenseSelectionModalProps (line 6) | interface LicenseSelectionModalProps {
  function LicenseSelectionModal (line 13) | function LicenseSelectionModal({ opened, licenses, onConfirm, onCancel }...

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/LoggedInView.tsx
  type LoggedInViewProps (line 14) | interface LoggedInViewProps {

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/LoginView.tsx
  type LoginViewProps (line 26) | interface LoginViewProps {

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/ModelManagement.tsx
  type ModelManagementProps (line 10) | interface ModelManagementProps {
  function ModelManagement (line 20) | function ModelManagement({

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/constants.ts
  constant LOGIN_POLLING_INTERVAL (line 2) | const LOGIN_POLLING_INTERVAL = 1000
  constant LOGIN_POLLING_TIMEOUT (line 3) | const LOGIN_POLLING_TIMEOUT = 3 * 60 * 1000
  constant VIEW_TRANSITION_DURATION (line 6) | const VIEW_TRANSITION_DURATION = 300
  constant VIEW_TRANSITION_TIMING (line 7) | const VIEW_TRANSITION_TIMING = 'ease'

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/types.ts
  type ViewMode (line 1) | type ViewMode = 'login' | 'licenseKey'
  type LoginState (line 3) | type LoginState = 'idle' | 'requesting' | 'polling' | 'success' | 'error...
  type AuthTokens (line 5) | interface AuthTokens {
  type UserProfile (line 10) | interface UserProfile {

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/useAuthTokens.ts
  function useAuthTokens (line 8) | function useAuthTokens() {

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/useLicenseActivation.ts
  type Settings (line 7) | interface Settings {
  type UseLicenseActivationParams (line 14) | interface UseLicenseActivationParams {
  function useLicenseActivation (line 19) | function useLicenseActivation({ settings, onActivationSuccess }: UseLice...

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/useLogin.ts
  type UseLoginParams (line 10) | interface UseLoginParams {
  function useLogin (line 19) | function useLogin({ language, onLoginSuccess }: UseLoginParams) {

FILE: src/renderer/routes/settings/provider/chatbox-ai/-components/useUserLicenses.ts
  type UseUserLicensesParams (line 6) | interface UseUserLicensesParams {
  function useUserLicenses (line 10) | function useUserLicenses({ loginState }: UseUserLicensesParams) {

FILE: src/renderer/routes/settings/provider/chatbox-ai/index.tsx
  function RouteComponent (line 24) | function RouteComponent() {

FILE: src/renderer/routes/settings/provider/index.tsx
  function RouteComponent (line 9) | function RouteComponent() {

FILE: src/renderer/routes/settings/provider/route.tsx
  function RouteComponent (line 30) | function RouteComponent() {

FILE: src/renderer/routes/settings/route.tsx
  constant ITEMS (line 28) | const ITEMS = [
  function RouteComponent (line 97) | function RouteComponent() {
  function SettingsRoot (line 128) | function SettingsRoot() {

FILE: src/renderer/routes/settings/web-search.tsx
  function RouteComponent (line 14) | function RouteComponent() {

FILE: src/renderer/setup/init_data.ts
  function initData (line 8) | async function initData() {
  function initSessionsIfNeeded (line 12) | async function initSessionsIfNeeded() {
  function initPresetSessions (line 26) | async function initPresetSessions() {

FILE: src/renderer/setup/mcp_bootstrap.ts
  function monitorServerStatus (line 6) | function monitorServerStatus() {

FILE: src/renderer/setup/protect.ts
  function protect (line 16) | function protect() {
  function toHomePage (line 28) | function toHomePage() {
  function simpleEncrypt (line 42) | function simpleEncrypt(text: string): string {
  function simpleDecrypt (line 52) | function simpleDecrypt(text: string): string {

FILE: src/renderer/setup/sentry_init.ts
  method beforeSend (line 33) | beforeSend(event) {

FILE: src/renderer/setup/storage_clear.ts
  function tickStorageTask (line 19) | async function tickStorageTask() {

FILE: src/renderer/storage/BaseStorage.ts
  class BaseStorage (line 6) | class BaseStorage {
    method constructor (line 7) | constructor() {}
    method getStorageType (line 9) | public getStorageType() {
    method setItem (line 13) | public async setItem<T>(key: string, value: T): Promise<void> {
    method setItemNow (line 17) | public async setItemNow<T>(key: string, value: T): Promise<void> {
    method getItem (line 37) | public async getItem<T>(key: string, initialValue: T): Promise<T> {
    method removeItem (line 61) | public async removeItem(key: string): Promise<void> {
    method getAll (line 65) | public async getAll(): Promise<{ [key: string]: any }> {
    method getAllKeys (line 74) | public async getAllKeys(): Promise<string[]> {
    method setAll (line 83) | public async setAll(data: { [key: string]: any }) {
    method setBlob (line 88) | public async setBlob(key: string, value: string) {
    method getBlob (line 91) | public async getBlob(key: string): Promise<string | null> {
    method delBlob (line 99) | public async delBlob(key: string) {
    method getBlobKeys (line 102) | public async getBlobKeys(): Promise<string[]> {

FILE: src/renderer/storage/ImageGenerationStorage.ts
  constant PAGE_SIZE (line 3) | const PAGE_SIZE = 20
  constant DB_NAME (line 4) | const DB_NAME = 'chatbox-image-generation'
  constant STORE_NAME (line 5) | const STORE_NAME = 'records'
  type ImageGenerationStorage (line 7) | interface ImageGenerationStorage {
  class IndexedDBImageGenerationStorage (line 17) | class IndexedDBImageGenerationStorage implements ImageGenerationStorage {
    method initialize (line 21) | initialize(): Promise<void> {
    method openDatabase (line 29) | private openDatabase(): Promise<void> {
    method getStore (line 50) | private getStore(mode: IDBTransactionMode): IDBObjectStore {
    method create (line 56) | async create(record: ImageGeneration): Promise<void> {
    method update (line 66) | async update(id: string, updates: Partial<ImageGeneration>): Promise<I...
    method getById (line 80) | async getById(id: string): Promise<ImageGeneration | null> {
    method delete (line 90) | async delete(id: string): Promise<void> {
    method getPage (line 100) | async getPage(cursor: number = 0, limit: number = PAGE_SIZE): Promise<...
    method getTotal (line 139) | async getTotal(): Promise<number> {

FILE: src/renderer/storage/SQLiteImageGenerationStorage.ts
  constant PAGE_SIZE (line 5) | const PAGE_SIZE = 20
  constant DB_NAME (line 6) | const DB_NAME = 'chatbox-image-generation'
  class SQLiteImageGenerationStorage (line 8) | class SQLiteImageGenerationStorage implements ImageGenerationStorage {
    method constructor (line 13) | constructor() {
    method initialize (line 17) | initialize(): Promise<void> {
    method openDatabase (line 25) | private async openDatabase(): Promise<void> {
    method recordToRow (line 59) | private recordToRow(record: ImageGeneration): Record<string, unknown> {
    method rowToRecord (line 77) | private rowToRecord(row: Record<string, unknown>): ImageGeneration {
    method create (line 97) | async create(record: ImageGeneration): Promise<void> {
    method update (line 123) | async update(id: string, updates: Partial<ImageGeneration>): Promise<I...
    method getById (line 157) | async getById(id: string): Promise<ImageGeneration | null> {
    method delete (line 164) | async delete(id: string): Promise<void> {
    method getPage (line 169) | async getPage(cursor: number = 0, limit: number = PAGE_SIZE): Promise<...
    method getTotal (line 186) | async getTotal(): Promise<number> {

FILE: src/renderer/storage/StoreStorage.ts
  type StorageKey (line 6) | enum StorageKey {
  method session (line 20) | session(id: string) {
  method picture (line 23) | picture(category: string) {
  method file (line 26) | file(sessionId: string, msgId: string) {
  class StoreStorage (line 31) | class StoreStorage extends BaseStorage {
    method constructor (line 32) | constructor() {
    method getItem (line 35) | public async getItem<T>(key: string, initialValue: T): Promise<T> {
    method setItem (line 47) | public async setItem<T>(key: string, value: T): Promise<void> {

FILE: src/renderer/stores/atoms/compactionAtoms.ts
  type CompactionStatus (line 3) | type CompactionStatus = 'idle' | 'running' | 'failed'
  type CompactionUIState (line 5) | interface CompactionUIState {
  function getCompactionUIState (line 19) | function getCompactionUIState(sessionId: string): CompactionUIState {
  function setCompactionUIState (line 25) | function setCompactionUIState(sessionId: string, state: Partial<Compacti...

FILE: src/renderer/stores/atoms/throttleWriteSessionAtom.ts
  class WriteQueue (line 23) | class WriteQueue {
    method constructor (line 31) | constructor() {}
    method flush (line 33) | private flush() {
    method push (line 60) | push(sessionId: string, update: SetStateAction<Session | null>) {
  function createSessionAtom (line 74) | function createSessionAtom(sessionId: string) {
  function cleanupSessionAtomCache (line 97) | function cleanupSessionAtomCache(sessionId: string) {

FILE: src/renderer/stores/authInfoStore.ts
  type AuthTokensState (line 6) | interface AuthTokensState {
  type AuthTokensActions (line 11) | interface AuthTokensActions {
  function useAuthInfoStore (line 65) | function useAuthInfoStore<U>(selector: Parameters<typeof useStore<typeof...

FILE: src/renderer/stores/chatStore.ts
  function _listSessionsMeta (line 45) | async function _listSessionsMeta(): Promise<SessionMeta[]> {
  function listSessionsMeta (line 64) | async function listSessionsMeta() {
  function useSessionList (line 68) | function useSessionList() {
  function updateSessionList (line 75) | async function updateSessionList(updater: UpdaterFn<SessionMeta[]>) {
  function _getSessionById (line 92) | async function _getSessionById(id: string): Promise<Session | null> {
  function getSession (line 114) | async function getSession(sessionId: string) {
  function useSession (line 118) | function useSession(sessionId: string | null) {
  function _setSessionCache (line 126) | function _setSessionCache(sessionId: string, updated: Session | null) {
  function createSession (line 132) | async function createSession(newSession: Omit<Session, 'id'>, previousId...
  function updateSessionWithMessages (line 163) | async function updateSessionWithMessages(sessionId: string, updater: Upd...
  function updateSession (line 204) | async function updateSession(sessionId: string, updater: Updater<Omit<Se...
  function updateSessionCache (line 218) | async function updateSessionCache(sessionId: string, updater: Updater<Se...
  function deleteSession (line 236) | async function deleteSession(id: string) {
  function mergeDefaultSessionSettings (line 256) | function mergeDefaultSessionSettings(session: Session): SessionSettings {
  function useSessionSettings (line 270) | function useSessionSettings(sessionId: string | null) {
  function getSessionSettings (line 284) | async function getSessionSettings(sessionId: string) {
  function listMessages (line 296) | async function listMessages(sessionId?: string | null): Promise<Message[...
  function insertMessage (line 308) | async function insertMessage(sessionId: string, message: Message, previo...
  function updateMessageCache (line 362) | async function updateMessageCache(sessionId: string, messageId: string, ...
  function updateMessages (line 366) | async function updateMessages(sessionId: string, updater: Updater<Messag...
  function updateMessage (line 379) | async function updateMessage(
  function removeMessage (line 437) | async function removeMessage(sessionId: string, messageId: string) {
  function cleanupEmptyForkBranches (line 481) | function cleanupEmptyForkBranches(
  function recoverSessionList (line 575) | async function recoverSessionList() {

FILE: src/renderer/stores/imageGenerationActions.ts
  type GenerateImageParams (line 24) | interface GenerateImageParams {
  function isGenerating (line 34) | function isGenerating(): boolean {
  function createAndGenerate (line 38) | async function createAndGenerate(params: GenerateImageParams): Promise<s...
  function generateImages (line 66) | async function generateImages(recordId: string, params: GenerateImagePar...
  function cancelGeneration (line 147) | function cancelGeneration(): void {
  function loadRecord (line 158) | async function loadRecord(recordId: string): Promise<ImageGeneration | n...
  function clearCurrentRecord (line 166) | function clearCurrentRecord(): void {
  function retryGeneration (line 170) | async function retryGeneration(recordId: string): Promise<void> {

FILE: src/renderer/stores/imageGenerationStore.ts
  type ImageGenerationUIState (line 11) | interface ImageGenerationUIState {
  type ImageGenerationUIActions (line 17) | interface ImageGenerationUIActions {
  function getStorage (line 35) | function getStorage(): ImageGenerationStorage {
  function initializeStore (line 42) | async function initializeStore(): Promise<void> {
  constant IMAGE_GEN_QUERY_KEY (line 56) | const IMAGE_GEN_QUERY_KEY = 'image-generation'
  constant IMAGE_GEN_LIST_QUERY_KEY (line 57) | const IMAGE_GEN_LIST_QUERY_KEY = 'image-generation-list'
  function useImageGenerationHistory (line 59) | function useImageGenerationHistory(pageSize: number = 20) {
  function useImageGenerationRecord (line 75) | function useImageGenerationRecord(id: string | null) {
  function createRecord (line 87) | async function createRecord(
  function updateRecord (line 107) | async function updateRecord(id: string, updates: Partial<ImageGeneration...
  function addGeneratedImage (line 115) | async function addGeneratedImage(id: string, storageKey: string): Promis...
  function deleteRecord (line 127) | async function deleteRecord(id: string): Promise<void> {
  function useCurrentGeneratingId (line 142) | function useCurrentGeneratingId() {
  function useCurrentRecordId (line 146) | function useCurrentRecordId() {

FILE: src/renderer/stores/lastUsedModelStore.ts
  type State (line 5) | type State = {

FILE: src/renderer/stores/migration.test.ts
  type StorageData (line 43) | type StorageData = { [key: string]: string }
  function createOldStorageMock (line 63) | function createOldStorageMock(
  method default (line 224) | get default() {
  method getStorageType (line 261) | getStorageType() {
  method setStoreValue (line 264) | setStoreValue(key: string, value: unknown) {
  method getStoreValue (line 268) | getStoreValue(key: string) {
  method delStoreValue (line 272) | delStoreValue(key: string) {
  method getAllStoreValues (line 276) | getAllStoreValues() {
  method getAllStoreKeys (line 287) | getAllStoreKeys() {
  method setAllStoreValues (line 290) | async setAllStoreValues(data: { [key: string]: unknown }) {

FILE: src/renderer/stores/migration.ts
  function migrate (line 40) | async function migrate() {
  type MigrateStore (line 53) | type MigrateStore = {
  function doMigrateStorage (line 62) | async function doMigrateStorage(oldStorage: Storage) {
  function findNewestStorage (line 106) | async function findNewestStorage(oldStorages: Storage[]): Promise<[numbe...
  function migrateStorage (line 120) | async function migrateStorage() {
  function migrateOnData (line 179) | async function migrateOnData(dataStore: MigrateStore, canRelaunch = true) {
  function migrate_0_to_1 (line 222) | async function migrate_0_to_1(dataStore: MigrateStore) {
  function migrate_1_to_2 (line 233) | async function migrate_1_to_2(dataStore: MigrateStore) {
  function migrate_2_to_3 (line 249) | async function migrate_2_to_3(dataStore: MigrateStore) {
  function migrate_3_to_4 (line 269) | async function migrate_3_to_4(dataStore: MigrateStore) {
  function migrate_4_to_5 (line 280) | async function migrate_4_to_5(dataStore: MigrateStore): Promise<boolean> {
  function migrate_5_to_6 (line 299) | async function migrate_5_to_6(dataStore: MigrateStore) {
  function migrate_6_to_7 (line 312) | async function migrate_6_to_7(dataStore: MigrateStore): Promise<boolean> {
  function migrate_7_to_8 (line 332) | async function migrate_7_to_8(dataStore: MigrateStore): Promise<boolean> {
  function migrate_8_to_9 (line 351) | async function migrate_8_to_9(dataStore: MigrateStore): Promise<boolean> {
  function setInitProcess (line 395) | function setInitProcess(process: string) {
  function migrate_9_to_10 (line 401) | async function migrate_9_to_10(dataStore: MigrateStore): Promise<boolean> {
  function migrate_10_to_11 (line 696) | async function migrate_10_to_11(dataStore: MigrateStore) {
  function migrate_11_to_12 (line 716) | async function migrate_11_to_12(dataStore: MigrateStore) {
  function migrate_12_to_13 (line 721) | async function migrate_12_to_13(dataStore: MigrateStore) {
  function migrate_13_to_14 (line 725) | async function migrate_13_to_14(dataStore: MigrateStore) {

FILE: src/renderer/stores/premiumActions.ts
  function useAutoValidate (line 17) | function useAutoValidate(): boolean {
  function deactivate (line 69) | async function deactivate(clearLoginState = true) {
  function activate (line 111) | async function activate(licenseKey: string, method: 'login' | 'manual' =...

FILE: src/renderer/stores/scrollActions.ts
  function scrollToMessage (line 6) | async function scrollToMessage(
  function scrollToIndex (line 28) | function scrollToIndex(
  function scrollToTop (line 37) | function scrollToTop(behavior: 'auto' | 'smooth' = 'auto') {
  function scrollToBottom (line 42) | function scrollToBottom(behavior: 'auto' | 'smooth' = 'auto') {
  function startAutoScroll (line 56) | function startAutoScroll(
  function clearAutoScroll (line 77) | function clearAutoScroll(id?: string) {
  function getMessageListViewportHeight (line 88) | function getMessageListViewportHeight() {

FILE: src/renderer/stores/session/crud.ts
  function create (line 15) | async function create(newSession: Omit<Session, 'id'>) {
  function createEmpty (line 24) | async function createEmpty(type: 'chat' | 'picture') {
  function copySession (line 42) | async function copySession(
  function copyAndSwitchSession (line 98) | async function copyAndSwitchSession(source: SessionMeta) {
  function switchCurrentSession (line 106) | function switchCurrentSession(sessionId: string) {
  function reorderSessions (line 118) | async function reorderSessions(oldIndex: number, newIndex: number) {
  function switchToIndex (line 134) | async function switchToIndex(index: number) {
  function switchToNext (line 146) | async function switchToNext(reversed?: boolean) {
  function clearSessionList (line 172) | async function clearSessionList(keepNum: number) {
  function clearConversationList (line 192) | async function clearConversationList(keepNum: number) {
  function clear (line 199) | async function clear(sessionId: string) {

FILE: src/renderer/stores/session/export.ts
  function exportSessionChat (line 5) | async function exportSessionChat(sessionId: string, content: ExportChatS...

FILE: src/renderer/stores/session/forks.ts
  function findMessageLocation (line 9) | function findMessageLocation(session: Session, messageId: string): Messa...
  function createNewFork (line 29) | async function createNewFork(sessionId: string, forkMessageId: string) {
  function switchFork (line 48) | async function switchFork(sessionId: string, forkMessageId: string, dire...
  function deleteFork (line 67) | async function deleteFork(sessionId: string, forkMessageId: string) {
  function expandFork (line 87) | async function expandFork(sessionId: string, forkMessageId: string) {
  function buildSwitchForkPatch (line 105) | function buildSwitchForkPatch(
  function switchForkInMessages (line 161) | function switchForkInMessages(
  function buildCreateForkPatch (line 231) | function buildCreateForkPatch(session: Session, forkMessageId: string): ...
  function buildDeleteForkPatch (line 288) | function buildDeleteForkPatch(session: Session, forkMessageId: string): ...
  function buildExpandForkPatch (line 332) | function buildExpandForkPatch(session: Session, forkMessageId: string): ...
  type ForkTransformResult (line 358) | type ForkTransformResult = { messages: Message[]; forkEntry: MessageFork...
  type ForkTransform (line 359) | type ForkTransform = (messages: Message[], forkEntry: MessageForkEntry) ...
  function applyForkTransform (line 361) | function applyForkTransform(
  function computeNextMessageForksHash (line 415) | function computeNextMessageForksHash(

FILE: src/renderer/stores/session/generation.ts
  function getSessionWebBrowsing (line 44) | function getSessionWebBrowsing(sessionId: string, provider: string | und...
  function trackGenerateEvent (line 56) | function trackGenerateEvent(
  function createLoadingPictures (line 96) | function createLoadingPictures(n: number): MessagePicture[] {
  function generate (line 110) | async function generate(
  function generateMore (line 323) | async function generateMore(sessionId: string, msgId: string) {
  function generateMoreInNewFork (line 330) | async function generateMoreInNewFork(sessionId: string, msgId: string) {
  type GenerateMoreFn (line 335) | type GenerateMoreFn = (sessionId: string, msgId: string) => Promise<void>
  function regenerateInNewFork (line 337) | async function regenerateInNewFork(
  function genMessageContext (line 379) | async function genMessageContext(
  function getMessageThreadContext (line 560) | async function getMessageThreadContext(sessionId: string, messageId: str...

FILE: src/renderer/stores/session/messages.ts
  function getSessionWebBrowsing (line 26) | function getSessionWebBrowsing(sessionId: string, provider: string | und...
  function insertMessage (line 40) | async function insertMessage(sessionId: string, msg: Message) {
  function insertMessageAfter (line 56) | async function insertMessageAfter(sessionId: string, msg: Message, after...
  function modifyMessage (line 73) | async function modifyMessage(
  function removeMessage (line 103) | async function removeMessage(sessionId: string, messageId: string) {
  function submitNewUserMessage (line 111) | async function submitNewUserMessage(

FILE: src/renderer/stores/session/naming.ts
  function modifyNameAndThreadName (line 17) | async function modifyNameAndThreadName(sessionId: string, name: string) {
  function modifyThreadName (line 24) | async function modifyThreadName(sessionId: string, threadName: string) {
  function _generateName (line 31) | async function _generateName(sessionId: string, modifyName: (sessionId: ...
  function generateNameAndThreadName (line 80) | async function generateNameAndThreadName(sessionId: string) {
  function generateThreadName (line 87) | async function generateThreadName(sessionId: string) {
  function scheduleGenerateNameAndThreadName (line 94) | function scheduleGenerateNameAndThreadName(sessionId: string) {
  function scheduleGenerateThreadName (line 123) | function scheduleGenerateThreadName(sessionId: string) {

FILE: src/renderer/stores/session/threads.ts
  function editThread (line 16) | async function editThread(sessionId: string, threadId: string, newThread...
  function removeThread (line 42) | async function removeThread(sessionId: string, threadId: string) {
  function switchThread (line 61) | async function switchThread(sessionId: string, threadId: string) {
  function refreshContextAndCreateNewThread (line 93) | async function refreshContextAndCreateNewThread(sessionId: string) {
  function startNewThread (line 120) | async function startNewThread(sessionId: string) {
  function removeCurrentThread (line 132) | async function removeCurrentThread(sessionId: string) {
  function compressAndCreateThread (line 156) | async function compressAndCreateThread(sessionId: string, summary: strin...
  function moveThreadToConversations (line 210) | async function moveThreadToConversations(sessionId: string, threadId: st...
  function moveCurrentThreadToConversations (line 235) | async function moveCurrentThreadToConversations(sessionId: string) {

FILE: src/renderer/stores/session/types.ts
  type MessageForkEntry (line 3) | type MessageForkEntry = NonNullable<Session['messageForksHash']>[string]
  type MessageLocation (line 4) | type MessageLocation = { list: Message[]; index: number }

FILE: src/renderer/stores/sessionActions.test.ts
  function makeMessage (line 140) | function makeMessage(id: string, role: Message['role'] = 'user'): Message {
  function cloneSession (line 148) | function cloneSession(session: Session): Session {

FILE: src/renderer/stores/sessionHelpers.ts
  function getCurrentTokenizerType (line 34) | function getCurrentTokenizerType(): 'default' | 'deepseek' {
  function computePreviewMetadata (line 39) | function computePreviewMetadata(
  function getEffectiveDocumentParserConfig (line 79) | function getEffectiveDocumentParserConfig(): DocumentParserConfig {
  function parseFileWithLocalParser (line 87) | async function parseFileWithLocalParser(
  function parseFileWithChatboxAI (line 123) | async function parseFileWithChatboxAI(
  function parseFileWithMineruService (line 156) | async function parseFileWithMineruService(
  function preprocessFile (line 200) | async function preprocessFile(
  function preprocessLink (line 350) | async function preprocessLink(
  function constructUserMessage (line 481) | function constructUserMessage(
  function exportChat (line 538) | async function exportChat(session: Session, scope: ExportChatScope, form...
  function mergeSettings (line 559) | function mergeSettings(
  function initEmptyChatSession (line 581) | function initEmptyChatSession(): Omit<Session, 'id'> {
  function initEmptyPictureSession (line 606) | function initEmptyPictureSession(): Omit<Session, 'id'> {
  function getSessionMeta (line 619) | function getSessionMeta(session: SessionMeta) {
  function _searchSessions (line 623) | function _searchSessions(regexp: RegExp, s: Session) {
  function searchSessions (line 647) | async function searchSessions(searchInput: string, sessionId?: string, o...
  function getCurrentThreadHistoryHash (line 685) | function getCurrentThreadHistoryHash(s: Session) {
  function getAllMessageList (line 713) | function getAllMessageList(s: Session) {

FILE: src/renderer/stores/settingActions.ts
  function needEditSetting (line 6) | function needEditSetting() {
  function getLanguage (line 37) | function getLanguage() {
  function getProxy (line 41) | function getProxy() {
  function getLicenseKey (line 45) | function getLicenseKey() {
  function getLicenseDetail (line 49) | function getLicenseDetail() {
  function isPaid (line 53) | function isPaid() {
  function isPro (line 57) | function isPro() {
  function getRemoteConfig (line 61) | function getRemoteConfig() {
  function getAutoGenerateTitle (line 66) | function getAutoGenerateTitle() {
  function getExtensionSettings (line 70) | function getExtensionSettings() {

FILE: src/renderer/stores/settingsStore.ts
  function getPlatformDefaultDocumentParser (line 23) | function getPlatformDefaultDocumentParser(): DocumentParserConfig {
  type Action (line 27) | type Action = {
  function useSettingsStore (line 147) | function useSettingsStore<U>(selector: Parameters<typeof useStore<typeof...

FILE: src/renderer/stores/toastActions.ts
  function add (line 3) | function add(content: string, duration?: number) {
  function remove (line 7) | function remove(id: string) {

FILE: src/renderer/stores/uiStore.ts
  function useUIStore (line 217) | function useUIStore<U>(selector: Parameters<typeof useStore<typeof uiSto...

FILE: src/renderer/stores/updateQueue.test.ts
  type State (line 4) | type State = { value: number }

FILE: src/renderer/stores/updateQueue.ts
  type QueueItem (line 4) | type QueueItem<T extends object> = {
  class UpdateQueue (line 9) | class UpdateQueue<T extends object> {
    method constructor (line 14) | constructor(
    method set (line 19) | set(update: UpdaterFn<T>): Promise<T> {
    method flush (line 32) | async flush(): Promise<void> {
    method settleQueue (line 86) | private settleQueue(resolved: { u: QueueItem<T>; s: T }[], rejected: {...

FILE: src/renderer/test.tsx
  function Test (line 1) | function Test(): React.JSX.Element {

FILE: src/renderer/utils/base64.ts
  function decodeBase64 (line 3) | function decodeBase64(str: string) {

FILE: src/renderer/utils/image.ts
  function saveImage (line 4) | async function saveImage(category: string, picBase64: string) {

FILE: src/renderer/utils/index.ts
  function delay (line 1) | function delay(ms: number): Promise<void> {

FILE: src/renderer/utils/message.ts
  function getMessageText (line 5) | function getMessageText(message: Message, includeImagePlaceHolder = true...
  function migrateMessage (line 27) | function migrateMessage(
  function cloneMessage (line 76) | function cloneMessage(message: Message): Message {
  function isEmptyMessage (line 80) | function isEmptyMessage(message: Message): boolean {
  function countMessageWords (line 84) | function countMessageWords(message: Message): number {
  function mergeMessages (line 88) | function mergeMessages(a: Message, b: Message): Message {
  function fixMessageRoleSequence (line 96) | function fixMessageRoleSequence(messages: Message[]): Message[] {
  function sequenceMessages (line 134) | function sequenceMessages(msgs: Message[]): Message[] {

FILE: src/renderer/utils/mobile-request.ts
  function handleMobileRequest (line 5) | async function handleMobileRequest(

FILE: src/renderer/utils/model-tester.ts
  type TestResult (line 8) | type TestResult = {
  type ModelTestState (line 13) | type ModelTestState = {
  type TestModelOptions (line 20) | type TestModelOptions = {
  constant TEST_IMAGE_BASE64 (line 29) | const TEST_IMAGE_BASE64 =
  function testModelCapabilities (line 36) | async function testModelCapabilities(options: TestModelOptions): Promise...
  function testBasicRequest (line 75) | async function testBasicRequest(modelInstance: ModelInterface, state: Mo...
  function testVisionRequest (line 92) | async function testVisionRequest(modelInstance: ModelInterface, state: M...
  function testToolUseRequest (line 123) | async function testToolUseRequest(modelInstance: ModelInterface, state: ...

FILE: src/renderer/utils/modelLogo.tsx
  type IconProps (line 25) | interface IconProps {
  type IconComponent (line 31) | type IconComponent = ComponentType<IconProps>
  type ModelLogoConfig (line 33) | interface ModelLogoConfig {
  function getModelLogoConfig (line 110) | function getModelLogoConfig(modelId: string): ModelLogoConfig | undefined {
  function renderModelIcon (line 130) | function renderModelIcon(

FILE: src/renderer/utils/provider-config.ts
  type ProviderConfig (line 46) | type ProviderConfig = z.infer<typeof ProviderConfigSchema>
  function parseProviderConfig (line 48) | function parseProviderConfig(json: unknown): ProviderInfo | (ProviderSet...
  function parseProviderFromJson (line 85) | function parseProviderFromJson(
  function validateProviderConfig (line 100) | function validateProviderConfig(config: unknown): ProviderConfig | undef...

FILE: src/renderer/utils/request.ts
  type RequestOptions (line 6) | interface RequestOptions {
  function retryRequest (line 15) | async function retryRequest<T>(fn: () => Promise<T>, retry: number, url:...
  function buildHeaders (line 41) | function buildHeaders(options: RequestOptions, url: string): Headers {
  function doRequest (line 53) | async function doRequest(url: string, options: RequestOptions): Promise<...
  method post (line 81) | async post(
  method get (line 90) | async get(url: string, headers: Record<string, string>, options?: Partia...
  function fetchWithProxy (line 95) | async function fetchWithProxy(input: RequestInfo | URL, init?: RequestIn...

FILE: src/renderer/utils/session-utils.ts
  function migrateSession (line 5) | function migrateSession(session: Session): Session {
  function sortSessions (line 29) | function sortSessions(sessions: SessionMeta[]): SessionMeta[] {

FILE: src/renderer/utils/track.ts
  function trackEvent (line 1) | function trackEvent(event: string, props: Record<string, unknown> = {}) {

FILE: src/renderer/variables.ts
  constant CHATBOX_BUILD_TARGET (line 4) | const CHATBOX_BUILD_TARGET = (process.env.CHATBOX_BUILD_TARGET || 'unkno...
  constant CHATBOX_BUILD_PLATFORM (line 5) | const CHATBOX_BUILD_PLATFORM = (process.env.CHATBOX_BUILD_PLATFORM || 'u...
  constant USE_LOCAL_API (line 12) | const USE_LOCAL_API = process.env.USE_LOCAL_API || ''
  constant USE_BETA_API (line 13) | const USE_BETA_API = process.env.USE_BETA_API || ''
  constant USE_LOCAL_CHATBOX (line 16) | const USE_LOCAL_CHATBOX = process.env.USE_LOCAL_CHATBOX || ''
  constant USE_BETA_CHATBOX (line 17) | const USE_BETA_CHATBOX = process.env.USE_BETA_CHATBOX || ''
  constant NODE_ENV (line 19) | const NODE_ENV = process.env.NODE_ENV || 'development'

FILE: src/shared/constants.ts
  type ContextWindowSize (line 1) | enum ContextWindowSize {

FILE: src/shared/defaults.ts
  function settings (line 4) | function settings(): Settings {
  function newConfigs (line 144) | function newConfigs(): Config {
  function getDefaultPrompt (line 148) | function getDefaultPrompt() {
  function chatSessionSettings (line 152) | function chatSessionSettings(): SessionSettings {
  function pictureSessionSettings (line 160) | function pictureSessionSettings(): SessionSettings {

FILE: src/shared/electron-types.ts
  type ElectronIPC (line 1) | interface ElectronIPC {

FILE: src/shared/file-extensions.ts
  function isOfficeFilePath (line 3) | function isOfficeFilePath(filePath: string) {
  function isLegacyOfficeFilePath (line 9) | function isLegacyOfficeFilePath(filePath: string) {
  function isTextFilePath (line 108) | function isTextFilePath(filePath: string) {
  function isEpubFilePath (line 114) | function isEpubFilePath(filePath: string) {
  function isSupportedFile (line 143) | function isSupportedFile(fileName: string): boolean {
  function getUnsupportedFileType (line 157) | function getUnsupportedFileType(fileName: string): string | null {
  function getFileAcceptString (line 172) | function getFileAcceptString(): string {
  function getFileAcceptConfig (line 182) | function getFileAcceptConfig(): Record<string, string[]> {

FILE: src/shared/models/abstract-ai-sdk.ts
  constant RETRY_CONFIG (line 35) | const RETRY_CONFIG = {
  function is5xxError (line 41) | function is5xxError(error: unknown): boolean {
  type CallSettings (line 61) | interface CallSettings {
  type ToolExecutionResult (line 68) | interface ToolExecutionResult {
  method isSupportToolUse (line 79) | public isSupportToolUse() {
  method isSupportVision (line 82) | public isSupportVision() {
  method isSupportReasoning (line 85) | public isSupportReasoning() {
  method isSupportTextEmbedding (line 89) | static isSupportTextEmbedding() {
  method constructor (line 93) | public constructor(
  method getImageModel (line 106) | protected getImageModel(): ImageModel | null {
  method getTextEmbeddingModel (line 110) | protected getTextEmbeddingModel(options: CallChatCompletionOptions): Emb...
  method isSupportSystemMessage (line 118) | public isSupportSystemMessage() {
  method getCallSettings (line 122) | protected getCallSettings(_options: CallChatCompletionOptions): CallSett...
  method chat (line 126) | public async chat(messages: ModelMessage[], options: CallChatCompletionO...
  method paint (line 158) | public async paint(
  method addContentPart (line 192) | private addContentPart(
  method processToolCalls (line 210) | private processToolCalls<T extends ToolSet>(
  method processToolResults (line 231) | private processToolResults<T extends ToolSet>(
  method processToolErrors (line 247) | private processToolErrors<T extends ToolSet>(
  method updateToolResultPart (line 275) | private updateToolResultPart(toolResult: ToolExecutionResult, contentPar...
  method createOrUpdateContentPart (line 305) | private createOrUpdateContentPart<T extends MessageTextPart | MessageRea...
  method createOrUpdateTextPart (line 319) | private createOrUpdateTextPart(
  method createOrUpdateReasoningPart (line 334) | private createOrUpdateReasoningPart(
  method processImageFile (line 352) | private async processImageFile(
  method processStreamChunk (line 362) | private async processStreamChunk<T extends ToolSet>(
  method handleError (line 435) | private handleError(error: unknown, context: string = ''): never {
  method finalizeResult (line 456) | private finalizeResult(
  method handleStreamingCompletion (line 482) | private async handleStreamingCompletion<T extends ToolSet>(
  method _callChatCompletion (line 540) | private async _callChatCompletion<T extends ToolSet>(

FILE: src/shared/models/errors.ts
  class BaseError (line 1) | class BaseError extends Error {
    method constructor (line 3) | constructor(message: string) {
  class ApiError (line 10) | class ApiError extends BaseError {
    method constructor (line 13) | constructor(message: string, responseBody?: string) {
  class NetworkError (line 19) | class NetworkError extends BaseError {
    method constructor (line 22) | constructor(message: string, host: string) {
  class AIProviderNoImplementedPaintError (line 28) | class AIProviderNoImplementedPaintError extends BaseError {
    method constructor (line 30) | constructor(aiProvider: string) {
  class AIProviderNoImplementedChatError (line 35) | class AIProviderNoImplementedChatError extends BaseError {
    method constructor (line 37) | constructor(aiProvider: string) {
  class OCRError (line 42) | class OCRError extends BaseError {
    method constructor (line 46) | constructor(ocrProvider: string, cause: Error) {
  class ChatboxAIAPIError (line 58) | class ChatboxAIAPIError extends BaseError {
    method fromCodeName (line 279) | static fromCodeName(response: string, codeName: string) {
    method getDetail (line 288) | static getDetail(code: number) {
    method constructor (line 301) | constructor(message: string, detail: ChatboxAIAPIErrorDetail) {
  type ChatboxAIAPIErrorDetail (line 308) | interface ChatboxAIAPIErrorDetail {

FILE: src/shared/models/openai-compatible.ts
  type OpenAICompatibleSettings (line 10) | interface OpenAICompatibleSettings {
  method constructor (line 24) | constructor(
  method getCallSettings (line 31) | protected getCallSettings() {
  method isSupportTextEmbedding (line 39) | static isSupportTextEmbedding() {
  method isSupportToolUse (line 42) | isSupportToolUse(scope?: ToolUseScope) {
  method getProvider (line 53) | protected getProvider() {
  method getChatModel (line 62) | protected getChatModel() {
  method listModels (line 70) | public async listModels(): Promise<ProviderModelInfo[]> {
  type ListModelsResponse (line 85) | interface ListModelsResponse {
  function fetchRemoteModels (line 118) | async function fetchRemoteModels(

FILE: src/shared/models/rerank.ts
  function rerank (line 6) | async function rerank(

FILE: src/shared/models/types.ts
  type ModelInterface (line 12) | interface ModelInterface {
  type CallChatCompletionOptions (line 39) | interface CallChatCompletionOptions<Tools extends ToolSet = ToolSet> {
  type ResultChange (line 49) | interface ResultChange {
  type OnResultChangeWithCancel (line 58) | type OnResultChangeWithCancel = (data: ResultChange & { cancel?: () => v...
  type OnResultChange (line 59) | type OnResultChange = (data: ResultChange) => void
  type OnStatusChange (line 60) | type OnStatusChange = (status: MessageStatus | null) => void

FILE: src/shared/models/utils/fetch-proxy.ts
  function createFetchWithProxy (line 8) | function createFetchWithProxy(useProxy: boolean | undefined, dependencie...

FILE: src/shared/providers/definitions/models/azure.ts
  type Options (line 8) | interface Options {
  class AzureOpenAI (line 27) | class AzureOpenAI extends AbstractAISDKModel {
    method constructor (line 30) | constructor(public options: Options, dependencies: ModelDependencies) {
    method isSupportTextEmbedding (line 34) | static isSupportTextEmbedding() {
    method getProvider (line 38) | protected getProvider() {
    method getCallSettings (line 46) | protected getCallSettings() {
    method getChatModel (line 54) | protected getChatModel() {
    method getImageModel (line 62) | protected getImageModel() {

FILE: src/shared/providers/definitions/models/chatboxai.ts
  type Options (line 14) | interface Options {
  type Config (line 29) | interface Config {
  class ChatboxAI (line 35) | class ChatboxAI extends AbstractAISDKModel implements ModelInterface {
    method constructor (line 38) | constructor(
    method chatboxAIFetch (line 47) | private async chatboxAIFetch(url: RequestInfo | URL, options?: Request...
    method isSupportTextEmbedding (line 51) | static isSupportTextEmbedding() {
    method getProvider (line 55) | protected getProvider(options: CallChatCompletionOptions) {
    method getCallSettings (line 85) | protected getCallSettings() {
    method getChatModel (line 93) | getChatModel(options: CallChatCompletionOptions) {
    method paint (line 102) | public async paint(
    method paintWithGemini (line 118) | private async paintWithGemini(
    method getGoogleProvider (line 168) | private getGoogleProvider(): GoogleGenerativeAIProvider {
    method paintWithChatboxAPI (line 182) | private async paintWithChatboxAPI(
    method callImageGeneration (line 206) | private async callImageGeneration(
    method isSupportSystemMessage (line 241) | isSupportSystemMessage() {
    method isSupportToolUse (line 250) | public isSupportToolUse() {

FILE: src/shared/providers/definitions/models/chatglm.ts
  type Options (line 4) | interface Options extends OpenAICompatibleSettings {}
  class ChatGLM (line 6) | class ChatGLM extends OpenAICompatible {
    method constructor (line 10) | constructor(options: Omit<Options, 'apiHost'>, dependencies: ModelDepe...
    method listModels (line 30) | public async listModels() {

FILE: src/shared/providers/definitions/models/claude.ts
  type Options (line 9) | interface Options {
  class Claude (line 19) | class Claude extends AbstractAISDKModel {
    method constructor (line 22) | constructor(public options: Options, dependencies: ModelDependencies) {
    method getProvider (line 26) | protected getProvider() {
    method getChatModel (line 36) | protected getChatModel() {
    method getCallSettings (line 41) | protected getCallSettings(options: CallChatCompletionOptions): CallSet...
    method listModels (line 70) | public async listModels(): Promise<ProviderModelInfo[]> {

FILE: src/shared/providers/definitions/models/custom-claude.ts
  type Options (line 10) | interface Options {
  class CustomClaude (line 20) | class CustomClaude extends AbstractAISDKModel {
    method constructor (line 23) | constructor(
    method getProvider (line 33) | protected getProvider() {
    method getChatModel (line 43) | protected getChatModel(_options: CallChatCompletionOptions): LanguageM...
    method getCallSettings (line 48) | protected getCallSettings(options: CallChatCompletionOptions): CallSet...
    method listModels (line 73) | public async listModels(): Promise<ProviderModelInfo[]> {

FILE: src/shared/providers/definitions/models/custom-gemini.ts
  constant GEMINI_IMAGE_MODELS (line 11) | const GEMINI_IMAGE_MODELS = [
  type Options (line 18) | interface Options {
  class CustomGemini (line 28) | class CustomGemini extends AbstractAISDKModel {
    method constructor (line 31) | constructor(
    method isSupportSystemMessage (line 39) | isSupportSystemMessage() {
    method getProvider (line 48) | protected getProvider() {
    method getChatModel (line 55) | protected getChatModel(_options: CallChatCompletionOptions): LanguageM...
    method getCallSettings (line 60) | protected getCallSettings(options: CallChatCompletionOptions): CallSet...
    method paint (line 106) | public async paint(
    method listModels (line 152) | async listModels(): Promise<ProviderModelInfo[]> {

FILE: src/shared/providers/definitions/models/custom-openai-responses.ts
  type Options (line 11) | interface Options {
  type FetchFunction (line 23) | type FetchFunction = typeof globalThis.fetch
  class CustomOpenAIResponses (line 25) | class CustomOpenAIResponses extends AbstractAISDKModel {
    method constructor (line 28) | constructor(
    method getCallSettings (line 37) | protected getCallSettings() {
    method isSupportTextEmbedding (line 46) | static isSupportTextEmbedding() {
    method getProvider (line 50) | protected getProvider(_options: CallChatCompletionOptions, fetchFuncti...
    method getChatModel (line 68) | protected getChatModel(options: CallChatCompletionOptions) {
    method listModels (line 79) | public listModels() {
    method getImageModel (line 90) | protected getImageModel() {

FILE: src/shared/providers/definitions/models/custom-openai.ts
  type Options (line 11) | interface Options {
  type FetchFunction (line 23) | type FetchFunction = typeof globalThis.fetch
  class CustomOpenAI (line 25) | class CustomOpenAI extends AbstractAISDKModel {
    method constructor (line 28) | constructor(public options: Options, dependencies: ModelDependencies) {
    method getCallSettings (line 34) | protected getCallSettings() {
    method isSupportTextEmbedding (line 43) | static isSupportTextEmbedding() {
    method getProvider (line 47) | protected getProvider(_options: CallChatCompletionOptions, fetchFuncti...
    method getChatModel (line 66) | protected getChatModel(options: CallChatCompletionOptions) {
    method listModels (line 77) | public listModels() {
    method getImageModel (line 88) | protected getImageModel() {

FILE: src/shared/providers/definitions/models/deepseek.ts
  type Options (line 9) | interface Options {
  class DeepSeek (line 18) | class DeepSeek extends AbstractAISDKModel {
    method constructor (line 21) | constructor(
    method getProvider (line 28) | protected getProvider() {
    method getChatModel (line 34) | protected getChatModel(_options: CallChatCompletionOptions): LanguageM...
    method getCallSettings (line 39) | protected getCallSettings(_options: CallChatCompletionOptions): CallSe...
    method isSupportToolUse (line 65) | isSupportToolUse(scope?: ToolUseScope) {
    method listModels (line 76) | async listModels(): Promise<ProviderModelInfo[]> {

FILE: src/shared/providers/definitions/models/gemini.ts
  constant GEMINI_IMAGE_MODELS (line 11) | const GEMINI_IMAGE_MODELS = [
  type Options (line 18) | interface Options {
  class Gemini (line 28) | class Gemini extends AbstractAISDKModel {
    method constructor (line 31) | constructor(public options: Options, dependencies: ModelDependencies) {
    method isSupportSystemMessage (line 36) | isSupportSystemMessage() {
    method getProvider (line 45) | protected getProvider() {
    method getChatModel (line 52) | protected getChatModel(_options: CallChatCompletionOptions): LanguageM...
    method getCallSettings (line 58) | protected getCallSettings(options: CallChatCompletionOptions): CallSet...
    method paint (line 100) | public async paint(
    method listModels (line 146) | async listModels(): Promise<ProviderModelInfo[]> {

FILE: src/shared/providers/definitions/models/groq.ts
  type Options (line 4) | interface Options extends OpenAICompatibleSettings {}
  class Groq (line 6) | class Groq extends OpenAICompatible {
    method constructor (line 9) | constructor(options: Omit<Options, 'apiHost'>, dependencies: ModelDepe...

FILE: src/shared/providers/definitions/models/lmstudio.ts
  type Options (line 5) | interface Options extends OpenAICompatibleSettings {}
  class LMStudio (line 7) | class LMStudio extends OpenAICompatible {
    method constructor (line 11) | constructor(options: Omit<Options, 'apiKey'>, dependencies: ModelDepen...

FILE: src/shared/providers/definitions/models/mistral-ai.ts
  type Options (line 8) | interface Options {
  class MistralAI (line 17) | class MistralAI extends AbstractAISDKModel {
    method constructor (line 20) | constructor(
    method getCallSettings (line 27) | protected getCallSettings() {
    method isSupportTextEmbedding (line 41) | static isSupportTextEmbedding() {
    method getProvider (line 45) | protected getProvider() {
    method getChatModel (line 57) | protected getChatModel() {
    method listModels (line 65) | public async listModels(): Promise<ProviderModelInfo[]> {

FILE: src/shared/providers/definitions/models/ollama.ts
  type OllamaOptions (line 41) | interface OllamaOptions extends OpenAICompatibleSettings {
  class Ollama (line 45) | class Ollama extends OpenAICompatible {
    method constructor (line 49) | constructor(options: Omit<OllamaOptions, 'apiKey' | 'apiHost'>, depend...
    method isSupportToolUse (line 70) | public isSupportToolUse(): boolean {
    method isSupportVision (line 73) | public isSupportVision(): boolean {

FILE: src/shared/providers/definitions/models/openai-responses.ts
  type Options (line 11) | interface Options {
  type FetchFunction (line 23) | type FetchFunction = typeof globalThis.fetch
  class OpenAIResponses (line 25) | class OpenAIResponses extends AbstractAISDKModel {
    method constructor (line 28) | constructor(
    method getCallSettings (line 37) | protected getCallSettings() {
    method isSupportTextEmbedding (line 46) | static isSupportTextEmbedding() {
    method getProvider (line 50) | protected getProvider(_options: CallChatCompletionOptions, fetchFuncti...
    method getChatModel (line 68) | protected getChatModel(options: CallChatCompletionOptions) {
    method listModels (line 79) | public listModels() {
    method getImageModel (line 90) | protected getImageModel() {

FILE: src/shared/providers/definitions/models/openai.ts
  type Options (line 11) | interface Options {
  class OpenAI (line 24) | class OpenAI extends AbstractAISDKModel {
    method constructor (line 28) | constructor(options: Options, dependencies: ModelDependencies) {
    method isSupportTextEmbedding (line 34) | static isSupportTextEmbedding() {
    method getProvider (line 38) | protected getProvider() {
    method getChatModel (line 52) | protected getChatModel() {
    method getImageModel (line 60) | protected getImageModel(modelId?: string) {
    method getCallSettings (line 66) | protected getCallSettings(options: CallChatCompletionOptions) {
    method listModels (line 83) | public listModels() {

FILE: src/shared/providers/definitions/models/openrouter.ts
  type Options (line 8) | interface Options {
  class OpenRouter (line 17) | class OpenRouter extends AbstractAISDKModel {
    method constructor (line 20) | constructor(
    method getCallSettings (line 27) | protected getCallSettings() {
    method getProvider (line 35) | protected getProvider() {
    method getChatModel (line 45) | protected getChatModel() {
    method listModels (line 53) | public async listModels(): Promise<ProviderModelInfo[]> {

FILE: src/shared/providers/definitions/models/perplexity.ts
  type Options (line 7) | interface Options {
  class Perplexity (line 16) | class Perplexity extends AbstractAISDKModel {
    method constructor (line 19) | constructor(public options: Options, dependencies: ModelDependencies) {
    method getProvider (line 23) | protected getProvider() {
    method getChatModel (line 29) | protected getChatModel() {

FILE: src/shared/providers/definitions/models/siliconflow.ts
  type Options (line 5) | interface Options extends OpenAICompatibleSettings {}
  class SiliconFlow (line 7) | class SiliconFlow extends OpenAICompatible {
    method constructor (line 10) | constructor(options: Omit<Options, 'apiHost'>, dependencies: ModelDepe...
    method isSupportToolUse (line 30) | isSupportToolUse(scope?: ToolUseScope) {

FILE: src/shared/providers/definitions/models/volcengine.ts
  type FetchFunction (line 6) | type FetchFunction = typeof globalThis.fetch
  type Options (line 8) | interface Options {
  class VolcEngine (line 20) | class VolcEngine extends AbstractAISDKModel {
    method constructor (line 23) | constructor(public options: Options, dependencies: ModelDependencies) {
    method getCallSettings (line 27) | protected getCallSettings() {
    method isSupportTextEmbedding (line 35) | static isSupportTextEmbedding() {
    method getProvider (line 39) | protected getProvider() {
    method getChatModel (line 49) | protected getChatModel() {
    method isSupportToolUse (line 54) | isSupportToolUse(scope?: ToolUseScope) {

FILE: src/shared/providers/definitions/models/xai.ts
  type Options (line 4) | interface Options extends OpenAICompatibleSettings {}
  class XAI (line 6) | class XAI extends OpenAICompatible {
    method constructor (line 9) | constructor(options: Omit<Options, 'apiHost'>, dependencies: ModelDepe...

FILE: src/shared/providers/index.ts
  function getProviderSettings (line 48) | function getProviderSettings(setting: SessionSettings, globalSettings: S...
  function getModelConfig (line 75) | function getModelConfig(settings: SessionSettings, globalSettings: Setti...
  function getModel (line 103) | function getModel(

FILE: src/shared/providers/registry.ts
  function defineProvider (line 6) | function defineProvider(definition: ProviderDefinitionInput): ProviderDe...
  function getProviderDefinition (line 14) | function getProviderDefinition(id: string): ProviderDefinition | undefin...
  function getAllProviders (line 18) | function getAllProviders(): ProviderDefinition[] {
  function hasProvider (line 22) | function hasProvider(id: string): boolean {
  function clearProviderRegistry (line 26) | function clearProviderRegistry(): void {
  function getSystemProviders (line 30) | function getSystemProviders(): BuiltinProviderBaseInfo[] {

FILE: src/shared/providers/types.ts
  type CreateModelConfig (line 10) | interface CreateModelConfig {
  type ProviderDefinition (line 33) | interface ProviderDefinition {
  type ProviderDefinitionInput (line 71) | type ProviderDefinitionInput = Omit<ProviderDefinition, 'id'> & {

FILE: src/shared/providers/utils.ts
  function createCustomProviderModel (line 10) | function createCustomProviderModel(

FILE: src/shared/request/chatboxai_pool.ts
  constant API_ORIGIN (line 5) | let API_ORIGIN = 'https://api.chatboxai.app'
  constant POOL (line 7) | let POOL = [
  function isChatboxAPI (line 14) | function isChatboxAPI(input: RequestInfo | URL) {
  function getChatboxAPIOrigin (line 19) | function getChatboxAPIOrigin() {
  function testApiOrigins (line 30) | async function testApiOrigins() {

FILE: src/shared/request/request.ts
  type PlatformInfo (line 5) | interface PlatformInfo {
  function createAfetch (line 12) | function createAfetch(platformInfo: PlatformInfo) {
  function uploadFile (line 75) | async function uploadFile(file: File, url: string) {
  type AuthTokens (line 101) | interface AuthTokens {
  type AuthenticatedAfetchConfig (line 106) | interface AuthenticatedAfetchConfig {
  function createAuthenticatedAfetch (line 113) | function createAuthenticatedAfetch(config: AuthenticatedAfetchConfig) {

FILE: src/shared/types.ts
  type Updater (line 13) | type Updater<T extends object> = Partial<T> | UpdaterFn<T>
  type UpdaterFn (line 14) | type UpdaterFn<T extends object> = (data: T | null | undefined) => T
  type MessageTokenCountResult (line 16) | type MessageTokenCountResult = { id: string; tokenCountMap: TokenCountMa...
  type SettingWindowTab (line 18) | type SettingWindowTab = 'ai' | 'display' | 'chat' | 'advanced' | 'extens...
  type ExportChatScope (line 20) | type ExportChatScope = 'all_threads' | 'current_thread'
  type ExportChatFormat (line 22) | type ExportChatFormat = 'Markdown' | 'TXT' | 'HTML'
  function isChatSession (line 24) | function isChatSession(session: Session) {
  function isPictureSession (line 27) | function isPictureSession(session: Session) {
  function createMessage (line 31) | function createMessage(role: MessageRole = MessageRoleEnum.User, content...
  type Language (line 40) | type Language =
  type Config (line 56) | interface Config {
  type SponsorAd (line 60) | interface SponsorAd {
  type SponsorAboutBanner (line 65) | interface SponsorAboutBanner {
  type CopilotDetail (line 74) | interface CopilotDetail {
  type Toast (line 86) | interface Toast {
  type RemoteConfig (line 92) | interface RemoteConfig {
  type ChatboxAIModel (line 103) | type ChatboxAIModel = 'chatboxai-3.5' | 'chatboxai-4' | string
  function copyMessage (line 105) | function copyMessage(source: Message): Message {
  function copyMessagesWithMapping (line 113) | function copyMessagesWithMapping(messages: Message[]): {
  function copyThreads (line 126) | function copyThreads(source?: SessionThread[], idMapping?: Map<string, s...
  type KnowledgeBaseProviderMode (line 167) | type KnowledgeBaseProviderMode = 'chatbox-ai' | 'custom'
  type KnowledgeBase (line 169) | interface KnowledgeBase {
  type KnowledgeBaseFile (line 180) | interface KnowledgeBaseFile {
  type KnowledgeBaseSearchResult (line 196) | interface KnowledgeBaseSearchResult {
  type FileMeta (line 206) | type FileMeta = {

FILE: src/shared/types/adapters.ts
  type ApiRequestOptions (line 3) | interface ApiRequestOptions {
  type StorageAdapter (line 13) | interface StorageAdapter {
  type RequestAdapter (line 18) | interface RequestAdapter {
  type ModelDependencies (line 27) | interface ModelDependencies {

FILE: src/shared/types/image-generation.ts
  type ImageGenerationStatus (line 5) | type ImageGenerationStatus = z.infer<typeof ImageGenerationStatusSchema>
  type ImageGenerationModel (line 12) | type ImageGenerationModel = z.infer<typeof ImageGenerationModelSchema>
  type ImageGeneration (line 29) | type ImageGeneration = z.infer<typeof ImageGenerationSchema>
  type ImageGenerationPage (line 32) | interface ImageGenerationPage {

FILE: src/shared/types/mcp.ts
  type MCPServerConfig (line 1) | type MCPServerConfig<TransportConfig = MCPTransportConfig> = {
  type MCPTransportConfig (line 8) | type MCPTransportConfig =
  type MCPServerStatus (line 21) | type MCPServerStatus = {

FILE: src/shared/types/provider.ts
  type ModelProviderEnum (line 4) | enum ModelProviderEnum {
  type ModelProviderType (line 25) | enum ModelProviderType {

FILE: src/shared/types/session.ts
  type TokenCacheKey (line 11) | type TokenCacheKey = z.infer<typeof TokenCacheKeySchema>
  constant TOKEN_CACHE_KEYS (line 14) | const TOKEN_CACHE_KEYS = TokenCacheKeySchema.enum
  type TokenCountMap (line 19) | type TokenCountMap = z.infer<typeof TokenCountMapSchema>
  type TokenCalculatedAt (line 31) | type TokenCalculatedAt = z.infer<typeof TokenCalculatedAtSchema>
  type MessageRole (line 84) | type MessageRole = (typeof MessageRoleEnum)[keyof typeof MessageRoleEnum]
  type SearchResultItem (line 286) | type SearchResultItem = z.infer<typeof SearchResultItemSchema>
  type SearchResult (line 287) | type SearchResult = z.infer<typeof SearchResultSchema>
  type MessageFile (line 288) | type MessageFile = z.infer<typeof MessageFileSchema>
  type MessageLink (line 289) | type MessageLink = z.infer<typeof MessageLinkSchema>
  type MessagePicture (line 290) | type MessagePicture = z.infer<typeof MessagePictureSchema>
  type MessageTextPart (line 291) | type MessageTextPart = z.infer<typeof MessageTextPartSchema>
  type MessageImagePart (line 292) | type MessageImagePart = z.infer<typeof MessageImagePartSchema>
  type MessageInfoPart (line 293) | type MessageInfoPart = z.infer<typeof MessageInfoPartSchema>
  type MessageReasoningPart (line 294) | type MessageReasoningPart = z.infer<typeof MessageReasoningPartSchema>
  type MessageToolCallPart (line 295) | type MessageToolCallPart<Args = unknown, Result = unknown> = z.infer<typ...
  type MessageContentParts (line 299) | type MessageContentParts = z.infer<typeof MessageContentPartsSchema>
  type StreamTextResult (line 300) | type StreamTextResult = z.infer<typeof StreamTextResultSchema>
  type ToolUseScope (line 301) | type ToolUseScope = z.infer<typeof ToolUseScopeSchema>
  type ModelProvider (line 302) | type ModelProvider = z.infer<typeof ModelProviderSchema>
  type MessageStatus (line 303) | type MessageStatus = z.infer<typeof MessageStatusSchema>
  type Message (line 304) | type Message = z.infer<typeof MessageSchema>
  type SessionType (line 305) | type SessionType = z.infer<typeof SessionTypeSchema>
  type CompactionPoint (line 306) | type CompactionPoint = z.infer<typeof CompactionPointSchema>
  type Session (line 307) | type Session = z.infer<typeof SessionSchema>
  type SessionMeta (line 308) | type SessionMeta = z.infer<typeof SessionMetaSchema>
  type SessionThread (line 309) | type SessionThread = z.infer<typeof SessionThreadSchema>
  type SessionThreadBrief (line 310) | type SessionThreadBrief = z.infer<typeof SessionThreadBriefSchema>

FILE: src/shared/types/settings.ts
  type DocumentParserType (line 16) | type DocumentParserType = 'none' | 'local' | 'chatbox-ai' | 'mineru'
  type DocumentParserConfig (line 27) | type DocumentParserConfig = z.infer<typeof DocumentParserConfigSchema>
  constant DEFAULT_DOCUMENT_PARSER_CONFIG (line 29) | const DEFAULT_DOCUMENT_PARSER_CONFIG: DocumentParserConfig = {
  type Theme (line 259) | enum Theme {
  type ProviderInfo (line 383) | type ProviderInfo = (ProviderBaseInfo | CustomProviderBaseInfo) & Provid...
  type SessionSettings (line 385) | type SessionSettings = z.infer<typeof SessionSettingsSchema>
  type Settings (line 386) | type Settings = z.infer<typeof SettingsSchema>
  type ProviderModelInfo (line 387) | type ProviderModelInfo = z.infer<typeof ProviderModelInfoSchema>
  type ProviderBaseInfo (line 388) | type ProviderBaseInfo = z.infer<typeof ProviderBaseInfoSchema>
  type ProviderSettings (line 389) | type ProviderSettings = z.infer<typeof ProviderSettingsSchema>
  type BuiltinProviderBaseInfo (line 390) | type BuiltinProviderBaseInfo = z.infer<typeof BuiltinProviderBaseInfoSch...
  type CustomProviderBaseInfo (line 391) | type CustomProviderBaseInfo = z.infer<typeof CustomProviderBaseInfoSchema>
  type ClaudeParams (line 392) | type ClaudeParams = z.infer<typeof ClaudeParamsSchema>
  type OpenAIParams (line 393) | type OpenAIParams = z.infer<typeof OpenAIParamsSchema>
  type GoogleParams (line 394) | type GoogleParams = z.infer<typeof GoogleParamsSchema>
  type ProviderOptions (line 395) | type ProviderOptions = z.infer<typeof ProviderOptionsSchema>
  type GlobalSessionSettings (line 396) | type GlobalSessionSettings = z.infer<typeof GlobalSessionSettingsSchema>
  type ChatboxAILicenseDetail (line 397) | type ChatboxAILicenseDetail = z.infer<typeof ChatboxAILicenseDetailSchema>
  type UnifiedTokenUsageDetail (line 398) | type UnifiedTokenUsageDetail = z.infer<typeof UnifiedTokenUsageDetailSch...
  type ShortcutSendValue (line 399) | type ShortcutSendValue = z.infer<typeof ShortcutSendValueSchema>
  type ShortcutToggleWindowValue (line 400) | type ShortcutToggleWindowValue = z.infer<typeof ShortcutToggleWindowValu...
  type ShortcutName (line 401) | type ShortcutName = keyof ShortcutSetting
  type ShortcutSetting (line 402) | type ShortcutSetting = z.infer<typeof ShortcutSettingSchema>
  type ExtensionSettings (line 403) | type ExtensionSettings = z.infer<typeof ExtensionSettingsSchema>
  type MCPTransportConfig (line 404) | type MCPTransportConfig = z.infer<typeof MCPTransportConfigSchema>
  type MCPServerConfig (line 405) | type MCPServerConfig = z.infer<typeof MCPServerConfigSchema>
  type MCPSettings (line 406) | type MCPSettings = z.infer<typeof MCPSettingsSchema>

FILE: src/shared/utils/cache.ts
  type CacheItem (line 2) | interface CacheItem<T> {
  class CrossPlatformStorage (line 14) | class CrossPlatformStorage {
    method constructor (line 18) | constructor(name: string) {
    method getItem (line 22) | async getItem(key: string): Promise<string | null> {
    method setItem (line 39) | async setItem(key: string, value: string): Promise<void> {
    method removeItem (line 58) | async removeItem(key: string): Promise<void> {
  function cacheWithStorage (line 80) | async function cacheWithStorage<T>(
  function cache (line 145) | async function cache<T>(

FILE: src/shared/utils/index.ts
  function formatNumber (line 7) | function formatNumber(num: number, unit = ''): string {
  function formatFileSize (line 20) | function formatFileSize(bytes: number): string {

FILE: src/shared/utils/json_utils.ts
  function parseJsonOrEmpty (line 6) | function parseJsonOrEmpty(json: string): any {

FILE: src/shared/utils/knowledge-base-model-parser.ts
  function parseKnowledgeBaseModelString (line 5) | function parseKnowledgeBaseModelString(modelString: string): { providerI...

FILE: src/shared/utils/llm_utils.ts
  function normalizeOpenAIApiHostAndPath (line 3) | function normalizeOpenAIApiHostAndPath(
  function normalizeOpenAIResponsesHostAndPath (line 69) | function normalizeOpenAIResponsesHostAndPath(options: { apiHost?: string...
  function normalizeClaudeHost (line 84) | function normalizeClaudeHost(apiHost: string) {
  function normalizeGeminiHost (line 98) | function normalizeGeminiHost(apiHost: string) {
  function normalizeAzureEndpoint (line 110) | function normalizeAzureEndpoint(endpoint: string) {
  function isOpenAICompatible (line 123) | function isOpenAICompatible(providerId: string, _modelId: string) {

FILE: src/shared/utils/message.ts
  function getMessageText (line 5) | function getMessageText(message: Message, includeImagePlaceHolder = true...
  function migrateMessage (line 27) | function migrateMessage(
  function cloneMessage (line 76) | function cloneMessage(message: Message): Message {
  function isEmptyMessage (line 80) | function isEmptyMessage(message: Message): boolean {
  function countMessageWords (line 84) | function countMessageWords(message: Message): number {
  function mergeMessages (line 88) | function mergeMessages(a: Message, b: Message): Message {
  function fixMessageRoleSequence (line 96) | function fixMessageRoleSequence(messages: Message[]): Message[] {
  function sequenceMessages (line 134) | function sequenceMessages(msgs: Message[]): Message[] {

FILE: src/shared/utils/model_settings.ts
  function getModelSettings (line 3) | function getModelSettings(globalSettings: Settings, providerId: string, ...

FILE: src/shared/utils/network_utils.ts
  function isLocalHost (line 6) | function isLocalHost(url: string): boolean {

FILE: src/shared/utils/sentry_adapter.ts
  type SentryAdapter (line 5) | interface SentryAdapter {
  type SentryScope (line 10) | interface SentryScope {

FILE: src/shared/utils/word_count.ts
  function countWord (line 18) | function countWord(data: string): number {

FILE: test/cases/file-conversation/sample.ts
  type User (line 9) | interface User {
  type UserList (line 18) | type UserList = User[];
  type OrderStatus (line 21) | enum OrderStatus {
  class Repository (line 30) | class Repository<T extends { id: string }> {
    method add (line 33) | add(item: T): void {
    method get (line 37) | get(id: string): T | undefined {
    method getAll (line 41) | getAll(): T[] {
    method delete (line 45) | delete(id: string): boolean {
    method count (line 49) | count(): number {
  function fetchUserData (line 55) | async function fetchUserData(userId: string): Promise<User> {
  function createLogger (line 70) | function createLogger(prefix: string) {
  function calculateTotalPrice (line 77) | function calculateTotalPrice(items: { price: number; quantity: number }[...
  function formatCurrency (line 81) | function formatCurrency(amount: number, currency: string = 'USD'): string {

FILE: test/integration/context-management/context-management.test.ts
  function createTestMessage (line 38) | function createTestMessage(
  function createToolCallPart (line 57) | function createToolCallPart(toolName: string, args: unknown = {}): Messa...
  function createCompactionPoint (line 68) | function createCompactionPoint(
  function createTestSession (line 80) | function createTestSession(
  function createTestThread (line 99) | function createTestThread(

FILE: test/integration/file-conversation/file-conversation.test.ts
  constant LICENSE_KEY (line 21) | const LICENSE_KEY = process.env.CHATBOX_LICENSE_KEY || ''
  constant TEST_OUTPUT_DIR (line 22) | const TEST_OUTPUT_DIR = path.join(__dirname, '../../../test/output/file-...
  constant TEST_CASES_DIR (line 23) | const TEST_CASES_DIR = path.join(__dirname, '../../../test/cases/file-co...
  constant TEST_TIMEOUT (line 24) | const TEST_TIMEOUT = Number(process.env.CHATBOX_TEST_TIMEOUT) || 120000
  constant DEFAULT_TEST_MODELS (line 28) | const DEFAULT_TEST_MODELS = [
  function getTestModels (line 39) | function getTestModels() {
  constant TEST_MODELS (line 48) | const TEST_MODELS = getTestModels()
  constant DEFAULT_SYSTEM_PROMPT (line 50) | const DEFAULT_SYSTEM_PROMPT = `You are a helpful AI assistant that can r...
  type TestCase (line 55) | interface TestCase {
  type ModelTestResult (line 64) | interface ModelTestResult {
  type TestReport (line 84) | interface TestReport {
  function loadTestFile (line 97) | function loadTestFile(fileName: string, fileType: string = 'text/plain')...
  function getFileType (line 104) | function getFileType(fileName: string): string {
  constant TEST_CASES (line 116) | const TEST_CASES: TestCase[] = [
  function getResponseText (line 196) | function getResponseText(result: TestResult): string {
  function generateMarkdownReport (line 205) | function generateMarkdownReport(report: TestReport): string {
  function generateHtmlReport (line 285) | function generateHtmlReport(report: TestReport): string {

FILE: test/integration/file-conversation/setup.ts
  function getTestPlatform (line 79) | function getTestPlatform(): TestPlatform {
  function resetTestPlatform (line 83) | function resetTestPlatform(): void {

FILE: test/integration/file-conversation/test-harness.ts
  type TestFile (line 23) | interface TestFile {
  type TestMessage (line 34) | interface TestMessage {
  type FileConversationTestCase (line 45) | interface FileConversationTestCase {
  type TestResult (line 60) | interface TestResult {
  function createUserMessageWithFiles (line 90) | function createUserMessageWithFiles(content: string, files: TestFile[]):...
  class FileConversationTestContext (line 108) | class FileConversationTestContext {
    method constructor (line 112) | constructor() {
    method createModelDependencies (line 121) | async createModelDependencies(): Promise<ModelDependencies> {
    method loadFiles (line 128) | loadFiles(files: TestFile[]): void {
    method createMessage (line 137) | createMessage(msg: TestMessage): Message {
    method clear (line 160) | clear(): void {
  type TestRunnerOptions (line 168) | interface TestRunnerOptions {
  class FileConversationTestRunner (line 181) | class FileConversationTestRunner {
    method constructor (line 186) | constructor(options: TestRunnerOptions) {
    method runTest (line 199) | async runTest(testCase: FileConversationTestCase): Promise<TestResult> {
    method runTests (line 357) | async runTests(testCases: FileConversationTestCase[]): Promise<TestRes...
    method printSummary (line 379) | private printSummary(): void {
    method exportResults (line 403) | async exportResults(): Promise<void> {
    method buildGlobalSettings (line 440) | private buildGlobalSettings(): Settings {
    method buildSessionSettings (line 454) | private buildSessionSettings(overrides?: Partial<SessionSettings>): Se...
    method replacePlatformForTest (line 471) | private async replacePlatformForTest(): Promise<any> {
    method restorePlatform (line 486) | private async restorePlatform(original: any): Promise<void> {
  type RunConversationTestOptions (line 493) | interface RunConversationTestOptions {
  function runConversationTest (line 520) | async function runConversationTest(options: RunConversationTestOptions):...
  function createTestFile (line 692) | function createTestFile(fileName: string, content: string, fileType: str...
  function loadTestFileFromDisk (line 704) | function loadTestFileFromDisk(filePath: string, fileType?: string): Test...
  function detectFileType (line 715) | function detectFileType(fileName: string): string {

FILE: test/integration/mocks/model-dependencies.ts
  function createMockModelDependencies (line 7) | async function createMockModelDependencies(

FILE: test/integration/mocks/sentry.ts
  class MockSentryAdapter (line 2) | class MockSentryAdapter implements SentryAdapter {
    method captureException (line 5) | captureException(error: any): void {
    method withScope (line 10) | withScope(callback: (scope: SentryScope) => void): void {
    method getErrors (line 18) | getErrors(): any[] {
    method clear (line 22) | clear(): void {

FILE: test/integration/model-provider/model-provider.test.ts
  function keyEnv (line 28) | function keyEnv(providerName: string): string {
  constant PROVIDER_TEST_MODELS (line 32) | const PROVIDER_TEST_MODELS: Record<ModelProvider, ProviderModelInfo[]> = {
  function runProviderTest (line 77) | function runProviderTest(providerName: ModelProviderEnum) {
Condensed preview — 617 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,372K chars).
[
  {
    "path": ".erb/.vscode/settings.json",
    "chars": 151,
    "preview": "{\n  \"cssVariables.lookupFiles\": [\n    \"**/*.css\",\n    \"**/*.scss\",\n    \"**/*.sass\",\n    \"**/*.less\",\n    \"node_modules/@"
  },
  {
    "path": ".erb/configs/.eslintrc",
    "chars": 130,
    "preview": "{\n    \"rules\": {\n        \"no-console\": \"off\",\n        \"global-require\": \"off\",\n        \"import/no-dynamic-require\": \"off"
  },
  {
    "path": ".erb/configs/webpack.config.base.ts",
    "chars": 2271,
    "preview": "/**\n * Base webpack config used across other specific configs\n */\n\nimport webpack from 'webpack'\nimport TsconfigPathsPlu"
  },
  {
    "path": ".erb/configs/webpack.config.eslint.ts",
    "chars": 134,
    "preview": "/* eslint import/no-unresolved: off, import/no-self-import: off */\n\nmodule.exports = require('./webpack.config.renderer."
  },
  {
    "path": ".erb/configs/webpack.config.main.prod.ts",
    "chars": 2371,
    "preview": "/**\n * Webpack config for production electron main process\n */\n\nimport path from 'path'\nimport TerserPlugin from 'terser"
  },
  {
    "path": ".erb/configs/webpack.config.preload.dev.ts",
    "chars": 2090,
    "preview": "import path from 'path'\nimport webpack from 'webpack'\nimport { merge } from 'webpack-merge'\nimport { BundleAnalyzerPlugi"
  },
  {
    "path": ".erb/configs/webpack.config.renderer.dev.dll.ts",
    "chars": 2015,
    "preview": "/**\n * Builds the DLL for development electron renderer process\n */\n\nimport webpack from 'webpack'\nimport path from 'pat"
  },
  {
    "path": ".erb/configs/webpack.config.renderer.dev.ts",
    "chars": 6381,
    "preview": "import 'webpack-dev-server'\nimport ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'\nimport { TanSta"
  },
  {
    "path": ".erb/configs/webpack.config.renderer.prod.ts",
    "chars": 5874,
    "preview": "/**\n * Build config for electron renderer process\n */\n\nimport { sentryWebpackPlugin } from '@sentry/webpack-plugin'\nimpo"
  },
  {
    "path": ".erb/configs/webpack.paths.ts",
    "chars": 1013,
    "preview": "import path from 'path'\n\nconst rootPath = path.join(__dirname, '../..')\n\nconst dllPath = path.join(__dirname, '../dll')\n"
  },
  {
    "path": ".erb/mocks/fileMock.js",
    "chars": 32,
    "preview": "export default 'test-file-stub'\n"
  },
  {
    "path": ".erb/scripts/.eslintrc",
    "chars": 182,
    "preview": "{\n    \"rules\": {\n        \"no-console\": \"off\",\n        \"global-require\": \"off\",\n        \"import/no-dynamic-require\": \"off"
  },
  {
    "path": ".erb/scripts/check-build-exists.ts",
    "chars": 707,
    "preview": "// Check if the renderer and main bundles are built\nimport path from 'path'\nimport chalk from 'chalk'\nimport fs from 'fs"
  },
  {
    "path": ".erb/scripts/check-native-dep.cjs",
    "chars": 6834,
    "preview": "const { execSync } = require('child_process')\nconst fs = require('fs')\nconst path = require('path')\nconst { dependencies"
  },
  {
    "path": ".erb/scripts/check-native-dep.js",
    "chars": 6456,
    "preview": "import chalk from 'chalk'\nimport { execSync } from 'child_process'\nimport fs from 'fs'\nimport path from 'path'\nimport { "
  },
  {
    "path": ".erb/scripts/check-node-env.js",
    "chars": 389,
    "preview": "import chalk from 'chalk'\n\nexport default function checkNodeEnv(expectedEnv) {\n    if (!expectedEnv) {\n        throw new"
  },
  {
    "path": ".erb/scripts/check-port-in-use.js",
    "chars": 443,
    "preview": "import chalk from 'chalk'\nimport detectPort from 'detect-port'\n\nconst port = process.env.PORT || '1212'\n\ndetectPort(port"
  },
  {
    "path": ".erb/scripts/clean.js",
    "chars": 328,
    "preview": "import { rimrafSync } from 'rimraf'\nimport fs from 'fs'\nimport webpackPaths from '../configs/webpack.paths'\n\nconst folde"
  },
  {
    "path": ".erb/scripts/delete-source-maps.js",
    "chars": 500,
    "preview": "import fs from 'fs'\nimport path from 'path'\nimport { rimrafSync } from 'rimraf'\nimport webpackPaths from '../configs/web"
  },
  {
    "path": ".erb/scripts/electron-rebuild.cjs",
    "chars": 894,
    "preview": "const { execSync } = require('child_process')\nconst fs = require('fs')\nconst path = require('path')\n\n// Inline the paths"
  },
  {
    "path": ".erb/scripts/electron-rebuild.js",
    "chars": 605,
    "preview": "import { execSync } from 'child_process'\nimport fs from 'fs'\nimport { dependencies } from '../../release/app/package.jso"
  },
  {
    "path": ".erb/scripts/link-modules.cjs",
    "chars": 417,
    "preview": "const fs = require('fs')\nconst path = require('path')\n\n// Inline the paths\nconst rootPath = path.join(__dirname, '../..'"
  },
  {
    "path": ".erb/scripts/link-modules.ts",
    "chars": 314,
    "preview": "import fs from 'fs'\nimport webpackPaths from '../configs/webpack.paths'\n\nconst { srcNodeModulesPath } = webpackPaths\ncon"
  },
  {
    "path": ".erb/scripts/notarize.js",
    "chars": 919,
    "preview": "const { notarize } = require('@electron/notarize')\n\nexports.default = async function notarizeMacos(context) {\n    const "
  },
  {
    "path": ".erb/scripts/patch-libsql.cjs",
    "chars": 3743,
    "preview": "const fs = require('fs')\nconst path = require('path')\n\nconst muslTargetBlock = `  // @neon-rs/load doesn't detect arm mu"
  },
  {
    "path": ".erb/scripts/postinstall.cjs",
    "chars": 882,
    "preview": "/**\n * Root postinstall script.\n * \n * NOTE: We intentionally do NOT run electron-builder install-app-deps here.\n * With"
  },
  {
    "path": ".eslintignore",
    "chars": 490,
    "preview": "# 暂时关掉 eslint\n* \n\n# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Coverage directory used by tools like istanbul\n"
  },
  {
    "path": ".eslintrc.js",
    "chars": 1251,
    "preview": "module.exports = {\n    extends: 'erb',\n    plugins: ['@typescript-eslint'],\n    rules: {\n        // A temporary hack rel"
  },
  {
    "path": ".gitattributes",
    "chars": 188,
    "preview": "*       text    eol=lf\n*.exe   binary\n*.png   binary\n*.jpg   binary\n*.jpeg  binary\n*.ico   binary\n*.icns  binary\n*.eot  "
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 735,
    "preview": "# These are supported funding model platforms\n\ngithub: Bin-Huang\npatreon: # Replace with a single Patreon username\nopen_"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1231,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve / BUG 反馈(提交前请搜索是否存在重复issues)\ntitle: '[BUG]'\nlabels: ''\nas"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "chars": 143,
    "preview": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here. / 其他建设性意见与讨论\ntitle: '[Other]'\nlabels"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 754,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project / 新功能新特性的想法(提交前请检查是否有重复 issues)\ntitle: '[Feature]'\nlab"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1136,
    "preview": "### Description\n\n[Please provide a detailed description of your contribution, including the main changes and their purpo"
  },
  {
    "path": ".github/config.yml",
    "chars": 131,
    "preview": "requiredHeaders:\n    - Prerequisites\n    - Expected Behavior\n    - Current Behavior\n    - Possible Solution\n    - Your E"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 205,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n  "
  },
  {
    "path": ".github/stale.yml",
    "chars": 698,
    "preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a "
  },
  {
    "path": ".gitignore",
    "chars": 2168,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs."
  },
  {
    "path": ".node-version",
    "chars": 8,
    "preview": "v22.7.0\n"
  },
  {
    "path": ".npmrc",
    "chars": 245,
    "preview": "# Electron compatibility - use flat node_modules\nnode-linker=hoisted\n\n# Auto-install peer dependencies\nauto-install-peer"
  },
  {
    "path": ".prettierrc",
    "chars": 303,
    "preview": "{\n    \"tabWidth\": 2,\n    \"singleQuote\": true,\n    \"printWidth\": 120,\n    \"semi\": false,\n    \"overrides\": [\n        {\n   "
  },
  {
    "path": "ERROR_HANDLING.md",
    "chars": 3637,
    "preview": "# Error Handling Improvements\n\nThis document describes the error handling improvements made to Chatbox to address the is"
  },
  {
    "path": "LICENSE",
    "chars": 35148,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "README.md",
    "chars": 9569,
    "preview": "<p align=\"right\">\n  <a href=\"README.md\">English</a> |\n  <a href=\"./doc/README-CN.md\">简体中文</a>\n</p>\n\nThis is the reposito"
  },
  {
    "path": "assets/assets.d.ts",
    "chars": 636,
    "preview": "type Styles = Record<string, string>\n\ndeclare module '*.svg' {\n    import React = require('react')\n\n    export const Rea"
  },
  {
    "path": "assets/entitlements.mac.plist",
    "chars": 333,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "assets/installer.nsh",
    "chars": 1901,
    "preview": "!include LogicLib.nsh\n\n!macro customInit\n  ; Check for x64 VC++ Redistributable (skip ARM64 check for now)\n  ReadRegDWOR"
  },
  {
    "path": "biome.json",
    "chars": 1748,
    "preview": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.0.0/schema.json\",\n  \"vcs\": { \"enabled\": false, \"clientKind\": \"git\", \"useIg"
  },
  {
    "path": "doc/FAQ-CN.md",
    "chars": 1599,
    "preview": "# 常见问题\n\n<p align=\"center\">\n    <a href=\"./FAQ.md\">English</a> | 中文\n</p>\n\n这里列举了一些最常见的问题和解决方案。如果你依然没有找到答案,也可以提交一个 [Issue]("
  },
  {
    "path": "doc/FAQ.md",
    "chars": 2893,
    "preview": "# Frequently Asked Questions\n\n<p align=\"center\">\n    English | <a href=\"./FAQ-CN.md\">中文</a>\n</p>\n\nIf you still haven't f"
  },
  {
    "path": "doc/README-CN.md",
    "chars": 6319,
    "preview": "<p align=\"right\">\n  <a href=\"../README.md\">English</a> |\n  <a href=\"README-CN.md\">简体中文</a>\n</p>\n\n这里是 Chatbox 社区版的代码仓库,以 "
  },
  {
    "path": "docs/adding-new-provider.md",
    "chars": 8774,
    "preview": "# Adding a New Provider (Registry Architecture)\n\nThis guide documents how to add a new AI provider to Chatbox using the "
  },
  {
    "path": "docs/adding-provider.md",
    "chars": 5624,
    "preview": "# Adding a New Provider to Chatbox\n\nThis guide documents all the steps and files that need to be modified when adding a "
  },
  {
    "path": "docs/dependency-reorg.md",
    "chars": 969,
    "preview": "# Dependency split for Electron Vite\n\n依据 electron-vite 的建议,本次调整将所有仅用于 renderer(前端打包)的依赖移动到 `devDependencies`,仅保留主进程(`src"
  },
  {
    "path": "docs/new-session-mechanism.md",
    "chars": 2827,
    "preview": "# 首页新会话机制文档\n\n## 概述\n\nChatbox 的首页(`/`路由)是用户创建新对话的入口。本文档详细说明了新会话的创建机制,特别是临时状态的管理和转移过程。\n\n## 核心概念\n\n### 1. 临时会话 ID:\"new\"\n\n在用户真"
  },
  {
    "path": "docs/rag.md",
    "chars": 972,
    "preview": "技术方案\n\n## 数据流\n\n1. 上传文件到目录\n2. 在数据库创建对每个文件预处理 task\n3. 触发 worker 处理 task,work 处理完自动停止,直到下次触发\n4. worker 根据后缀名或 mime type,找到 l"
  },
  {
    "path": "docs/session-module-split-plan.md",
    "chars": 12713,
    "preview": "# Session Module Split Plan\n\n**Purpose**: Document the dependency analysis and proposed module split for `src/renderer/s"
  },
  {
    "path": "docs/storage.md",
    "chars": 3825,
    "preview": "# 存储架构文档\n\nChatbox 跨平台存储方案和版本迁移机制说明。\n\n## 跨平台存储方案\n\n### 存储类型\n\n- **DESKTOP_FILE**: 桌面端文件存储(通过 IPC)\n- **INDEXEDDB**: IndexedD"
  },
  {
    "path": "docs/testing.md",
    "chars": 5133,
    "preview": "# Testing Strategy and Implementation\n\n## Current Testing Infrastructure\n\n### Test Framework\n- **Vitest** - Modern, ESM-"
  },
  {
    "path": "docs/token-estimation.md",
    "chars": 8603,
    "preview": "# Token Estimation System\n\nToken 预估系统用于异步计算聊天消息和附件的 token 数量,在不阻塞 UI 的情况下提供实时的 token 统计。\n\n## 架构概览\n\n```text\n┌────────────"
  },
  {
    "path": "electron-builder.yml",
    "chars": 1650,
    "preview": "productName: Chatbox\nappId: xyz.chatboxapp.app\nasar: true\nasarUnpack:\n  - \"**\\\\*.{node,dll}\"\n  - \"**/node_modules/libsql"
  },
  {
    "path": "electron.vite.config.ts",
    "chars": 8348,
    "preview": "import { sentryVitePlugin } from '@sentry/vite-plugin'\nimport { TanStackRouterVite } from '@tanstack/router-plugin/vite'"
  },
  {
    "path": "i18next-parser.config.mjs",
    "chars": 387,
    "preview": "export default {\n  input: ['src/renderer/**/*.{js,jsx,ts,tsx}'],\n  output: 'src/renderer/i18n/locales/$LOCALE/$NAMESPACE"
  },
  {
    "path": "package.json",
    "chars": 12585,
    "preview": "{\n  \"name\": \"xyz.chatboxapp.ce\",\n  \"productName\": \"xyz.chatboxapp.ce\",\n  \"version\": \"0.0.1\",\n  \"engines\": {\n    \"node\": "
  },
  {
    "path": "patches/libsql@0.5.22.patch",
    "chars": 1610,
    "preview": "diff --git a/index.js b/index.js\nindex e24987954ec427320f51fd8037f9754b60ffa363..6c73d898462a0ecd2b0a34070bc6da9424f3435"
  },
  {
    "path": "patches/mdast-util-gfm-autolink-literal@2.0.1.patch",
    "chars": 993,
    "preview": "diff --git a/lib/index.js b/lib/index.js\nindex c5ca771c24dd914e342f791716a822431ee32b3a..541065ab36b44b9b1d98036aae3b961"
  },
  {
    "path": "pnpm-workspace.yaml",
    "chars": 32,
    "preview": "packages:\n  - .\n  - release/app\n"
  },
  {
    "path": "postcss.config.js",
    "chars": 416,
    "preview": "module.exports = {\n  plugins: {\n    'tailwindcss/nesting': {},\n    tailwindcss: {},\n    autoprefixer: {},\n    'postcss-p"
  },
  {
    "path": "release/app/package.json",
    "chars": 594,
    "preview": "{\n  \"name\": \"xyz.chatboxapp.ce\",\n  \"productName\": \"xyz.chatboxapp.ce\",\n  \"version\": \"1.19.1\",\n  \"description\": \"A deskto"
  },
  {
    "path": "script/translate.mjs",
    "chars": 2942,
    "preview": "import fs from 'node:fs/promises'\nimport { google } from '@ai-sdk/google'\nimport { generateText } from 'ai'\nimport pMap "
  },
  {
    "path": "scripts/ralph/prompt-opencode.md",
    "chars": 4540,
    "preview": "# Ralph Agent Instructions\n\nYou are an autonomous coding agent working on a software project.\n\n## Your Task\n\n1. Read the"
  },
  {
    "path": "scripts/ralph/ralph.sh",
    "chars": 3846,
    "preview": "#!/bin/bash\n# Ralph Wiggum - Long-running AI agent loop\n# Usage: ./ralph.sh [max_iterations] [cli_tool] [model] [share]\n"
  },
  {
    "path": "src/__tests__/App.test.tsx.bk",
    "chars": 229,
    "preview": "import '@testing-library/jest-dom'\nimport { render } from '@testing-library/react'\nimport App from '../renderer/App'\n\nde"
  },
  {
    "path": "src/main/adapters/index.ts",
    "chars": 2132,
    "preview": "import { app } from 'electron'\nimport fs from 'fs'\nimport os from 'os'\nimport path from 'path'\nimport { createAfetch } f"
  },
  {
    "path": "src/main/adapters/sentry.ts",
    "chars": 1920,
    "preview": "import * as Sentry from '@sentry/node'\nimport { app } from 'electron'\nimport type { SentryAdapter, SentryScope } from '."
  },
  {
    "path": "src/main/analystic-node.ts",
    "chars": 1224,
    "preview": "import * as store from './store-node'\nimport { app } from 'electron'\nimport { ofetch } from 'ofetch'\n\n// Measurement Pro"
  },
  {
    "path": "src/main/app-updater.ts",
    "chars": 1569,
    "preview": "import { autoUpdater } from 'electron-updater'\nimport { getSettings } from './store-node'\nimport { getLogger } from './u"
  },
  {
    "path": "src/main/autoLauncher.ts",
    "chars": 858,
    "preview": "import AutoLaunch from 'auto-launch'\nimport { getSettings } from './store-node'\n\n// 开机自启动\nlet _autoLaunch: AutoLaunch | "
  },
  {
    "path": "src/main/cache.ts",
    "chars": 788,
    "preview": "export interface CacheItem<T> {\n  value: T\n  expireAt: number\n}\n\n// In-memory cache store\nconst memoryCache = new Map<st"
  },
  {
    "path": "src/main/deeplinks.ts",
    "chars": 1474,
    "preview": "import type { BrowserWindow } from 'electron'\nimport log from 'electron-log/main'\n\nexport function handleDeepLink(mainWi"
  },
  {
    "path": "src/main/file-parser.ts",
    "chars": 5985,
    "preview": "import * as chardet from 'chardet'\nimport Epub from 'epub'\nimport * as fs from 'fs-extra'\nimport * as iconv from 'iconv-"
  },
  {
    "path": "src/main/knowledge-base/db.ts",
    "chars": 10456,
    "preview": "import fs from 'node:fs'\nimport path from 'node:path'\nimport type { Client } from '@libsql/client'\nimport { LibSQLVector"
  },
  {
    "path": "src/main/knowledge-base/file-loaders.ts",
    "chars": 19143,
    "preview": "import { setTimeout } from 'node:timers/promises'\nimport { MDocument } from '@mastra/rag'\nimport { embedMany } from 'ai'"
  },
  {
    "path": "src/main/knowledge-base/index.ts",
    "chars": 2114,
    "preview": "import { sentry } from '../adapters/sentry'\nimport { getLogger } from '../util'\nimport { initializeDatabase } from './db"
  },
  {
    "path": "src/main/knowledge-base/ipc-handlers.ts",
    "chars": 28375,
    "preview": "import { ipcMain } from 'electron'\nimport type { FileMeta } from 'src/shared/types'\nimport { sentry } from '../adapters/"
  },
  {
    "path": "src/main/knowledge-base/model-providers.ts",
    "chars": 9332,
    "preview": "import { CohereClient } from 'cohere-ai'\nimport { getModel, getProviderSettings } from '../../shared/models'\nimport { ge"
  },
  {
    "path": "src/main/knowledge-base/parsers/chatbox-parser.ts",
    "chars": 668,
    "preview": "import type { DocumentParserType } from '../../../shared/types/settings'\nimport { parseFileRemotely } from '../remote-fi"
  },
  {
    "path": "src/main/knowledge-base/parsers/index.ts",
    "chars": 2977,
    "preview": "import { isTextFilePath } from '../../../shared/file-extensions'\nimport type { DocumentParserConfig, DocumentParserType "
  },
  {
    "path": "src/main/knowledge-base/parsers/local-parser.ts",
    "chars": 2658,
    "preview": "import fs from 'node:fs'\nimport type { ModelMessage } from 'ai'\nimport {\n  isEpubFilePath,\n  isLegacyOfficeFilePath,\n  i"
  },
  {
    "path": "src/main/knowledge-base/parsers/mineru-parser.ts",
    "chars": 10064,
    "preview": "import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport AdmZip from 'adm-zip'\nimport type "
  },
  {
    "path": "src/main/knowledge-base/parsers/types.ts",
    "chars": 1768,
    "preview": "import type { DocumentParserType } from '../../../shared/types/settings'\n\n/**\n * File metadata for parsing\n */\nexport in"
  },
  {
    "path": "src/main/knowledge-base/remote-file-parser.ts",
    "chars": 4604,
    "preview": "import fs from 'node:fs'\nimport os from 'node:os'\nimport { app } from 'electron'\nimport { getChatboxAPIOrigin } from '.."
  },
  {
    "path": "src/main/locales.ts",
    "chars": 1344,
    "preview": "import { app } from 'electron'\n\nexport default class Locale {\n  locale: string = 'en'\n\n  constructor() {\n    try {\n     "
  },
  {
    "path": "src/main/main.ts",
    "chars": 20439,
    "preview": "/* eslint global-require: off, no-console: off, promise/always-return: off */\n\n/**\n * This module executes inside of ele"
  },
  {
    "path": "src/main/mcp/ipc-stdio-transport.ts",
    "chars": 3391,
    "preview": "// 和 renderer/packages/mcp/ipc-stdio-transport.ts 配套的main进程ipc handler\n\nimport { StdioClientTransport, type StdioServerP"
  },
  {
    "path": "src/main/mcp/shell-env.cjs",
    "chars": 59729,
    "preview": "var __create = Object.create;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __defProp = Object.defineProperty;\nvar __get"
  },
  {
    "path": "src/main/mcp/shell-env.d.ts",
    "chars": 70,
    "preview": "export function shellEnv(): Promise<Readonly<Record<string, string>>>\n"
  },
  {
    "path": "src/main/menu.ts",
    "chars": 9487,
    "preview": "import { app, type BrowserWindow, Menu, MenuItem, type MenuItemConstructorOptions, shell } from 'electron'\nimport Locale"
  },
  {
    "path": "src/main/proxy.ts",
    "chars": 351,
    "preview": "import { session } from 'electron'\nimport * as store from './store-node'\n\nexport function init() {\n  const { proxy } = s"
  },
  {
    "path": "src/main/readability.ts",
    "chars": 1102,
    "preview": "// import { Readability } from '@mozilla/readability'\n// import { parseHTML } from 'linkedom'\n// import { fetch } from '"
  },
  {
    "path": "src/main/store-node.ts",
    "chars": 7720,
    "preview": "import { app, powerMonitor } from 'electron'\nimport Store from 'electron-store'\nimport * as fs from 'fs-extra'\nimport pa"
  },
  {
    "path": "src/main/util.ts",
    "chars": 1058,
    "preview": "import log from 'electron-log/main'\nimport path from 'path'\nimport { URL } from 'url'\n\nexport function resolveHtmlPath(h"
  },
  {
    "path": "src/main/window_state.ts",
    "chars": 8580,
    "preview": "// 保持窗口大小位置变化的代码,很大程度参考了 VSCODE 的实现\n// /Users/benn/Documents/w/vscode/src/vs/platform/windows/electron-main/windowImpl.t"
  },
  {
    "path": "src/preload/index.ts",
    "chars": 2497,
    "preview": "// Disable no-unused-vars, broken for spread args\n/* eslint no-unused-vars: off */\nimport { contextBridge, ipcRenderer }"
  },
  {
    "path": "src/renderer/Sidebar.tsx",
    "chars": 9109,
    "preview": "import { ActionIcon, Box, Button, Flex, Image, NavLink, Stack, Text, Tooltip } from '@mantine/core'\nimport SwipeableDraw"
  },
  {
    "path": "src/renderer/adapters/index.ts",
    "chars": 2201,
    "preview": "import { createAfetch } from '@shared/request/request'\nimport type { ApiRequestOptions, ModelDependencies } from '@share"
  },
  {
    "path": "src/renderer/adapters/sentry.ts",
    "chars": 681,
    "preview": "import * as Sentry from '@sentry/react'\nimport type { SentryAdapter, SentryScope } from '../../shared/utils/sentry_adapt"
  },
  {
    "path": "src/renderer/components/Accordion.tsx",
    "chars": 1337,
    "preview": "import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'\nimport MuiAccordion, { type AccordionPro"
  },
  {
    "path": "src/renderer/components/ActionMenu.tsx",
    "chars": 9250,
    "preview": "import { Menu, type MenuItemProps, type MenuProps, Stack, Text, useMantineTheme } from '@mantine/core'\nimport { IconChec"
  },
  {
    "path": "src/renderer/components/AdaptiveSelect.tsx",
    "chars": 2434,
    "preview": "import type { SelectProps as MantineSelectProps } from '@mantine/core'\nimport { Button, Select, Stack, Text } from '@man"
  },
  {
    "path": "src/renderer/components/Artifact.tsx",
    "chars": 9098,
    "preview": "import NiceModal from '@ebay/nice-modal-react'\nimport ReplayOutlinedIcon from '@mui/icons-material/ReplayOutlined'\nimpor"
  },
  {
    "path": "src/renderer/components/CustomProviderIcon.tsx",
    "chars": 1175,
    "preview": "import { Flex, Text } from '@mantine/core'\nimport type { FC } from 'react'\n\nexport type CustomProviderIconProps = {\n  pr"
  },
  {
    "path": "src/renderer/components/EditableAvatar.tsx",
    "chars": 2243,
    "preview": "import { Badge, Box, IconButton, useTheme } from '@mui/material'\nimport Avatar from '@mui/material/Avatar'\nimport React,"
  },
  {
    "path": "src/renderer/components/ErrorTestPannel.tsx",
    "chars": 2000,
    "preview": "import React, { useState } from 'react'\n\n// 组件内部的错误测试工具\nexport function ErrorTestPanel() {\n  const [shouldError, setShou"
  },
  {
    "path": "src/renderer/components/FileIcon.tsx",
    "chars": 2697,
    "preview": "import { FileText } from 'lucide-react'\nimport mdIcon from '../static/icons/icons8-markdown-48.png'\nimport htmlIcon from"
  },
  {
    "path": "src/renderer/components/Image.tsx",
    "chars": 2324,
    "preview": "import BrokenImageOutlinedIcon from '@mui/icons-material/BrokenImageOutlined'\nimport CircularProgressIcon from '@mui/mat"
  },
  {
    "path": "src/renderer/components/ImageCountSlider.tsx",
    "chars": 1551,
    "preview": "import { TextField, Slider, Typography, Box } from '@mui/material'\nimport { useTranslation } from 'react-i18next'\n\nexpor"
  },
  {
    "path": "src/renderer/components/ImageModelSelect.tsx",
    "chars": 8405,
    "preview": "import { Combobox, type ComboboxProps, Divider, Text, useCombobox } from '@mantine/core'\nimport { type ModelProvider, Mo"
  },
  {
    "path": "src/renderer/components/ImageStyleSelect.tsx",
    "chars": 735,
    "preview": "import { useTranslation } from 'react-i18next'\nimport type { SessionSettings } from '../../shared/types'\nimport { Adapti"
  },
  {
    "path": "src/renderer/components/InputBox/Attachments.tsx",
    "chars": 8355,
    "preview": "import NiceModal from '@ebay/nice-modal-react'\nimport { Tooltip, Typography } from '@mui/material'\nimport { ChatboxAIAPI"
  },
  {
    "path": "src/renderer/components/InputBox/ImageUploadButton.tsx",
    "chars": 1100,
    "preview": "import { ActionIcon, Tooltip } from '@mantine/core'\nimport { IconPhoto } from '@tabler/icons-react'\nimport { forwardRef "
  },
  {
    "path": "src/renderer/components/InputBox/ImageUploadInput.tsx",
    "chars": 742,
    "preview": "import type React from 'react'\nimport { forwardRef } from 'react'\n\ninterface ImageUploadInputProps {\n  onChange: (event:"
  },
  {
    "path": "src/renderer/components/InputBox/InputBox.tsx",
    "chars": 50855,
    "preview": "import NiceModal from '@ebay/nice-modal-react'\nimport {\n  ActionIcon,\n  Box,\n  Button,\n  Flex,\n  Loader,\n  Menu,\n  Stack"
  },
  {
    "path": "src/renderer/components/InputBox/SessionSettingsButton.tsx",
    "chars": 1337,
    "preview": "import { ActionIcon, Tooltip } from '@mantine/core'\nimport { IconAdjustmentsHorizontal } from '@tabler/icons-react'\nimpo"
  },
  {
    "path": "src/renderer/components/InputBox/TokenCountMenu.tsx",
    "chars": 5997,
    "preview": "import { Flex, Loader, Menu, Switch, Text, Tooltip } from '@mantine/core'\nimport { formatNumber } from '@shared/utils'\ni"
  },
  {
    "path": "src/renderer/components/InputBox/WebBrowsingButton.tsx",
    "chars": 1165,
    "preview": "import { ActionIcon } from '@mantine/core'\nimport { IconWorld } from '@tabler/icons-react'\nimport { forwardRef } from 'r"
  },
  {
    "path": "src/renderer/components/InputBox/actionIconStyles.ts",
    "chars": 249,
    "preview": "export const mobileActionIconProps = {\n  variant: 'transparent' as const,\n  w: 20,\n  h: 20,\n  miw: 20,\n  mih: 20,\n  bd: "
  },
  {
    "path": "src/renderer/components/InputBox/index.ts",
    "chars": 524,
    "preview": "// Re-export named exports\n\nexport { desktopActionIconProps, mobileActionIconProps } from './actionIconStyles'\nexport { "
  },
  {
    "path": "src/renderer/components/InputBox/preprocessState.ts",
    "chars": 4568,
    "preview": "import { StorageKeyGenerator } from '@/storage/StoreStorage'\nimport type { PreConstructedMessageState, PreprocessedFile,"
  },
  {
    "path": "src/renderer/components/Markdown.tsx",
    "chars": 16634,
    "preview": "import { sanitizeUrl } from '@braintree/sanitize-url'\nimport { useTheme } from '@mui/material'\nimport {\n  createContext,"
  },
  {
    "path": "src/renderer/components/Mermaid.tsx",
    "chars": 6629,
    "preview": "/** biome-ignore-all lint/security/noDangerouslySetInnerHtml: <explanation> */\nimport DataObjectIcon from '@mui/icons-ma"
  },
  {
    "path": "src/renderer/components/ModelList.tsx",
    "chars": 8568,
    "preview": "import { Badge, Button, Flex, Stack, Text, TextInput, Tooltip } from '@mantine/core'\nimport type { ProviderModelInfo } f"
  },
  {
    "path": "src/renderer/components/ModelSelector/DesktopModelSelector.tsx",
    "chars": 11247,
    "preview": "import {\n  Button,\n  Collapse,\n  Combobox,\n  type ComboboxProps,\n  Flex,\n  SegmentedControl,\n  Stack,\n  Text,\n  TextInpu"
  },
  {
    "path": "src/renderer/components/ModelSelector/MobileModelSelector.tsx",
    "chars": 10278,
    "preview": "import { Collapse, Flex, Stack, Tabs, Text, TextInput } from '@mantine/core'\nimport type { ProviderModelInfo } from '@sh"
  },
  {
    "path": "src/renderer/components/ModelSelector/ProviderHeader.tsx",
    "chars": 4307,
    "preview": "import { Flex, Text } from '@mantine/core'\nimport { IconChevronDown, IconServer, IconStarFilled } from '@tabler/icons-re"
  },
  {
    "path": "src/renderer/components/ModelSelector/SimplePreview.tsx",
    "chars": 7306,
    "preview": "/**\n * Simple Preview for ModelSelector\n * A standalone preview that doesn't require complex mocking\n */\n\nimport { Badge"
  },
  {
    "path": "src/renderer/components/ModelSelector/index.tsx",
    "chars": 4107,
    "preview": "import type { ComboboxProps } from '@mantine/core'\nimport type { ModelProvider, ProviderModelInfo } from '@shared/types'"
  },
  {
    "path": "src/renderer/components/ModelSelector/shared.tsx",
    "chars": 7463,
    "preview": "import { Badge, Combobox, Flex, Text, Tooltip } from '@mantine/core'\nimport type { ProviderModelInfo } from '@shared/typ"
  },
  {
    "path": "src/renderer/components/Shortcut.tsx",
    "chars": 8246,
    "preview": "import { Box, Combobox, Flex, Input, InputBase, Kbd, Select, Table, Text, useCombobox } from '@mantine/core'\nimport {\n  "
  },
  {
    "path": "src/renderer/components/SortableItem.tsx",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/renderer/components/SponsorChip.tsx",
    "chars": 1420,
    "preview": "import CampaignOutlinedIcon from '@mui/icons-material/CampaignOutlined'\nimport CancelOutlinedIcon from '@mui/icons-mater"
  },
  {
    "path": "src/renderer/components/StyledMenu.tsx",
    "chars": 2020,
    "preview": "import { Menu, type MenuProps } from '@mui/material'\nimport 'katex/dist/katex.min.css'\nimport { alpha, styled } from '@m"
  },
  {
    "path": "src/renderer/components/UpdateAvailableButton.tsx",
    "chars": 687,
    "preview": "import { Button } from '@mantine/core'\nimport { IconRefresh } from '@tabler/icons-react'\nimport { useTranslation } from "
  },
  {
    "path": "src/renderer/components/chat/CompactionStatus.tsx",
    "chars": 5132,
    "preview": "import { ActionIcon, Box, Button, Collapse, Flex, Text, Tooltip } from '@mantine/core'\nimport {\n  IconAlertCircle,\n  Ico"
  },
  {
    "path": "src/renderer/components/chat/Message.tsx",
    "chars": 27589,
    "preview": "import NiceModal from '@ebay/nice-modal-react'\nimport { ActionIcon, type ActionIconProps, Flex, Image as Img, Loader, Te"
  },
  {
    "path": "src/renderer/components/chat/MessageAttachmentGrid.tsx",
    "chars": 2606,
    "preview": "import type { MessageFile, MessageLink } from '@shared/types'\nimport { ChevronDown, ChevronUp } from 'lucide-react'\nimpo"
  },
  {
    "path": "src/renderer/components/chat/MessageErrTips.tsx",
    "chars": 10805,
    "preview": "import { ActionIcon, Collapse, Flex, Tooltip } from '@mantine/core'\nimport { Link } from '@mui/material'\nimport Alert fr"
  },
  {
    "path": "src/renderer/components/chat/MessageList.tsx",
    "chars": 19460,
    "preview": "import NiceModal from '@ebay/nice-modal-react'\nimport { ActionIcon, Button, Flex, Stack, Text, Transition } from '@manti"
  },
  {
    "path": "src/renderer/components/chat/MessageLoading.tsx",
    "chars": 4335,
    "preview": "import { Typography } from '@mui/material'\nimport type { Message } from '@shared/types'\nimport { useAtomValue } from 'jo"
  },
  {
    "path": "src/renderer/components/chat/MessageNavigation.tsx",
    "chars": 3237,
    "preview": "import { Box, Button, Divider, Stack } from '@mantine/core'\nimport { IconArrowDown, IconChevronDown, IconChevronsDown, I"
  },
  {
    "path": "src/renderer/components/chat/SummaryMessage.tsx",
    "chars": 5061,
    "preview": "import NiceModal from '@ebay/nice-modal-react'\nimport { ActionIcon, Button, Collapse, Flex, Group, Stack, Text, Tooltip "
  },
  {
    "path": "src/renderer/components/common/AdaptiveModal.tsx",
    "chars": 2570,
    "preview": "import type { ModalProps as MantineModalProps } from '@mantine/core'\nimport { Button, type ButtonProps, Flex, Stack, Tex"
  },
  {
    "path": "src/renderer/components/common/Avatar.tsx",
    "chars": 4772,
    "preview": "import { Avatar, type AvatarProps, type PolymorphicComponentProps } from '@mantine/core'\nimport { IconMessageCircle, Ico"
  },
  {
    "path": "src/renderer/components/common/CompressionModal.tsx",
    "chars": 1169,
    "preview": "import { Button, Stack, Text } from '@mantine/core'\nimport type { Session } from '@shared/types/session'\nimport { useTra"
  },
  {
    "path": "src/renderer/components/common/ConfirmDeleteButton.tsx",
    "chars": 4000,
    "preview": "import { useState, useEffect } from 'react'\nimport { MenuItem, Button } from '@mui/material'\nimport DeleteIcon from '@mu"
  },
  {
    "path": "src/renderer/components/common/CreatableSelect.tsx",
    "chars": 3063,
    "preview": "import type * as React from 'react'\nimport TextField from '@mui/material/TextField'\nimport Autocomplete from '@mui/mater"
  },
  {
    "path": "src/renderer/components/common/Divider.tsx",
    "chars": 1754,
    "preview": "import clsx from 'clsx'\nimport { useMemo } from 'react'\nimport { useIsSmallScreen } from '@/hooks/useScreenChange'\n\nexpo"
  },
  {
    "path": "src/renderer/components/common/ErrorBoundary.tsx",
    "chars": 4957,
    "preview": "import * as Sentry from '@sentry/react'\nimport React from 'react'\nimport { getLogger } from '../../lib/utils'\nimport { r"
  },
  {
    "path": "src/renderer/components/common/LazyNumberInput.tsx",
    "chars": 3886,
    "preview": "import { ActionIcon, CloseButton, Stack, TextInput } from '@mantine/core'\nimport { IconChevronDown, IconChevronUp } from"
  },
  {
    "path": "src/renderer/components/common/LazySlider.tsx",
    "chars": 731,
    "preview": "import { Slider, type SliderProps } from '@mantine/core'\nimport { type FC, useCallback, useState } from 'react'\n\nexport "
  },
  {
    "path": "src/renderer/components/common/Link.tsx",
    "chars": 695,
    "preview": "import platform from '@/platform'\nimport { useTheme } from '@mui/material'\n\nexport default function LinkTargetBlank(prop"
  },
  {
    "path": "src/renderer/components/common/Mark.tsx",
    "chars": 483,
    "preview": "import { type ReactElement, useEffect, useRef } from 'react'\nimport markjs from 'mark.js'\n\nexport default function Mark("
  },
  {
    "path": "src/renderer/components/common/MaxContextMessageCountSlider.tsx",
    "chars": 4709,
    "preview": "import { Flex, Slider, Stack, type StackProps, Text, TextInput, type TextProps, Tooltip } from '@mantine/core'\nimport { "
  },
  {
    "path": "src/renderer/components/common/MiniButton.tsx",
    "chars": 1542,
    "preview": "import type React from 'react'\nimport { forwardRef } from 'react'\nimport { Tooltip } from '@mantine/core'\nimport { cn } "
  },
  {
    "path": "src/renderer/components/common/PasswordTextField.tsx",
    "chars": 1588,
    "preview": "import React from 'react'\nimport { TextField, InputAdornment, IconButton } from '@mui/material'\nimport Visibility from '"
  },
  {
    "path": "src/renderer/components/common/PopoverConfirm.tsx",
    "chars": 2404,
    "preview": "import { Button, type ButtonProps, Flex, Popover, type PopoverProps, Stack, Text } from '@mantine/core'\nimport { t } fro"
  },
  {
    "path": "src/renderer/components/common/ScalableIcon.tsx",
    "chars": 514,
    "preview": "import { useMantineTheme } from '@mantine/core'\nimport type { IconProps } from '@tabler/icons-react'\nimport { forwardRef"
  },
  {
    "path": "src/renderer/components/common/SegmentedControl.tsx",
    "chars": 645,
    "preview": "import { SegmentedControl as MantineSegmentedControl } from '@mantine/core'\n\nexport default function SegmentedControl({\n"
  },
  {
    "path": "src/renderer/components/common/SliderWithInput.tsx",
    "chars": 2945,
    "preview": "import { CloseButton, Flex, Slider, TextInput } from '@mantine/core'\nimport clsx from 'clsx'\nimport { type ChangeEvent, "
  },
  {
    "path": "src/renderer/components/common/TemperatureSlider.tsx",
    "chars": 2045,
    "preview": "import { useEffect, useState } from 'react'\nimport { TextField, Slider, Typography, Box } from '@mui/material'\nimport { "
  },
  {
    "path": "src/renderer/components/common/TextFieldReset.tsx",
    "chars": 1094,
    "preview": "import type React from 'react'\nimport { TextField, Button } from '@mui/material'\nimport { useTranslation } from 'react-i"
  },
  {
    "path": "src/renderer/components/common/Toasts.tsx",
    "chars": 685,
    "preview": "import { Snackbar } from '@mui/material'\nimport {} from 'react'\nimport { useStore } from 'zustand'\nimport { uiStore } fr"
  },
  {
    "path": "src/renderer/components/common/TopPSlider.tsx",
    "chars": 2091,
    "preview": "import { useEffect, useState } from 'react'\nimport { TextField, Slider, Typography, Box } from '@mui/material'\nimport { "
  },
  {
    "path": "src/renderer/components/dev/DevHeader.tsx",
    "chars": 1860,
    "preview": "import { ActionIcon, Group, Paper, Text, Tooltip } from '@mantine/core'\nimport { IconArrowLeft, IconHome } from '@tabler"
  },
  {
    "path": "src/renderer/components/dev/ThemeSwitchButton.tsx",
    "chars": 1486,
    "preview": "import { ActionIcon, type ActionIconProps, Tooltip } from '@mantine/core'\nimport { Theme } from '@shared/types'\nimport {"
  },
  {
    "path": "src/renderer/components/icons/ArrowRightIcon.tsx",
    "chars": 520,
    "preview": "import { MouseEventHandler } from 'react'\n\nexport default function ArrowRightIcon(props: { className?: string; onClick?:"
  },
  {
    "path": "src/renderer/components/icons/BrandGithub.tsx",
    "chars": 984,
    "preview": "import * as React from 'react'\n\nfunction BrandGithub(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg width=\""
  },
  {
    "path": "src/renderer/components/icons/BrandRedNote.tsx",
    "chars": 6064,
    "preview": "import { MouseEventHandler } from 'react'\n\nexport default function BrandRedNote(props: { className?: string; onClick?: M"
  },
  {
    "path": "src/renderer/components/icons/BrandWechat.tsx",
    "chars": 3421,
    "preview": "import { MouseEventHandler } from 'react'\n\nexport default function BrandWechat(props: { className?: string; onClick?: Mo"
  },
  {
    "path": "src/renderer/components/icons/BrandX.tsx",
    "chars": 611,
    "preview": "import { MouseEventHandler } from 'react'\n\nexport default function BrandX(props: { className?: string; onClick?: MouseEv"
  },
  {
    "path": "src/renderer/components/icons/Broom.tsx",
    "chars": 1607,
    "preview": "import type { IconProps } from '@tabler/icons-react';\n\nexport default function Broom(props: IconProps) {\n  const { size,"
  },
  {
    "path": "src/renderer/components/icons/Dart.tsx",
    "chars": 1171,
    "preview": "import type { IconProps } from \"@tabler/icons-react\";\n\nexport default function Dart(props: IconProps) {\n  const { size, "
  },
  {
    "path": "src/renderer/components/icons/FullscreenIcon.tsx",
    "chars": 674,
    "preview": "import { MouseEventHandler } from 'react'\n\nexport default function FullscreenIcon(props: { className?: string; onClick?:"
  },
  {
    "path": "src/renderer/components/icons/HomepageIcon.tsx",
    "chars": 2394,
    "preview": "import { useComputedColorScheme, useMantineColorScheme } from '@mantine/core'\nimport * as React from 'react'\n\nfunction H"
  },
  {
    "path": "src/renderer/components/icons/Java.tsx",
    "chars": 2572,
    "preview": "import type { IconProps } from \"@tabler/icons-react\";\n\nexport default function Java(props: IconProps) {\n  const { size, "
  },
  {
    "path": "src/renderer/components/icons/LayoutExpand.tsx",
    "chars": 840,
    "preview": "import type { IconProps } from '@tabler/icons-react';\n\nexport default function LayoutExpand(props: IconProps) {\n  const "
  },
  {
    "path": "src/renderer/components/icons/LayoutShrink.tsx",
    "chars": 840,
    "preview": "import type { IconProps } from '@tabler/icons-react';\n\nexport default function LayoutShrink(props: IconProps) {\n  const "
  },
  {
    "path": "src/renderer/components/icons/Loading.tsx",
    "chars": 2647,
    "preview": "import * as React from 'react'\n\nfunction Loading(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg height=\"1em"
  },
  {
    "path": "src/renderer/components/icons/ModelIcon.tsx",
    "chars": 1883,
    "preview": "import { useComputedColorScheme } from '@mantine/core'\nimport type { ModelProvider } from '@shared/types'\nimport { rende"
  },
  {
    "path": "src/renderer/components/icons/ProviderIcon.tsx",
    "chars": 18173,
    "preview": "import { type ModelProvider, ModelProviderEnum } from '@shared/types';\n\nexport default function ProviderIcon(props: { cl"
  },
  {
    "path": "src/renderer/components/icons/ProviderImageIcon.tsx",
    "chars": 1816,
    "preview": "/// <reference types=\"vite/client\" />\n\nimport { Image } from '@mantine/core'\nimport type { ModelProvider } from '@shared"
  },
  {
    "path": "src/renderer/components/icons/Robot.tsx",
    "chars": 1479,
    "preview": "import type { IconProps } from '@tabler/icons-react';\n\nexport default function Robot(props: IconProps) {\n  const { size,"
  },
  {
    "path": "src/renderer/components/knowledge-base/ChunksPreviewModal.tsx",
    "chars": 3975,
    "preview": "import { Center, Code, Group, Loader, Paper, ScrollArea, Stack, Text } from '@mantine/core'\nimport type { KnowledgeBaseF"
  },
  {
    "path": "src/renderer/components/knowledge-base/KnowledgeBase.tsx",
    "chars": 21423,
    "preview": "import { Alert, Button, Flex, Group, Paper, Pill, Stack, Text, Title } from '@mantine/core'\nimport { SystemProviders } f"
  },
  {
    "path": "src/renderer/components/knowledge-base/KnowledgeBaseDocuments.tsx",
    "chars": 36521,
    "preview": "import {\n  ActionIcon,\n  Alert,\n  Box,\n  Button,\n  Center,\n  Collapse,\n  Flex,\n  Group,\n  Loader,\n  Paper,\n  Pill,\n  Pro"
  },
  {
    "path": "src/renderer/components/knowledge-base/KnowledgeBaseForm.tsx",
    "chars": 11394,
    "preview": "import { Button, Group, Input, PasswordInput, Pill, Radio, Select, Stack, Text } from '@mantine/core'\nimport type { Docu"
  },
  {
    "path": "src/renderer/components/knowledge-base/KnowledgeBaseMenu.tsx",
    "chars": 2369,
    "preview": "import { Button, Flex, Group, Menu, Text } from '@mantine/core'\nimport type { KnowledgeBase } from '@shared/types'\nimpor"
  },
  {
    "path": "src/renderer/components/knowledge-base/RemoteRetryModal.tsx",
    "chars": 6442,
    "preview": "import { Alert, Button, Flex, Group, Pill, ScrollArea, Stack, Text, Tooltip } from '@mantine/core'\nimport { ChatboxAIAPI"
  },
  {
    "path": "src/renderer/components/layout/ExitFullscreenButton.tsx",
    "chars": 1004,
    "preview": "import { debounce } from 'lodash'\nimport { useEffect, useState } from 'react'\nimport platform from '@/platform'\n\n/**\n * "
  },
  {
    "path": "src/renderer/components/layout/Header.tsx",
    "chars": 3582,
    "preview": "import NiceModal from '@ebay/nice-modal-react'\nimport { ActionIcon, Flex, Title, Tooltip } from '@mantine/core'\nimport t"
  }
]

// ... and 417 more files (download for full content)

About this extraction

This page contains the full source code of the chatboxai/chatbox GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 617 files (4.0 MB), approximately 1.1M tokens, and a symbol index with 2029 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!