Full Code of RSSNext/Folo for AI

dev ecf1ca57ac21 cached
2869 files
8.2 MB
2.3M tokens
3942 symbols
1 requests
Download .txt
Showing preview only (9,242K chars total). Download the full file or copy to clipboard to get everything.
Repository: RSSNext/Folo
Branch: dev
Commit: ecf1ca57ac21
Files: 2869
Total size: 8.2 MB

Directory structure:
gitextract_ni4pl4rv/

├── .agents/
│   ├── settings.local.json
│   └── skills/
│       ├── desktop-release/
│       │   └── SKILL.md
│       ├── installing-mobile-preview-builds/
│       │   └── SKILL.md
│       ├── mobile-e2e/
│       │   └── SKILL.md
│       ├── mobile-release/
│       │   └── SKILL.md
│       ├── mobile-self-test/
│       │   └── SKILL.md
│       └── update-deps/
│           └── SKILL.md
├── .cursorignore
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   ├── i18n.yml
│   │   └── typo.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   ├── setup-version/
│   │   │   └── action.yml
│   │   └── setup-xcode/
│   │       └── action.yml
│   ├── advanced-issue-labeler.yml
│   ├── copilot-instructions.md
│   ├── dependabot.yaml
│   ├── prompts/
│   │   └── similar_issues.prompt.yml
│   ├── scripts/
│   │   └── extract-release-info.mjs
│   └── workflows/
│       ├── build-android.yml
│       ├── build-desktop.yml
│       ├── build-ios-development.yml
│       ├── build-ios.yml
│       ├── build-web.yml
│       ├── deploy-cloudflare-desktop.yml
│       ├── deploy-cloudflare-landing.yml
│       ├── deploy-cloudflare-ssr.yml
│       ├── issue-labeler.yml
│       ├── lint.yml
│       ├── pr-title-check.yml
│       ├── similar-issues.yml
│       ├── sync.yaml
│       ├── tag.yml
│       └── translator.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc.mjs
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── AGENTS.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── api/
│   └── vercel_webhook.ts
├── apps/
│   ├── cli/
│   │   ├── package.json
│   │   ├── skill.md
│   │   ├── src/
│   │   │   ├── args.test.ts
│   │   │   ├── args.ts
│   │   │   ├── browser-login.test.ts
│   │   │   ├── browser-login.ts
│   │   │   ├── cli.e2e.test.ts
│   │   │   ├── client.ts
│   │   │   ├── command.ts
│   │   │   ├── commands/
│   │   │   │   ├── auth.ts
│   │   │   │   ├── collection.ts
│   │   │   │   ├── entry.ts
│   │   │   │   ├── feed.ts
│   │   │   │   ├── list.ts
│   │   │   │   ├── opml.ts
│   │   │   │   ├── search.ts
│   │   │   │   ├── subscription.ts
│   │   │   │   ├── timeline.ts
│   │   │   │   └── unread.ts
│   │   │   ├── config.ts
│   │   │   ├── index.ts
│   │   │   ├── output.test.ts
│   │   │   └── output.ts
│   │   ├── tsconfig.json
│   │   ├── tsup.config.ts
│   │   └── vitest.config.ts
│   ├── desktop/
│   │   ├── .env.example
│   │   ├── AGENTS.md
│   │   ├── build/
│   │   │   ├── appxmanifest-template.xml
│   │   │   ├── dev.pfx
│   │   │   ├── entitlements.mac.plist
│   │   │   ├── entitlements.mas.child.plist
│   │   │   └── entitlements.mas.plist
│   │   ├── bump.config.ts
│   │   ├── bump.hotfix.config.js
│   │   ├── changelog/
│   │   │   ├── 0.1.2.md
│   │   │   ├── 0.2.0.md
│   │   │   ├── 0.2.1.md
│   │   │   ├── 0.2.2.md
│   │   │   ├── 0.2.3.md
│   │   │   ├── 0.2.4.md
│   │   │   ├── 0.2.5.md
│   │   │   ├── 0.2.6.md
│   │   │   ├── 0.2.7.md
│   │   │   ├── 0.2.8.md
│   │   │   ├── 0.2.9.md
│   │   │   ├── 0.3.0.md
│   │   │   ├── 0.3.1.md
│   │   │   ├── 0.3.10.md
│   │   │   ├── 0.3.11.md
│   │   │   ├── 0.3.12.md
│   │   │   ├── 0.3.13.md
│   │   │   ├── 0.3.2.md
│   │   │   ├── 0.3.3.md
│   │   │   ├── 0.3.4.md
│   │   │   ├── 0.3.5.md
│   │   │   ├── 0.3.6.md
│   │   │   ├── 0.3.7.md
│   │   │   ├── 0.3.8.md
│   │   │   ├── 0.3.9.md
│   │   │   ├── 0.4.0.md
│   │   │   ├── 0.4.1.md
│   │   │   ├── 0.4.2.md
│   │   │   ├── 0.4.3.md
│   │   │   ├── 0.4.4.md
│   │   │   ├── 0.4.5.md
│   │   │   ├── 0.4.6.md
│   │   │   ├── 0.4.8.md
│   │   │   ├── 0.5.0.md
│   │   │   ├── 0.6.0.md
│   │   │   ├── 0.6.1.md
│   │   │   ├── 0.6.2.md
│   │   │   ├── 0.6.3.md
│   │   │   ├── 0.7.0.md
│   │   │   ├── 0.8.0.md
│   │   │   ├── 0.9.0.md
│   │   │   ├── 1.0.0.md
│   │   │   ├── 1.1.0.md
│   │   │   ├── 1.2.2.md
│   │   │   ├── 1.2.6.md
│   │   │   ├── 1.3.0.md
│   │   │   ├── 1.3.1.md
│   │   │   ├── 1.4.0.md
│   │   │   ├── next.md
│   │   │   └── next.template.md
│   │   ├── configs/
│   │   │   ├── vite.electron-render.config.ts
│   │   │   └── vite.render.config.ts
│   │   ├── dev-only/
│   │   │   └── dev-app-update.yml
│   │   ├── e2e/
│   │   │   ├── playwright.config.ts
│   │   │   ├── scripts/
│   │   │   │   └── capture-ui-audit.ts
│   │   │   ├── support/
│   │   │   │   ├── account.ts
│   │   │   │   ├── app.ts
│   │   │   │   ├── auth-bootstrap.ts
│   │   │   │   ├── electron.ts
│   │   │   │   └── env.ts
│   │   │   └── tests/
│   │   │       ├── electron/
│   │   │       │   └── core.spec.ts
│   │   │       └── web/
│   │   │           ├── core.spec.ts
│   │   │           └── settings-sync.spec.ts
│   │   ├── electron.vite.config.ts
│   │   ├── forge.config.cts
│   │   ├── layer/
│   │   │   ├── main/
│   │   │   │   ├── export.ts
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── package.json
│   │   │   │   ├── preload/
│   │   │   │   │   ├── index.d.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── src/
│   │   │   │   │   ├── @types/
│   │   │   │   │   │   ├── constants.ts
│   │   │   │   │   │   ├── i18next.d.ts
│   │   │   │   │   │   └── resources.ts
│   │   │   │   │   ├── before-bootstrap.ts
│   │   │   │   │   ├── bootstrap.ts
│   │   │   │   │   ├── constants/
│   │   │   │   │   │   ├── app.ts
│   │   │   │   │   │   └── system.ts
│   │   │   │   │   ├── env.ts
│   │   │   │   │   ├── helper.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── ipc/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── services/
│   │   │   │   │   │       ├── app.ts
│   │   │   │   │   │       ├── auth.ts
│   │   │   │   │   │       ├── cli.ts
│   │   │   │   │   │       ├── debug.ts
│   │   │   │   │   │       ├── dock.ts
│   │   │   │   │   │       ├── integration.ts
│   │   │   │   │   │       ├── menu.ts
│   │   │   │   │   │       ├── reader.ts
│   │   │   │   │   │       └── setting.ts
│   │   │   │   │   ├── lib/
│   │   │   │   │   │   ├── api-client.ts
│   │   │   │   │   │   ├── auth-cookie-migration.ts
│   │   │   │   │   │   ├── cleaner.ts
│   │   │   │   │   │   ├── cli-session-sync.ts
│   │   │   │   │   │   ├── dock.ts
│   │   │   │   │   │   ├── download.ts
│   │   │   │   │   │   ├── i18n.ts
│   │   │   │   │   │   ├── proxy.test.ts
│   │   │   │   │   │   ├── proxy.ts
│   │   │   │   │   │   ├── router.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── tray.ts
│   │   │   │   │   │   ├── user.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── logger.ts
│   │   │   │   │   ├── manager/
│   │   │   │   │   │   ├── app.ts
│   │   │   │   │   │   ├── bootstrap.ts
│   │   │   │   │   │   ├── lifecycle.ts
│   │   │   │   │   │   └── window.ts
│   │   │   │   │   ├── menu.ts
│   │   │   │   │   ├── modules/
│   │   │   │   │   │   └── language-detection/
│   │   │   │   │   │       └── index.ts
│   │   │   │   │   ├── shims/
│   │   │   │   │   │   └── utf-8-validate.cjs
│   │   │   │   │   └── updater/
│   │   │   │   │       ├── api.ts
│   │   │   │   │       ├── configs.ts
│   │   │   │   │       ├── follow-update-provider.ts
│   │   │   │   │       ├── hot-updater.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── logger.ts
│   │   │   │   │       └── windows-updater.ts
│   │   │   │   ├── tsconfig.json
│   │   │   │   └── vitest.config.ts
│   │   │   └── renderer/
│   │   │       ├── debug_proxy.html
│   │   │       ├── global.d.ts
│   │   │       ├── index.html
│   │   │       ├── package.json
│   │   │       ├── pwa-assets.config.ts
│   │   │       ├── setup-file.ts
│   │   │       ├── src/
│   │   │       │   ├── @types/
│   │   │       │   │   ├── constants.ts
│   │   │       │   │   ├── default-resource.electron.ts
│   │   │       │   │   ├── default-resource.ts
│   │   │       │   │   └── i18next.d.ts
│   │   │       │   ├── App.tsx
│   │   │       │   ├── atoms/
│   │   │       │   │   ├── ai-summary.ts
│   │   │       │   │   ├── ai-translation.ts
│   │   │       │   │   ├── app.ts
│   │   │       │   │   ├── context-menu.ts
│   │   │       │   │   ├── corner-player.ts
│   │   │       │   │   ├── debug-feature.ts
│   │   │       │   │   ├── dom.ts
│   │   │       │   │   ├── lang.ts
│   │   │       │   │   ├── network.ts
│   │   │       │   │   ├── player.ts
│   │   │       │   │   ├── popover.ts
│   │   │       │   │   ├── preview.ts
│   │   │       │   │   ├── readability.ts
│   │   │       │   │   ├── server-configs.ts
│   │   │       │   │   ├── settings/
│   │   │       │   │   │   ├── ai.ts
│   │   │       │   │   │   ├── general.ts
│   │   │       │   │   │   ├── integration.ts
│   │   │       │   │   │   └── ui.ts
│   │   │       │   │   ├── sidebar.ts
│   │   │       │   │   ├── source-content.tsx
│   │   │       │   │   ├── updater.ts
│   │   │       │   │   └── user.ts
│   │   │       │   ├── components/
│   │   │       │   │   ├── common/
│   │   │       │   │   │   ├── AppErrorBoundary.tsx
│   │   │       │   │   │   ├── ErrorBoundary.tsx
│   │   │       │   │   │   ├── ErrorElement.tsx
│   │   │       │   │   │   ├── ErrorTooltip.tsx
│   │   │       │   │   │   ├── ExPromise.tsx
│   │   │       │   │   │   ├── Focusable.tsx
│   │   │       │   │   │   ├── Fragment.tsx
│   │   │       │   │   │   ├── ImpressionTracker.tsx
│   │   │       │   │   │   ├── LCPEndDetector.tsx
│   │   │       │   │   │   ├── LoadMoreIndicator.tsx
│   │   │       │   │   │   ├── LoadRemixAsyncComponent.tsx
│   │   │       │   │   │   ├── Motion.tsx
│   │   │       │   │   │   ├── NotFound.tsx
│   │   │       │   │   │   ├── PoweredByFooter.tsx
│   │   │       │   │   │   ├── ProviderComposer.tsx
│   │   │       │   │   │   ├── ReloadPrompt.tsx
│   │   │       │   │   │   ├── ShadowDOM.tsx
│   │   │       │   │   │   ├── SharePanel.tsx
│   │   │       │   │   │   └── withAppErrorBoundary.tsx
│   │   │       │   │   ├── errors/
│   │   │       │   │   │   ├── EntryNotFound.tsx
│   │   │       │   │   │   ├── FeedNotFound.tsx
│   │   │       │   │   │   ├── ModalError.tsx
│   │   │       │   │   │   ├── PageError.tsx
│   │   │       │   │   │   ├── RSSHubError.tsx
│   │   │       │   │   │   ├── enum.ts
│   │   │       │   │   │   ├── helper.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── mobile/
│   │   │       │   │   │   └── button.tsx
│   │   │       │   │   ├── ui/
│   │   │       │   │   │   ├── ai-summary-card/
│   │   │       │   │   │   │   ├── AISummaryCardBase.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── auto-completion/
│   │   │       │   │   │   │   ├── AutoCompletion.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── background/
│   │   │       │   │   │   │   ├── WindowUnderBlur.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── button/
│   │   │       │   │   │   │   ├── AnimatedCommandButton.tsx
│   │   │       │   │   │   │   ├── CommandActionButton.tsx
│   │   │       │   │   │   │   ├── CopyButton.tsx
│   │   │       │   │   │   │   ├── GlassButton.tsx
│   │   │       │   │   │   │   └── HeaderActionButton.tsx
│   │   │       │   │   │   ├── code-highlighter/
│   │   │       │   │   │   │   ├── constants/
│   │   │       │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   └── shiki/
│   │   │       │   │   │   │       ├── Shiki.tsx
│   │   │       │   │   │   │       ├── hooks.ts
│   │   │       │   │   │   │       ├── index.ts
│   │   │       │   │   │   │       ├── shared.ts
│   │   │       │   │   │   │       └── shiki.module.css
│   │   │       │   │   │   ├── crop/
│   │   │       │   │   │   │   └── AvatarUploadModal.tsx
│   │   │       │   │   │   ├── datetime/
│   │   │       │   │   │   │   └── index.tsx
│   │   │       │   │   │   ├── dropdown-menu/
│   │   │       │   │   │   │   └── dropdown-menu.tsx
│   │   │       │   │   │   ├── fab/
│   │   │       │   │   │   │   ├── FABContainer.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── hover-preview/
│   │   │       │   │   │   │   ├── EntryPreviewCard.tsx
│   │   │       │   │   │   │   ├── FeedPreviewCard.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── keyboard-recorder/
│   │   │       │   │   │   │   ├── KeyRecorder.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── markdown/
│   │   │       │   │   │   │   ├── HTML.tsx
│   │   │       │   │   │   │   ├── Markdown.tsx
│   │   │       │   │   │   │   ├── components/
│   │   │       │   │   │   │   │   ├── Toc.tsx
│   │   │       │   │   │   │   │   ├── TocItem.tsx
│   │   │       │   │   │   │   │   └── hooks.tsx
│   │   │       │   │   │   │   ├── context.tsx
│   │   │       │   │   │   │   ├── renderers/
│   │   │       │   │   │   │   │   ├── BlockErrorBoundary.tsx
│   │   │       │   │   │   │   │   ├── BlockImage.tsx
│   │   │       │   │   │   │   │   ├── Heading.tsx
│   │   │       │   │   │   │   │   ├── InlineImage.tsx
│   │   │       │   │   │   │   │   ├── MarkdownLink.tsx
│   │   │       │   │   │   │   │   ├── MarkdownP.tsx
│   │   │       │   │   │   │   │   ├── ctx.tsx
│   │   │       │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   ├── media/
│   │   │       │   │   │   │   ├── Media.tsx
│   │   │       │   │   │   │   ├── MediaContainerWidthContext.tsx
│   │   │       │   │   │   │   ├── MediaContainerWidthProvider.tsx
│   │   │       │   │   │   │   ├── MediaInfoRecord.tsx
│   │   │       │   │   │   │   ├── MediaInfoRecordContext.tsx
│   │   │       │   │   │   │   ├── MediaInfoRecordProvider.tsx
│   │   │       │   │   │   │   ├── PreviewMediaContent.tsx
│   │   │       │   │   │   │   ├── SwipeMedia.tsx
│   │   │       │   │   │   │   ├── VideoPlayer.tsx
│   │   │       │   │   │   │   ├── VolumeSlider.tsx
│   │   │       │   │   │   │   └── hooks.tsx
│   │   │       │   │   │   ├── modal/
│   │   │       │   │   │   │   ├── components/
│   │   │       │   │   │   │   │   └── close.tsx
│   │   │       │   │   │   │   ├── helper/
│   │   │       │   │   │   │   │   ├── useAsyncModal.tsx
│   │   │       │   │   │   │   │   └── useModalStackCalculationAndEffect.tsx
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   ├── inspire/
│   │   │       │   │   │   │   │   ├── InPeekModal.tsx
│   │   │       │   │   │   │   │   └── PeekModal.tsx
│   │   │       │   │   │   │   └── stacked/
│   │   │       │   │   │   │       ├── AsyncModalContent.tsx
│   │   │       │   │   │   │       ├── atom.ts
│   │   │       │   │   │   │       ├── bus.ts
│   │   │       │   │   │   │       ├── components.tsx
│   │   │       │   │   │   │       ├── constants.ts
│   │   │       │   │   │   │       ├── context.tsx
│   │   │       │   │   │   │       ├── custom-modal.tsx
│   │   │       │   │   │   │       ├── declarative-modal.tsx
│   │   │       │   │   │   │       ├── helper.tsx
│   │   │       │   │   │   │       ├── hooks.tsx
│   │   │       │   │   │   │       ├── index.ts
│   │   │       │   │   │   │       ├── internal/
│   │   │       │   │   │   │       │   ├── use-animate.ts
│   │   │       │   │   │   │       │   ├── use-drag.ts
│   │   │       │   │   │   │       │   ├── use-select.ts
│   │   │       │   │   │   │       │   └── use-subscriber.ts
│   │   │       │   │   │   │       ├── modal-stack.tsx
│   │   │       │   │   │   │       ├── modal.tsx
│   │   │       │   │   │   │       ├── overlay.tsx
│   │   │       │   │   │   │       ├── provider.tsx
│   │   │       │   │   │   │       └── types.tsx
│   │   │       │   │   │   ├── paper/
│   │   │       │   │   │   │   ├── Paper.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   └── peek-modal/
│   │   │       │   │   │       ├── EntryModalPreview.tsx
│   │   │       │   │   │       ├── EntryMoreActions.tsx
│   │   │       │   │   │       └── EntryToastPreview.tsx
│   │   │       │   │   └── ux/
│   │   │       │   │       ├── pull-to-refresh/
│   │   │       │   │       │   └── index.tsx
│   │   │       │   │       └── transition/
│   │   │       │   │           └── icon.tsx
│   │   │       │   ├── constants/
│   │   │       │   │   ├── app.tsx
│   │   │       │   │   ├── copy.ts
│   │   │       │   │   ├── dom.ts
│   │   │       │   │   ├── env.ts
│   │   │       │   │   ├── hotkeys.ts
│   │   │       │   │   ├── index.ts
│   │   │       │   │   └── ui.ts
│   │   │       │   ├── env.d.ts
│   │   │       │   ├── errors/
│   │   │       │   │   └── CustomSafeError.ts
│   │   │       │   ├── global.d.ts
│   │   │       │   ├── hooks/
│   │   │       │   │   ├── biz/
│   │   │       │   │   │   ├── useAsRead.ts
│   │   │       │   │   │   ├── useContextMenuActionShortCutTrigger.ts
│   │   │       │   │   │   ├── useDiscoverRSSHubRoute.tsx
│   │   │       │   │   │   ├── useEntryActions.tsx
│   │   │       │   │   │   ├── useEntryContextMenu.ts
│   │   │       │   │   │   ├── useFeature.ts
│   │   │       │   │   │   ├── useFeedActions.tsx
│   │   │       │   │   │   ├── useFollow.tsx
│   │   │       │   │   │   ├── useNavigateEntry.ts
│   │   │       │   │   │   ├── usePeekModal.tsx
│   │   │       │   │   │   ├── useProxySetting.ts
│   │   │       │   │   │   ├── useReduceMotion.ts
│   │   │       │   │   │   ├── useRenderStyle.tsx
│   │   │       │   │   │   ├── useRouteParams.ts
│   │   │       │   │   │   ├── useShowEntryDetailsColumn.ts
│   │   │       │   │   │   ├── useSubscriptionActions.tsx
│   │   │       │   │   │   ├── useTimelineList.ts
│   │   │       │   │   │   └── useTraySetting.ts
│   │   │       │   │   └── common/
│   │   │       │   │       ├── index.ts
│   │   │       │   │       ├── useBizQuery.ts
│   │   │       │   │       ├── useContextMenu.tsx
│   │   │       │   │       ├── useFeedSafeUrl.ts
│   │   │       │   │       ├── useI18n.ts
│   │   │       │   │       ├── useLoginModal.tsx
│   │   │       │   │       ├── usePreventOverscrollBounce.ts
│   │   │       │   │       ├── useRecaptchaToken.ts
│   │   │       │   │       ├── useRequireLogin.ts
│   │   │       │   │       └── useSyncTheme.ts
│   │   │       │   ├── i18n.ts
│   │   │       │   ├── initialize/
│   │   │       │   │   ├── analytics.ts
│   │   │       │   │   ├── global-shortcuts.ts
│   │   │       │   │   ├── helper.ts
│   │   │       │   │   ├── history.ts
│   │   │       │   │   ├── index.ts
│   │   │       │   │   ├── migrates/
│   │   │       │   │   │   ├── helper.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   └── v/
│   │   │       │   │   │       └── v1.ts
│   │   │       │   │   ├── queue.ts
│   │   │       │   │   └── settings.ts
│   │   │       │   ├── lib/
│   │   │       │   │   ├── __tests__/
│   │   │       │   │   │   └── parse-html.test.ts
│   │   │       │   │   ├── api-client.ts
│   │   │       │   │   ├── app.ts
│   │   │       │   │   ├── auth.ts
│   │   │       │   │   ├── avatar-upload.ts
│   │   │       │   │   ├── client-session.ts
│   │   │       │   │   ├── client.ts
│   │   │       │   │   ├── clipboard.ts
│   │   │       │   │   ├── defineQuery.ts
│   │   │       │   │   ├── dev.tsx
│   │   │       │   │   ├── error-parser.ts
│   │   │       │   │   ├── export.ts
│   │   │       │   │   ├── features.tsx
│   │   │       │   │   ├── ga4.ts
│   │   │       │   │   ├── img-proxy.ts
│   │   │       │   │   ├── issues.ts
│   │   │       │   │   ├── jotai.ts
│   │   │       │   │   ├── language.ts
│   │   │       │   │   ├── load-language.ts
│   │   │       │   │   ├── log.ts
│   │   │       │   │   ├── native-menu.ts
│   │   │       │   │   ├── observe-resize.ts
│   │   │       │   │   ├── parse-html.ts
│   │   │       │   │   ├── parse-markdown.ts
│   │   │       │   │   ├── parsers.ts
│   │   │       │   │   ├── query-client.ts
│   │   │       │   │   ├── simple-text-selection.ts
│   │   │       │   │   ├── url-builder.ts
│   │   │       │   │   └── utils.ts
│   │   │       │   ├── main.tsx
│   │   │       │   ├── modules/
│   │   │       │   │   ├── action/
│   │   │       │   │   │   ├── action-setting.tsx
│   │   │       │   │   │   ├── constants.tsx
│   │   │       │   │   │   ├── rule-card.tsx
│   │   │       │   │   │   ├── rule-summary.ts
│   │   │       │   │   │   ├── then-section.tsx
│   │   │       │   │   │   ├── utils.ts
│   │   │       │   │   │   └── when-section.tsx
│   │   │       │   │   ├── ai-chat/
│   │   │       │   │   │   ├── atoms/
│   │   │       │   │   │   │   └── session.ts
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── 3d-models/
│   │   │       │   │   │   │   │   ├── AISpline.ts
│   │   │       │   │   │   │   │   └── AISplineLoader.tsx
│   │   │       │   │   │   │   ├── context-bar/
│   │   │       │   │   │   │   │   ├── MentionButton.tsx
│   │   │       │   │   │   │   │   ├── blocks/
│   │   │       │   │   │   │   │   │   ├── ContextBlock.tsx
│   │   │       │   │   │   │   │   │   ├── TitleComponents.tsx
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   └── menus/
│   │   │       │   │   │   │   │       ├── ShortcutsMenuContent.tsx
│   │   │       │   │   │   │   │       └── index.ts
│   │   │       │   │   │   │   ├── displays/
│   │   │       │   │   │   │   │   ├── AIChainOfThought.tsx
│   │   │       │   │   │   │   │   ├── AIDisplayFlowPart.tsx
│   │   │       │   │   │   │   │   ├── AIReasoningPart.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── share.tsx
│   │   │       │   │   │   │   │   └── shared/
│   │   │       │   │   │   │   │       ├── AnalyticsMetrics.tsx
│   │   │       │   │   │   │   │       ├── CategoryTag.tsx
│   │   │       │   │   │   │   │       ├── DisplayHeader.tsx
│   │   │       │   │   │   │   │       ├── EmptyState.tsx
│   │   │       │   │   │   │   │       ├── FeedItemCard.tsx
│   │   │       │   │   │   │   │       ├── GroupedContent.tsx
│   │   │       │   │   │   │   │       ├── StatCard.tsx
│   │   │       │   │   │   │   │       └── index.ts
│   │   │       │   │   │   │   ├── file/
│   │   │       │   │   │   │   │   └── GlobalFileDropZone.tsx
│   │   │       │   │   │   │   ├── layouts/
│   │   │       │   │   │   │   │   ├── AIChatContextBar.tsx
│   │   │       │   │   │   │   │   ├── AIChatRoot.tsx
│   │   │       │   │   │   │   │   ├── AIChatSendButton.tsx
│   │   │       │   │   │   │   │   ├── AIErrorFallback.tsx
│   │   │       │   │   │   │   │   ├── AIModelIndicator.tsx
│   │   │       │   │   │   │   │   ├── AISmartSidebar.css
│   │   │       │   │   │   │   │   ├── AISmartSidebar.tsx
│   │   │       │   │   │   │   │   ├── ChatBottomPanel.tsx
│   │   │       │   │   │   │   │   ├── ChatHeader.tsx
│   │   │       │   │   │   │   │   ├── ChatHistoryDropdown.tsx
│   │   │       │   │   │   │   │   ├── ChatInput.tsx
│   │   │       │   │   │   │   │   ├── ChatInterface.tsx
│   │   │       │   │   │   │   │   ├── ChatMessageContainer.tsx
│   │   │       │   │   │   │   │   ├── ChatMoreDropdown.tsx
│   │   │       │   │   │   │   │   ├── ChatShortcutsRow.tsx
│   │   │       │   │   │   │   │   ├── ChatTitle.tsx
│   │   │       │   │   │   │   │   ├── Messages.tsx
│   │   │       │   │   │   │   │   ├── RateLimitNotice.tsx
│   │   │       │   │   │   │   │   ├── ScrollToBottomButton.tsx
│   │   │       │   │   │   │   │   ├── TaskReportDropdown.tsx
│   │   │       │   │   │   │   │   ├── WelcomeScreen.tsx
│   │   │       │   │   │   │   │   └── shared/
│   │   │       │   │   │   │   │       ├── ChatSessionComponents.tsx
│   │   │       │   │   │   │   │       ├── index.ts
│   │   │       │   │   │   │   │       ├── useChatSessionHandlers.tsx
│   │   │       │   │   │   │   │       └── utils.ts
│   │   │       │   │   │   │   ├── message/
│   │   │       │   │   │   │   │   ├── AIChatMessage.tsx
│   │   │       │   │   │   │   │   ├── AIDataBlockPart.tsx
│   │   │       │   │   │   │   │   ├── AIMarkdownMessage.tsx
│   │   │       │   │   │   │   │   ├── AIMessageIdContext.tsx
│   │   │       │   │   │   │   │   ├── AIMessageParts.tsx
│   │   │       │   │   │   │   │   ├── BlockTitleComponents.tsx
│   │   │       │   │   │   │   │   ├── EditableMessage.tsx
│   │   │       │   │   │   │   │   ├── ErrorMessage.tsx
│   │   │       │   │   │   │   │   ├── ImageThumbnail.tsx
│   │   │       │   │   │   │   │   ├── TokenUsagePill.tsx
│   │   │       │   │   │   │   │   ├── ToolInvocationComponent.tsx
│   │   │       │   │   │   │   │   ├── UserChatMessage.tsx
│   │   │       │   │   │   │   │   ├── UserMessageParts.tsx
│   │   │       │   │   │   │   │   ├── UserRichTextMessage.tsx
│   │   │       │   │   │   │   │   ├── ai-block-constants.ts
│   │   │       │   │   │   │   │   ├── animated/
│   │   │       │   │   │   │   │   │   ├── AnimatedMarkdown.tsx
│   │   │       │   │   │   │   │   │   ├── TokenizedText.tsx
│   │   │       │   │   │   │   │   │   └── constants.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── parse-incomplete-markdown.ts
│   │   │       │   │   │   │   │   └── useContextBlockPresentation.tsx
│   │   │       │   │   │   │   ├── shared/
│   │   │       │   │   │   │   │   └── common-states.tsx
│   │   │       │   │   │   │   ├── ui/
│   │   │       │   │   │   │   │   ├── AIShortcutButton.tsx
│   │   │       │   │   │   │   │   ├── ShortcutTooltip.tsx
│   │   │       │   │   │   │   │   └── UploadProgress.tsx
│   │   │       │   │   │   │   └── welcome/
│   │   │       │   │   │   │       ├── DefaultWelcomeContent.tsx
│   │   │       │   │   │   │       ├── EntrySummaryCard.tsx
│   │   │       │   │   │   │       ├── EntryWelcomeContent.tsx
│   │   │       │   │   │   │       └── index.ts
│   │   │       │   │   │   ├── constants/
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── editor/
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   └── plugins/
│   │   │       │   │   │   │       ├── file-upload/
│   │   │       │   │   │   │       │   ├── FileAttachmentNode.tsx
│   │   │       │   │   │   │       │   ├── FileUploadPlugin.tsx
│   │   │       │   │   │   │       │   ├── components/
│   │   │       │   │   │   │       │   │   └── FileDropZone.tsx
│   │   │       │   │   │   │       │   ├── hooks/
│   │   │       │   │   │   │       │   │   ├── useFileAttachmentBlockSync.ts
│   │   │       │   │   │   │       │   │   └── useFileUploadIntegration.ts
│   │   │       │   │   │   │       │   ├── index.ts
│   │   │       │   │   │   │       │   ├── types.ts
│   │   │       │   │   │   │       │   └── utils/
│   │   │       │   │   │   │       │       └── file-handling.ts
│   │   │       │   │   │   │       ├── index.tsx
│   │   │       │   │   │   │       ├── mention/
│   │   │       │   │   │   │       │   ├── MentionNode.tsx
│   │   │       │   │   │   │       │   ├── MentionPlugin.tsx
│   │   │       │   │   │   │       │   ├── components/
│   │   │       │   │   │   │       │   │   ├── MentionComponent.tsx
│   │   │       │   │   │   │       │   │   ├── MentionDropdown.tsx
│   │   │       │   │   │   │       │   │   └── shared/
│   │   │       │   │   │   │       │   │       └── MentionTypeIcon.tsx
│   │   │       │   │   │   │       │   ├── constants.ts
│   │   │       │   │   │   │       │   ├── hooks/
│   │   │       │   │   │   │       │   │   ├── dateMentionConfig.ts
│   │   │       │   │   │   │       │   │   ├── dateMentionParsers.ts
│   │   │       │   │   │   │       │   │   ├── dateMentionSearch.ts
│   │   │       │   │   │   │       │   │   ├── dateMentionUtils.ts
│   │   │       │   │   │   │       │   │   ├── useMentionKeyboard.ts
│   │   │       │   │   │   │       │   │   ├── useMentionSearch.ts
│   │   │       │   │   │   │       │   │   ├── useMentionSearchService.ts
│   │   │       │   │   │   │       │   │   ├── useMentionSelection.ts
│   │   │       │   │   │   │       │   │   └── useMentionTrigger.ts
│   │   │       │   │   │   │       │   ├── index.ts
│   │   │       │   │   │   │       │   ├── types.ts
│   │   │       │   │   │   │       │   └── utils/
│   │   │       │   │   │   │       │       ├── mentionTextValue.ts
│   │   │       │   │   │   │       │       ├── parseNaturalLanguageDate.ts
│   │   │       │   │   │   │       │       ├── textReplacement.ts
│   │   │       │   │   │   │       │       └── triggerDetection.ts
│   │   │       │   │   │   │       ├── selection/
│   │   │       │   │   │   │       │   ├── SelectedTextNode.tsx
│   │   │       │   │   │   │       │   ├── SelectedTextNodeComponent.tsx
│   │   │       │   │   │   │       │   ├── SelectedTextPlugin.tsx
│   │   │       │   │   │   │       │   ├── index.ts
│   │   │       │   │   │   │       │   ├── insertSelectedTextNode.ts
│   │   │       │   │   │   │       │   └── selectedTextBridge.ts
│   │   │       │   │   │   │       ├── shared/
│   │   │       │   │   │   │       │   ├── components/
│   │   │       │   │   │   │       │   │   ├── MentionLikePill.tsx
│   │   │       │   │   │   │       │   │   ├── TypeaheadDropdown.tsx
│   │   │       │   │   │   │       │   │   └── index.ts
│   │   │       │   │   │   │       │   ├── hooks/
│   │   │       │   │   │   │       │   │   ├── useListKeyboardNavigation.ts
│   │   │       │   │   │   │       │   │   ├── useTextTrigger.ts
│   │   │       │   │   │   │       │   │   └── useTypeaheadSelection.ts
│   │   │       │   │   │   │       │   └── utils/
│   │   │       │   │   │   │       │       └── positioning.ts
│   │   │       │   │   │   │       └── shortcut/
│   │   │       │   │   │   │           ├── ShortcutNode.tsx
│   │   │       │   │   │   │           ├── ShortcutPlugin.tsx
│   │   │       │   │   │   │           ├── components/
│   │   │       │   │   │   │           │   ├── ShortcutComponent.tsx
│   │   │       │   │   │   │           │   └── ShortcutDropdown.tsx
│   │   │       │   │   │   │           ├── constants.ts
│   │   │       │   │   │   │           ├── hooks/
│   │   │       │   │   │   │           │   ├── useShortcutKeyboard.ts
│   │   │       │   │   │   │           │   ├── useShortcutSearch.ts
│   │   │       │   │   │   │           │   ├── useShortcutSearchService.ts
│   │   │       │   │   │   │           │   ├── useShortcutSelection.ts
│   │   │       │   │   │   │           │   └── useShortcutTrigger.ts
│   │   │       │   │   │   │           ├── index.ts
│   │   │       │   │   │   │           ├── types.ts
│   │   │       │   │   │   │           └── utils/
│   │   │       │   │   │   │               ├── index.ts
│   │   │       │   │   │   │               ├── positioning.ts
│   │   │       │   │   │   │               ├── shortcutTextValue.ts
│   │   │       │   │   │   │               ├── textReplacement.ts
│   │   │       │   │   │   │               └── triggerDetection.ts
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── useAIConfiguration.ts
│   │   │       │   │   │   │   ├── useAIModel.ts
│   │   │       │   │   │   │   ├── useAIShortcut.ts
│   │   │       │   │   │   │   ├── useAttachScrollBeyond.tsx
│   │   │       │   │   │   │   ├── useAutoScroll.tsx
│   │   │       │   │   │   │   ├── useAutoTimelineSummaryShortcut.ts
│   │   │       │   │   │   │   ├── useChatHistory.ts
│   │   │       │   │   │   │   ├── useDisplayBlocks.ts
│   │   │       │   │   │   │   ├── useFeedEntrySearchService.ts
│   │   │       │   │   │   │   ├── useFileUpload.ts
│   │   │       │   │   │   │   ├── useLoadMessages.ts
│   │   │       │   │   │   │   ├── useMainEntryId.ts
│   │   │       │   │   │   │   ├── useSendAIShortcut.ts
│   │   │       │   │   │   │   └── useTimelineSummaryAutoContext.ts
│   │   │       │   │   │   ├── services/
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── store/
│   │   │       │   │   │   │   ├── AIChatContext.ts
│   │   │       │   │   │   │   ├── chat-core/
│   │   │       │   │   │   │   │   ├── chat-actions.ts
│   │   │       │   │   │   │   │   ├── chat-instance.ts
│   │   │       │   │   │   │   │   ├── chat-state.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   ├── event-system/
│   │   │       │   │   │   │   │   ├── event-emitter.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   ├── hooks.ts
│   │   │       │   │   │   │   ├── slices/
│   │   │       │   │   │   │   │   ├── block.slice.ts
│   │   │       │   │   │   │   │   ├── chat.slice.ts
│   │   │       │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   ├── store.ts
│   │   │       │   │   │   │   ├── transport.ts
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   ├── types/
│   │   │       │   │   │   │   └── ChatSession.ts
│   │   │       │   │   │   └── utils/
│   │   │       │   │   │       ├── error.ts
│   │   │       │   │   │       ├── extractor.ts
│   │   │       │   │   │       ├── file-processing.ts
│   │   │       │   │   │       ├── file-validation.ts
│   │   │       │   │   │       ├── mentionDate.ts
│   │   │       │   │   │       ├── rate-limit.ts
│   │   │       │   │   │       ├── shortcut.ts
│   │   │       │   │   │       └── titleGeneration.ts
│   │   │       │   │   ├── ai-chat-session/
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── query.ts
│   │   │       │   │   │   ├── service.ts
│   │   │       │   │   │   └── store.ts
│   │   │       │   │   ├── ai-onboarding/
│   │   │       │   │   │   ├── ai-chat-pane.tsx
│   │   │       │   │   │   ├── ai-onboarding-modal-content.tsx
│   │   │       │   │   │   ├── feeds-selection-list.tsx
│   │   │       │   │   │   ├── modal.tsx
│   │   │       │   │   │   └── store.ts
│   │   │       │   │   ├── ai-task/
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── ai-item-actions.tsx
│   │   │       │   │   │   │   ├── ai-task-modal.tsx
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   ├── notify-channels-config.tsx
│   │   │       │   │   │   │   ├── schedule-config.tsx
│   │   │       │   │   │   │   ├── task-item.tsx
│   │   │       │   │   │   │   └── task-list.tsx
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── query.ts
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── app/
│   │   │       │   │   │   ├── EnvironmentIndicator.tsx
│   │   │       │   │   │   ├── NetworkStatusIndicator.tsx
│   │   │       │   │   │   └── Titlebar.tsx
│   │   │       │   │   ├── app-layout/
│   │   │       │   │   │   ├── LAYOUT_ARCHITECTURE.md
│   │   │       │   │   │   ├── MainDestopLayout.tsx
│   │   │       │   │   │   ├── ai/
│   │   │       │   │   │   │   ├── AIChatFixedPanel.tsx
│   │   │       │   │   │   │   ├── AIChatFloatingPanel.tsx
│   │   │       │   │   │   │   └── AISplineButton.tsx
│   │   │       │   │   │   ├── ai-enhanced-timeline/
│   │   │       │   │   │   │   ├── AIEnhancedTimelineLayout.tsx
│   │   │       │   │   │   │   ├── MobileTimelineLayout.tsx
│   │   │       │   │   │   │   └── index.tsx
│   │   │       │   │   │   ├── entry-content/
│   │   │       │   │   │   │   └── EntryContentPlaceholder.tsx
│   │   │       │   │   │   ├── subscription-column/
│   │   │       │   │   │   │   ├── SubscriptionColumn.tsx
│   │   │       │   │   │   │   ├── components/
│   │   │       │   │   │   │   │   └── PodcastButton.tsx
│   │   │       │   │   │   │   └── index.tsx
│   │   │       │   │   │   └── subview/
│   │   │       │   │   │       ├── SubviewLayout.tsx
│   │   │       │   │   │       ├── hooks.ts
│   │   │       │   │   │       └── index.tsx
│   │   │       │   │   ├── app-tip/
│   │   │       │   │   │   ├── AICopilotMedia.tsx
│   │   │       │   │   │   ├── AppTipDialog.tsx
│   │   │       │   │   │   ├── AppTipMediaPreview.tsx
│   │   │       │   │   │   ├── AppTipModalContent.tsx
│   │   │       │   │   │   ├── OverviewMedia.tsx
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── types.ts
│   │   │       │   │   │   └── useNewUserGuideState.ts
│   │   │       │   │   ├── auth/
│   │   │       │   │   │   ├── Form.tsx
│   │   │       │   │   │   ├── LoginModalContent.tsx
│   │   │       │   │   │   └── TokenModal.tsx
│   │   │       │   │   ├── claim/
│   │   │       │   │   │   ├── feed-claim-modal.tsx
│   │   │       │   │   │   ├── hooks.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── command/
│   │   │       │   │   │   ├── README.md
│   │   │       │   │   │   ├── command-button.test-d.ts
│   │   │       │   │   │   ├── command-button.tsx
│   │   │       │   │   │   ├── command-manager.ts
│   │   │       │   │   │   ├── commands/
│   │   │       │   │   │   │   ├── entry-render.tsx
│   │   │       │   │   │   │   ├── entry.tsx
│   │   │       │   │   │   │   ├── global.tsx
│   │   │       │   │   │   │   ├── id.ts
│   │   │       │   │   │   │   ├── integration.tsx
│   │   │       │   │   │   │   ├── layout.tsx
│   │   │       │   │   │   │   ├── list.tsx
│   │   │       │   │   │   │   ├── settings.tsx
│   │   │       │   │   │   │   ├── subscription.tsx
│   │   │       │   │   │   │   ├── timeline.tsx
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── use-command-binding.ts
│   │   │       │   │   │   │   ├── use-command.test-d.ts
│   │   │       │   │   │   │   ├── use-command.ts
│   │   │       │   │   │   │   ├── use-register-command.test-d.ts
│   │   │       │   │   │   │   ├── use-register-command.ts
│   │   │       │   │   │   │   ├── use-register-hotkey.test-d.ts
│   │   │       │   │   │   │   └── use-register-hotkey.ts
│   │   │       │   │   │   ├── mutation-command-ids.ts
│   │   │       │   │   │   ├── registry/
│   │   │       │   │   │   │   ├── command.test-d.ts
│   │   │       │   │   │   │   ├── command.ts
│   │   │       │   │   │   │   └── registry.ts
│   │   │       │   │   │   ├── shortcuts/
│   │   │       │   │   │   │   └── SettingShortcuts.tsx
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── customize-toolbar/
│   │   │       │   │   │   ├── constant.ts
│   │   │       │   │   │   ├── dnd.tsx
│   │   │       │   │   │   ├── hooks.ts
│   │   │       │   │   │   └── modal.tsx
│   │   │       │   │   ├── debug/
│   │   │       │   │   │   └── registry.ts
│   │   │       │   │   ├── discover/
│   │   │       │   │   │   ├── DiscoverFeedCard.tsx
│   │   │       │   │   │   ├── DiscoverFeedForm.tsx
│   │   │       │   │   │   ├── DiscoverForm.tsx
│   │   │       │   │   │   ├── DiscoverImport.tsx
│   │   │       │   │   │   ├── DiscoverInboxList.tsx
│   │   │       │   │   │   ├── DiscoverTransform.tsx
│   │   │       │   │   │   ├── DiscoverUser.tsx
│   │   │       │   │   │   ├── DiscoveryContent.tsx
│   │   │       │   │   │   ├── FeedForm.tsx
│   │   │       │   │   │   ├── FeedSummary.tsx
│   │   │       │   │   │   ├── Inbox/
│   │   │       │   │   │   │   ├── ConfirmDestroyModalContent.tsx
│   │   │       │   │   │   │   ├── InboxActions.tsx
│   │   │       │   │   │   │   ├── InboxEmail.tsx
│   │   │       │   │   │   │   ├── InboxSecret.tsx
│   │   │       │   │   │   │   ├── InboxTable.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── InboxForm.tsx
│   │   │       │   │   │   ├── ListForm.tsx
│   │   │       │   │   │   ├── OpmlAbstractGraphic.tsx
│   │   │       │   │   │   ├── OpmlSelectionModal.tsx
│   │   │       │   │   │   ├── RecommendationContent.tsx
│   │   │       │   │   │   ├── TrendingFeedCard.tsx
│   │   │       │   │   │   ├── UnifiedDiscoverForm.tsx
│   │   │       │   │   │   ├── atoms/
│   │   │       │   │   │   │   └── discover.ts
│   │   │       │   │   │   ├── example-data.json
│   │   │       │   │   │   ├── recommendations.tsx
│   │   │       │   │   │   ├── types.ts
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── download/
│   │   │       │   │   │   └── index.tsx
│   │   │       │   │   ├── editor/
│   │   │       │   │   │   └── css-editor.tsx
│   │   │       │   │   ├── entry-column/
│   │   │       │   │   │   ├── EntryColumnShortcutHandler.tsx
│   │   │       │   │   │   ├── EntryItemSkeleton.tsx
│   │   │       │   │   │   ├── Items/
│   │   │       │   │   │   │   ├── all-item.tsx
│   │   │       │   │   │   │   ├── article-item.tsx
│   │   │       │   │   │   │   ├── audio-item.tsx
│   │   │       │   │   │   │   ├── getItemComponentByView.ts
│   │   │       │   │   │   │   ├── getSkeletonItemComponentByView.ts
│   │   │       │   │   │   │   ├── list-item.tsx
│   │   │       │   │   │   │   ├── media-gallery.tsx
│   │   │       │   │   │   │   ├── notification-item.tsx
│   │   │       │   │   │   │   ├── picture-item-skeleton.tsx
│   │   │       │   │   │   │   ├── picture-item-stateless.tsx
│   │   │       │   │   │   │   ├── picture-item.tsx
│   │   │       │   │   │   │   ├── picture-masonry.tsx
│   │   │       │   │   │   │   ├── social-media-item.tsx
│   │   │       │   │   │   │   └── video-item.tsx
│   │   │       │   │   │   ├── atoms/
│   │   │       │   │   │   │   ├── ai-timeline.ts
│   │   │       │   │   │   │   └── social-media-content-width.ts
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── DateItem.tsx
│   │   │       │   │   │   │   ├── FooterMarkItem.tsx
│   │   │       │   │   │   │   ├── VirtualRowItem.tsx
│   │   │       │   │   │   │   ├── ai-timeline-loading/
│   │   │       │   │   │   │   │   ├── AITimelineLoadingOverlay.css
│   │   │       │   │   │   │   │   └── AITimelineLoadingOverlay.tsx
│   │   │       │   │   │   │   ├── entry-column-wrapper/
│   │   │       │   │   │   │   │   ├── EntryColumnWrapper.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   └── types.tsx
│   │   │       │   │   │   │   └── mark-all-button.tsx
│   │   │       │   │   │   ├── context/
│   │   │       │   │   │   │   └── EntriesContext.tsx
│   │   │       │   │   │   ├── grid.tsx
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── useAttachScrollBeyond.tsx
│   │   │       │   │   │   │   ├── useEntriesByView.ts
│   │   │       │   │   │   │   ├── useEntryIdListSnap.ts
│   │   │       │   │   │   │   ├── useEntryMarkReadHandler.tsx
│   │   │       │   │   │   │   ├── useEntryVirtualization.ts
│   │   │       │   │   │   │   ├── useIsPreviewFeed.ts
│   │   │       │   │   │   │   ├── useLocalEntries.ts
│   │   │       │   │   │   │   ├── useMarkAll.ts
│   │   │       │   │   │   │   ├── useNavigateFirstEntry.tsx
│   │   │       │   │   │   │   └── useWheelGestureClose.ts
│   │   │       │   │   │   ├── index.tsx
│   │   │       │   │   │   ├── item-stateless.tsx
│   │   │       │   │   │   ├── item.tsx
│   │   │       │   │   │   ├── layouts/
│   │   │       │   │   │   │   ├── AppendTaildingDivider.tsx
│   │   │       │   │   │   │   ├── EntryItemWrapper.tsx
│   │   │       │   │   │   │   ├── EntryListHeader.tsx
│   │   │       │   │   │   │   └── buttons/
│   │   │       │   │   │   │       └── SwitchToMasonryButton.tsx
│   │   │       │   │   │   ├── list.tsx
│   │   │       │   │   │   ├── star-icon.tsx
│   │   │       │   │   │   ├── store/
│   │   │       │   │   │   │   └── EntryColumnContext.ts
│   │   │       │   │   │   ├── styles.ts
│   │   │       │   │   │   ├── templates/
│   │   │       │   │   │   │   ├── grid-item-template.tsx
│   │   │       │   │   │   │   └── list-item-template.tsx
│   │   │       │   │   │   ├── translation.tsx
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── entry-content/
│   │   │       │   │   │   ├── EntryContent.tsx
│   │   │       │   │   │   ├── EntryContentForPreview.tsx
│   │   │       │   │   │   ├── actions/
│   │   │       │   │   │   │   ├── header-actions.tsx
│   │   │       │   │   │   │   ├── more-actions.tsx
│   │   │       │   │   │   │   └── picture-gallery.tsx
│   │   │       │   │   │   ├── atoms.tsx
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── AISummary.tsx
│   │   │       │   │   │   │   ├── ApplyEntryActions.tsx
│   │   │       │   │   │   │   ├── EntryAttachments.tsx
│   │   │       │   │   │   │   ├── EntryPlaceholderLogo.tsx
│   │   │       │   │   │   │   ├── EntryTimelineSidebar.tsx
│   │   │       │   │   │   │   ├── EntryTitle.tsx
│   │   │       │   │   │   │   ├── ImageGalleryContent.tsx
│   │   │       │   │   │   │   ├── SourceContentView.tsx
│   │   │       │   │   │   │   ├── WarnGoToExternalLink.tsx
│   │   │       │   │   │   │   ├── entry-content/
│   │   │       │   │   │   │   │   ├── EntryCommandShortcutRegister.tsx
│   │   │       │   │   │   │   │   ├── EntryContentFallback.tsx
│   │   │       │   │   │   │   │   ├── EntryContentLoading.tsx
│   │   │       │   │   │   │   │   ├── EntryNoContent.tsx
│   │   │       │   │   │   │   │   ├── EntryRenderError.tsx
│   │   │       │   │   │   │   │   ├── EntryScrollingAndNavigationHandler.tsx
│   │   │       │   │   │   │   │   ├── EntryTitleMetaHandler.tsx
│   │   │       │   │   │   │   │   ├── ReadabilityNotice.tsx
│   │   │       │   │   │   │   │   ├── accessories/
│   │   │       │   │   │   │   │   │   ├── ContainerToc.tsx
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   └── types.tsx
│   │   │       │   │   │   │   ├── entry-header/
│   │   │       │   │   │   │   │   ├── AIEntryHeader.tsx
│   │   │       │   │   │   │   │   ├── EntryHeader.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── internal/
│   │   │       │   │   │   │   │   │   ├── EntryHeaderActionsContainer.tsx
│   │   │       │   │   │   │   │   │   ├── EntryHeaderBreadcrumb.tsx
│   │   │       │   │   │   │   │   │   ├── EntryHeaderMeta.tsx
│   │   │       │   │   │   │   │   │   ├── EntryHeaderReadHistory.tsx
│   │   │       │   │   │   │   │   │   └── context.tsx
│   │   │       │   │   │   │   │   └── types.tsx
│   │   │       │   │   │   │   ├── entry-read-history/
│   │   │       │   │   │   │   │   ├── EntryReadHistory.tsx
│   │   │       │   │   │   │   │   ├── EntryUser.tsx
│   │   │       │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   ├── layouts/
│   │   │       │   │   │   │   │   ├── ArticleLayout.tsx
│   │   │       │   │   │   │   │   ├── MediaLayout.tsx
│   │   │       │   │   │   │   │   ├── PicturesLayout.tsx
│   │   │       │   │   │   │   │   ├── SocialMediaLayout.tsx
│   │   │       │   │   │   │   │   ├── VideosLayout.tsx
│   │   │       │   │   │   │   │   ├── factory.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── shared/
│   │   │       │   │   │   │   │   │   ├── AudioPlayer.tsx
│   │   │       │   │   │   │   │   │   ├── AuthorHeader.tsx
│   │   │       │   │   │   │   │   │   ├── ContentBody.tsx
│   │   │       │   │   │   │   │   │   ├── MediaTranscript.tsx
│   │   │       │   │   │   │   │   │   ├── TranscriptToggle.tsx
│   │   │       │   │   │   │   │   │   ├── VideoPlayer.tsx
│   │   │       │   │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   │   └── useTranscription.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   └── selection/
│   │   │       │   │   │   │       ├── GlassButton.tsx
│   │   │       │   │   │   │       ├── SharePosterModal.tsx
│   │   │       │   │   │   │       └── TextSelectionToolbar.tsx
│   │   │       │   │   │   ├── constants/
│   │   │       │   │   │   │   └── navigation-hints.ts
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   └── useEntryNavigationHints.ts
│   │   │       │   │   │   └── hooks.tsx
│   │   │       │   │   ├── feed/
│   │   │       │   │   │   ├── feed-certification.tsx
│   │   │       │   │   │   ├── feed-icon.tsx
│   │   │       │   │   │   ├── feed-summary.tsx
│   │   │       │   │   │   ├── feed-title.tsx
│   │   │       │   │   │   └── view-select-content.tsx
│   │   │       │   │   ├── integration/
│   │   │       │   │   │   ├── CustomIntegrationPreview.tsx
│   │   │       │   │   │   ├── CustomIntegrationValidator.tsx
│   │   │       │   │   │   ├── PlaceholderHelp.tsx
│   │   │       │   │   │   ├── URLSchemePreview.tsx
│   │   │       │   │   │   ├── custom-integration-manager.ts
│   │   │       │   │   │   ├── fetch-adapter.ts
│   │   │       │   │   │   └── url-scheme-handler.ts
│   │   │       │   │   ├── modal/
│   │   │       │   │   │   ├── ConfirmDestroyModalContent.tsx
│   │   │       │   │   │   ├── ShortcutModalContent.tsx
│   │   │       │   │   │   └── hooks/
│   │   │       │   │   │       ├── useConfirmUnsubscribeSubscriptionModal.tsx
│   │   │       │   │   │       └── useShortcutsModal.tsx
│   │   │       │   │   ├── new-user-guide/
│   │   │       │   │   │   ├── ai-chat-pane.tsx
│   │   │       │   │   │   ├── discover-import-step.tsx
│   │   │       │   │   │   ├── feeds-selection-list.tsx
│   │   │       │   │   │   ├── pre-finish.tsx
│   │   │       │   │   │   └── store.ts
│   │   │       │   │   ├── panel/
│   │   │       │   │   │   ├── cmdf.tsx
│   │   │       │   │   │   ├── cmdk.module.css
│   │   │       │   │   │   ├── cmdk.tsx
│   │   │       │   │   │   └── cmdn.tsx
│   │   │       │   │   ├── plan/
│   │   │       │   │   │   ├── UpgradePlanModalContent.tsx
│   │   │       │   │   │   └── index.tsx
│   │   │       │   │   ├── player/
│   │   │       │   │   │   ├── corner-player.tsx
│   │   │       │   │   │   └── entry-tts.ts
│   │   │       │   │   ├── power/
│   │   │       │   │   │   ├── my-wallet-section/
│   │   │       │   │   │   │   ├── create-wallet.tsx
│   │   │       │   │   │   │   ├── index.tsx
│   │   │       │   │   │   │   └── withdraw.tsx
│   │   │       │   │   │   └── transaction-section/
│   │   │       │   │   │       ├── TransactionsSection.tsx
│   │   │       │   │   │       ├── index.ts
│   │   │       │   │   │       └── tx-table/
│   │   │       │   │   │           ├── TxTable.tsx
│   │   │       │   │   │           ├── components.tsx
│   │   │       │   │   │           └── index.ts
│   │   │       │   │   ├── profile/
│   │   │       │   │   │   ├── account-management.tsx
│   │   │       │   │   │   ├── email-management.tsx
│   │   │       │   │   │   ├── hooks.ts
│   │   │       │   │   │   ├── profile-setting-form.tsx
│   │   │       │   │   │   ├── two-factor.tsx
│   │   │       │   │   │   ├── update-password-form.tsx
│   │   │       │   │   │   ├── user-profile-modal/
│   │   │       │   │   │   │   ├── UserProfileModalContent.tsx
│   │   │       │   │   │   │   ├── constants.ts
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   └── shared.tsx
│   │   │       │   │   │   └── user-profile-modal.constants.ts
│   │   │       │   │   ├── renderer/
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   └── TimeStamp.tsx
│   │   │       │   │   │   ├── context.tsx
│   │   │       │   │   │   ├── html.tsx
│   │   │       │   │   │   ├── markdown.tsx
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── review-prompt/
│   │   │       │   │   │   ├── ReviewPromptModalContent.tsx
│   │   │       │   │   │   ├── debug.ts
│   │   │       │   │   │   ├── provider.tsx
│   │   │       │   │   │   ├── use-review-prompt-state.ts
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── rsshub/
│   │   │       │   │   │   ├── add-modal-content.tsx
│   │   │       │   │   │   ├── delete-modal-content.tsx
│   │   │       │   │   │   └── set-modal-content.tsx
│   │   │       │   │   ├── settings/
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── context.tsx
│   │   │       │   │   │   ├── control.tsx
│   │   │       │   │   │   ├── helper/
│   │   │       │   │   │   │   ├── EnhancedIndicator.tsx
│   │   │       │   │   │   │   ├── SyncIndicator.tsx
│   │   │       │   │   │   │   ├── builder.ts
│   │   │       │   │   │   │   ├── setting-builder.tsx
│   │   │       │   │   │   │   ├── sync-queue.ts
│   │   │       │   │   │   │   └── withSettingEnable.tsx
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── use-setting-ctx.ts
│   │   │       │   │   │   │   └── useWrapEnhancedSettingItem.ts
│   │   │       │   │   │   ├── modal/
│   │   │       │   │   │   │   ├── SettingModalContent.tsx
│   │   │       │   │   │   │   ├── context.tsx
│   │   │       │   │   │   │   ├── hooks.ts
│   │   │       │   │   │   │   ├── layout.tsx
│   │   │       │   │   │   │   ├── use-setting-modal-hack.ts
│   │   │       │   │   │   │   └── useSettingModal.ts
│   │   │       │   │   │   ├── section.tsx
│   │   │       │   │   │   ├── sections/
│   │   │       │   │   │   │   └── fonts.tsx
│   │   │       │   │   │   ├── settings-glob.ts
│   │   │       │   │   │   ├── tabs/
│   │   │       │   │   │   │   ├── about.tsx
│   │   │       │   │   │   │   ├── ai/
│   │   │       │   │   │   │   │   ├── PanelStyleSection.tsx
│   │   │       │   │   │   │   │   ├── PersonalizePromptSection.tsx
│   │   │       │   │   │   │   │   ├── TimelinePromptSection.tsx
│   │   │       │   │   │   │   │   ├── byok/
│   │   │       │   │   │   │   │   │   ├── ByokProviderItem.tsx
│   │   │       │   │   │   │   │   │   ├── ByokProviderModalContent.tsx
│   │   │       │   │   │   │   │   │   ├── ByokSection.tsx
│   │   │       │   │   │   │   │   │   ├── constants.ts
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── mcp/
│   │   │       │   │   │   │   │   │   ├── MCPPresetCard.tsx
│   │   │       │   │   │   │   │   │   ├── MCPPresetSelectionModal.tsx
│   │   │       │   │   │   │   │   │   ├── MCPServiceItem.tsx
│   │   │       │   │   │   │   │   │   ├── MCPServiceModalContent.tsx
│   │   │       │   │   │   │   │   │   ├── MCPServicesSection.tsx
│   │   │       │   │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   │   ├── shortcuts/
│   │   │       │   │   │   │   │   │   ├── AIShortcutsSection.tsx
│   │   │       │   │   │   │   │   │   ├── ShortcutItem.tsx
│   │   │       │   │   │   │   │   │   ├── ShortcutModalContent.tsx
│   │   │       │   │   │   │   │   │   ├── hooks.tsx
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   ├── tasks/
│   │   │       │   │   │   │   │   │   ├── TaskSchedulingSection.tsx
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   └── usage/
│   │   │       │   │   │   │   │       ├── UsageAnalysisSection.tsx
│   │   │       │   │   │   │   │       ├── components/
│   │   │       │   │   │   │   │       │   ├── DetailedUsageModal.tsx
│   │   │       │   │   │   │   │       │   ├── EfficiencyTab.tsx
│   │   │       │   │   │   │   │       │   ├── HistoryTab.tsx
│   │   │       │   │   │   │   │       │   ├── OverviewTab.tsx
│   │   │       │   │   │   │   │       │   ├── PatternsTab.tsx
│   │   │       │   │   │   │   │       │   ├── UsageProgressRing.tsx
│   │   │       │   │   │   │   │       │   ├── UsageWarningBanner.tsx
│   │   │       │   │   │   │   │       │   ├── charts/
│   │   │       │   │   │   │   │       │   │   ├── BarList.tsx
│   │   │       │   │   │   │   │       │   │   ├── Sparkline.tsx
│   │   │       │   │   │   │   │       │   │   ├── TinyBars.tsx
│   │   │       │   │   │   │   │       │   │   └── index.ts
│   │   │       │   │   │   │   │       │   └── index.ts
│   │   │       │   │   │   │   │       ├── index.ts
│   │   │       │   │   │   │   │       ├── types.ts
│   │   │       │   │   │   │   │       └── utils.ts
│   │   │       │   │   │   │   ├── ai.tsx
│   │   │       │   │   │   │   ├── appearance.tsx
│   │   │       │   │   │   │   ├── cli.tsx
│   │   │       │   │   │   │   ├── data-control.tsx
│   │   │       │   │   │   │   ├── feeds.tsx
│   │   │       │   │   │   │   ├── general.tsx
│   │   │       │   │   │   │   ├── integration/
│   │   │       │   │   │   │   │   ├── CustomIntegrationModal.tsx
│   │   │       │   │   │   │   │   ├── CustomIntegrationSection.tsx
│   │   │       │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   ├── lists/
│   │   │       │   │   │   │   │   ├── hooks.tsx
│   │   │       │   │   │   │   │   ├── index.tsx
│   │   │       │   │   │   │   │   └── modals.tsx
│   │   │       │   │   │   │   ├── notifications.tsx
│   │   │       │   │   │   │   ├── plan.tsx
│   │   │       │   │   │   │   └── shortcut.tsx
│   │   │       │   │   │   ├── title.tsx
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── shared/
│   │   │       │   │   │   └── ViewSelectorRadioGroup.tsx
│   │   │       │   │   ├── subscription-column/
│   │   │       │   │   │   ├── CategoryRemoveDialogContent.tsx
│   │   │       │   │   │   ├── CategoryUnsubscribeDialogContent.tsx
│   │   │       │   │   │   ├── FeedCategory.tsx
│   │   │       │   │   │   ├── FeedItem.tsx
│   │   │       │   │   │   ├── RenameCategoryForm.tsx
│   │   │       │   │   │   ├── SimpleDiscoverModal.tsx
│   │   │       │   │   │   ├── SortedFeedItems.tsx
│   │   │       │   │   │   ├── SubscriptionColumnHeader.tsx
│   │   │       │   │   │   ├── SubscriptionTabButton.tsx
│   │   │       │   │   │   ├── TimelineTabsSettingsModal.tsx
│   │   │       │   │   │   ├── UnreadNumber.tsx
│   │   │       │   │   │   ├── atom.ts
│   │   │       │   │   │   ├── context.ts
│   │   │       │   │   │   ├── hook.ts
│   │   │       │   │   │   ├── index.tsx
│   │   │       │   │   │   ├── sort-by/
│   │   │       │   │   │   │   ├── SortByAlphabeticalList.tsx
│   │   │       │   │   │   │   ├── SortByUnreadList.tsx
│   │   │       │   │   │   │   ├── index.tsx
│   │   │       │   │   │   │   └── types.tsx
│   │   │       │   │   │   ├── styles.ts
│   │   │       │   │   │   └── subscription-list/
│   │   │       │   │   │       ├── EmptyFeedList.tsx
│   │   │       │   │   │       ├── ListHeader.tsx
│   │   │       │   │   │       ├── SortButton.tsx
│   │   │       │   │   │       ├── StarredItem.tsx
│   │   │       │   │   │       ├── SubscriptionList.tsx
│   │   │       │   │   │       ├── SubscriptionListGuard.tsx
│   │   │       │   │   │       └── index.ts
│   │   │       │   │   ├── trending/
│   │   │       │   │   │   └── index.tsx
│   │   │       │   │   ├── update-notice/
│   │   │       │   │   │   └── UpdateNotice.tsx
│   │   │       │   │   ├── upgrade/
│   │   │       │   │   │   ├── container.tsx
│   │   │       │   │   │   ├── lazy/
│   │   │       │   │   │   │   ├── index.electron.ts
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── user/
│   │   │       │   │   │   ├── LoginButton.tsx
│   │   │       │   │   │   ├── ProfileButton.tsx
│   │   │       │   │   │   ├── UserAvatar.tsx
│   │   │       │   │   │   ├── UserGallery.tsx
│   │   │       │   │   │   ├── UserProBadge.tsx
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   └── wallet/
│   │   │       │   │       └── balance.tsx
│   │   │       │   ├── pages/
│   │   │       │   │   ├── (main)/
│   │   │       │   │   │   ├── (layer)/
│   │   │       │   │   │   │   ├── (ai)/
│   │   │       │   │   │   │   │   └── ai/
│   │   │       │   │   │   │   │       └── index.tsx
│   │   │       │   │   │   │   ├── (subview)/
│   │   │       │   │   │   │   │   ├── action/
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   ├── discover/
│   │   │       │   │   │   │   │   │   ├── category/
│   │   │       │   │   │   │   │   │   │   └── [category].tsx
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   ├── layout.tsx
│   │   │       │   │   │   │   │   ├── power/
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   └── rsshub/
│   │   │       │   │   │   │   │       └── index.tsx
│   │   │       │   │   │   │   └── timeline/
│   │   │       │   │   │   │       └── [timelineId]/
│   │   │       │   │   │   │           ├── [feedId]/
│   │   │       │   │   │   │           │   ├── [entryId]/
│   │   │       │   │   │   │           │   │   └── index.tsx
│   │   │       │   │   │   │           │   ├── index.tsx
│   │   │       │   │   │   │           │   └── layout.tsx
│   │   │       │   │   │   │           └── layout.tsx
│   │   │       │   │   │   ├── index.sync.tsx
│   │   │       │   │   │   └── layout.tsx
│   │   │       │   │   └── settings/
│   │   │       │   │       ├── (settings)/
│   │   │       │   │       │   ├── about.tsx
│   │   │       │   │       │   ├── ai.tsx
│   │   │       │   │       │   ├── appearance.tsx
│   │   │       │   │       │   ├── cli.tsx
│   │   │       │   │       │   ├── data-control.tsx
│   │   │       │   │       │   ├── feeds.tsx
│   │   │       │   │       │   ├── general.tsx
│   │   │       │   │       │   ├── integration.tsx
│   │   │       │   │       │   ├── list.tsx
│   │   │       │   │       │   ├── notifications.tsx
│   │   │       │   │       │   ├── plan.tsx
│   │   │       │   │       │   ├── profile.tsx
│   │   │       │   │       │   └── shortcuts.tsx
│   │   │       │   │       ├── index.tsx
│   │   │       │   │       └── layout.tsx
│   │   │       │   ├── providers/
│   │   │       │   │   ├── app-grid-layout-container-provider.tsx
│   │   │       │   │   ├── context-menu-provider.tsx
│   │   │       │   │   ├── extension-expose-provider.tsx
│   │   │       │   │   ├── external-jump-in-provider.tsx
│   │   │       │   │   ├── global-focusable-provider.tsx
│   │   │       │   │   ├── global-hotkeys-provider.tsx
│   │   │       │   │   ├── hotkey-provider.tsx
│   │   │       │   │   ├── i18n-provider.tsx
│   │   │       │   │   ├── inject-styles-provider.tsx
│   │   │       │   │   ├── invalidate-query-provider.tsx
│   │   │       │   │   ├── lazy/
│   │   │       │   │   │   ├── index.electron.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── main-view-hotkeys-provider.tsx
│   │   │       │   │   ├── popover-provider.tsx
│   │   │       │   │   ├── root-providers.tsx
│   │   │       │   │   ├── server-configs-provider.tsx
│   │   │       │   │   ├── setting-sync.tsx
│   │   │       │   │   ├── user-provider.tsx
│   │   │       │   │   └── wrapped-element-provider.tsx
│   │   │       │   ├── push-notification.ts
│   │   │       │   ├── queries/
│   │   │       │   │   ├── _.ts
│   │   │       │   │   ├── auth.ts
│   │   │       │   │   ├── discover.ts
│   │   │       │   │   ├── entries.ts
│   │   │       │   │   ├── feed.ts
│   │   │       │   │   ├── index.ts
│   │   │       │   │   ├── mcp.ts
│   │   │       │   │   ├── messaging.ts
│   │   │       │   │   ├── rsshub.ts
│   │   │       │   │   ├── server-configs.ts
│   │   │       │   │   ├── settings.ts
│   │   │       │   │   ├── types.d.ts
│   │   │       │   │   ├── users.ts
│   │   │       │   │   └── wallet.tsx
│   │   │       │   ├── router.tsx
│   │   │       │   ├── router.web.tsx
│   │   │       │   ├── store/
│   │   │       │   │   ├── feed/
│   │   │       │   │   │   └── hooks.ts
│   │   │       │   │   ├── image/
│   │   │       │   │   │   ├── db.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── search/
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── helper.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   └── utils/
│   │   │       │   │       ├── clear.ts
│   │   │       │   │       ├── helper.test.ts
│   │   │       │   │       └── helper.ts
│   │   │       │   ├── styles/
│   │   │       │   │   ├── additional.css
│   │   │       │   │   ├── base.css
│   │   │       │   │   ├── cursor.css
│   │   │       │   │   ├── font.css
│   │   │       │   │   ├── main.css
│   │   │       │   │   └── scrollbar.css
│   │   │       │   ├── sw.ts
│   │   │       │   ├── wdyr.ts
│   │   │       │   └── workers/
│   │   │       │       └── sw/
│   │   │       │           ├── index.ts
│   │   │       │           └── pusher.ts
│   │   │       ├── tsconfig.json
│   │   │       └── vitest.config.ts
│   │   ├── package.json
│   │   ├── plugins/
│   │   │   └── vite/
│   │   │       ├── ast.ts
│   │   │       ├── cleanup.ts
│   │   │       ├── compress.ts
│   │   │       ├── deps.ts
│   │   │       ├── generate-main-hash.ts
│   │   │       ├── hmr.ts
│   │   │       ├── html-inject.ts
│   │   │       ├── i18n-hmr.ts
│   │   │       ├── locales-json.ts
│   │   │       ├── locales.ts
│   │   │       ├── manifest.ts
│   │   │       ├── specific-import.ts
│   │   │       └── utils/
│   │   │           └── i18n-completeness.ts
│   │   ├── postcss.config.cjs
│   │   ├── resources/
│   │   │   ├── app-update.yml
│   │   │   ├── icon-staging.icns
│   │   │   └── icon.icns
│   │   ├── scripts/
│   │   │   ├── apply-changelog.ts
│   │   │   ├── generate-appx-manifest.ts
│   │   │   ├── merge-yml.ts
│   │   │   └── update-windows-yml.ts
│   │   ├── static/
│   │   │   └── dmg-icon.icns
│   │   ├── tailwind.config.ts
│   │   ├── vite.config.electron-render.ts
│   │   ├── vite.config.ts
│   │   └── wrangler.jsonc
│   ├── landing/
│   │   ├── .prettierrc.mjs
│   │   ├── components.json
│   │   ├── eslint.config.mjs
│   │   ├── global.d.ts
│   │   ├── next.config.mjs
│   │   ├── package.json
│   │   ├── plugins/
│   │   │   └── eslint-recursive-sort.mjs
│   │   ├── postcss.config.mjs
│   │   ├── public/
│   │   │   ├── discover-sources.json
│   │   │   └── manifest.json
│   │   ├── src/
│   │   │   ├── app/
│   │   │   │   ├── ClientInit.tsx
│   │   │   │   ├── InitInClient.ts
│   │   │   │   ├── [locale]/
│   │   │   │   │   ├── download/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── error.tsx
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   ├── pricing/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── privacy-policy/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   └── terms-of-service/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── apple-app-site-association/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── discover-sources/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── globals.css
│   │   │   │   ├── init.ts
│   │   │   │   ├── layout.tsx
│   │   │   │   └── robots.ts
│   │   │   ├── atoms/
│   │   │   │   ├── css-media.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── is-interactive.ts
│   │   │   │   └── viewport.ts
│   │   │   ├── components/
│   │   │   │   ├── brand/
│   │   │   │   │   ├── Folo.tsx
│   │   │   │   │   └── Logo.tsx
│   │   │   │   ├── common/
│   │   │   │   │   ├── ClientOnly.tsx
│   │   │   │   │   ├── ErrorBoundary.tsx
│   │   │   │   │   ├── GithubTrending.tsx
│   │   │   │   │   ├── HydrationEndDetector.tsx
│   │   │   │   │   ├── Lazyload.tsx
│   │   │   │   │   ├── LightRays.tsx
│   │   │   │   │   ├── ProviderComposer.tsx
│   │   │   │   │   ├── QueryHydrate.tsx
│   │   │   │   │   └── ScrollTop.tsx
│   │   │   │   ├── hoc/
│   │   │   │   │   └── with-no-ssr.tsx
│   │   │   │   ├── layout/
│   │   │   │   │   ├── container/
│   │   │   │   │   │   └── Normal.tsx
│   │   │   │   │   ├── content/
│   │   │   │   │   │   ├── Content.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── footer/
│   │   │   │   │   │   └── Footer.tsx
│   │   │   │   │   └── root/
│   │   │   │   │       └── Root.tsx
│   │   │   │   ├── ui/
│   │   │   │   │   ├── 3d-models/
│   │   │   │   │   │   ├── AISpline.tsx
│   │   │   │   │   │   └── AISplineLoader.tsx
│   │   │   │   │   ├── accordion/
│   │   │   │   │   │   └── Accordion.tsx
│   │   │   │   │   ├── border-beam.tsx
│   │   │   │   │   ├── button/
│   │   │   │   │   │   ├── Button.tsx
│   │   │   │   │   │   ├── MotionButton.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── checkbox/
│   │   │   │   │   │   ├── Checkbox.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── collapse/
│   │   │   │   │   │   ├── CollapseCss.tsx
│   │   │   │   │   │   ├── hooks.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── dialog/
│   │   │   │   │   │   ├── Dialog.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── divider/
│   │   │   │   │   │   ├── Divider.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── dropdown-menu/
│   │   │   │   │   │   └── DropdownMenu.tsx
│   │   │   │   │   ├── effects/
│   │   │   │   │   │   ├── GridGuides.tsx
│   │   │   │   │   │   ├── ParticlesAura.tsx
│   │   │   │   │   │   └── TiltCard.tsx
│   │   │   │   │   ├── glass/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── highlighter.tsx
│   │   │   │   │   ├── hover-card/
│   │   │   │   │   │   ├── HoverCard.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── Input.tsx
│   │   │   │   │   │   ├── TextArea.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── json-highlighter/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── label/
│   │   │   │   │   │   └── Label.tsx
│   │   │   │   │   ├── light-rays.tsx
│   │   │   │   │   ├── loading/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── magic-card.tsx
│   │   │   │   │   ├── markdown/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── modal/
│   │   │   │   │   │   ├── ModalContainer.tsx
│   │   │   │   │   │   ├── ModalManager.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── panel/
│   │   │   │   │   │   └── PanelSplitter.tsx
│   │   │   │   │   ├── portal/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── provider.tsx
│   │   │   │   │   ├── prompts/
│   │   │   │   │   │   ├── BasePrompt.tsx
│   │   │   │   │   │   ├── InputPrompt.tsx
│   │   │   │   │   │   ├── Prompt.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── radio/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── relative-time/
│   │   │   │   │   │   ├── RelativeTime.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── scroll-areas/
│   │   │   │   │   │   ├── ScrollArea.tsx
│   │   │   │   │   │   ├── ctx.ts
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── segment-tab/
│   │   │   │   │   │   ├── SegmentTab.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── select/
│   │   │   │   │   │   ├── ComboboxSelect.tsx
│   │   │   │   │   │   ├── MultiSelect.tsx
│   │   │   │   │   │   ├── Select.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── sheet/
│   │   │   │   │   │   ├── Sheet.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── skeleton/
│   │   │   │   │   │   ├── Skeleton.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── switch/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── theme-switcher/
│   │   │   │   │   │   ├── ThemeSwitcher.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── tooltip/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── styles.ts
│   │   │   │   │   ├── transition/
│   │   │   │   │   │   ├── BottomToUpSoftScaleTransitionView.tsx
│   │   │   │   │   │   ├── BottomToUpTransitionView.tsx
│   │   │   │   │   │   ├── FadeInOutTransitionView.tsx
│   │   │   │   │   │   ├── IconTransiton.tsx
│   │   │   │   │   │   ├── LeftToRightTransitionView.tsx
│   │   │   │   │   │   ├── RightToLeftTransitionView.tsx
│   │   │   │   │   │   ├── ScaleTransitionView.tsx
│   │   │   │   │   │   ├── TextUpTransitionView.tsx
│   │   │   │   │   │   ├── factor.tsx
│   │   │   │   │   │   └── typings.ts
│   │   │   │   │   └── viewport/
│   │   │   │   │       ├── OnlyDesktop.tsx
│   │   │   │   │       ├── OnlyMobile.tsx
│   │   │   │   │       └── index.ts
│   │   │   │   └── widgets/
│   │   │   │       ├── download/
│   │   │   │       │   ├── DownloadHero.tsx
│   │   │   │       │   └── PlatformDownloads.tsx
│   │   │   │       ├── landing/
│   │   │   │       │   ├── Audience.tsx
│   │   │   │       │   ├── BuiltOpen.tsx
│   │   │   │       │   ├── Features.tsx
│   │   │   │       │   ├── Header.tsx
│   │   │   │       │   ├── Hero.tsx
│   │   │   │       │   ├── PromptDemo.tsx
│   │   │   │       │   ├── RepoStats.tsx
│   │   │   │       │   ├── SocialProof.tsx
│   │   │   │       │   ├── TrustedBy.tsx
│   │   │   │       │   ├── ViewsShowcase.tsx
│   │   │   │       │   └── WindowChrome.tsx
│   │   │   │       ├── pricing/
│   │   │   │       │   └── PricingPlans.tsx
│   │   │   │       └── simulators/
│   │   │   │           ├── EntryChatPanel.tsx
│   │   │   │           ├── EntryPage.tsx
│   │   │   │           ├── ListDemo.tsx
│   │   │   │           ├── TimelineChatDemo.tsx
│   │   │   │           ├── components/
│   │   │   │           │   ├── EntryPageOverlay.tsx
│   │   │   │           │   ├── ai/
│   │   │   │           │   │   ├── AIChainOfThought.tsx
│   │   │   │           │   │   ├── AIMarkdownMessage.tsx
│   │   │   │           │   │   ├── AIReasoningPart.tsx
│   │   │   │           │   │   ├── ToolInvocationComponent.tsx
│   │   │   │           │   │   ├── animated/
│   │   │   │           │   │   │   ├── AnimatedMarkdown.tsx
│   │   │   │           │   │   │   ├── TokenizedText.tsx
│   │   │   │           │   │   │   └── constants.ts
│   │   │   │           │   │   ├── mocks.ts
│   │   │   │           │   │   ├── parse-incomplete-markdown.ts
│   │   │   │           │   │   ├── reasoning-mock.json
│   │   │   │           │   │   └── shiny-text/
│   │   │   │           │   │       ├── ShinyText.tsx
│   │   │   │           │   │       └── index.module.css
│   │   │   │           │   └── chat/
│   │   │   │           │       ├── AiMessageContextBar.tsx
│   │   │   │           │       ├── AiMockMessage.tsx
│   │   │   │           │       ├── AiUserMessage.tsx
│   │   │   │           │       ├── ListChatPlayer.tsx
│   │   │   │           │       ├── MarkdownMessage.tsx
│   │   │   │           │       └── stream.ts
│   │   │   │           └── mocks.tsx
│   │   │   ├── constants/
│   │   │   │   ├── download.ts
│   │   │   │   ├── env.ts
│   │   │   │   ├── site.ts
│   │   │   │   └── spring.ts
│   │   │   ├── hooks/
│   │   │   │   ├── biz/
│   │   │   │   │   └── use-github-star.ts
│   │   │   │   ├── common/
│   │   │   │   │   ├── use-before-mounted.ts
│   │   │   │   │   ├── use-click-away.ts
│   │   │   │   │   ├── use-debounce-value.ts
│   │   │   │   │   ├── use-event-callback.ts
│   │   │   │   │   ├── use-input-composition.ts
│   │   │   │   │   ├── use-is-active.ts
│   │   │   │   │   ├── use-is-client.ts
│   │   │   │   │   ├── use-is-dark.ts
│   │   │   │   │   ├── use-is-mounted.ts
│   │   │   │   │   ├── use-is-unmounted.ts
│   │   │   │   │   ├── use-previous.ts
│   │   │   │   │   ├── use-ref-value.ts
│   │   │   │   │   ├── use-safe-setState.ts
│   │   │   │   │   ├── use-state-ref.ts
│   │   │   │   │   ├── use-sync-effect.ts
│   │   │   │   │   └── useMeasure.ts
│   │   │   │   └── shared/
│   │   │   │       └── use-mask-scrollarea.ts
│   │   │   ├── i18n/
│   │   │   │   ├── request.ts
│   │   │   │   └── routing.ts
│   │   │   ├── legal/
│   │   │   │   ├── privacy.md
│   │   │   │   └── tos.md
│   │   │   ├── lib/
│   │   │   │   ├── apple-app-site-association.ts
│   │   │   │   ├── cn.ts
│   │   │   │   ├── color.ts
│   │   │   │   ├── cookie.ts
│   │   │   │   ├── datetime.ts
│   │   │   │   ├── dom.ts
│   │   │   │   ├── env.ts
│   │   │   │   ├── fonts.ts
│   │   │   │   ├── helper.ts
│   │   │   │   ├── jotai.ts
│   │   │   │   ├── landing-data.ts
│   │   │   │   ├── noop.ts
│   │   │   │   ├── platform.ts
│   │   │   │   ├── pricing-data.ts
│   │   │   │   ├── query-client.server.ts
│   │   │   │   ├── scroller.ts
│   │   │   │   ├── sleep.ts
│   │   │   │   ├── spring.ts
│   │   │   │   └── store.ts
│   │   │   ├── messages/
│   │   │   │   ├── en.json
│   │   │   │   ├── jp.json
│   │   │   │   └── zh.json
│   │   │   ├── providers/
│   │   │   │   ├── root/
│   │   │   │   │   ├── debug-provider.tsx
│   │   │   │   │   ├── event-provider.tsx
│   │   │   │   │   ├── framer-lazy-feature.ts
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── jotai-provider.tsx
│   │   │   │   │   ├── page-scroll-info-provider.tsx
│   │   │   │   │   ├── react-query-provider.tsx
│   │   │   │   │   └── sonner.tsx
│   │   │   │   └── shared/
│   │   │   │       ├── LayoutRightSideProvider.tsx
│   │   │   │       └── WrappedElementProvider.tsx
│   │   │   ├── proxy.ts
│   │   │   └── styles/
│   │   │       ├── globals.css
│   │   │       └── pastel-theme-oklch.css
│   │   ├── tsconfig.json
│   │   ├── vite.config.ts
│   │   ├── worker/
│   │   │   └── index.js
│   │   └── wrangler.jsonc
│   ├── mobile/
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .watchmanconfig
│   │   ├── AGENTS.md
│   │   ├── README.md
│   │   ├── app.config.ts
│   │   ├── assets/
│   │   │   └── font/
│   │   │       └── sn-pro/
│   │   │           ├── SNPro-Black.otf
│   │   │           ├── SNPro-BlackItalic.otf
│   │   │           ├── SNPro-Bold.otf
│   │   │           ├── SNPro-BoldItalic.otf
│   │   │           ├── SNPro-Book.otf
│   │   │           ├── SNPro-BookItalic.otf
│   │   │           ├── SNPro-Heavy.otf
│   │   │           ├── SNPro-HeavyItalic.otf
│   │   │           ├── SNPro-Light.otf
│   │   │           ├── SNPro-LightItalic.otf
│   │   │           ├── SNPro-Medium.otf
│   │   │           ├── SNPro-MediumItalic.otf
│   │   │           ├── SNPro-Regular.otf
│   │   │           ├── SNPro-RegularItalic.otf
│   │   │           ├── SNPro-Semibold.otf
│   │   │           ├── SNPro-SemiboldItalic.otf
│   │   │           ├── SNPro-Thin.otf
│   │   │           └── SNPro-ThinItalic.otf
│   │   ├── babel.config.js
│   │   ├── build/
│   │   │   ├── GoogleService-Info.plist
│   │   │   └── google-services.json
│   │   ├── bump.config.ts
│   │   ├── changelog/
│   │   │   ├── 0.1.3.md
│   │   │   ├── 0.1.4.md
│   │   │   ├── 0.1.5.md
│   │   │   ├── 0.1.6.md
│   │   │   ├── 0.1.7.md
│   │   │   ├── 0.1.8.md
│   │   │   ├── 0.1.9.md
│   │   │   ├── 0.2.0.md
│   │   │   ├── 0.2.1.md
│   │   │   ├── 0.2.10.md
│   │   │   ├── 0.2.2.md
│   │   │   ├── 0.2.3.md
│   │   │   ├── 0.2.4.md
│   │   │   ├── 0.2.5.md
│   │   │   ├── 0.2.6.md
│   │   │   ├── 0.2.8.md
│   │   │   ├── 0.3.0.md
│   │   │   ├── 0.4.0.md
│   │   │   ├── next.md
│   │   │   └── next.template.md
│   │   ├── e2e/
│   │   │   ├── README.md
│   │   │   ├── flows/
│   │   │   │   ├── ios/
│   │   │   │   │   ├── auth.yaml
│   │   │   │   │   ├── content.yaml
│   │   │   │   │   ├── core.yaml
│   │   │   │   │   ├── dismiss-overlays.yaml
│   │   │   │   │   ├── ensure-onboarding-unfollowed.yaml
│   │   │   │   │   ├── follow-onboarding.yaml
│   │   │   │   │   ├── login.yaml
│   │   │   │   │   ├── register.yaml
│   │   │   │   │   ├── sign-out.yaml
│   │   │   │   │   ├── timeline-entry.yaml
│   │   │   │   │   └── unfollow-onboarding.yaml
│   │   │   │   └── shared/
│   │   │   │       ├── core.yaml
│   │   │   │       ├── dismiss-ios-system-modal.yaml
│   │   │   │       ├── ensure-onboarding-unfollowed.yaml
│   │   │   │       ├── follow-onboarding.yaml
│   │   │   │       ├── login.yaml
│   │   │   │       ├── open-auth.yaml
│   │   │   │       ├── register.yaml
│   │   │   │       ├── sign-out.yaml
│   │   │   │       ├── timeline-entry.yaml
│   │   │   │       └── unfollow-onboarding.yaml
│   │   │   └── run-maestro.sh
│   │   ├── eas.json
│   │   ├── global.d.ts
│   │   ├── ios/
│   │   │   ├── .gitignore
│   │   │   ├── .xcode.env
│   │   │   ├── Assets.xcassets/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── black_board_2_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── black_board_2_cute_re.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── home_5_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── home_5_cute_re.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── search_3_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── search_3_cute_re.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── settings_1_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── settings_1_cute_re.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── Folo/
│   │   │   │   ├── AppDelegate.swift
│   │   │   │   ├── Folo-Bridging-Header.h
│   │   │   │   ├── Folo.entitlements
│   │   │   │   ├── Images.xcassets/
│   │   │   │   │   ├── AppIcon.appiconset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   ├── Contents.json
│   │   │   │   │   └── SplashScreenBackground.colorset/
│   │   │   │   │       └── Contents.json
│   │   │   │   ├── Info.plist
│   │   │   │   ├── PrivacyInfo.xcprivacy
│   │   │   │   ├── SplashScreen.storyboard
│   │   │   │   └── Supporting/
│   │   │   │       └── Expo.plist
│   │   │   ├── Folo - Follow everything.storekit
│   │   │   ├── Folo.xcodeproj/
│   │   │   │   ├── project.pbxproj
│   │   │   │   └── xcshareddata/
│   │   │   │       └── xcschemes/
│   │   │   │           └── Folo.xcscheme
│   │   │   ├── Folo.xcworkspace/
│   │   │   │   └── contents.xcworkspacedata
│   │   │   ├── Podfile
│   │   │   └── Podfile.properties.json
│   │   ├── metro.config.js
│   │   ├── native/
│   │   │   ├── .eslintrc.js
│   │   │   ├── .gitignore
│   │   │   ├── .npmignore
│   │   │   ├── README.md
│   │   │   ├── expo-module.config.json
│   │   │   ├── ios/
│   │   │   │   ├── Controllers/
│   │   │   │   │   ├── ModalWebViewController.swift
│   │   │   │   │   ├── RNSViewController.swift
│   │   │   │   │   └── WebViewController.swift
│   │   │   │   ├── Extensions/
│   │   │   │   │   ├── UIColor+Hex.swift
│   │   │   │   │   ├── UIImage+asActivityItemSource.swift
│   │   │   │   │   ├── UIImage.swift
│   │   │   │   │   └── UIWindow.swift
│   │   │   │   ├── FollowNative.podspec
│   │   │   │   ├── Models/
│   │   │   │   │   ├── ProfileData.swift
│   │   │   │   │   └── UserData.swift
│   │   │   │   ├── Modules/
│   │   │   │   │   ├── AppleIntelligenceGlowEffect/
│   │   │   │   │   │   ├── AppleIntelligenceGlowEffectModule.swift
│   │   │   │   │   │   ├── IntelligenceAnimationController.swift
│   │   │   │   │   │   └── IntelligenceAnimationView.swift
│   │   │   │   │   ├── Helper/
│   │   │   │   │   │   ├── Helper+Image.swift
│   │   │   │   │   │   └── HelperModule.swift
│   │   │   │   │   ├── ItemPressable/
│   │   │   │   │   │   └── ItemPressableModule.swift
│   │   │   │   │   ├── PagerView/
│   │   │   │   │   │   ├── EnhancePageViewModule.swift
│   │   │   │   │   │   ├── EnhancePagerController.swift
│   │   │   │   │   │   └── EnhancePagerViewModule.swift
│   │   │   │   │   ├── SharedWebView/
│   │   │   │   │   │   ├── FOWebView.swift
│   │   │   │   │   │   ├── FollowImageURLSchemeHandler.swift
│   │   │   │   │   │   ├── Injected/
│   │   │   │   │   │   │   ├── at_end.js
│   │   │   │   │   │   │   └── at_start.js
│   │   │   │   │   │   ├── SharedWebView+BridgeData.swift
│   │   │   │   │   │   ├── SharedWebView.swift
│   │   │   │   │   │   ├── SharedWebViewModule.swift
│   │   │   │   │   │   ├── WebViewManager.swift
│   │   │   │   │   │   └── WebViewState.swift
│   │   │   │   │   ├── StoreKitTestHelper/
│   │   │   │   │   │   └── StoreKitTestHelperModule.swift
│   │   │   │   │   ├── TabBar/
│   │   │   │   │   │   ├── TabBarBottomAccessoryModule.swift
│   │   │   │   │   │   ├── TabBarModule.swift
│   │   │   │   │   │   ├── TabBarPortalModule.swift
│   │   │   │   │   │   ├── TabBarRootView.swift
│   │   │   │   │   │   ├── TabScreenModule.swift
│   │   │   │   │   │   └── TabScreenView.swift
│   │   │   │   │   └── Toaster/
│   │   │   │   │       ├── Toast.swift
│   │   │   │   │       └── ToasterModule.swift
│   │   │   │   ├── Packages/
│   │   │   │   │   ├── ImageViewer_swift/
│   │   │   │   │   │   ├── ImageCarouselViewController.swift
│   │   │   │   │   │   ├── ImageCarouselViewControllerProtocol.swift
│   │   │   │   │   │   ├── ImageItem.swift
│   │   │   │   │   │   ├── ImageLoader.swift
│   │   │   │   │   │   ├── ImageViewerController.swift
│   │   │   │   │   │   ├── ImageViewerOption.swift
│   │   │   │   │   │   ├── ImageViewerTransitionPresentationManager.swift
│   │   │   │   │   │   ├── ImageViewer_swift.h
│   │   │   │   │   │   ├── LISENCE
│   │   │   │   │   │   ├── SimpleImageDatasource.swift
│   │   │   │   │   │   ├── UIImageView_Extensions.swift
│   │   │   │   │   │   ├── UINavigationBar_Extensions.swift
│   │   │   │   │   │   └── UIView_Extensions.swift
│   │   │   │   │   └── SPIndicator/
│   │   │   │   │       └── LICENSE
│   │   │   │   └── Utils/
│   │   │   │       └── Utils.swift
│   │   │   └── package.json
│   │   ├── nativewind-env.d.ts
│   │   ├── package.json
│   │   ├── plugins/
│   │   │   ├── android-trust-user-certs.js
│   │   │   ├── network_security_config.xml
│   │   │   ├── with-android-jdk-21.js
│   │   │   ├── with-android-manifest-plugin.js
│   │   │   ├── with-follow-app-delegate.js
│   │   │   ├── with-follow-assets.js
│   │   │   └── with-gradle-jvm-heap-size-increase.js
│   │   ├── postcss.config.js
│   │   ├── scripts/
│   │   │   ├── apply-changelog.ts
│   │   │   ├── e2e-prod-ios-auth-bootstrap.ts
│   │   │   └── expo-update.ts
│   │   ├── shim-env.d.ts
│   │   ├── src/
│   │   │   ├── @types/
│   │   │   │   ├── constants.ts
│   │   │   │   ├── default-resource.ts
│   │   │   │   └── i18next.d.ts
│   │   │   ├── App.tsx
│   │   │   ├── atoms/
│   │   │   │   ├── app.ts
│   │   │   │   ├── hooks/
│   │   │   │   │   └── useDeviceType.ts
│   │   │   │   ├── server-configs.ts
│   │   │   │   └── settings/
│   │   │   │       ├── data.ts
│   │   │   │       ├── general.ts
│   │   │   │       ├── internal/
│   │   │   │       │   └── helper.ts
│   │   │   │       └── ui.ts
│   │   │   ├── components/
│   │   │   │   ├── common/
│   │   │   │   │   ├── AnimatedComponents.tsx
│   │   │   │   │   ├── Balance.tsx
│   │   │   │   │   ├── BlurEffect.tsx
│   │   │   │   │   ├── CopyButton.tsx
│   │   │   │   │   ├── ErrorBoundary.tsx
│   │   │   │   │   ├── FullWindowOverlay.ios.tsx
│   │   │   │   │   ├── FullWindowOverlay.tsx
│   │   │   │   │   ├── Link.tsx
│   │   │   │   │   ├── NoLoginInfo.tsx
│   │   │   │   │   ├── RefreshControl.tsx
│   │   │   │   │   ├── RotateableLoading.tsx
│   │   │   │   │   ├── SubmitButton.tsx
│   │   │   │   │   ├── SwipeableItem.tsx
│   │   │   │   │   └── ThemedBlurView.tsx
│   │   │   │   ├── errors/
│   │   │   │   │   ├── GlobalErrorScreen.tsx
│   │   │   │   │   ├── ListErrorView.tsx
│   │   │   │   │   └── ScreenErrorScreen.tsx
│   │   │   │   ├── icons/
│   │   │   │   │   ├── OouiUserAnonymous.tsx
│   │   │   │   │   └── PhUsersBold.tsx
│   │   │   │   ├── layouts/
│   │   │   │   │   ├── contexts/
│   │   │   │   │   │   └── ModalScrollViewContext.ts
│   │   │   │   │   ├── header/
│   │   │   │   │   │   ├── FakeNativeHeaderTitle.tsx
│   │   │   │   │   │   ├── HeaderElements.tsx
│   │   │   │   │   │   ├── NavigationHeader.tsx
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── tabbar/
│   │   │   │   │   │   ├── BottomTabHeightProvider.tsx
│   │   │   │   │   │   ├── BottomTabProvider.tsx
│   │   │   │   │   │   ├── BottomTabs.tsx
│   │   │   │   │   │   ├── ReactNativeTab.ios.tsx
│   │   │   │   │   │   ├── ReactNativeTab.tsx
│   │   │   │   │   │   ├── Tabbar.tsx
│   │   │   │   │   │   ├── contexts/
│   │   │   │   │   │   │   ├── BottomTabBarBackgroundContext.tsx
│   │   │   │   │   │   │   ├── BottomTabBarHeightContext.tsx
│   │   │   │   │   │   │   └── BottomTabBarVisibleContext.tsx
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── utils/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   └── views/
│   │   │   │   │       ├── NavigationHeaderContext.tsx
│   │   │   │   │       └── SafeNavigationScrollView.tsx
│   │   │   │   ├── native/
│   │   │   │   │   ├── PagerView/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── specs.ts
│   │   │   │   │   └── webview/
│   │   │   │   │       ├── DebugPanel.tsx
│   │   │   │   │       ├── EntryContentWebView.tsx
│   │   │   │   │       ├── atom.ts
│   │   │   │   │       ├── constants.ts
│   │   │   │   │       ├── hooks.ts
│   │   │   │   │       ├── index.android.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── injected-js.ts
│   │   │   │   │       ├── native-webview.android.tsx
│   │   │   │   │       ├── native-webview.tsx
│   │   │   │   │       └── webview-manager.ts
│   │   │   │   └── ui/
│   │   │   │       ├── accordion/
│   │   │   │       │   └── AccordionItem.tsx
│   │   │   │       ├── action-bar/
│   │   │   │       │   └── ActionBarItem.tsx
│   │   │   │       ├── avatar/
│   │   │   │       │   └── UserAvatar.tsx
│   │   │   │       ├── button/
│   │   │   │       │   └── UIBarButton.tsx
│   │   │   │       ├── carousel/
│   │   │   │       │   └── MediaCarousel.tsx
│   │   │   │       ├── context-menu/
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── datetime/
│   │   │   │       │   └── RelativeDateTime.tsx
│   │   │   │       ├── form/
│   │   │   │       │   ├── FormProvider.tsx
│   │   │   │       │   ├── Label.tsx
│   │   │   │       │   ├── PickerIos.tsx
│   │   │   │       │   ├── Select.android.tsx
│   │   │   │       │   ├── Select.tsx
│   │   │   │       │   ├── Slider.tsx
│   │   │   │       │   ├── Switch.tsx
│   │   │   │       │   └── TextField.tsx
│   │   │   │       ├── grid/
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── grouped/
│   │   │   │       │   ├── GroupedInsetListCardItemStyle.tsx
│   │   │   │       │   ├── GroupedList.tsx
│   │   │   │       │   └── constants.ts
│   │   │   │       ├── icon/
│   │   │   │       │   ├── fallback-icon.tsx
│   │   │   │       │   └── feed-icon.tsx
│   │   │   │       ├── image/
│   │   │   │       │   ├── Image.tsx
│   │   │   │       │   ├── ImageContextMenu.tsx
│   │   │   │       │   └── utils.ts
│   │   │   │       ├── lightbox/
│   │   │   │       │   ├── ImageViewing/
│   │   │   │       │   │   ├── @types/
│   │   │   │       │   │   │   └── index.ts
│   │   │   │       │   │   ├── components/
│   │   │   │       │   │   │   ├── ImageDefaultHeader.tsx
│   │   │   │       │   │   │   └── ImageItem/
│   │   │   │       │   │   │       ├── ImageItem.android.tsx
│   │   │   │       │   │   │       ├── ImageItem.ios.tsx
│   │   │   │       │   │   │       └── ImageItem.tsx
│   │   │   │       │   │   ├── index.tsx
│   │   │   │       │   │   └── transforms.ts
│   │   │   │       │   ├── Lightbox.tsx
│   │   │   │       │   └── lightboxState.tsx
│   │   │   │       ├── loading/
│   │   │   │       │   └── PlatformActivityIndicator.tsx
│   │   │   │       ├── logo/
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── modal/
│   │   │   │       │   ├── BottomModal.tsx
│   │   │   │       │   └── imperative-modal/
│   │   │   │       │       ├── index.tsx
│   │   │   │       │       ├── modal.tsx
│   │   │   │       │       └── templates.tsx
│   │   │   │       ├── overlay/
│   │   │   │       │   └── Overlay.tsx
│   │   │   │       ├── pressable/
│   │   │   │       │   ├── IosItemPressable.ios.tsx
│   │   │   │       │   ├── IosItemPressable.tsx
│   │   │   │       │   ├── ItemPressable.ios.tsx
│   │   │   │       │   ├── ItemPressable.tsx
│   │   │   │       │   ├── NativePressable.ios.tsx
│   │   │   │       │   ├── NativePressable.tsx
│   │   │   │       │   ├── NativePressable.types.tsx
│   │   │   │       │   └── enum.ts
│   │   │   │       ├── qrcode/
│   │   │   │       │   ├── LICENSE
│   │   │   │       │   ├── QRCode.tsx
│   │   │   │       │   ├── SVGPieces.tsx
│   │   │   │       │   ├── SVGRadialGradient.tsx
│   │   │   │       │   ├── adapter.ts
│   │   │   │       │   ├── constants.ts
│   │   │   │       │   ├── helper.ts
│   │   │   │       │   ├── types.ts
│   │   │   │       │   └── useQRCodeData.ts
│   │   │   │       ├── slider/
│   │   │   │       │   ├── Slider.tsx
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── switch/
│   │   │   │       │   └── Switch.tsx
│   │   │   │       ├── tabview/
│   │   │   │       │   ├── TabBar.tsx
│   │   │   │       │   ├── TabView.tsx
│   │   │   │       │   └── types.ts
│   │   │   │       ├── toast/
│   │   │   │       │   ├── CenteredToast.tsx
│   │   │   │       │   ├── ToastContainer.tsx
│   │   │   │       │   ├── constants.ts
│   │   │   │       │   ├── ctx.tsx
│   │   │   │       │   ├── manager.tsx
│   │   │   │       │   └── types.ts
│   │   │   │       ├── typography/
│   │   │   │       │   ├── HtmlWeb.tsx
│   │   │   │       │   ├── MarkdownNative.tsx
│   │   │   │       │   ├── MonoText.tsx
│   │   │   │       │   └── Text.tsx
│   │   │   │       └── video/
│   │   │   │           ├── PlayerAction.tsx
│   │   │   │           └── VideoPlayer.tsx
│   │   │   ├── constants/
│   │   │   │   ├── native-images.ts
│   │   │   │   ├── spring.ts
│   │   │   │   ├── ui.ts
│   │   │   │   └── views.tsx
│   │   │   ├── database/
│   │   │   │   └── index.ts
│   │   │   ├── global.css
│   │   │   ├── hooks/
│   │   │   │   ├── useBackHandler.ts
│   │   │   │   ├── useDefaultHeaderHeight.ts
│   │   │   │   ├── useIntentHandler.ts
│   │   │   │   ├── useLoadingCallback.tsx
│   │   │   │   ├── useMessaging.ts
│   │   │   │   ├── useOnboarding.ts
│   │   │   │   ├── useUnreadCountBadge.ts
│   │   │   │   └── useWebViewNavigation.tsx
│   │   │   ├── icons/
│   │   │   │   ├── AZ_sort_ascending_letters_cute_re.tsx
│   │   │   │   ├── AZ_sort_descending_letters_cute_re.tsx
│   │   │   │   ├── VIP_2_cute_fi.tsx
│   │   │   │   ├── VIP_2_cute_re.tsx
│   │   │   │   ├── add_cute_fi.tsx
│   │   │   │   ├── add_cute_re.tsx
│   │   │   │   ├── ai_cute_fi.tsx
│   │   │   │   ├── ai_cute_re.tsx
│   │   │   │   ├── alert_cute_fi.tsx
│   │   │   │   ├── align_justify_cute_re.tsx
│   │   │   │   ├── align_left_cute_re.tsx
│   │   │   │   ├── announcement_cute_fi.tsx
│   │   │   │   ├── apple_cute_fi.tsx
│   │   │   │   ├── arrow_left_cute_re.tsx
│   │   │   │   ├── arrow_right_circle_cute_fi.tsx
│   │   │   │   ├── arrow_right_up_cute_re.tsx
│   │   │   │   ├── arrow_up_circle_cute_fi.tsx
│   │   │   │   ├── at_cute_re.tsx
│   │   │   │   ├── attachment_cute_re.tsx
│   │   │   │   ├── back_2_cute_re.tsx
│   │   │   │   ├── black_board_2_cute_fi.tsx
│   │   │   │   ├── black_board_2_cute_re.tsx
│   │   │   │   ├── book_6_cute_re.tsx
│   │   │   │   ├── bookmark_cute_re.tsx
│   │   │   │   ├── bubble_cute_fi.tsx
│   │   │   │   ├── bug_cute_re.tsx
│   │   │   │   ├── calendar_time_add_cute_re.tsx
│   │   │   │   ├── celebrate_cute_re.tsx
│   │   │   │   ├── certificate_cute_fi.tsx
│   │   │   │   ├── certificate_cute_re.tsx
│   │   │   │   ├── check_circle_cute_re.tsx
│   │   │   │   ├── check_circle_filled.tsx
│   │   │   │   ├── check_cute_re.tsx
│   │   │   │   ├── check_filled.tsx
│   │   │   │   ├── check_line.tsx
│   │   │   │   ├── classify_2_cute_re.tsx
│   │   │   │   ├── close_circle_fill.tsx
│   │   │   │   ├── close_cute_re.tsx
│   │   │   │   ├── comment_2_cute_re.tsx
│   │   │   │   ├── comment_cute_fi.tsx
│   │   │   │   ├── comment_cute_li.tsx
│   │   │   │   ├── comment_cute_re.tsx
│   │   │   │   ├── compass_3_cute_re.tsx
│   │   │   │   ├── compass_cute_fi.tsx
│   │   │   │   ├── copy_2_cute_re.tsx
│   │   │   │   ├── copy_cute_re.tsx
│   │   │   │   ├── cursor_3_cute_re.tsx
│   │   │   │   ├── danmaku_cute_fi.tsx
│   │   │   │   ├── database.tsx
│   │   │   │   ├── delete_2_cute_re.tsx
│   │   │   │   ├── department_cute_re.tsx
│   │   │   │   ├── discord_cute_fi.tsx
│   │   │   │   ├── docment_cute_fi.tsx
│   │   │   │   ├── docment_cute_re.tsx
│   │   │   │   ├── documents_cute_re.tsx
│   │   │   │   ├── download_2_cute_fi.tsx
│   │   │   │   ├── download_2_cute_re.tsx
│   │   │   │   ├── edit_cute_re.tsx
│   │   │   │   ├── emoji_2_cute_re.tsx
│   │   │   │   ├── exit_cute_fi.tsx
│   │   │   │   ├── exit_cute_re.tsx
│   │   │   │   ├── external_link_cute_re.tsx
│   │   │   │   ├── eye_2_cute_re.tsx
│   │   │   │   ├── eye_close_cute_re.tsx
│   │   │   │   ├── facebook_cute_fi.tsx
│   │   │   │   ├── facebook_cute_re.tsx
│   │   │   │   ├── fast_forward_cute_re.tsx
│   │   │   │   ├── file_import_cute_re.tsx
│   │   │   │   ├── file_upload_cute_re.tsx
│   │   │   │   ├── filter_cute_re.tsx
│   │   │   │   ├── finger_press_cute_re.tsx
│   │   │   │   ├── fire_cute_fi.tsx
│   │   │   │   ├── fire_cute_re.tsx
│   │   │   │   ├── flag_1_cute_fi.tsx
│   │   │   │   ├── folder_open_cute_re.tsx
│   │   │   │   ├── forward_2_cute_re.tsx
│   │   │   │   ├── fullscreen_2_cute_re.tsx
│   │   │   │   ├── fullscreen_cute_re.tsx
│   │   │   │   ├── fullscreen_exit_cute_re.tsx
│   │   │   │   ├── ghost_cute_re.tsx
│   │   │   │   ├── gift_cute_re.tsx
│   │   │   │   ├── github_2_cute_fi.tsx
│   │   │   │   ├── github_cute_fi.tsx
│   │   │   │   ├── google_cute_fi.tsx
│   │   │   │   ├── grid_2_cute_re.tsx
│   │   │   │   ├── grid_cute_re.tsx
│   │   │   │   ├── hammer_cute_re.tsx
│   │   │   │   ├── heart_cute_fi.tsx
│   │   │   │   ├── history_cute_re.tsx
│   │   │   │   ├── home_5_cute_fi.tsx
│   │   │   │   ├── home_5_cute_re.tsx
│   │   │   │   ├── hotkey_cute_re.tsx
│   │   │   │   ├── inbox_cute_fi.tsx
│   │   │   │   ├── inbox_cute_re.tsx
│   │   │   │   ├── info_circle_fill.tsx
│   │   │   │   ├── information_cute_re.tsx
│   │   │   │   ├── instagram_cute_fi.tsx
│   │   │   │   ├── key_2_cute_re.tsx
│   │   │   │   ├── layout_4_cute_re.tsx
│   │   │   │   ├── layout_leftbar_close_cute_re.tsx
│   │   │   │   ├── layout_leftbar_open_cute_re.tsx
│   │   │   │   ├── left_cute_fi.tsx
│   │   │   │   ├── left_small_sharp.tsx
│   │   │   │   ├── line_cute_re.tsx
│   │   │   │   ├── link_cute_re.tsx
│   │   │   │   ├── list_check_2_cute_re.tsx
│   │   │   │   ├── list_check_3_cute_re.tsx
│   │   │   │   ├── list_check_cute_re.tsx
│   │   │   │   ├── list_collapse_cute_fi.tsx
│   │   │   │   ├── list_collapse_cute_re.tsx
│   │   │   │   ├── list_expansion_cute_fi.tsx
│   │   │   │   ├── list_expansion_cute_re.tsx
│   │   │   │   ├── loading_3_cute_li.tsx
│   │   │   │   ├── loading_3_cute_re.tsx
│   │   │   │   ├── love_cute_fi.tsx
│   │   │   │   ├── love_cute_re.tsx
│   │   │   │   ├── magic_2_cute_fi.tsx
│   │   │   │   ├── magic_2_cute_re.tsx
│   │   │   │   ├── mail_cute_re.tsx
│   │   │   │   ├── mic_cute_fi.tsx
│   │   │   │   ├── mic_cute_re.tsx
│   │   │   │   ├── mind_map_cute_re.tsx
│   │   │   │   ├── mingcute_down_line.tsx
│   │   │   │   ├── mingcute_left_line.tsx
│   │   │   │   ├── mingcute_right_line.tsx
│   │   │   │   ├── more_1_cute_re.tsx
│   │   │   │   ├── music_2_cute_fi.tsx
│   │   │   │   ├── notification_cute_re.tsx
│   │   │   │   ├── numbers_09_sort_ascending_cute_re.tsx
│   │   │   │   ├── numbers_09_sort_descending_cute_re.tsx
│   │   │   │   ├── numbers_90_sort_ascending_cute_re.tsx
│   │   │   │   ├── numbers_90_sort_descending_cute_re.tsx
│   │   │   │   ├── palette_cute_fi.tsx
│   │   │   │   ├── palette_cute_re.tsx
│   │   │   │   ├── paper_cute_fi.tsx
│   │   │   │   ├── paste_cute_re.tsx
│   │   │   │   ├── pause_cute_fi.tsx
│   │   │   │   ├── pause_cute_re.tsx
│   │   │   │   ├── pdf_cute_re.tsx
│   │   │   │   ├── photo_album_cute_fi.tsx
│   │   │   │   ├── photo_album_cute_re.tsx
│   │   │   │   ├── pic_cute_fi.tsx
│   │   │   │   ├── pic_cute_re.tsx
│   │   │   │   ├── play_cute_fi.tsx
│   │   │   │   ├── play_cute_re.tsx
│   │   │   │   ├── plugin_2_cute_re.tsx
│   │   │   │   ├── polygon_cute_re.tsx
│   │   │   │   ├── power.tsx
│   │   │   │   ├── power_mono.tsx
│   │   │   │   ├── power_outline.tsx
│   │   │   │   ├── question_cute_re.tsx
│   │   │   │   ├── quill_pen_cute_re.tsx
│   │   │   │   ├── rada_cute_fi.tsx
│   │   │   │   ├── rada_cute_re.tsx
│   │   │   │   ├── refresh_2_cute_re.tsx
│   │   │   │   ├── rewind_backward_15_cute_re.tsx
│   │   │   │   ├── rewind_forward_30_cute_re.tsx
│   │   │   │   ├── right_cute_fi.tsx
│   │   │   │   ├── right_cute_li.tsx
│   │   │   │   ├── right_cute_re.tsx
│   │   │   │   ├── right_small_sharp.tsx
│   │   │   │   ├── rocket_cute_fi.tsx
│   │   │   │   ├── rocket_cute_re.tsx
│   │   │   │   ├── round_cute_fi.tsx
│   │   │   │   ├── round_cute_re.tsx
│   │   │   │   ├── rss_2_cute_fi.tsx
│   │   │   │   ├── rss_cute_fi.tsx
│   │   │   │   ├── sad_cute_re.tsx
│   │   │   │   ├── safe_alert_cute_re.tsx
│   │   │   │   ├── safe_lock_filled.tsx
│   │   │   │   ├── safety_certificate_cute_re.tsx
│   │   │   │   ├── save_cute_re.tsx
│   │   │   │   ├── search_2_cute_re.tsx
│   │   │   │   ├── search_3_cute_fi.tsx
│   │   │   │   ├── search_3_cute_re.tsx
│   │   │   │   ├── search_cute_re.tsx
│   │   │   │   ├── send_plane_cute_fi.tsx
│   │   │   │   ├── send_plane_cute_re.tsx
│   │   │   │   ├── settings_1_cute_fi.tsx
│   │   │   │   ├── settings_1_cute_re.tsx
│   │   │   │   ├── settings_7_cute_re.tsx
│   │   │   │   ├── share_forward_cute_re.tsx
│   │   │   │   ├── shuffle_2_cute_re.tsx
│   │   │   │   ├── social_x_cute_li.tsx
│   │   │   │   ├── social_x_cute_re.tsx
│   │   │   │   ├── sort_ascending_cute_re.tsx
│   │   │   │   ├── sort_descending_cute_re.tsx
│   │   │   │   ├── star_cute_fi.tsx
│   │   │   │   ├── star_cute_re.tsx
│   │   │   │   ├── stop_circle_cute_fi.tsx
│   │   │   │   ├── telegram_cute_fi.tsx
│   │   │   │   ├── telegram_cute_re.tsx
│   │   │   │   ├── thought_cute_fi.tsx
│   │   │   │   ├── time_cute_re.tsx
│   │   │   │   ├── tool_cute_re.tsx
│   │   │   │   ├── train_cute_fi.tsx
│   │   │   │   ├── translate_2_ai_cute_re.tsx
│   │   │   │   ├── translate_2_cute_re.tsx
│   │   │   │   ├── trending_up_cute_re.tsx
│   │   │   │   ├── trophy_cute_fi.tsx
│   │   │   │   ├── trophy_cute_re.tsx
│   │   │   │   ├── twitter_cute_fi.tsx
│   │   │   │   ├── up_cute_re.tsx
│   │   │   │   ├── user_3_cute_fi.tsx
│   │   │   │   ├── user_3_cute_re.tsx
│   │   │   │   ├── user_4_cute_fi.tsx
│   │   │   │   ├── user_4_cute_re.tsx
│   │   │   │   ├── user_add_2_cute_fi.tsx
│   │   │   │   ├── user_heart_cute_fi.tsx
│   │   │   │   ├── user_heart_cute_re.tsx
│   │   │   │   ├── user_setting_cute_fi.tsx
│   │   │   │   ├── user_setting_cute_re.tsx
│   │   │   │   ├── video_cute_fi.tsx
│   │   │   │   ├── video_cute_re.tsx
│   │   │   │   ├── voice_cute_re.tsx
│   │   │   │   ├── volume_cute_re.tsx
│   │   │   │   ├── volume_mute_cute_re.tsx
│   │   │   │   ├── volume_off_cute_re.tsx
│   │   │   │   ├── wallet_2_cute_fi.tsx
│   │   │   │   ├── warning_cute_re.tsx
│   │   │   │   ├── web_cute_re.tsx
│   │   │   │   ├── webhook_cute_re.tsx
│   │   │   │   ├── weibo_cute_re.tsx
│   │   │   │   ├── wifi_off_cute_re.tsx
│   │   │   │   ├── world_2_cute_fi.tsx
│   │   │   │   ├── world_2_cute_re.tsx
│   │   │   │   └── youtube_cute_fi.tsx
│   │   │   ├── initialize/
│   │   │   │   ├── analytics.ts
│   │   │   │   ├── app-check.ts
│   │   │   │   ├── background.ts
│   │   │   │   ├── dayjs.ts
│   │   │   │   ├── device.ts
│   │   │   │   ├── hydrate.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── migration.ts
│   │   │   │   └── player.ts
│   │   │   ├── interfaces/
│   │   │   │   └── settings/
│   │   │   │       └── data.ts
│   │   │   ├── lib/
│   │   │   │   ├── api-client.ts
│   │   │   │   ├── auth-cookie-migration.ts
│   │   │   │   ├── auth.ts
│   │   │   │   ├── client-session.ts
│   │   │   │   ├── dialog-state.ts
│   │   │   │   ├── dialog.tsx
│   │   │   │   ├── e2e-config.ts
│   │   │   │   ├── error-parser.ts
│   │   │   │   ├── event-bus.ts
│   │   │   │   ├── ga4.ts
│   │   │   │   ├── i18n.ts
│   │   │   │   ├── image.ts
│   │   │   │   ├── img-proxy.ts
│   │   │   │   ├── jotai.ts
│   │   │   │   ├── kv.ts
│   │   │   │   ├── loading.tsx
│   │   │   │   ├── markdown.tsx
│   │   │   │   ├── native/
│   │   │   │   │   ├── index.ios.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── picker.ts
│   │   │   │   │   └── user-agent.ts
│   │   │   │   ├── navigation/
│   │   │   │   │   ├── AttachNavigationScrollViewContext.tsx
│   │   │   │   │   ├── ChainNavigationContext.tsx
│   │   │   │   │   ├── GroupedNavigationRouteContext.ts
│   │   │   │   │   ├── Navigation.ts
│   │   │   │   │   ├── NavigationInstanceContext.ts
│   │   │   │   │   ├── NavigationLink.tsx
│   │   │   │   │   ├── ScreenItemContext.ts
│   │   │   │   │   ├── ScreenNameContext.tsx
│   │   │   │   │   ├── ScreenOptionsContext.ts
│   │   │   │   │   ├── StackNavigation.tsx
│   │   │   │   │   ├── StackScreenHeaderPortal.tsx
│   │   │   │   │   ├── WrappedScreenItem.tsx
│   │   │   │   │   ├── __internal/
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── biz/
│   │   │   │   │   │   └── Destination.ts
│   │   │   │   │   ├── bottom-tab/
│   │   │   │   │   │   ├── BottomTabContext.tsx
│   │   │   │   │   │   ├── TabBarPortal.tsx
│   │   │   │   │   │   ├── TabRoot.tsx
│   │   │   │   │   │   ├── TabScreen.tsx
│   │   │   │   │   │   ├── TabScreenContext.tsx
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── native.ios.tsx
│   │   │   │   │   │   ├── native.tsx
│   │   │   │   │   │   ├── shared.tsx
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── config.ts
│   │   │   │   │   ├── debug/
│   │   │   │   │   │   └── DebugButtonGroup.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   ├── readme.md
│   │   │   │   │   ├── sitemap/
│   │   │   │   │   │   └── registry.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── onboarding.ts
│   │   │   │   ├── parse-api-error.ts
│   │   │   │   ├── payment.ts
│   │   │   │   ├── permission.ts
│   │   │   │   ├── platform.ts
│   │   │   │   ├── player.ts
│   │   │   │   ├── proxy-env.ts
│   │   │   │   ├── query-client.ts
│   │   │   │   ├── responsive.ts
│   │   │   │   ├── secure-store.ts
│   │   │   │   ├── toast.tsx
│   │   │   │   ├── token.ts
│   │   │   │   ├── url-builder.ts
│   │   │   │   └── volume.ts
│   │   │   ├── main.tsx
│   │   │   ├── modules/
│   │   │   │   ├── ai/
│   │   │   │   │   └── summary.tsx
│   │   │   │   ├── context-menu/
│   │   │   │   │   ├── entry.tsx
│   │   │   │   │   ├── feeds.tsx
│   │   │   │   │   ├── inbox.tsx
│   │   │   │   │   ├── lists.tsx
│   │   │   │   │   └── video.tsx
│   │   │   │   ├── debug/
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── dialogs/
│   │   │   │   │   ├── ConfirmPasswordDialog.tsx
│   │   │   │   │   ├── ConfirmTOTPCodeDialog.tsx
│   │   │   │   │   ├── MarkAllAsReadDialog.tsx
│   │   │   │   │   └── UpgradeRequiredDialog.tsx
│   │   │   │   ├── discover/
│   │   │   │   │   ├── Category.tsx
│   │   │   │   │   ├── Content.tsx
│   │   │   │   │   ├── DiscoverContent.tsx
│   │   │   │   │   ├── FeedSummary.tsx
│   │   │   │   │   ├── RecommendationListItem.tsx
│   │   │   │   │   ├── Recommendations.tsx
│   │   │   │   │   ├── SearchContent.tsx
│   │   │   │   │   ├── SearchTabBar.tsx
│   │   │   │   │   ├── Trending.tsx
│   │   │   │   │   ├── api.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── ctx.tsx
│   │   │   │   │   ├── search-tabs/
│   │   │   │   │   │   ├── SearchFeed.tsx
│   │   │   │   │   │   ├── SearchFeedCard.tsx
│   │   │   │   │   │   ├── SearchList.tsx
│   │   │   │   │   │   ├── __base.tsx
│   │   │   │   │   │   └── hooks.tsx
│   │   │   │   │   └── search.tsx
│   │   │   │   ├── entry-content/
│   │   │   │   │   ├── EntryAISummary.tsx
│   │   │   │   │   ├── EntryContentHeaderRightActions.tsx
│   │   │   │   │   ├── EntryGridFooter.tsx
│   │   │   │   │   ├── EntryNavigationHeader.tsx
│   │   │   │   │   ├── EntryReadHistory.tsx
│   │   │   │   │   ├── EntryTitle.tsx
│   │   │   │   │   ├── ctx.ts
│   │   │   │   │   └── pull-up-navigation/
│   │   │   │   │       ├── PullUpIndicatorAndroid.tsx
│   │   │   │   │       ├── PullUpIndicatorIos.tsx
│   │   │   │   │       ├── types.ts
│   │   │   │   │       ├── use-pull-up-navigation.android.tsx
│   │   │   │   │       └── use-pull-up-navigation.tsx
│   │   │   │   ├── entry-list/
│   │   │   │   │   ├── EntryListContentArticle.tsx
│   │   │   │   │   ├── EntryListContentPicture.tsx
│   │   │   │   │   ├── EntryListContentSocial.tsx
│   │   │   │   │   ├── EntryListContentVideo.tsx
│   │   │   │   │   ├── EntryListContext.tsx
│   │   │   │   │   ├── EntryListEmpty.tsx
│   │   │   │   │   ├── EntryListFooter.tsx
│   │   │   │   │   ├── EntryListSelector.tsx
│   │   │   │   │   ├── ItemSeparator.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── templates/
│   │   │   │   │   │   ├── EntryNormalItem.tsx
│   │   │   │   │   │   ├── EntryPictureItem.tsx
│   │   │   │   │   │   ├── EntrySocialItem.tsx
│   │   │   │   │   │   ├── EntryTranslation.tsx
│   │   │   │   │   │   └── EntryVideoItem.tsx
│   │   │   │   │   └── types.ts
│   │   │   │   ├── feed/
│   │   │   │   │   ├── FollowFeed.tsx
│   │   │   │   │   └── view-selector.tsx
│   │   │   │   ├── list/
│   │   │   │   │   └── FollowList.tsx
│   │   │   │   ├── login/
│   │   │   │   │   ├── email.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── social.tsx
│   │   │   │   ├── onboarding/
│   │   │   │   │   ├── feeds-english.json
│   │   │   │   │   ├── feeds.json
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-reading-behavior.ts
│   │   │   │   │   ├── preset.ts
│   │   │   │   │   ├── shared.tsx
│   │   │   │   │   ├── step-finished.tsx
│   │   │   │   │   ├── step-interests.tsx
│   │   │   │   │   ├── step-preferences.tsx
│   │   │   │   │   └── step-welcome.tsx
│   │   │   │   ├── player/
│   │   │   │   │   ├── GlassPlayerTabBar.tsx
│   │   │   │   │   ├── PlayerTabBar.tsx
│   │   │   │   │   ├── context.ts
│   │   │   │   │   ├── control.tsx
│   │   │   │   │   └── hooks.ts
│   │   │   │   ├── review-prompt/
│   │   │   │   │   ├── debug.ts
│   │   │   │   │   ├── provider.tsx
│   │   │   │   │   ├── use-review-prompt-state.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── rsshub/
│   │   │   │   │   └── preview-url.tsx
│   │   │   │   ├── screen/
│   │   │   │   │   ├── PagerList.ios.tsx
│   │   │   │   │   ├── PagerList.tsx
│   │   │   │   │   ├── PagerListContext.ts
│   │   │   │   │   ├── TimelineSelectorList.tsx
│   │   │   │   │   ├── TimelineSelectorProvider.tsx
│   │   │   │   │   ├── TimelineViewSelector.tsx
│   │   │   │   │   ├── TimelineViewSelectorContextMenu.tsx
│   │   │   │   │   ├── action.tsx
│   │   │   │   │   ├── atoms.ts
│   │   │   │   │   └── hooks/
│   │   │   │   │       └── useHeaderHeight.tsx
│   │   │   │   ├── settings/
│   │   │   │   │   ├── SettingsList.tsx
│   │   │   │   │   ├── UserHeaderBanner.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   └── OTPWindow.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── useShareSubscription.tsx
│   │   │   │   │   │   └── useTOTPModalWrapper.tsx
│   │   │   │   │   ├── routes/
│   │   │   │   │   │   ├── 2FASetting.tsx
│   │   │   │   │   │   ├── About.tsx
│   │   │   │   │   │   ├── Account.tsx
│   │   │   │   │   │   ├── Achievement.tsx
│   │   │   │   │   │   ├── Actions.tsx
│   │   │   │   │   │   ├── Appearance.tsx
│   │   │   │   │   │   ├── Data.tsx
│   │   │   │   │   │   ├── EditCondition.tsx
│   │   │   │   │   │   ├── EditProfile.tsx
│   │   │   │   │   │   ├── EditRewriteRules.tsx
│   │   │   │   │   │   ├── EditRule.tsx
│   │   │   │   │   │   ├── EditWebhooks.tsx
│   │   │   │   │   │   ├── Feeds.tsx
│   │   │   │   │   │   ├── General.tsx
│   │   │   │   │   │   ├── Lists.tsx
│   │   │   │   │   │   ├── ManageList.tsx
│   │   │   │   │   │   ├── Notifications.tsx
│   │   │   │   │   │   ├── Plan.tsx
│   │   │   │   │   │   ├── Privacy.tsx
│   │   │   │   │   │   ├── ResetPassword.tsx
│   │   │   │   │   │   └── navigateToPlanScreen.ts
│   │   │   │   │   ├── sync-queue.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   └── subscription/
│   │   │   │       ├── CategoryGrouped.tsx
│   │   │   │       ├── ItemSeparator.tsx
│   │   │   │       ├── SubscriptionLists.tsx
│   │   │   │       ├── UnGroupedList.tsx
│   │   │   │       ├── atoms.ts
│   │   │   │       ├── constants.ts
│   │   │   │       ├── ctx.ts
│   │   │   │       ├── header-actions.tsx
│   │   │   │       └── items/
│   │   │   │           ├── InboxItem.tsx
│   │   │   │           ├── ListSubscriptionItem.tsx
│   │   │   │           ├── SubscriptionItem.tsx
│   │   │   │           ├── UnreadCount.tsx
│   │   │   │           └── types.tsx
│   │   │   ├── polyfill/
│   │   │   │   ├── index.ts
│   │   │   │   └── promise-with-resolvers.ts
│   │   │   ├── providers/
│   │   │   │   ├── AppleIAPProvider.tsx
│   │   │   │   ├── FontScalingProvider.tsx
│   │   │   │   ├── ServerConfigsLoader.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── migration.tsx
│   │   │   ├── screens/
│   │   │   │   ├── (headless)/
│   │   │   │   │   ├── (debug)/
│   │   │   │   │   │   ├── markdown.tsx
│   │   │   │   │   │   └── text.tsx
│   │   │   │   │   └── DebugScreen.tsx
│   │   │   │   ├── (modal)/
│   │   │   │   │   ├── DiscoverSettingsScreen.tsx
│   │   │   │   │   ├── EditEmailScreen.tsx
│   │   │   │   │   ├── FollowScreen.tsx
│   │   │   │   │   ├── ForgetPasswordScreen.tsx
│   │   │   │   │   ├── ListScreen.tsx
│   │   │   │   │   ├── LoginScreen.tsx
│   │   │   │   │   ├── ProfileScreen.tsx
│   │   │   │   │   ├── RsshubFormScreen.tsx
│   │   │   │   │   ├── TwoFactorAuthScreen.tsx
│   │   │   │   │   └── onboarding/
│   │   │   │   │       ├── EditProfileScreen.tsx
│   │   │   │   │       └── SelectReadingModeScreen.tsx
│   │   │   │   ├── (stack)/
│   │   │   │   │   ├── (tabs)/
│   │   │   │   │   │   ├── discover.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   ├── settings.tsx
│   │   │   │   │   │   └── subscriptions.tsx
│   │   │   │   │   ├── entries/
│   │   │   │   │   │   └── [entryId]/
│   │   │   │   │   │       └── EntryDetailScreen.tsx
│   │   │   │   │   ├── feeds/
│   │   │   │   │   │   └── [feedId]/
│   │   │   │   │   │       └── FeedScreen.tsx
│   │   │   │   │   └── recommendation/
│   │   │   │   │       └── RecommendationCategoryScreen.tsx
│   │   │   │   ├── +native-intent.tsx
│   │   │   │   ├── OnboardingScreen.tsx
│   │   │   │   └── PlayerScreen.tsx
│   │   │   ├── sitemap.tsx
│   │   │   ├── spec/
│   │   │   │   └── typography.ts
│   │   │   ├── store/
│   │   │   │   └── image/
│   │   │   │       ├── hooks.ts
│   │   │   │       └── store.ts
│   │   │   └── theme/
│   │   │       ├── colors.ts
│   │   │       ├── utils.ts
│   │   │       └── web.ts
│   │   ├── tailwind.config.ts
│   │   ├── tailwind.dom.config.ts
│   │   ├── tsconfig.json
│   │   └── web-app/
│   │       ├── html-renderer/
│   │       │   ├── global.d.ts
│   │       │   ├── index.html
│   │       │   ├── package.json
│   │       │   ├── postcss.config.cjs
│   │       │   ├── src/
│   │       │   │   ├── App.tsx
│   │       │   │   ├── HTML.tsx
│   │       │   │   ├── atoms/
│   │       │   │   │   └── index.ts
│   │       │   │   ├── common/
│   │       │   │   │   ├── ProviderComposer.tsx
│   │       │   │   │   └── WrappedElementProvider.tsx
│   │       │   │   ├── components/
│   │       │   │   │   ├── __internal/
│   │       │   │   │   │   ├── calculateDimensions.tsx
│   │       │   │   │   │   └── ctx.ts
│   │       │   │   │   ├── heading.tsx
│   │       │   │   │   ├── image.tsx
│   │       │   │   │   ├── index.ts
│   │       │   │   │   ├── link.tsx
│   │       │   │   │   ├── math.tsx
│   │       │   │   │   ├── p.tsx
│   │       │   │   │   └── shiki/
│   │       │   │   │       ├── Shiki.tsx
│   │       │   │   │       ├── hooks.ts
│   │       │   │   │       ├── index.ts
│   │       │   │   │       ├── shared.ts
│   │       │   │   │       └── shiki.module.css
│   │       │   │   ├── index.css
│   │       │   │   ├── index.ts
│   │       │   │   ├── managers/
│   │       │   │   │   └── webview-bridge.ts
│   │       │   │   ├── parser.tsx
│   │       │   │   ├── test.txt
│   │       │   │   └── utils.ts
│   │       │   ├── tailwind.config.ts
│   │       │   ├── tsconfig.json
│   │       │   ├── types/
│   │       │   │   └── index.ts
│   │       │   └── vite.config.mts
│   │       └── package.json
│   └── ssr/
│       ├── .env.example
│       ├── api/
│       │   └── index.ts
│       ├── client/
│       │   ├── @types/
│       │   │   ├── constants.ts
│       │   │   ├── default-resource.ts
│       │   │   └── i18next.d.ts
│       │   ├── App.tsx
│       │   ├── atoms/
│       │   │   ├── server-configs.ts
│       │   │   ├── settings/
│       │   │   │   ├── general.ts
│       │   │   │   └── helper.ts
│       │   │   └── user.ts
│       │   ├── components/
│       │   │   ├── common/
│       │   │   │   ├── 404.tsx
│       │   │   │   └── PoweredByFooter.tsx
│       │   │   ├── items/
│       │   │   │   ├── grid.tsx
│       │   │   │   ├── index.tsx
│       │   │   │   ├── normal.tsx
│       │   │   │   └── picture.tsx
│       │   │   ├── layout/
│       │   │   │   └── header/
│       │   │   │       └── index.tsx
│       │   │   └── ui/
│       │   │       ├── feed-certification.tsx
│       │   │       ├── feed-icon.tsx
│       │   │       ├── image.tsx
│       │   │       └── user-avatar.tsx
│       │   ├── configs.ts
│       │   ├── global.d.ts
│       │   ├── hooks/
│       │   │   └── useRecaptchaToken.ts
│       │   ├── i18n.ts
│       │   ├── index.tsx
│       │   ├── initialize/
│       │   │   ├── helper.ts
│       │   │   ├── index.ts
│       │   │   └── sentry.ts
│       │   ├── lib/
│       │   │   ├── api-fetch.ts
│       │   │   ├── auth.ts
│       │   │   ├── helper.ts
│       │   │   ├── query-client.ts
│       │   │   ├── store.ts
│       │   │   └── url-builder.ts
│       │   ├── modules/
│       │   │   └── login/
│       │   │       └── index.tsx
│       │   ├── pages/
│       │   │   ├── (login)/
│       │   │   │   ├── forget-password.tsx
│       │   │   │   ├── layout.tsx
│       │   │   │   ├── login/
│       │   │   │   │   ├── index.tsx
│       │   │   │   │   └── metadata.ts
│       │   │   │   ├── register.tsx
│       │   │   │   └── reset-password.tsx
│       │   │   ├── (main)/
│       │   │   │   ├── index.tsx
│       │   │   │   ├── layout.tsx
│       │   │   │   └── share/
│       │   │   │       ├── feeds/
│       │   │   │       │   └── [id]/
│       │   │   │       │       ├── index.tsx
│       │   │   │       │       └── metadata.ts
│       │   │   │       ├── lists/
│       │   │   │       │   └── [id]/
│       │   │   │       │       ├── index.tsx
│       │   │   │       │       └── metadata.ts
│       │   │   │       └── users/
│       │   │   │           └── [id]/
│       │   │   │               ├── index.tsx
│       │   │   │               └── metadata.ts
│       │   │   └── layout.tsx
│       │   ├── providers/
│       │   │   ├── root-providers.tsx
│       │   │   ├── server-configs-provider.tsx
│       │   │   └── user-provider.tsx
│       │   ├── query/
│       │   │   ├── auth.ts
│       │   │   ├── entries.ts
│       │   │   ├── feed.ts
│       │   │   ├── list.ts
│       │   │   └── users.ts
│       │   ├── router.tsx
│       │   └── styles/
│       │       └── index.css
│       ├── global.ts
│       ├── helper/
│       │   └── meta-map.ts
│       ├── index.html
│       ├── index.ts
│       ├── note.md
│       ├── package.json
│       ├── postcss.config.cjs
│       ├── public/
│       │   └── manifest.json
│       ├── scripts/
│       │   ├── check-fonts.ts
│       │   ├── cleanup-vercel-build.ts
│       │   ├── generate-font-data.ts
│       │   ├── patch-worker-build.ts
│       │   ├── prepare-vercel-build.ts
│       │   ├── skip-ssr-app-vercel-build.sh
│       │   └── upload-fonts-to-r2.ts
│       ├── src/
│       │   ├── global.d.ts
│       │   ├── lib/
│       │   │   ├── api-client.ts
│       │   │   ├── dev-vite.ts
│       │   │   ├── load-env.ts
│       │   │   ├── load-env.worker.ts
│       │   │   ├── not-found.ts
│       │   │   ├── og/
│       │   │   │   ├── fonts.ts
│       │   │   │   ├── fonts.worker.ts
│       │   │   │   ├── render-to-image.ts
│       │   │   │   ├── render-to-image.worker.ts
│       │   │   │   └── resvg-wasm-shim.ts
│       │   │   ├── seo.ts
│       │   │   └── worker-request-context.ts
│       │   ├── meta-handler.map.ts
│       │   ├── meta-handler.ts
│       │   └── router/
│       │       ├── global.ts
│       │       └── og/
│       │           ├── __base.tsx
│       │           ├── feed.tsx
│       │           ├── index.ts
│       │           ├── list.tsx
│       │           └── user.tsx
│       ├── tailwind.config.ts
│       ├── tsconfig.json
│       ├── tsdown.config.ts
│       ├── tsdown.worker.config.ts
│       ├── vercel.json
│       ├── vite.config.mts
│       ├── worker-app.ts
│       ├── worker-entry.ts
│       └── wrangler.jsonc
├── changelogithub.config.ts
├── conductor.json
├── eslint.config.mjs
├── locales/
│   ├── ai/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── app/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── common/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── errors/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── external/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── lang/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── mobile/
│   │   └── default/
│   │       ├── en.json
│   │       ├── fr-FR.json
│   │       ├── ja.json
│   │       ├── zh-CN.json
│   │       └── zh-TW.json
│   ├── native/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── settings/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   └── shortcuts/
│       ├── en.json
│       ├── fr-FR.json
│       ├── ja.json
│       ├── zh-CN.json
│       └── zh-TW.json
├── package.json
├── packages/
│   ├── configs/
│   │   ├── package.json
│   │   ├── tailwindcss/
│   │   │   ├── ratio-mixing-plugin.js
│   │   │   ├── tailwind-extend.css
│   │   │   ├── tw-css-plugin.js
│   │   │   └── web.ts
│   │   └── tsconfig.extend.json
│   ├── internal/
│   │   ├── AGENTS.md
│   │   ├── atoms/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── atoms/
│   │   │   │   │   └── user.ts
│   │   │   │   └── helper/
│   │   │   │       └── setting.ts
│   │   │   └── tsconfig.json
│   │   ├── components/
│   │   │   ├── assets/
│   │   │   │   ├── colors-media.css
│   │   │   │   ├── colors.css
│   │   │   │   ├── font.css
│   │   │   │   ├── index.css
│   │   │   │   └── tailwind.css
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── atoms/
│   │   │   │   │   ├── mouse.ts
│   │   │   │   │   ├── route.ts
│   │   │   │   │   └── viewport.ts
│   │   │   │   ├── common/
│   │   │   │   │   ├── Focusable/
│   │   │   │   │   │   ├── Focusable.tsx
│   │   │   │   │   │   ├── GlobalFocusableProvider.tsx
│   │   │   │   │   │   ├── context.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── Fragment.ts
│   │   │   │   │   ├── MemoedDangerousHTMLStyle.tsx
│   │   │   │   │   ├── MotionProvider.tsx
│   │   │   │   │   └── ReparentPortal.tsx
│   │   │   │   ├── constants/
│   │   │   │   │   └── spring.ts
│   │   │   │   ├── hooks/
│   │   │   │   │   ├── useMedia.ts
│   │   │   │   │   ├── useMobile.ts
│   │   │   │   │   ├── useMouse.ts
│   │   │   │   │   └── useViewport.ts
│   │   │   │   ├── icons/
│   │   │   │   │   ├── Database.tsx
│   │   │   │   │   ├── Meditation.tsx
│   │   │   │   │   ├── MynauiInboxArchive.tsx
│   │   │   │   │   ├── OouiUserAnonymous.tsx
│   │   │   │   │   ├── PhCloudCheck.tsx
│   │   │   │   │   ├── PhCloudWarning.tsx
│   │   │   │   │   ├── PhCloudX.tsx
│   │   │   │   │   ├── Progress.tsx
│   │   │   │   │   ├── empty.tsx
│   │   │   │   │   ├── follow.tsx
│   │   │   │   │   ├── folo.tsx
│   │   │   │   │   ├── infinify.tsx
│   │   │   │   │   ├── logo.tsx
│   │   │   │   │   ├── nft.tsx
│   │   │   │   │   ├── resize.tsx
│   │   │   │   │   ├── user.tsx
│   │   │   │   │   └── users.tsx
│   │   │   │   ├── providers/
│   │   │   │   │   ├── event-provider.tsx
│   │   │   │   │   └── stable-router-provider.tsx
│   │   │   │   ├── ui/
│   │   │   │   │   ├── auto-resize-height/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── avatar/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── avatar-group/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── button/
│   │   │   │   │   │   ├── action-button.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   ├── interface.ts
│   │   │   │   │   │   └── variants.tsx
│   │   │   │   │   ├── card/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── checkbox/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── collapse/
│   │   │   │   │   │   ├── Collapse.tsx
│   │   │   │   │   │   ├── CollapseCss.tsx
│   │   │   │   │   │   ├── hooks.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── context-menu/
│   │   │   │   │   │   ├── context-menu.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── datetime/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── utils.tsx
│   │   │   │   │   ├── divider/
│   │   │   │   │   │   ├── Divider.tsx
│   │   │   │   │   │   ├── PanelSplitter.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── drop-zone/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── effect/
│   │   │   │   │   │   └── MagneticHoverEffect.tsx
│   │   │   │   │   ├── form/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── hover-card/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── icon/
│   │   │   │   │   │   └── SiteIcon.tsx
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── DateTimePicker.tsx
│   │   │   │   │   │   ├── Input.tsx
│   │   │   │   │   │   ├── InputV2.tsx
│   │   │   │   │   │   ├── OTP.tsx
│   │   │   │   │   │   ├── TextArea.tsx
│   │   │   │   │   │   ├── TextAreaWrapper.tsx
│   │   │   │   │   │   ├── TimeSelect.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── json-highlighter/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── katex/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── lazy.tsx
│   │   │   │   │   ├── kbd/
│   │   │   │   │   │   └── Kbd.tsx
│   │   │   │   │   ├── key-value-editor/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── label/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── lexical-rich-editor/
│   │   │   │   │   │   ├── LexicalRichEditor.tsx
│   │   │   │   │   │   ├── LexicalRichEditorTextArea.tsx
│   │   │   │   │   │   ├── editor.tsx
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── nodes.ts
│   │   │   │   │   │   ├── plugins/
│   │   │   │   │   │   │   ├── code-highlighting/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   ├── exit-code/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── keyboard/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   ├── string-length-change/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   └── triple-backtick-toggle/
│   │   │   │   │   │   │       └── index.tsx
│   │   │   │   │   │   ├── theme.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── link/
│   │   │   │   │   │   ├── LinkWithTooltip.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── loading/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── markdown/
│   │   │   │   │   │   └── html.tsx
│   │   │   │   │   ├── marquee/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── masonry/
│   │   │   │   │   │   ├── contexts.tsx
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── navigation-menu/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── style.ts
│   │   │   │   │   ├── platform-icon/
│   │   │   │   │   │   ├── collections/
│   │   │   │   │   │   │   ├── cubox.tsx
│   │   │   │   │   │   │   ├── eagle.tsx
│   │   │   │   │   │   │   ├── instapaper.tsx
│   │   │   │   │   │   │   ├── obsidian.tsx
│   │   │   │   │   │   │   ├── outline.tsx
│   │   │   │   │   │   │   ├── readeck.tsx
│   │   │   │   │   │   │   ├── readwise.tsx
│   │   │   │   │   │   │   ├── rss3.tsx
│   │   │   │   │   │   │   ├── rsshub.tsx
│   │   │   │   │   │   │   └── zotero.tsx
│   │   │   │   │   │   ├── icons.ts
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── utils.tsx
│   │   │   │   │   ├── popover/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── portal/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── provider.tsx
│   │   │   │   │   ├── progress/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── progressive-blur/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── radio-group/
│   │   │   │   │   │   ├── RadioCard.tsx
│   │   │   │   │   │   ├── RadioGroup.tsx
│   │   │   │   │   │   ├── context.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── motion.tsx
│   │   │   │   │   ├── scroll-area/
│   │   │   │   │   │   ├── ScrollArea.tsx
│   │   │   │   │   │   ├── ctx.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.module.css
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── segment/
│   │   │   │   │   │   ├── ctx.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── select/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── responsive.tsx
│   │   │   │   │   ├── sheet/
│   │   │   │   │   │   ├── Sheet.tsx
│   │   │   │   │   │   ├── context.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── shiny-text/
│   │   │   │   │   │   ├── ShinyText.tsx
│   │   │   │   │   │   └── index.module.css
│   │   │   │   │   ├── shrinking-focus-border/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── skeleton/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── slider/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── switch/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── table/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── variants.tsx
│   │   │   │   │   ├── tabs/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── toast/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── styles.ts
│   │   │   │   │   ├── tooltip/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── styles.ts
│   │   │   │   │   ├── typography/
│   │   │   │   │   │   ├── EllipsisWithTooltip.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── z-index/
│   │   │   │   │       ├── ctx.tsx
│   │   │   │   │       └── index.tsx
│   │   │   │   └── utils/
│   │   │   │       ├── dayjs.ts
│   │   │   │       ├── icon.ts
│   │   │   │       ├── parse-markdown.tsx
│   │   │   │       └── selector.tsx
│   │   │   └── tsconfig.json
│   │   ├── constants/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── app.ts
│   │   │   │   ├── auth-providers.ts
│   │   │   │   ├── enums.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── rsshub.ts
│   │   │   │   ├── social.ts
│   │   │   │   └── tabs.tsx
│   │   │   └── tsconfig.json
│   │   ├── database/
│   │   │   ├── drizzle.config.ts
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── DatabaseSource.js
│   │   │   │   ├── ResourceLock.ts
│   │   │   │   ├── constant.ts
│   │   │   │   ├── db.desktop.ts
│   │   │   │   ├── db.rn.ts
│   │   │   │   ├── db.ts
│   │   │   │   ├── drizzle/
│   │   │   │   │   ├── 0000_harsh_shiva.sql
│   │   │   │   │   ├── 0001_bored_hobgoblin.sql
│   │   │   │   │   ├── 0002_smart_power_man.sql
│   │   │   │   │   ├── 0003_known_roland_deschain.sql
│   │   │   │   │   ├── 0004_majestic_thunderbolt_ross.sql
│   │   │   │   │   ├── 0005_tense_sleepwalker.sql
│   │   │   │   │   ├── 0006_exotic_kid_colt.sql
│   │   │   │   │   ├── 0007_curvy_tarantula.sql
│   │   │   │   │   ├── 0008_last_the_santerians.sql
│   │   │   │   │   ├── 0009_lucky_power_man.sql
│   │   │   │   │   ├── 0010_legal_ben_grimm.sql
│   │   │   │   │   ├── 0011_mysterious_stark_industries.sql
│   │   │   │   │   ├── 0012_magenta_thing.sql
│   │   │   │   │   ├── 0013_chunky_stephen_strange.sql
│   │   │   │   │   ├── 0014_chemical_shocker.sql
│   │   │   │   │   ├── 0015_colorful_warbird.sql
│   │   │   │   │   ├── 0016_curious_carnage.sql
│   │   │   │   │   ├── 0017_talented_captain_cross.sql
│   │   │   │   │   ├── 0018_dashing_the_fury.sql
│   │   │   │   │   ├── 0019_wonderful_shape.sql
│   │   │   │   │   ├── 0020_little_marauders.sql
│   │   │   │   │   ├── 0021_wakeful_onslaught.sql
│   │   │   │   │   ├── 0022_tiny_northstar.sql
│   │   │   │   │   ├── 0023_pink_namor.sql
│   │   │   │   │   ├── 0024_spooky_alex_power.sql
│   │   │   │   │   ├── 0025_colorful_valkyrie.sql
│   │   │   │   │   ├── 0026_numerous_slyde.sql
│   │   │   │   │   ├── 0027_nostalgic_human_torch.sql
│   │   │   │   │   ├── 0028_chief_cyclops.sql
│   │   │   │   │   ├── 0029_flaky_gorgon.sql
│   │   │   │   │   ├── 0030_common_gabe_jones.sql
│   │   │   │   │   ├── 0031_kind_ikaris.sql
│   │   │   │   │   ├── 0032_orange_prima.sql
│   │   │   │   │   ├── 0033_shiny_sebastian_shaw.sql
│   │   │   │   │   ├── 0034_curly_darkstar.sql
│   │   │   │   │   ├── 0035_last_valeria_richards.sql
│   │   │   │   │   ├── 0036_entry_tag_summary.sql
│   │   │   │   │   ├── 0037_bored_the_leader.sql
│   │   │   │   │   ├── meta/
│   │   │   │   │   │   ├── 0000_snapshot.json
│   │   │   │   │   │   ├── 0001_snapshot.json
│   │   │   │   │   │   ├── 0002_snapshot.json
│   │   │   │   │   │   ├── 0003_snapshot.json
│   │   │   │   │   │   ├── 0004_snapshot.json
│   │   │   │   │   │   ├── 0005_snapshot.json
│   │   │   │   │   │   ├── 0006_snapshot.json
│   │   │   │   │   │   ├── 0007_snapshot.json
│   │   │   │   │   │   ├── 0008_snapshot.json
│   │   │   │   │   │   ├── 0009_snapshot.json
│   │   │   │   │   │   ├── 0010_snapshot.json
│   │   │   │   │   │   ├── 0011_snapshot.json
│   │   │   │   │   │   ├── 0012_snapshot.json
│   │   │   │   │   │   ├── 0013_snapshot.json
│   │   │   │   │   │   ├── 0014_snapshot.json
│   │   │   │   │   │   ├── 0015_snapshot.json
│   │   │   │   │   │   ├── 0016_snapshot.json
│   │   │   │   │   │   ├── 0017_snapshot.json
│   │   │   │   │   │   ├── 0018_snapshot.json
│   │   │   │   │   │   ├── 0019_snapshot.json
│   │   │   │   │   │   ├── 0020_snapshot.json
│   │   │   │   │   │   ├── 0021_snapshot.json
│   │   │   │   │   │   ├── 0022_snapshot.json
│   │   │   │   │   │   ├── 0023_snapshot.json
│   │   │   │   │   │   ├── 0024_snapshot.json
│   │   │   │   │   │   ├── 0025_snapshot.json
│   │   │   │   │   │   ├── 0026_snapshot.json
│   │   │   │   │   │   ├── 0027_snapshot.json
│   │   │   │   │   │   ├── 0028_snapshot.json
│   │   │   │   │   │   ├── 0029_snapshot.json
│   │   │   │   │   │   ├── 0030_snapshot.json
│   │   │   │   │   │   ├── 0031_snapshot.json
│   │   │   │   │   │   ├── 0032_snapshot.json
│   │   │   │   │   │   ├── 0033_snapshot.json
│   │   │   │   │   │   ├── 0034_snapshot.json
│   │   │   │   │   │   ├── 0035_snapshot.json
│   │   │   │   │   │   ├── 0036_snapshot.json
│   │   │   │   │   │   ├── 0037_snapshot.json
│   │   │   │   │   │   └── _journal.json
│   │   │   │   │   └── migrations.js
│   │   │   │   ├── migrator.ts
│   │   │   │   ├── schemas/
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── services/
│   │   │   │   │   ├── collection.ts
│   │   │   │   │   ├── entry.ts
│   │   │   │   │   ├── feed.ts
│   │   │   │   │   ├── image.ts
│   │   │   │   │   ├── inbox.ts
│   │   │   │   │   ├── internal/
│   │   │   │   │   │   ├── base.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── list.ts
│   │   │   │   │   ├── subscription.ts
│   │   │   │   │   ├── summary.ts
│   │   │   │   │   ├── translation.ts
│   │   │   │   │   ├── unread.ts
│   │   │   │   │   └── user.ts
│   │   │   │   └── types.ts
│   │   │   └── tsconfig.json
│   │   ├── hooks/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── factory/
│   │   │   │   │   └── createHTMLMediaHook.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── internal/
│   │   │   │   │   └── for-theme.ts
│   │   │   │   ├── optimistic/
│   │   │   │   │   ├── config.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── strategies.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── useOptimisticMutation.ts
│   │   │   │   ├── useAnyPointDown.ts
│   │   │   │   ├── useControlled.ts
│   │   │   │   ├── useCountDown.ts
│   │   │   │   ├── useDark.ts
│   │   │   │   ├── useElementWidth.ts
│   │   │   │   ├── useInputComposition.ts
│   │   │   │   ├── useInterval.ts
│   │   │   │   ├── useIsOnline.ts
│   │   │   │   ├── useLongPress.ts
│   │   │   │   ├── useMeasure.ts
│   │   │   │   ├── useOnce.ts
│   │   │   │   ├── usePageVisibility.ts
│   │   │   │   ├── usePrevious.ts
│   │   │   │   ├── useRefValue.ts
│   │   │   │   ├── useSetState.ts
│   │   │   │   ├── useSmoothScroll.ts
│   │   │   │   ├── useSyncTheme.ts
│   │   │   │   ├── useTitle.ts
│   │   │   │   ├── useTriangleMenu.ts
│   │   │   │   ├── useTypescriptHappyCallback.ts
│   │   │   │   └── useVideo.ts
│   │   │   └── tsconfig.json
│   │   ├── logger/
│   │   │   ├── electron.ts
│   │   │   ├── package.json
│   │   │   ├── tsconfig.json
│   │   │   └── web.ts
│   │   ├── models/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── index.ts
│   │   │   │   └── rsshub.ts
│   │   │   └── tsconfig.json
│   │   ├── shared/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── auth.ts
│   │   │   │   ├── bridge.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── electron.ts
│   │   │   │   ├── env.common.ts
│   │   │   │   ├── env.desktop.ts
│   │   │   │   ├── env.rn.ts
│   │   │   │   ├── env.ssr.ts
│   │   │   │   ├── event.ts
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── language.ts
│   │   │   │   ├── queue.ts
│   │   │   │   ├── review-prompt.test.ts
│   │   │   │   ├── review-prompt.ts
│   │   │   │   └── settings/
│   │   │   │       ├── constants.ts
│   │   │   │       ├── defaults.ts
│   │   │   │       ├── hook.ts
│   │   │   │       └── interface.ts
│   │   │   └── tsconfig.json
│   │   ├── store/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── @types/
│   │   │   │   │   ├── default-resource.ts
│   │   │   │   │   └── i18next.d.ts
│   │   │   │   ├── constants/
│   │   │   │   │   ├── app.ts
│   │   │   │   │   └── onboarding.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── hydrate.ts
│   │   │   │   ├── lib/
│   │   │   │   │   ├── base.ts
│   │   │   │   │   ├── helper.ts
│   │   │   │   │   └── stream.ts
│   │   │   │   ├── modules/
│   │   │   │   │   ├── action/
│   │   │   │   │   │   ├── constant.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   └── store.ts
│   │   │   │   │   ├── collection/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── entry/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── feed/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── selectors.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── image/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   └── store.ts
│   │   │   │   │   ├── inbox/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── list/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── subscription/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── selectors.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── summary/
│   │   │   │   │   │   ├── enum.ts
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── translation/
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── unread/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── selectors.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   └── user/
│   │   │   │   │       ├── constants.ts
│   │   │   │   │       ├── getters.ts
│   │   │   │   │       ├── hooks.ts
│   │   │   │   │       ├── store.ts
│   │   │   │   │       └── types.ts
│   │   │   │   ├── morph/
│   │   │   │   │   ├── api.ts
│   │   │   │   │   ├── db-store.ts
│   │   │   │   │   └── store-db.ts
│   │   │   │   ├── reset.ts
│   │   │   │   └── types.ts
│   │   │   └── tsconfig.json
│   │   ├── tracker/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── adapters/
│   │   │   │   │   ├── base.ts
│   │   │   │   │   ├── firebase.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── posthog.ts
│   │   │   │   │   └── proxy.ts
│   │   │   │   ├── enums.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   ├── track-manager.ts
│   │   │   │   ├── tracker-points.ts
│   │   │   │   ├── types.ts
│   │   │   │   └── utils.ts
│   │   │   └── tsconfig.json
│   │   ├── types/
│   │   │   ├── global.d.ts
│   │   │   ├── package.json
│   │   │   ├── react-global.d.ts
│   │   │   └── vite-env.d.ts
│   │   └── utils/
│   │       ├── package.json
│   │       ├── src/
│   │       │   ├── attribution.ts
│   │       │   ├── bind-this.ts
│   │       │   ├── chain.ts
│   │       │   ├── cjk.ts
│   │       │   ├── color.ts
│   │       │   ├── data-structure/
│   │       │   │   ├── index.ts
│   │       │   │   └── set.ts
│   │       │   ├── dom.ts
│   │       │   ├── duration.ts
│   │       │   ├── environment.ts
│   │       │   ├── event-bus.rn.ts
│   │       │   ├── event-bus.ts
│   │       │   ├── headers.ts
│   │       │   ├── html.ts
│   │       │   ├── img-proxy.ts
│   │       │   ├── index.ts
│   │       │   ├── jotai.ts
│   │       │   ├── json-codec.ts
│   │       │   ├── language.ts
│   │       │   ├── link-parser.ts
│   │       │   ├── lru-cache.test.ts
│   │       │   ├── lru-cache.ts
│   │       │   ├── noop.ts
│   │       │   ├── ns.ts
│   │       │   ├── path-parser.test.ts
│   │       │   ├── path-parser.ts
│   │       │   ├── react.ts
│   │       │   ├── resize.ts
│   │       │   ├── scroller.ts
│   │       │   ├── url-builder.ts
│   │       │   ├── url-for-video.ts
│   │       │   ├── utils.spec.ts
│   │       │   └── utils.ts
│   │       ├── tsconfig.json
│   │       └── vitest.config.ts
│   └── readability/
│       ├── bump.config.ts
│       ├── package.json
│       ├── src/
│       │   └── index.ts
│       ├── tsconfig.json
│       └── tsdown.config.ts
├── patches/
│   ├── @microflash__remark-callout-directives.patch
│   ├── @mozilla__readability@0.6.0.patch
│   ├── @pengx17__electron-forge-maker-appimage.patch
│   ├── daisyui@4.12.24.patch
│   ├── re-resizable@6.11.2.patch
│   ├── react-native-sheet-transitions.patch
│   ├── react-native-track-player@4.1.1.patch
│   └── workbox-precaching.patch
├── plugins/
│   ├── eslint/
│   │   ├── eslint-check-i18n-json.js
│   │   ├── eslint-no-debug.js
│   │   ├── eslint-package-json.js
│   │   └── eslint-recursive-sort.js
│   └── utils.js
├── pnpm-workspace.yaml
├── scripts/
│   ├── copy-translation.ts
│   ├── increment-build-id.sh
│   ├── lib.ts
│   ├── mitproxy.py
│   ├── run-proxy.sh
│   ├── skip-main-app-vercel-build.sh
│   ├── svg-to-rn.ts
│   └── update-icon.ts
├── tsconfig.json
├── tsslint.config.ts
├── turbo.json
├── vercel.json
├── vitest.workspace.js
├── vitest.workspace.ts
└── wiki/
    └── contribute-i18n.md

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

================================================
FILE: .agents/settings.local.json
================================================
{
  "permissions": {
    "allow": [
      "Bash(gh pr view:*)",
      "Bash(pnpm bump:*)",
      "Bash(git add:*)",
      "Bash(git commit:*)",
      "Bash(ls:*)",
      "Bash(npx:*)",
      "Bash(git stash:*)",
      "Bash(git show:*)",
      "Bash(git -C /Users/diygod/Code/Projects/Folo status --short)",
      "Bash(git -C /Users/diygod/Code/Projects/Folo add apps/mobile/changelog/next.md)",
      "Bash(git -C /Users/diygod/Code/Projects/Folo commit:*)",
      "Bash(git checkout:*)",
      "Bash(git fetch:*)",
      "Bash(git merge:*)",
      "Bash(git rm:*)",
      "Bash(git push:*)",
      "Bash(gh api:*)",
      "Bash(pnpm exec vv:*)"
    ]
  }
}


================================================
FILE: .agents/skills/desktop-release/SKILL.md
================================================
---
name: desktop-release
description: Perform a regular desktop release from the dev branch. Gathers commits since last release, updates changelog, evaluates mainHash changes, bumps version, and creates release PR.
disable-model-invocation: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
---

# Desktop Regular Release

Perform a regular desktop release. This skill handles the full release workflow from the `dev` branch.

## Pre-flight checks

1. Confirm the current branch is `dev`. If not, abort with a warning.
2. Run `git pull --rebase` in the repo root to ensure the local branch is up to date.
3. Read `apps/desktop/package.json` to get the current `version` and `mainHash`.

## Step 1: Gather changes since last release

1. Find the last release tag:
   ```bash
   git tag --sort=-creatordate | grep '^desktop/v' | head -1
   ```
2. Get all commits since that tag on the current branch:
   ```bash
   git log <last-tag>..HEAD --oneline --no-merges
   ```
3. Categorize commits into:
   - **Shiny new things** (feat: commits, new features)
   - **Improvements** (refactor:, perf:, chore: improvements, dependency updates)
   - **No longer broken** (fix: commits, bug fixes)
   - **Thanks** (identify external contributor GitHub usernames from commits)

## Step 2: Update changelog

1. Read `apps/desktop/changelog/next.md`.
2. Present the categorized changes to the user and draft the changelog content.
3. Wait for user confirmation or edits before writing.
4. Write the final content to `apps/desktop/changelog/next.md`, following the template format:

   ```markdown
   # What's new in vNEXT_VERSION

   ## Shiny new things

   - description of new feature

   ## Improvements

   - description of improvement

   ## No longer broken

   - description of fix

   ## Thanks

   Special thanks to volunteer contributors @username for their valuable contributions
   ```

5. Keep `NEXT_VERSION` as the placeholder - it will be replaced by `apply-changelog.ts` during bump.

## Step 3: Commit changelog updates before bump

`nbump` requires a clean working tree. Commit changelog edits before running bump.

1. Stage the changelog update:
   ```bash
   git add apps/desktop/changelog/next.md
   ```
2. Commit it on `dev`:
   ```bash
   git commit -m "docs(desktop): prepare release changelog"
   ```
3. If there are no changes to commit, continue without creating an extra commit.

## Step 4: Evaluate mainHash

This is critical for determining whether users need a full app update or can use the lightweight renderer hot update.

1. Check what files changed in `apps/desktop/layer/main/` since the last release tag:
   ```bash
   git diff <last-tag>..HEAD --name-only -- apps/desktop/layer/main/
   ```
2. Also check changes to `apps/desktop/package.json` fields other than version/mainHash (since package.json is included in the hash calculation):
   ```bash
   git diff <last-tag>..HEAD -- apps/desktop/package.json
   ```

**Decision logic:**

- If there are **NO changes** in `layer/main/` and no meaningful `package.json` changes (only version/mainHash/changelog-related), then mainHash should NOT be updated. Users will get a fast renderer-only hot update.
- If there are **trivial changes** in `layer/main/` (typo fixes, comment changes, logging tweaks) that don't affect runtime behavior, recommend NOT updating mainHash. Present the changes to the user and ask for confirmation.
- If there are **meaningful changes** in `layer/main/` (new features, bug fixes, dependency changes, API changes), mainHash MUST be updated. Users will need a full app update.

Present your analysis to the user with:

- List of changed files in `layer/main/`
- A summary of what changed
- Your recommendation (update or skip mainHash)
- Ask for explicit confirmation

## Step 5: Save old mainHash and execute bump

1. Save the current mainHash from `apps/desktop/package.json` for later comparison.
2. Verify working tree is clean before bump:
   ```bash
   git status --short
   ```
3. Change directory to `apps/desktop/` and run the bump:
   ```bash
   cd apps/desktop && pnpm bump
   ```
4. This command will:
   - Pull latest changes
   - Apply changelog (rename next.md to {version}.md, create new next.md)
   - Recalculate mainHash and write to package.json
   - Format package.json
   - Bump minor version
   - Commit with message `release(desktop): release v{NEW_VERSION}`
   - Create branch `release/desktop/{NEW_VERSION}`
   - Push branch and create PR to `main`

## Step 6: Restore mainHash if skipping update

If Step 4 decided mainHash should NOT be updated, restore the old value now. The bump has already committed, pushed, and created the PR on a new release branch, so we amend the commit and force push. This is safe because the release branch was just created.

1. Change back to the repo root first (Step 5 left the working directory at `apps/desktop/`):
   ```bash
   cd ../..
   ```
2. Ensure you are on the `release/desktop/{NEW_VERSION}` branch (bump should have switched to it).
3. Replace the recalculated mainHash with the saved old value in `apps/desktop/package.json`.
4. Stage and amend the release commit:
   ```bash
   git add apps/desktop/package.json && git commit --amend --no-edit
   ```
5. Force push the release branch:
   ```bash
   git push --force origin release/desktop/{NEW_VERSION}
   ```

If Step 4 decided mainHash SHOULD be updated, skip this step entirely — the bump already wrote the correct new value.

## Step 7: Verify

1. Confirm the PR was created successfully by checking the output.
2. Report the new version number and PR URL to the user.
3. Summarize:
   - New version: v{NEW_VERSION}
   - mainHash updated: yes/no (and why)
   - Changelog highlights
   - PR URL

## Reference

- Bump config: `apps/desktop/bump.config.ts`
- Changelog dir: `apps/desktop/changelog/`
- Changelog template: `apps/desktop/changelog/next.template.md`
- mainHash generator: `apps/desktop/plugins/vite/generate-main-hash.ts`
- Hot updater logic: `apps/desktop/layer/main/src/updater/hot-updater.ts`
- CI build workflow: `.github/workflows/build-desktop.yml`
- Tag workflow: `.github/workflows/tag.yml`


================================================
FILE: .agents/skills/installing-mobile-preview-builds/SKILL.md
================================================
---
name: installing-mobile-preview-builds
description: Builds and installs the iOS preview build for apps/mobile using EAS local build and devicectl. Use when the user asks to install a preview/internal iOS build on a connected iPhone for production-like testing.
disable-model-invocation: true
allowed-tools: Bash, Read, Glob, Grep
argument-hint: "[device-udid-or-name(optional)]"
---

# Install Mobile Preview Build (iOS)

Use this skill to create a fresh local `preview` iOS build and install it on a connected iPhone.

## Inputs

- Optional `$ARGUMENTS`: device identifier (UDID or exact device name).
- If no argument is provided, auto-select the first paired iPhone from `xcrun devicectl list devices`.

## Workflow

1. Validate repo and tooling.
   - Run from repo root and ensure `apps/mobile` exists.
   - Verify `pnpm`, `xcrun`, `xcodebuild`, and `eas-cli` are available.
   - Verify EAS login:
     ```bash
     cd apps/mobile
     pnpm dlx eas-cli whoami
     ```
2. Resolve target device.
   - List paired devices:
     ```bash
     xcrun devicectl list devices
     ```
   - Choose device in this order:
     - `$ARGUMENTS` if provided and matches exactly one device.
     - Otherwise, first paired iPhone.
3. Trigger local `preview` iOS build.

   ```bash
   mkdir -p .context/preview-install
   cd apps/mobile
   pnpm dlx eas-cli build -p ios --profile preview --non-interactive --local --output=./build-preview.ipa
   cd ../..
   cp apps/mobile/build-preview.ipa .context/preview-install/folo-preview.ipa
   ```

4. Install to device locally.
   ```bash
   unzip -q -o .context/preview-install/folo-preview.ipa -d .context/preview-install/unpacked
   APP_PATH=$(find .context/preview-install/unpacked/Payload -maxdepth 1 -name '*.app' -type d | head -n 1)
   xcrun devicectl device install app --device "<device-id>" "$APP_PATH"
   ```
5. Try launching app.

   ```bash
   xcrun devicectl device process launch --device "<device-id>" is.follow --activate
   ```

   - If launch fails due to locked device, instruct the user to unlock iPhone and open `Folo` manually.

## Failure Handling

- If local build fails, report:
  - build mode (`local`)
  - failing command
  - key error message from command output
- If app config fails with `Assets source directory not found ... /out/rn-web`, prebuild assets then retry once:
  ```bash
  pnpm --filter @follow/rn-micro-web-app build --outDir out/rn-web/html-renderer
  ```

## Output Format

Always return:

1. Build mode (`local`) and final status.
2. Local IPA path.
3. Target device identifier.
4. Install result (`installed` or `failed`) and launch result.
5. Next action for the user if manual action is required.


================================================
FILE: .agents/skills/mobile-e2e/SKILL.md
================================================
---
name: mobile-e2e
description: Run apps/mobile Maestro end-to-end tests in this repo. Use when an agent needs to validate mobile auth flows on iOS Simulator or Android Emulator. Current maintained coverage is register, sign out, and sign in.
disable-model-invocation: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
---

# Mobile E2E

Run the mobile Maestro tests for `apps/mobile`.

## Files that matter

- Runner: `apps/mobile/e2e/run-maestro.sh`
- iOS auth flow: `apps/mobile/e2e/flows/ios/auth.yaml`
- Android auth flow: `apps/mobile/e2e/flows/android/core.yaml`
- Shared auth flows: `apps/mobile/e2e/flows/shared/*.yaml`
- Artifacts: `apps/mobile/e2e/artifacts/`

## Always do first

From repo root:

```bash
cd apps/mobile
pnpm run e2e:doctor
pnpm run typecheck
```

## iOS

Use a simulator `.app` build, not an Expo development client.

### Preferred simulator

Prefer the **latest installed iOS runtime** and a **latest-generation iPhone simulator**.

When multiple simulators are available, bias toward the newest iPhone model on the newest installed iOS version.

### Boot simulator

```bash
xcrun simctl boot <IOS_UDID>
xcrun simctl bootstatus <IOS_UDID> -b
open -a Simulator --args -CurrentDeviceUDID <IOS_UDID>
```

### App bundle

`run-maestro.sh` can resolve the app bundle from one of these sources:

- `MAESTRO_IOS_APP_PATH`
- a local `build-*.tar.gz` in `apps/mobile`
- an existing `DerivedData/.../Release-iphonesimulator/Folo.app`

If none of those exist, build one first.

### Build simulator app when missing

If `Folo.app` is not available yet:

```bash
cd apps/mobile/ios
pod install
xcodebuild -workspace Folo.xcworkspace \
  -scheme Folo \
  -configuration Release \
  -sdk iphonesimulator \
  -destination 'id=<IOS_UDID>' \
  build
```

### Apple Silicon simulator optimization

When running on an Apple Silicon Mac and building only for the simulator used in the current run, prefer compiling only the active `arm64` simulator architecture:

```bash
xcodebuild ... \
  ONLY_ACTIVE_ARCH=YES \
  ARCHS=arm64
```

Use this optimization only for local self-test / e2e simulator builds tied to the current machine. Do not use it when you need a universal simulator app for other machines or when running on Intel Macs.

Expected output pattern:

```bash
~/Library/Developer/Xcode/DerivedData/.../Build/Products/Release-iphonesimulator/Folo.app
```

### Run iOS auth flow

```bash
cd apps/mobile
MAESTRO_IOS_DEVICE_ID=<IOS_UDID> \
MAESTRO_IOS_APP_PATH=<PATH_TO_Folo.app> \
pnpm run e2e:ios
```

## Android

Use a **release APK**, not an Expo development build.

### Java

Use Android Studio bundled JBR:

```bash
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
export PATH="$JAVA_HOME/bin:$PATH"
```

### Android SDK

```bash
export ANDROID_HOME="$HOME/Library/Android/sdk"
export ANDROID_SDK_ROOT="$HOME/Library/Android/sdk"
```

If `apps/mobile/android/local.properties` is missing, create it with:

```bash
echo "sdk.dir=$HOME/Library/Android/sdk" > apps/mobile/android/local.properties
```

### Build release APK

If `apps/mobile/android` does not exist locally, generate it first with Expo prebuild / run-android tooling.

Then build the release APK:

```bash
cd apps/mobile/android
./gradlew app:assembleRelease --console=plain
```

Expected APK path:

```bash
apps/mobile/android/app/build/outputs/apk/release/app-release.apk
```

### Install to emulator

```bash
adb -s emulator-5554 install -r apps/mobile/android/app/build/outputs/apk/release/app-release.apk
```

### Run Android auth flow

Start a booted emulator first, then:

```bash
cd apps/mobile
pnpm run e2e:android
```

## Result checks

Successful auth validation means:

- register flow finishes
- sign-out reaches `login-screen`
- login flow makes `login-screen` disappear

## Debugging output

Inspect these folders after a run:

```bash
apps/mobile/e2e/artifacts/ios/
apps/mobile/e2e/artifacts/android/
```

For a one-off focused run, invoke Maestro directly against a single flow and a custom debug directory.


================================================
FILE: .agents/skills/mobile-release/SKILL.md
================================================
---
name: mobile-release
description: Perform a regular mobile release from the dev branch. Gathers commits since last release, updates changelog, bumps version, updates iOS Info.plist, and creates release PR to mobile-main.
disable-model-invocation: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
---

# Mobile Regular Release

Perform a regular mobile release. This skill handles the full release workflow from the `dev` branch.

## Pre-flight checks

1. Confirm the current branch is `dev`. If not, abort with a warning.
2. Run `git pull --rebase` in the repo root to ensure the local branch is up to date.
3. Read `apps/mobile/package.json` to get the current `version`.

## Step 1: Gather changes since last release

1. Find the last release tag (both old `mobile@` and new `mobile/v` prefixes exist):
   ```bash
   git tag --sort=-creatordate | grep -E '^mobile[@/]' | head -1
   ```
2. If no tag found, find the last release commit by matching only the subject line:
   ```bash
   git log --format="%H %s" | grep "^[a-f0-9]* release(mobile): release v" | head -1 | awk '{print $1}'
   ```
3. Get all commits since the last release on the current branch:
   ```bash
   git log <last-tag-or-commit>..HEAD --oneline --no-merges
   ```
4. Categorize commits into:
   - **Shiny new things** (feat: commits, new features)
   - **Improvements** (refactor:, perf:, chore: improvements, dependency updates)
   - **No longer broken** (fix: commits, bug fixes)
   - **Thanks** (identify external contributor GitHub usernames from commits)

## Step 2: Update changelog

1. Read `apps/mobile/changelog/next.md`.
2. Present the categorized changes to the user and draft the changelog content.
3. Wait for user confirmation or edits before writing.
4. Write the final content to `apps/mobile/changelog/next.md`, following the template format:

   ```markdown
   # What's New in vNEXT_VERSION

   ## Shiny new things

   - description of new feature

   ## Improvements

   - description of improvement

   ## No longer broken

   - description of fix

   ## Thanks

   Special thanks to volunteer contributors @username for their valuable contributions
   ```

5. Keep `NEXT_VERSION` as the placeholder - it will be replaced by `apply-changelog.ts` during bump.

## Step 3: Commit changelog updates before bump

`nbump` requires a clean working tree. Commit changelog edits before running bump.

1. Stage the changelog update:
   ```bash
   git add apps/mobile/changelog/next.md
   ```
2. Commit it on `dev`:
   ```bash
   git commit -m "docs(mobile): prepare release changelog"
   ```
3. If there are no changes to commit, continue without creating an extra commit.

## Step 4: Execute bump

1. Verify working tree is clean before bump:
   ```bash
   git status --short
   ```
2. Change directory to `apps/mobile/` and run the bump:
   ```bash
   cd apps/mobile && pnpm bump
   ```
3. This is an interactive `nbump` command that prompts for version selection. It will:
   - Pull latest changes
   - Apply changelog (rename next.md to {version}.md, create new next.md from template)
   - Format package.json with eslint + prettier
   - Bump version in `package.json`
   - Update `ios/Folo/Info.plist`:
     - Set `CFBundleShortVersionString` to the new version
     - Increment `CFBundleVersion` (build number) by 1
   - Commit with message `release(mobile): release v{NEW_VERSION}`
   - Create branch `release/mobile/{NEW_VERSION}`
   - Push branch and create PR to `mobile-main`

## Step 5: Verify

1. Confirm the PR was created successfully by checking the output.
2. Report the new version number and PR URL to the user.
3. Summarize:
   - New version: v{NEW_VERSION}
   - Changelog highlights
   - PR URL

## Post-release (manual steps, inform user)

After the release PR is merged to `mobile-main`:

1. **Trigger production builds** via GitHub Actions `workflow_dispatch`:
   - Go to "Build iOS" workflow, select `mobile-main` branch, profile = `production`
   - Go to "Build Android" workflow, select `mobile-main` branch, profile = `production`
2. Production builds auto-submit to App Store (via `eas submit`) and Google Play (as draft).
3. After submission, go to App Store Connect and Google Play Console to complete the review/release process.

## Reference

- Bump config: `apps/mobile/bump.config.ts`
- Changelog dir: `apps/mobile/changelog/`
- Changelog template: `apps/mobile/changelog/next.template.md`
- Apply changelog script: `apps/mobile/scripts/apply-changelog.ts`
- EAS config: `apps/mobile/eas.json`
- App config: `apps/mobile/app.config.ts`
- iOS Info.plist: `apps/mobile/ios/Folo/Info.plist`
- CI build iOS: `.github/workflows/build-ios.yml`
- CI build Android: `.github/workflows/build-android.yml`


================================================
FILE: .agents/skills/mobile-self-test/SKILL.md
================================================
---
name: mobile-self-test
description: Self-test a mobile feature change or bug fix after implementation in `apps/mobile`. Use this whenever the user asks to verify a mobile change, run simulator acceptance, smoke-test a mobile PR, or provide screenshot proof for a mobile fix. This skill decides between prod vs local API mode, starts the local follow-server when needed, builds a release app, uses Maestro only to bootstrap registration for non-auth work, then switches to screenshot-driven visual validation and returns screenshot evidence.
disable-model-invocation: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
---

# Mobile Self Test

Validate a mobile change after implementation.

This skill extends `../mobile-e2e/SKILL.md`. Read that skill first for the baseline doctor checks, iOS simulator boot rules, Java/Android SDK setup, and Maestro artifact conventions. Then apply the extra rules in this skill.

## Files that matter

- Reference skill: `../mobile-e2e/SKILL.md`
- Runner: `apps/mobile/e2e/run-maestro.sh`
- iOS register flow: `apps/mobile/e2e/flows/ios/register.yaml`
- Android register flow: `apps/mobile/e2e/flows/android/register.yaml`
- Shared auth flows: `apps/mobile/e2e/flows/shared/*.yaml`
- Expo config: `apps/mobile/app.config.ts`
- Build profiles: `apps/mobile/eas.json`
- Mobile artifacts: `apps/mobile/e2e/artifacts/`
- Local server repo: `/Users/diygod/Code/Projects/follow-server`

## Default assumptions

- Prefer **iOS simulator** unless the user explicitly asks for Android or the change is Android-specific.
- Default to **prod API mode** when the user did not specify a mode.
- Default to **local API mode** when the task also involves local server changes, backend debugging, or modified files in `/Users/diygod/Code/Projects/follow-server`.
- Keep `EXPO_PUBLIC_E2E_LANGUAGE=en` unless the user explicitly wants another language. The existing Maestro flows assume English UI.

## Simulator and emulator isolation

This section overrides the shared device-selection guidance from `../mobile-e2e/SKILL.md`.

Self-test runs must be isolated because other agents may be using simulators or emulators on the same machine.

- Always create a dedicated temporary simulator or emulator for the current run.
- Never reuse `booted`, an already-running simulator, or a generic Android serial such as `emulator-5554`.
- Record the temporary device name and identifier immediately after creation, then use only that stored identifier for build, install, launch, screenshots, and Maestro.
- Register cleanup before booting the device so the temporary simulator or emulator is deleted even if the test fails midway.
- If cleanup fails, report the leftover device name and identifier in the final response.

## Decide API mode first

Use this decision order:

1. If the user explicitly asks for `prod` or `local`, obey that.
2. Otherwise, if the task depends on local backend changes or local server behavior, use `local`.
3. Otherwise, use `prod`.

Map the chosen mode into the release build:

- `prod` mode: `EXPO_PUBLIC_E2E_ENV_PROFILE=prod`
- `local` mode: `EXPO_PUBLIC_E2E_ENV_PROFILE=local`

Do not silently reuse a build from the other mode. Rebuild the release app when switching between `prod` and `local`.

## Always do first

From repo root:

```bash
cd apps/mobile
pnpm run e2e:doctor
pnpm run typecheck
```

If these fail, stop and report the blocker before attempting simulator work.

## Local server mode

`local` mode requires the local server to be available at `http://localhost:3000`.

Before starting anything, check whether it is already running. Do not start a duplicate server.

```bash
FOLLOW_SERVER_LOG=/tmp/follow-server-dev-core.log

if pgrep -af "pnpm dev:core" >/dev/null 2>&1 || lsof -nP -iTCP:3000 -sTCP:LISTEN >/dev/null 2>&1; then
  echo "follow-server already running"
else
  (
    cd /Users/diygod/Code/Projects/follow-server
    nohup pnpm dev:core >"$FOLLOW_SERVER_LOG" 2>&1 &
  )
fi

for _ in $(seq 1 60); do
  nc -z 127.0.0.1 3000 >/dev/null 2>&1 && break
  sleep 2
done

nc -z 127.0.0.1 3000 >/dev/null 2>&1
```

If the task depends on other local surfaces such as `http://localhost:2233`, call that out explicitly instead of pretending the mobile test fully covers it.

## Release build profiles for self-test

Use release-style builds so the test matches user-facing behavior.

- iOS simulator builds: `PROFILE=e2e-ios-simulator`
- Android emulator builds: `PROFILE=e2e-android`

Always pair those with the chosen API mode and language:

```bash
export EXPO_PUBLIC_E2E_ENV_PROFILE=<prod-or-local>
export EXPO_PUBLIC_E2E_LANGUAGE=en
```

## iOS workflow

Do not attach to an existing simulator from `../mobile-e2e/SKILL.md`. Create a dedicated temporary simulator for this run and keep using only its UDID.

### Create a dedicated temporary simulator

Pick the latest available iOS runtime and a recent iPhone device type, then create a temporary simulator.

```bash
IOS_SIM_NAME="CodexSelfTest-$(date +%Y%m%d-%H%M%S)"
IOS_RUNTIME_ID="<latest available iOS runtime identifier from `xcrun simctl list runtimes`>"
IOS_DEVICE_TYPE_ID="<recent iPhone device type identifier from `xcrun simctl list devicetypes`>"

IOS_UDID="$(xcrun simctl create "$IOS_SIM_NAME" "$IOS_DEVICE_TYPE_ID" "$IOS_RUNTIME_ID")"

cleanup_ios_simulator() {
  xcrun simctl shutdown "$IOS_UDID" >/dev/null 2>&1 || true
  xcrun simctl delete "$IOS_UDID" >/dev/null 2>&1 || true
}

trap cleanup_ios_simulator EXIT
```

Do not switch to another simulator after `IOS_UDID` is created.

### Boot the dedicated simulator

```bash
xcrun simctl boot "$IOS_UDID"
xcrun simctl bootstatus "$IOS_UDID" -b
open -a Simulator --args -CurrentDeviceUDID "$IOS_UDID"
```

If other simulators are already booted, leave them alone and continue using only `IOS_UDID`.

### Build release simulator app

```bash
cd apps/mobile/ios
pod install

PROFILE=e2e-ios-simulator \
EXPO_PUBLIC_E2E_ENV_PROFILE=<prod-or-local> \
EXPO_PUBLIC_E2E_LANGUAGE=en \
xcodebuild -workspace Folo.xcworkspace \
  -scheme Folo \
  -configuration Release \
  -sdk iphonesimulator \
  -destination "id=$IOS_UDID" \
  clean build
```

On Apple Silicon Macs, when the build is only for the dedicated simulator created for the current self-test run, prefer compiling only the active `arm64` simulator architecture:

```bash
ONLY_ACTIVE_ARCH=YES \
ARCHS=arm64
```

Do not use that optimization when you need a universal simulator bundle for other machines or when the host Mac is Intel.

Expected output pattern:

```bash
~/Library/Developer/Xcode/DerivedData/.../Build/Products/Release-iphonesimulator/Folo.app
```

### Install app on simulator

```bash
xcrun simctl install "$IOS_UDID" <PATH_TO_Folo.app>
xcrun simctl launch "$IOS_UDID" is.follow
```

## Android workflow

Reuse the Java and Android SDK setup from `../mobile-e2e/SKILL.md`.

Do not attach to a shared emulator. Create a dedicated temporary AVD for this run and keep using only its recorded serial.

### Create a dedicated temporary AVD

Create a fresh AVD backed by an installed phone system image.

```bash
ANDROID_AVD_NAME="codex-self-test-$(date +%Y%m%d-%H%M%S)"
ANDROID_AVD_PACKAGE="<installed Android system image package>"
ANDROID_AVD_DEVICE="<phone hardware profile>"

avdmanager create avd -n "$ANDROID_AVD_NAME" -k "$ANDROID_AVD_PACKAGE" -d "$ANDROID_AVD_DEVICE" --force

ANDROID_EMULATOR_PORT=""
for port in 5554 5556 5558 5560 5562 5564; do
  if ! lsof -nP -iTCP:$port >/dev/null 2>&1 && ! lsof -nP -iTCP:$((port + 1)) >/dev/null 2>&1; then
    ANDROID_EMULATOR_PORT="$port"
    break
  fi
done

[ -n "$ANDROID_EMULATOR_PORT" ] || {
  echo "No free Android emulator port found"
  exit 1
}

ANDROID_DEVICE_ID="emulator-$ANDROID_EMULATOR_PORT"

cleanup_android_emulator() {
  adb -s "$ANDROID_DEVICE_ID" emu kill >/dev/null 2>&1 || true
  avdmanager delete avd -n "$ANDROID_AVD_NAME" >/dev/null 2>&1 || true
}

trap cleanup_android_emulator EXIT
```

### Boot the dedicated emulator

```bash
emulator @"$ANDROID_AVD_NAME" -port "$ANDROID_EMULATOR_PORT" -no-snapshot -wipe-data &
adb -s "$ANDROID_DEVICE_ID" wait-for-device
```

If other emulators are already booted, ignore them and continue using only `ANDROID_DEVICE_ID`.

If `apps/mobile/android` does not exist locally, generate it first.

```bash
cd apps/mobile
pnpm expo prebuild android
```

### Build release APK

```bash
cd apps/mobile/android

PROFILE=e2e-android \
EXPO_PUBLIC_E2E_ENV_PROFILE=<prod-or-local> \
EXPO_PUBLIC_E2E_LANGUAGE=en \
./gradlew clean app:assembleRelease --console=plain
```

Expected APK path:

```bash
apps/mobile/android/app/build/outputs/apk/release/app-release.apk
```

### Install app on emulator

```bash
adb -s "$ANDROID_DEVICE_ID" install -r apps/mobile/android/app/build/outputs/apk/release/app-release.apk
adb -s "$ANDROID_DEVICE_ID" shell monkey -p is.follow -c android.intent.category.LAUNCHER 1
```

## Cleanup is mandatory

Delete the temporary simulator or emulator created for the run before returning control to the user.

### iOS cleanup

```bash
xcrun simctl shutdown "$IOS_UDID" >/dev/null 2>&1 || true
xcrun simctl delete "$IOS_UDID" >/dev/null 2>&1 || true
```

### Android cleanup

```bash
adb -s "$ANDROID_DEVICE_ID" emu kill >/dev/null 2>&1 || true
avdmanager delete avd -n "$ANDROID_AVD_NAME" >/dev/null 2>&1 || true
```

Do not leave temporary devices behind for other agents.

## Choose the auth strategy

This is the core difference from `mobile-e2e`.

### A. Change is **not** related to login or registration

Use the existing automated **registration** flow first to bootstrap a clean logged-in account, then do the real verification visually.

Examples:

- timeline behavior
- subscription management
- onboarding content after auth
- settings pages unrelated to sign-in state
- player, reader, share, discover, profile editing

Generate a unique test account before running the flow:

```bash
export E2E_PASSWORD='Password123!'
export E2E_EMAIL="folo-self-test-$(date +%Y%m%d%H%M%S)@example.com"
```

For non-auth iOS self-tests, bootstrap auth through the standard iOS runner mode after the app has been installed and launched once:

```bash
cd apps/mobile
pnpm run e2e:ios:bootstrap
```

This bootstrap path is the default for `prod` and `local` self-tests. Only skip it when the feature under test is login, registration, sign-out, session restoration, or another auth-specific flow that must be validated visually end-to-end.

#### iOS registration bootstrap

```bash
cd apps/mobile
maestro test --format junit --platform ios --device "$IOS_UDID" \
  --debug-output e2e/artifacts/ios/register-bootstrap \
  -e E2E_EMAIL="$E2E_EMAIL" \
  -e E2E_PASSWORD="$E2E_PASSWORD" \
  e2e/flows/ios/register.yaml
```

#### Android registration bootstrap

```bash
cd apps/mobile
maestro test --format junit --platform android --device "$ANDROID_DEVICE_ID" \
  --debug-output e2e/artifacts/android/register-bootstrap \
  -e E2E_EMAIL="$E2E_EMAIL" \
  -e E2E_PASSWORD="$E2E_PASSWORD" \
  e2e/flows/android/register.yaml
```

After registration succeeds, continue with screenshot-driven visual testing.

### B. Change **is** related to login, registration, logout, session handling, auth validation, or onboarding gates

Do **not** rely on the existing Maestro auth flows for the actual verification. Use a fully visual/manual run instead so the changed UX itself is what gets tested.

Examples:

- register screen changes
- login screen changes
- credential validation changes
- auth toggle changes
- logout behavior
- auth/session restoration
- onboarding shown or hidden based on auth state

For auth-related work:

- create the test account manually through the UI if needed
- use screenshots after every critical step
- verify success and error states visually
- keep a clean record of the exact screen sequence shown to the user

## Screenshot-driven visual testing

Once the app is in the right state, drive the rest of the validation with the visual toolchain available in the current environment. Screenshots are the source of truth for acceptance.

Create a timestamped artifact folder first:

```bash
REPO_ROOT="$(git rev-parse --show-toplevel)"
ARTIFACT_DIR="$REPO_ROOT/apps/mobile/e2e/artifacts/manual/$(date +%Y%m%d-%H%M%S)-<platform>-<prod-or-local>"
mkdir -p "$ARTIFACT_DIR"
```

Capture screenshots after each meaningful checkpoint.

### iOS screenshot command

```bash
xcrun simctl io "$IOS_UDID" screenshot "$ARTIFACT_DIR/<name>.png"
```

### Android screenshot command

```bash
adb -s "$ANDROID_DEVICE_ID" exec-out screencap -p > "$ARTIFACT_DIR/<name>.png"
```

Minimum screenshot set for a complete self-test:

1. entry screen before the changed flow
2. the changed screen or interaction in progress
3. the final success state or the reproduced bug state

Add more screenshots when the flow has multiple important states.

Do not report success without screenshot evidence.

## What to validate visually

Use the screenshots to confirm at least these points when relevant:

- the correct screen is reached
- the changed control, copy, or layout is visible
- loading, empty, error, and success states look correct
- the operation completes without obvious regressions or blocking dialogs
- the app is talking to the intended environment (`prod` or `local`)

If the UI or behavior is ambiguous, capture another screenshot instead of guessing.

## Final user-facing output

The final response must include:

- API mode used and why it was chosen
- platform and dedicated simulator/emulator name plus identifier used
- cleanup result for the temporary simulator/emulator
- whether the local server was reused or started, plus log path if started
- build command used
- whether auth bootstrap was automated or fully visual
- concise step-by-step result summary
- pass/fail conclusion
- screenshot evidence with absolute file paths

If the client supports local image rendering, attach the key screenshots as images in the final message. Otherwise, list the absolute paths clearly so the user can open them.

## Failure handling

- If doctor, typecheck, build, install, or server startup fails, stop and report the exact failing command.
- If `local` mode cannot reach the local server, do not silently fall back to `prod`.
- If the visual flow cannot be completed because the environment lacks the required interaction tooling, report that limitation clearly and still return the screenshots you captured.


================================================
FILE: .agents/skills/update-deps/SKILL.md
================================================
---
name: update-deps
description: Update all dependencies across frontend and backend projects. Reads changelogs for breaking changes, checks affected code, runs tests, and provides a summary. Use when updating npm dependencies across the monorepo.
disable-model-invocation: true
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, Task
---

# Update All Dependencies

Update all npm dependencies across the frontend (current repo) and backend projects. This skill handles the full update workflow including changelog review, code impact analysis, testing, and summarization.

## Step 0: Ask for backend directory

Ask the user for the backend project directory path. Do NOT hardcode any path. Example prompt:

> Please provide the backend project directory path (e.g., `/path/to/backend`).

Wait for the user's response before proceeding. Save the path as `BACKEND_DIR` for later use.

## Step 1: Analyze current dependencies

### Frontend (current repo)

1. Run `pnpm outdated --recursive` in the current repo root to identify all outdated dependencies.
2. Save the full output for later analysis.

### Backend

1. Run `pnpm outdated --recursive` in `BACKEND_DIR` to identify all outdated dependencies.
2. Save the full output for later analysis.

Present the user with a summary of how many dependencies are outdated in each project.

## Step 2: Update dependencies

### Strategy

Update in two phases to isolate issues:

**Phase 1 — Patch and minor updates (safer):**

From the `pnpm outdated` output, identify all dependencies where the update is a patch or minor version bump. Update them in batch:

1. Frontend: For each patch/minor outdated package, run `pnpm update <package>@latest --recursive` in the repo root.
2. Backend: For each patch/minor outdated package, run `pnpm update <package>@latest --recursive` in `BACKEND_DIR`.

> **Why `@latest`?** Both projects use `save-exact=true`, so versions are pinned without `^` or `~`. Without `--latest`, `pnpm update` only resolves within the existing range, which for exact versions is a no-op.

**Phase 2 — Major updates (requires careful review):**

For each dependency with a major version update available:

1. Identify the dependency name, current version, and latest version.
2. **Read the changelog** (Step 3) before updating.
3. Only update after confirming no blocking breaking changes.
4. Use `pnpm update <package>@latest --recursive` to update specific packages.

**Important pnpm workspace notes:**

- The frontend project uses `pnpm catalog` in `pnpm-workspace.yaml` for some shared versions. If a dependency is managed via catalog, update the version in `pnpm-workspace.yaml` instead of individual `package.json` files.
- Both projects use `save-exact=true`, so versions are pinned without `^` or `~`.
- Check `patchedDependencies` in `pnpm-workspace.yaml` or `package.json` — if a patched dependency is being updated, verify the patch still applies or remove it if no longer needed.

## Step 3: Review changelogs for major updates

For each dependency with a **major version update**, you MUST read the changelog before updating.

### How to find changelogs

Use these methods in order of preference:

1. **npm registry**: Run `npm view <package> repository.url` to find the repo, then check for `CHANGELOG.md` or GitHub releases.
2. **GitHub releases**: Search `https://github.com/<owner>/<repo>/releases` using WebFetch.
3. **Web search**: Use WebSearch to find `<package> changelog <old-version> to <new-version>`.

### What to look for

- **Breaking changes**: API removals, renamed exports, changed defaults, dropped Node.js version support.
- **Deprecated features**: Features being removed in future versions.
- **Migration guides**: Official upgrade instructions.
- **Peer dependency changes**: New or changed peer dependency requirements.

### Document findings

For each major update, record:

- Package name and version change (e.g., `foo: 2.x → 3.x`)
- Breaking changes summary
- Whether our code is affected (and how)

## Step 4: Check affected code

For each dependency with breaking changes identified in Step 3:

1. Use `Grep` to find all imports and usages of the affected package across the relevant project (frontend or backend).
2. Read the files containing usages.
3. Compare the usage against the breaking change description.
4. If our code uses an affected API:
   - Attempt to fix the code following the migration guide.
   - If the fix is complex or risky, **skip updating this dependency** and note it in the summary.
5. If our code does NOT use any affected API, proceed with the update.

## Step 5: Run tests and checks

After all updates are applied:

### Frontend

Run these commands sequentially in the repo root and capture results:

```bash
pnpm install
pnpm typecheck
pnpm test
pnpm lint
```

### Backend

Run these commands sequentially in `BACKEND_DIR` and capture results:

```bash
pnpm install
pnpm typecheck
pnpm test
pnpm lint
```

### Handle failures

- **TypeScript errors**: Read the error output, identify which updated dependency caused the issue, and fix the type errors. If unfixable, revert that specific dependency update.
- **Test failures**: Analyze the failure, check if it's related to a dependency update, and fix or revert.
- **Lint errors**: Run `pnpm lint:fix` first. If issues persist, fix manually or revert the causing update.

Repeat the test cycle until all checks pass.

## Step 6: Summary

Present the user with a comprehensive summary:

### Update report

```
## Dependencies Updated

### Frontend
- <package>: <old-version> → <new-version> (patch/minor/major)
- ...

### Backend
- <package>: <old-version> → <new-version> (patch/minor/major)
- ...

## Skipped Updates (with reasons)
- <package>: <reason why not updated>
- ...

## Key Changelog Highlights

### Breaking Changes Applied
- <package> <version>: <what changed and how we adapted>

### Notable New Features
- <package> <version>: <brief description>

### Deprecation Warnings
- <package> <version>: <what's deprecated and timeline>

## Test Results
- Frontend typecheck: ✅/❌
- Frontend tests: ✅/❌
- Frontend lint: ✅/❌
- Backend typecheck: ✅/❌
- Backend tests: ✅/❌
- Backend lint: ✅/❌
```

Ask the user if they want to commit the changes.


================================================
FILE: .cursorignore
================================================
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)


================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true


================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
*.splinecode filter=lfs diff=lfs merge=lfs -text


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 🐞 Bug report
description: Report an issue
labels: [pending triage, bug]
type: Bug
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!

  - type: dropdown
    id: platform
    attributes:
      label: Platform
      description: On which platforms does this bug occur?
      multiple: true
      options:
        - Desktop - macOS
        - Desktop - Windows
        - Desktop - Linux
        - Desktop - Web
        - Mobile - iOS
        - Mobile - Android
        - Mobile - Web
    validations:
      required: true

  - type: textarea
    id: bug-description
    attributes:
      label: Describe the bug
      description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
      placeholder: Bug description
    validations:
      required: true

  - type: input
    id: entryId
    attributes:
      label: Entry ID
      description: Please provide the entry id of the entry that is causing the issue. If you are not sure, please provide the entry url.

  - type: textarea
    id: relevant-information
    attributes:
      label: Relevant Information
      description: Please provide your user id, feed id, feed url, or any other information that can help us reproduce the issue.
      placeholder: User ID, Feed ID, Feed URL, etc.
    validations:
      required: false

  - type: textarea
    id: reproduction
    attributes:
      label: Reproduction Video
      description: If possible, please provide a video that demonstrates the bug.
    validations:
      required: false

  - type: textarea
    id: environment
    attributes:
      label: Environment
      description: Please provide the environment in which you are using the application. You can find this information by going to Preferences > About and clicking the copy button next to the version tag.

  - type: checkboxes
    id: checkboxes
    attributes:
      label: Validations
      description: Before submitting the issue, please make sure you do the following
      options:
        - label: Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
          required: true
        - label: Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
          required: true
        - label: This issue is valid
          required: true

  - type: checkboxes
    id: contributions
    attributes:
      label: Contributions
      description: Please note that Open Source projects are maintained by volunteers, where your cases might not be always relevant to the others. It would make things move faster if you could help investigate and propose solutions.
      options:
        - label: I am willing to submit a PR to fix this issue
        - label: I am willing to submit a PR with failing tests (actually just go ahead and do it, thanks!)


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: 💬 Follow's Discord Server
    url: https://discord.gg/tUDVZjEr
    about: Want to discuss / chat with the community? Here you go!
  - name: Discuss an issue
    url: https://github.com/RSSNext/Follow/discussions
    about: For general questions, ideas, or non-bug related discussions, please use GitHub Discussions.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: 🚀 New feature proposal
description: Propose a new feature
labels: [enhancement]
type: Feature
body:
  - type: markdown
    attributes:
      value: |
        Thanks for your interest in the project and taking the time to fill out this feature report!
  - type: textarea
    id: feature-description
    attributes:
      label: Clear and concise description of the problem
      description: "As a developer using this project I want [goal / wish] so that [benefit]. If you intend to submit a PR for this issue, tell us in the description. Thanks!"
    validations:
      required: true
  - type: textarea
    id: suggested-solution
    attributes:
      label: Suggested solution
      description: "In module [xy] we could provide following implementation..."
    validations:
      required: true
  - type: textarea
    id: alternative
    attributes:
      label: Alternative
      description: Clear and concise description of any alternative solutions or features you've considered.
  - type: textarea
    id: additional-context
    attributes:
      label: Additional context
      description: Any other context or screenshots about the feature request here.
  - type: checkboxes
    id: checkboxes
    attributes:
      label: Validations
      description: Before submitting the issue, please make sure you do the following
      options:
        - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
          required: true
        - label: This issue is valid
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/i18n.yml
================================================
name: 🌐 Internationalization (i18n)
description: Contribute to or report issues with translations
title: "[i18n]: "
labels: ["i18n", "triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to contribute to our internationalization efforts!

        Before proceeding, please check our [i18n Contribution Guidelines](https://github.com/RSSNext/Follow/blob/dev/wiki/contribute-i18n.md) for detailed instructions.
  - type: dropdown
    id: type
    attributes:
      label: Type of i18n contribution
      options:
        - New language support
        - Update existing translations
        - Report incorrect translation
        - Other i18n-related issue
    validations:
      required: true
  - type: input
    id: language
    attributes:
      label: Language
      description: What language are you contributing to or reporting about?
      placeholder: e.g., Spanish, French, Japanese
    validations:
      required: true
  - type: textarea
    id: description
    attributes:
      label: Description
      description: Please provide details about your contribution or the issue you're reporting.
      placeholder: |
        For new languages: List any specific challenges or considerations.
        For updates: Describe what you're changing and why.
        For issues: Provide the incorrect translation and suggest a correction.
    validations:
      required: true
  - type: textarea
    id: additional-context
    attributes:
      label: Additional context
      description: Add any other context, screenshots, or file references here.
  - type: checkboxes
    id: terms
    attributes:
      label: Code of Conduct
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com/code-of-conduct)
      options:
        - label: I agree to follow this project's Code of Conduct
          required: true
        - label: This issue is valid
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/typo.yml
================================================
name: 👀 Typo / Grammar fix
description: You can just go ahead and send a PR! Thank you!
labels: []
body:
  - type: markdown
    attributes:
      value: |
        ## PR Welcome!

        If the typo / grammar issue is trivial and straightforward, you can help by **directly sending a quick pull request**!
        If you spot multiple of them, we suggest combining them into a single PR. Thanks!
  - type: textarea
    id: context
    attributes:
      label: Additional context
  - type: checkboxes
    id: checkboxes
    attributes:
      label: Validations
      description: Before submitting the issue, please make sure you do the following
      options:
        - label: This issue is valid
          required: true


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!-- DO NOT IGNORE THE TEMPLATE!

Thank you for contributing!

Before submitting the PR, please make sure you do the following:

- Read the [Contributing Guide](/contribute).
- Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
- Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `fixes #123`).
- Ideally, include relevant tests that fail without this PR but pass with it.

-->

### Description

<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->

### PR Type

<!-- Please check the type of PR: -->

- [ ] Feature
- [ ] Bugfix
- [ ] Hotfix
- [ ] Other (please describe):

### Screenshots (if UI change)

### Demo Video (if new feature)

### Linked Issues

### Additional context

<!-- e.g. is there anything you'd like reviewers to focus on? -->

### Changelog

<!-- Please ensure the changelog/next.md is updated if this is a feature or hotfix: -->

- [ ] I have updated the changelog/next.md with my changes.


================================================
FILE: .github/actions/setup-version/action.yml
================================================
name: Setup Version
description: "Setup Version"
inputs:
  type:
    required: true
    description: "Type of the app, either 'desktop' or 'mobile'"
    default: "desktop"
outputs:
  APP_VERSION:
    description: "App Version"
    value: ${{ steps.version.outputs.APP_VERSION }}
runs:
  using: "composite"
  steps:
    - name: "Write Version"
      id: version
      shell: bash
      run: |
        if [ "${{ github.ref_type }}" == "tag" ]; then
          # Handle new tag format: desktop/v1.2.3 or mobile/v1.2.3
          if [[ "${{ github.ref_name }}" =~ ^(desktop|mobile)/v(.*)$ ]]; then
            APP_VERSION="${BASH_REMATCH[2]}"
          else
            # Fallback for old format: v1.2.3
            APP_VERSION=$(echo "${{ github.ref_name }}" | sed 's/^v//')
          fi
        else
          if [ "${{ inputs.type }}" == "desktop" ]; then
            APP_VERSION=$(node -p "require('./apps/desktop/package.json').version")
          else
            APP_VERSION=$(node -p "require('./apps/mobile/package.json').version")
          fi
        fi
        echo $APP_VERSION
        echo "APP_VERSION=$APP_VERSION" >> "$GITHUB_OUTPUT"


================================================
FILE: .github/actions/setup-xcode/action.yml
================================================
name: "Setup Xcode"
description: "Setup specific Xcode version for iOS builds"
inputs:
  xcode-version:
    description: "Xcode version to use"
    required: false
    default: "26.0.1"

runs:
  using: "composite"
  steps:
    - uses: maxim-lobanov/setup-xcode@v1
      with:
        xcode-version: ${{ inputs.xcode-version }}

    - name: Setup Xcode Environment
      run: |
        set -e

        echo "=== Checking CI Environment ==="
        echo "Current user: $(whoami)"
        echo "User groups: $(groups)"
        echo "Sudo available: $(sudo -n true 2>/dev/null && echo 'YES' || echo 'NO')"
        echo "Xcode path: $(xcode-select -p 2>/dev/null || echo 'Not set')"

        # Set Xcode path and accept license
        echo "=== Setting up Xcode ==="
        if command -v sudo >/dev/null && sudo -n true 2>/dev/null; then
            sudo xcode-select -s /Applications/Xcode_${{ inputs.xcode-version }}.app/Contents/Developer
            sudo xcodebuild -license accept
            echo "Using sudo for Xcode setup"
        else
            xcode-select -s /Applications/Xcode_${{ inputs.xcode-version }}.app/Contents/Developer
            xcodebuild -license accept
            echo "Using direct access for Xcode setup"
        fi

        echo "Final Xcode path: $(xcode-select -p)"
      shell: bash

    - name: Start Simulator Service
      run: |
        echo "=== Starting Simulator Service ==="
        # Try to start simulator service (ignore errors if already running)
        sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.CoreSimulator.CoreSimulatorService.plist 2>/dev/null || {
            echo "CoreSimulator service might already be running or command unavailable"
        }

        # Alternative for newer macOS versions
        sudo launchctl bootstrap system /System/Library/LaunchDaemons/com.apple.CoreSimulator.CoreSimulatorService.plist 2>/dev/null || {
            echo "Bootstrap command failed or service already running"
        }

        # Wait a moment for service to start
        sleep 3
      shell: bash

    - name: Download iOS Platform
      run: |
        set -e

        echo "=== Checking Available SDKs ==="
        echo "Currently available SDKs:"
        xcodebuild -showsdks | grep -E "(iOS|Simulator)" || echo "No iOS SDKs found yet"

        echo "=== Attempting iOS Platform Download ==="

        # Method 1: Try standard download
        if xcodebuild -downloadPlatform iOS -quiet 2>/dev/null; then
            echo "✅ iOS platform downloaded successfully with xcodebuild"
        elif xcrun xcodebuild -downloadPlatform iOS -quiet 2>/dev/null; then
            echo "✅ iOS platform downloaded successfully with xcrun xcodebuild"
        else
            echo "⚠️  Platform download failed, checking if iOS SDK is already available..."

            # Check if iOS SDK is available
            if xcodebuild -showsdks | grep -q "iOS"; then
                echo "✅ iOS SDK is already available, continuing..."
            else
                echo "❌ No iOS SDK found and download failed"
                echo "Available SDKs:"
                xcodebuild -showsdks

                # Try alternative download method
                echo "Trying alternative download method..."
                xcodebuild -downloadAllPlatforms -quiet || {
                    echo "❌ All download methods failed"
                    exit 1
                }
            fi
        fi

        echo "=== Final SDK Status ==="
        echo "Available iOS SDKs:"
        xcodebuild -showsdks | grep -E "(iOS|Simulator)" || echo "No iOS SDKs found"

        echo "Available simulators:"
        xcrun simctl list devices available | head -20 || echo "No simulators found"
      shell: bash

    - name: Verify Setup
      run: |
        echo "=== Verification ==="
        echo "Xcode version: $(xcodebuild -version | head -1)"
        echo "Selected Xcode: $(xcode-select -p)"
        echo "iOS SDK available: $(xcodebuild -showsdks | grep -c iOS || echo 0)"

        # Test basic functionality
        if xcodebuild -showsdks | grep -q "iOS"; then
            echo "✅ Setup completed successfully!"
        else
            echo "⚠️  Setup completed but iOS SDK may not be fully available"
        fi
      shell: bash


================================================
FILE: .github/advanced-issue-labeler.yml
================================================
policy:
  - section:
      - id: [platform]
        block-list: ["None", "Other"]
        label:
          - name: "platform: desktop"
            keys: ["Desktop - macOS", "Desktop - Windows", "Desktop - Linux", "Desktop - Web"]
          - name: "platform: mobile"
            keys: ["Mobile - iOS", "Mobile - Android", "Mobile - Web"]


================================================
FILE: .github/copilot-instructions.md
================================================
Before you start, you need to read and follow the rules in @../CLAUDE.md


================================================
FILE: .github/dependabot.yaml
================================================
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
      day: friday
      time: "12:00"
      timezone: Asia/Singapore
    target-branch: dev
    ignore:
      - dependency-name: "@shopify/flash-list"
        versions: [">1.7.3"]

      # Stuck by tailwindcss 4
      - dependency-name: tailwindcss
        versions: [">=4.0.0"]
      - dependency-name: daisyui
        versions: [">=5.0.0"]

      # Stuck by expo 52
      - dependency-name: react-native
        versions: [">=0.78.0"]

      # It's using export map and metro doesn't support it well
      - dependency-name: unist-util-visit-parents
        versions: [">=6.0.0"]
      # electron 35
      - dependency-name: electron
        versions: [">=35.0.0"]
      - dependency-name: react-native-sheet-transitions
        versions: [">0.1.2"]

      # filter not work
      - dependency-name: unplugin-ast
        versions: ["0.14.5"]

    open-pull-requests-limit: 100
    groups:
      minor-and-patch:
        applies-to: version-updates
        update-types:
          - "minor"
          - "patch"
      pathed:
        patterns:
          - immer
          - re-resizable
          - electron-context-menu
          - "@mozilla/readability"
          - daisyui
          - jsonpointer
          - workbox-precaching
          - "@pengx17/electron-forge-maker-appimage"
          - "@microflash/remark-callout-directives"
          - react-native-track-player
          - react-native-sheet-transitions

  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: daily
    target-branch: dev
    open-pull-requests-limit: 100


================================================
FILE: .github/prompts/similar_issues.prompt.yml
================================================
messages:
  - role: system
    content: |-
      You are a GitHub assistant with access to GitHub Model Context Protocol (MCP)
      tools in read-only mode. Your task is to search this repository's issues to find
      previously filed issues similar to the provided issue title and body. Use the
      GitHub tools via MCP to perform the search and retrieve real issue data (do not
      fabricate results).

      Consider semantic similarity across title and body. Exclude
      issues with the same ID/number as the current issue. Return up to 3 of the most similar past issues. If none are
      reasonably similar, return an empty list. Output must follow the response schema
      exactly and include only data you actually retrieved from GitHub tools.
      The current GitHub repository is: "{{repository}}".
  - role: user
    content: |-
      Find similar issues for this new issue:
      Title: {{issue_title}}
      Body:
      {{issue_body}}
model: openai/gpt-4.1-mini
responseFormat: json_schema
jsonSchema: |-
  {
    "name": "similar_issues_result",
    "strict": true,
    "schema": {
      "type": "object",
      "properties": {
        "matches": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "number": { "type": "integer" },
              "title": { "type": "string" },
              "url": { "type": "string" },
              "similarity_score": { "type": "number", "minimum": 0, "maximum": 1 }
            },
            "required": ["number", "title", "url", "similarity_score"],
            "additionalProperties": false
          }
        }
      },
      "required": ["matches"],
      "additionalProperties": false
    }
  }


================================================
FILE: .github/scripts/extract-release-info.mjs
================================================
#!/usr/bin/env node

/**
 * Extract release version and platform information from git commit messages
 * Used by GitHub Actions to determine if a release tag should be created
 */

import { execSync } from "node:child_process"
import { appendFileSync } from "node:fs"

// Configuration
const RELEASE_PATTERNS = {
  desktop: /release\(desktop\): Release (v\d+\.\d+\.\d+(-[0-9A-Z-.]+)?)/i,
  mobile: /release\(mobile\): Release (v\d+\.\d+\.\d+(-[0-9A-Z-.]+)?)/i,
}

const EXIT_CODES = {
  SUCCESS: 0,
  GIT_ERROR: 2,
  ENV_ERROR: 3,
  OUTPUT_ERROR: 4,
}

/**
 * Write environment variable to GitHub Environment
 * @param {string} key - Environment variable key
 * @param {string} value - Environment variable value
 */
function setGitHubEnv(key, value) {
  try {
    if (!process.env.GITHUB_ENV) {
      throw new Error("GITHUB_ENV not set - not running in GitHub Actions")
    }
    appendFileSync(process.env.GITHUB_ENV, `${key}=${value}\n`)
  } catch (error) {
    console.error(`Failed to set environment variable ${key}:`, error.message)
    process.exit(EXIT_CODES.ENV_ERROR)
  }
}

/**
 * Write output variable to GitHub Output
 * @param {string} key - Output key
 * @param {string} value - Output value
 */
function setGitHubOutput(key, value) {
  try {
    if (!process.env.GITHUB_OUTPUT) {
      return
    }
    appendFileSync(process.env.GITHUB_OUTPUT, `${key}=${value}\n`)
  } catch (error) {
    console.error(`Failed to set output variable ${key}:`, error.message)
    process.exit(EXIT_CODES.OUTPUT_ERROR)
  }
}

/**
 * Get the latest commit message
 * @returns {string} Latest commit message
 */
function getLatestCommitMessage() {
  try {
    return execSync("git log -1 --pretty=%B", { encoding: "utf-8" }).toString().trim()
  } catch (error) {
    console.error("Failed to get git commit message:", error.message)
    process.exit(EXIT_CODES.GIT_ERROR)
  }
}

/**
 * Extract release information from commit message
 * @param {string} commitMessage - Git commit message
 * @returns {Object|null} Release information or null if no release found
 */
function extractReleaseInfo(commitMessage) {
  for (const [platform, regex] of Object.entries(RELEASE_PATTERNS)) {
    const match = commitMessage.match(regex)
    if (match) {
      const version = match[1]
      const tagName = `${platform}/${version}`

      return {
        platform,
        version,
        tagName,
      }
    }
  }

  return null
}

/**
 * Main execution function
 */
function main() {
  try {
    console.info("Extracting release information from commit message...")

    const commitMessage = getLatestCommitMessage()
    console.info(`Commit message: ${commitMessage}`)

    const releaseInfo = extractReleaseInfo(commitMessage)

    if (!releaseInfo) {
      console.info("No desktop or mobile release found in commit message.")
      process.exit(EXIT_CODES.SUCCESS)
    }

    const { platform, version, tagName } = releaseInfo

    // Set GitHub Environment variables
    setGitHubEnv("tag_version", tagName)
    setGitHubEnv("platform", platform)
    setGitHubEnv("version", version)
    setGitHubOutput("tag_version", tagName)
    setGitHubOutput("platform", platform)
    setGitHubOutput("version", version)

    console.info(`Found ${platform} release: ${version}`)
    console.info(`Tag will be created: ${tagName}`)

    process.exit(EXIT_CODES.SUCCESS)
  } catch (error) {
    console.error("Unexpected error:", error.message)
    process.exit(EXIT_CODES.GIT_ERROR)
  }
}

main()


================================================
FILE: .github/workflows/build-android.yml
================================================
name: 🤖 Build Android

on:
  push:
    branches:
      - "**"
    paths:
      - "apps/mobile/**"
      - "pnpm-lock.yaml"
  workflow_dispatch:
    inputs:
      profile:
        type: choice
        default: preview
        options:
          - preview
          - production
        description: "Build profile"
      release:
        type: boolean
        default: false
        description: "Create a release draft for the build"

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.profile }}
  cancel-in-progress: true

jobs:
  build:
    name: Build Android apk for device
    if: github.secret_source != 'None' && (github.event_name != 'push' || !contains(github.event.head_commit.message || '', 'release(mobile):'))
    runs-on: ubuntu-latest

    steps:
      - name: 🧹 Claim disk space
        run: |
          df -h /
          sudo rm -rf /usr/share/dotnet
          sudo rm -rf /usr/local/.ghcup
          sudo rm -rf /opt/hostedtoolcache/CodeQL
          sudo rm -rf /usr/share/swift
          sudo rm -rf /usr/local/julia*
          df -h /

      - name: 📦 Checkout code
        uses: actions/checkout@v6

      - name: 📦 Setup pnpm
        uses: pnpm/action-setup@v4

      - name: 🏗 Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: "pnpm"

      - name: Set up JDK 17
        uses: actions/setup-java@v5
        with:
          java-version: "17"
          distribution: "zulu"

      - name: Setup Android SDK
        uses: android-actions/setup-android@v3

      - name: 📱 Setup EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - name: Install dependencies
        run: pnpm install

      - name: 🔨 Build Android app
        working-directory: apps/mobile
        run: eas build --platform android --profile ${{ github.event.inputs.profile || 'preview' }} --local --output=${{ github.workspace }}/build.${{ github.event.inputs.profile == 'production' && 'aab' || 'apk' }}

      - name: 📤 Upload apk Artifact
        if: github.event.inputs.profile != 'production'
        uses: actions/upload-artifact@v7
        with:
          name: app-android
          path: ${{ github.workspace }}/build.apk
          retention-days: 90

      - name: 📤 Upload aab Artifact
        if: github.event.inputs.profile == 'production'
        uses: actions/upload-artifact@v7
        with:
          name: aab-android
          path: ${{ github.workspace }}/build.aab
          retention-days: 90

      - name: Submit to Google Play
        if: github.event.inputs.profile == 'production'
        working-directory: apps/mobile
        run: eas submit --platform android --path ${{ github.workspace }}/build.aab --non-interactive

      - name: Setup Version
        if: github.event.inputs.release == 'true'
        id: version
        uses: ./.github/actions/setup-version
        with:
          type: "mobile"

      - name: Prepare Release Notes
        if: github.event.inputs.release == 'true'
        id: release_notes
        run: |
          version="${{ steps.version.outputs.APP_VERSION }}"
          changelog_file="apps/mobile/changelog/${version}.md"
          release_notes_file="$RUNNER_TEMP/mobile-release-notes.md"

          if [ -f "$changelog_file" ]; then
            cp "$changelog_file" "$release_notes_file"
          else
            {
              echo "# What's New in v${version}"
              echo
              echo "- No changelog file found at ${changelog_file}."
            } > "$release_notes_file"
          fi

          echo "body_path=${release_notes_file}" >> "$GITHUB_OUTPUT"

      - name: Create Release Draft
        if: github.event.inputs.release == 'true'
        uses: softprops/action-gh-release@v2
        with:
          name: Mobile v${{ steps.version.outputs.APP_VERSION }}
          draft: true
          prerelease: true
          tag_name: mobile/v${{ steps.version.outputs.APP_VERSION }}
          body_path: ${{ steps.release_notes.outputs.body_path }}
          # .aab cannot be installed directly on your Android Emulator or device.
          files: ${{ github.workspace }}/build.apk


================================================
FILE: .github/workflows/build-desktop.yml
================================================
name: 🖥️ Build Desktop

on:
  push:
    branches:
      - "**"
    paths:
      - "apps/desktop/**"
      - "packages/**"
      - "pnpm-lock.yaml"
      - ".github/workflows/build-desktop.yml"
  workflow_dispatch:
    inputs:
      tag_version:
        type: boolean
        description: "Tag Version"
      store:
        type: boolean
        description: "Build for Mac App Store and Microsoft Store"
      build_version:
        type: string
        description: "Build Version, only available when mas is true"

# https://docs.github.com/en/enterprise-cloud@latest/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.tag_version == 'true' && 'tag-version' || github.event.inputs.store == 'true' && 'store' || 'manual') || 'build' }}
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}
env:
  VITE_WEB_URL: ${{ vars.VITE_WEB_URL }}
  VITE_API_URL: ${{ vars.VITE_API_URL }}
  VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
  VITE_FIREBASE_CONFIG: ${{ vars.VITE_FIREBASE_CONFIG }}
  SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
  NODE_OPTIONS: --max-old-space-size=8192

jobs:
  release:
    if: github.secret_source != 'None' && (github.event_name != 'push' || !contains(github.event.head_commit.message || '', 'release(desktop):'))
    runs-on: ${{ matrix.os }}
    env:
      PROD: ${{ github.event.inputs.tag_version == 'true' || github.ref_type == 'tag' || github.event.inputs.store == 'true' }}
      RELEASE: ${{ github.event.inputs.tag_version == 'true' || github.ref_type == 'tag' }}

    strategy:
      fail-fast: false
      matrix:
        os: [macos-latest, ubuntu-latest, windows-latest]
        exclude:
          - os: ${{ github.event.inputs.store == 'true' && 'ubuntu-latest' }}

    permissions:
      id-token: write
      contents: write
      attestations: write

    steps:
      - name: Check out Git repository Fully
        uses: actions/checkout@v6
        if: env.PROD == 'true'
        with:
          fetch-depth: 0
          lfs: true
      - name: Check out Git repository
        uses: actions/checkout@v6
        if: env.PROD == 'false'
        with:
          fetch-depth: 1
          lfs: true

      - name: Setup pnpm
        uses: pnpm/action-setup@v4

      - name: Use Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: "pnpm"

      - name: Install Python setuptools
        if: runner.os == 'macOS'
        run: brew install python-setuptools

      - name: Install appdmg
        if: runner.os == 'macOS'
        run: pnpm add -g appdmg

      - name: Install the Apple certificate and provisioning profile
        env:
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          BUILD_CERTIFICATE_MAS_BASE64: ${{ secrets.BUILD_CERTIFICATE_MAS_BASE64 }}
          BUILD_CERTIFICATE_MASPKG_BASE64: ${{ secrets.BUILD_CERTIFICATE_MASPKG_BASE64 }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        if: runner.os == 'macOS' && env.BUILD_CERTIFICATE_BASE64 != '' && env.BUILD_CERTIFICATE_MAS_BASE64 != '' && env.BUILD_CERTIFICATE_MASPKG_BASE64 != '' && env.BUILD_PROVISION_PROFILE_BASE64 != ''
        run: |
          # create variables
          CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
          CERTIFICATE_MAS_PATH=$RUNNER_TEMP/build_certificate_mas.p12
          CERTIFICATE_MASPKG_PATH=$RUNNER_TEMP/build_certificate_maspkg.p12
          PP_PATH=$RUNNER_TEMP/build_pp.provisionprofile
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

          # import certificate and provisioning profile from secrets
          echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
          echo -n "$BUILD_CERTIFICATE_MAS_BASE64" | base64 --decode -o $CERTIFICATE_MAS_PATH
          echo -n "$BUILD_CERTIFICATE_MASPKG_BASE64" | base64 --decode -o $CERTIFICATE_MASPKG_PATH
          echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH

          # create temporary keychain
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # import certificate to keychain
          security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security import $CERTIFICATE_MAS_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security import $CERTIFICATE_MASPKG_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH
          security find-identity $KEYCHAIN_PATH

          # apply provisioning profile
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

      - name: Install dependencies
        run: pnpm i

      - name: Prebuild packages (Windows)
        if: runner.os == 'windows'
        run: pnpm run build:packages

      - name: Update main hash
        working-directory: apps/desktop
        run: pnpm update:main-hash
      - name: Build - Vite
        working-directory: apps/desktop
        run: pnpm build:electron-vite ${{ env.PROD == 'false' && '--mode staging' || '' }}

      - name: Build - Windows and Linux
        if: runner.os == 'Linux' || (runner.os == 'Windows' && github.event.inputs.store != 'true')
        working-directory: apps/desktop
        run: pnpm build:electron-forge ${{ env.PROD == 'false' && '--mode=staging' || '' }}

      - name: Build - macOS
        if: runner.os == 'macOS' && github.event.inputs.store != 'true'
        env:
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          OSX_SIGN_KEYCHAIN_PATH: ${{ runner.temp }}/app-signing.keychain-db
          OSX_SIGN_IDENTITY: ${{ secrets.OSX_SIGN_IDENTITY }}
        uses: nick-fields/retry@v3
        with:
          max_attempts: 3
          timeout_minutes: 10
          command: |
            cd apps/desktop
            npx electron-forge make --arch=x64 --platform=darwin ${{ env.PROD == 'false' && '--mode=staging' || '' }}
            npx electron-forge make --arch=arm64 --platform=darwin ${{ env.PROD == 'false' && '--mode=staging' || '' }}
            npx tsx scripts/merge-yml.ts

      - name: Build - Mac App Store
        if: runner.os == 'macOS' && github.event.inputs.store == 'true'
        env:
          OSX_SIGN_KEYCHAIN_PATH: ${{ runner.temp }}/app-signing.keychain-db
          OSX_SIGN_IDENTITY: ${{ secrets.OSX_SIGN_IDENTITY_MAS }}
          OSX_SIGN_PROVISIONING_PROFILE_PATH: ${{ runner.temp }}/build_pp.provisionprofile
          BUILD_VERSION: ${{ github.event.inputs.build_version }}
        working-directory: apps/desktop
        run: pnpm build:electron-forge:mas

      - name: Build - Microsoft Store
        if: runner.os == 'Windows' && github.event.inputs.store == 'true'
        working-directory: apps/desktop
        run: pnpm build:electron-forge:ms

      - name: Build - Renderer
        if: runner.os == 'Linux'
        working-directory: apps/desktop
        run: pnpm build:render

      - name: Upload file (macos-arm64-dmg)
        uses: actions/upload-artifact@v7
        if: runner.os == 'macOS'
        with:
          name: macos-arm64-dmg
          path: |
            apps/desktop/out/make/**/*arm64.dmg
            apps/desktop/out/make/**/latest-mac.yml
          retention-days: 90

      - name: Upload file (macos-x64-dmg)
        uses: actions/upload-artifact@v7
        if: runner.os == 'macOS'
        with:
          name: macos-x64-dmg
          path: |
            apps/desktop/out/make/**/*x64.dmg
            apps/desktop/out/make/**/latest-mac.yml
          retention-days: 90

      - name: Upload file (macos-mas-pkg)
        uses: actions/upload-artifact@v7
        if: runner.os == 'macOS'
        with:
          name: macos-mas-pkg
          path: |
            apps/desktop/out/make/**/*.pkg
          retention-days: 90

      - name: Upload file (windows-x64-exe unsigned)
        uses: actions/upload-artifact@v7
        id: upload-unsigned-windows-x64-exe
        if: runner.os == 'windows' && github.event.inputs.store != 'true'
        with:
          name: windows-x64-exe
          path: |
            apps/desktop/out/make/**/*x64.exe
            apps/desktop/out/make/**/latest.yml
          retention-days: 90

      - uses: signpath/github-action-submit-signing-request@v2.0
        continue-on-error: true
        if: runner.os == 'windows' && env.RELEASE == 'true' && github.event.inputs.store != 'true'
        with:
          api-token: "${{ secrets.SIGNPATH_API_TOKEN }}"
          organization-id: "8c651516-fdaf-40a1-9fea-001dffde850e"
          project-slug: "Folo"
          signing-policy-slug: "release-signing"
          artifact-configuration-slug: "github"
          github-artifact-id: "${{ steps.upload-unsigned-windows-x64-exe.outputs.artifact-id }}"
          output-artifact-directory: "apps/desktop/out/make/"

      - name: Update latest.yml
        if: runner.os == 'windows' && env.RELEASE == 'true' && github.event.inputs.store != 'true'
        run: npx tsx apps/desktop/scripts/update-windows-yml.ts

      - name: Upload file (windows-x64-exe signed)
        uses: actions/upload-artifact@v7
        if: runner.os == 'windows' && env.RELEASE == 'true' && github.event.inputs.store != 'true'
        with:
          name: windows-x64-exe
          path: |
            apps/desktop/out/make/**/*x64.exe
            apps/desktop/out/make/**/latest.yml
          retention-days: 90
          overwrite: true

      - name: Upload file (windows-x64-appx)
        uses: actions/upload-artifact@v7
        if: runner.os == 'windows'
        with:
          name: windows-x64-appx
          path: |
            apps/desktop/out/make/**/*.appx
          retention-days: 90

      - name: Upload file (linux-x64-appimage)
        uses: actions/upload-artifact@v7
        if: runner.os == 'linux'
        with:
          name: linux-x64-appimage
          path: |
            apps/desktop/out/make/**/*x64.AppImage
            apps/desktop/out/make/**/latest-linux.yml
          retention-days: 90

      - name: Generate artifact attestation
        if: env.RELEASE == 'true'
        continue-on-error: true
        uses: actions/attest-build-provenance@v4
        with:
          subject-path: |
            apps/desktop/out/make/**/Folo-*.dmg
            apps/desktop/out/make/**/Folo-*.zip
            apps/desktop/out/make/**/Folo-*.exe
            apps/desktop/out/make/**/Folo-*.AppImage
            apps/desktop/out/make/**/*.yml
            apps/desktop/dist/manifest.yml
            apps/desktop/dist/*.tar.gz

      - run: npx changelogithub
        if: env.RELEASE == 'true'
        continue-on-error: true
        env:
          GITHUB_TOKEN: ${{ github.token }}

      - name: Setup Version
        if: env.RELEASE == 'true'
        id: version
        uses: ./.github/actions/setup-version
        with:
          type: "desktop"

      - name: Prepare Release Notes
        if: env.RELEASE == 'true'
        id: release_notes
        shell: bash
        run: |
          version="${{ steps.version.outputs.APP_VERSION }}"
          changelog_file="apps/desktop/changelog/${version}.md"
          release_notes_file="$RUNNER_TEMP/desktop-release-notes.md"

          if [ -f "$changelog_file" ]; then
            cp "$changelog_file" "$release_notes_file"
          else
            {
              echo "# What's New in v${version}"
              echo
              echo "- No changelog file found at ${changelog_file}."
            } > "$release_notes_file"
          fi

          echo "body_path=${release_notes_file}" >> "$GITHUB_OUTPUT"

      - name: Create Release Draft
        if: env.RELEASE == 'true'
        uses: softprops/action-gh-release@v2
        with:
          name: Desktop v${{ steps.version.outputs.APP_VERSION }}
          draft: false
          prerelease: true
          tag_name: desktop/v${{ steps.version.outputs.APP_VERSION }}
          body_path: ${{ steps.release_notes.outputs.body_path }}
          files: |
            apps/desktop/out/make/**/Folo-*.dmg
            apps/desktop/out/make/**/Folo-*.zip
            apps/desktop/out/make/**/Folo-*.exe
            apps/desktop/out/make/**/Folo-*.AppImage
            apps/desktop/out/make/**/*.yml
            apps/desktop/dist/manifest.yml
            apps/desktop/dist/*.tar.gz


================================================
FILE: .github/workflows/build-ios-development.yml
================================================
name: 📱 Build iOS for development

on:
  push:
    branches:
      - "**"
    paths:
      - "apps/mobile/web-app/**"
      - "apps/mobile/native/**"
      - "apps/mobile/package.json"
      - "apps/mobile/app.config.ts"

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  check-runner:
    runs-on: ubuntu-latest
    outputs:
      runner-label: ${{ steps.set-runner.outputs.runner-label }}

    steps:
      - name: Set runner
        id: set-runner
        run: |
          runners=$(curl -s -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.RUNNER_GITHUB_TOKEN }}" "https://api.github.com/repos/${{ github.repository }}/actions/runners")
          available=$(echo "$runners" | jq '.runners[]? | select(.status == "online" and .busy == false and .labels[] .name == "macOS")')
          if [ -n "$available" ]; then
            echo "runner-label=self-hosted" >> $GITHUB_OUTPUT
          else
            echo "runner-label=macos-latest" >> $GITHUB_OUTPUT
          fi

  build-ipa-device-self-hosted:
    name: Build iOS IPA for device (self-hosted)
    if: github.secret_source != 'None' && needs.check-runner.outputs.runner-label == 'self-hosted'
    needs: check-runner
    runs-on: [self-hosted, macOS]

    steps:
      - name: 📦 Checkout code
        uses: actions/checkout@v6

      - name: 📱 Setup EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - name: Install dependencies
        run: pnpm install

      - name: 🔨 Build iOS IPA
        working-directory: apps/mobile
        run: eas build --platform ios --profile development --non-interactive --local --output=./build.ipa
        env:
          SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
          CI: true

      # Optional: Upload artifact
      - name: 📤 Upload IPA
        uses: actions/upload-artifact@v7
        with:
          name: app-ios-development-device
          path: apps/mobile/build.ipa
          retention-days: 90

      - name: Clear Xcode cache
        run: |
          rm -rf ~/Library/Developer/Xcode/DerivedData

  build-ipa-device-github:
    name: Build iOS IPA for device (GitHub-hosted)
    if: github.secret_source != 'None' && needs.check-runner.outputs.runner-label == 'macos-latest'
    needs: check-runner
    runs-on: macos-latest

    steps:
      - name: 📦 Checkout code
        uses: actions/checkout@v6

      - name: 🔧 Setup Xcode
        uses: ./.github/actions/setup-xcode

      - name: 📦 Setup pnpm
        uses: pnpm/action-setup@v4

      - name: 🏗 Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: "pnpm"

      - name: 📱 Setup EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - name: Install dependencies
        run: pnpm install

      - name: 🔨 Build iOS IPA
        working-directory: apps/mobile
        run: eas build --platform ios --profile development --non-interactive --local --output=./build.ipa
        env:
          CI: true
          SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
      # Optional: Upload artifact
      - name: 📤 Upload IPA
        uses: actions/upload-artifact@v7
        with:
          name: app-ios-development-device
          path: apps/mobile/build.ipa
          retention-days: 90

  build-simulator:
    name: Build iOS IPA for simulator
    if: github.secret_source != 'None'
    runs-on: macos-latest

    steps:
      - name: 📦 Checkout code
        uses: actions/checkout@v6

      - name: 🔧 Setup Xcode
        uses: ./.github/actions/setup-xcode

      - name: 📦 Setup pnpm
        uses: pnpm/action-setup@v4

      - name: 🏗 Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: "pnpm"

      - name: 📱 Setup EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - name: Install dependencies
        run: pnpm install

      - name: 🔨 Build iOS IPA
        working-directory: apps/mobile
        run: eas build --platform ios --profile ios-simulator --non-interactive --local --output=./build-simulator.ipa
        env:
          CI: true
          SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
      # Optional: Upload artifact
      - name: 📤 Upload IPA
        uses: actions/upload-artifact@v7
        with:
          name: app-ios-development-simulator
          path: apps/mobile/build-simulator.ipa
          retention-days: 90


================================================
FILE: .github/workflows/build-ios.yml
================================================
name: 🍎 Build iOS

on:
  push:
    branches:
      - "**"
    paths:
      - "apps/mobile/**"
      - "pnpm-lock.yaml"
  workflow_dispatch:
    inputs:
      profile:
        type: choice
        default: preview
        options:
          - preview
          - production
        description: "Build profile"

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.profile }}
  cancel-in-progress: true

jobs:
  check-runner:
    if: github.secret_source != 'None' && (github.event_name != 'push' || !contains(github.event.head_commit.message || '', 'release(mobile):'))
    runs-on: ubuntu-latest
    outputs:
      runner-label: ${{ steps.set-runner.outputs.runner-label }}

    steps:
      - name: Set runner
        id: set-runner
        run: |
          runners=$(curl -s -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.RUNNER_GITHUB_TOKEN }}" "https://api.github.com/repos/${{ github.repository }}/actions/runners")
          available=$(echo "$runners" | jq '.runners[]? | select(.status == "online" and .busy == false and .labels[] .name == "macOS")')
          if [ -n "$available" ]; then
            echo "runner-label=self-hosted" >> $GITHUB_OUTPUT
          else
            echo "runner-label=macos-latest" >> $GITHUB_OUTPUT
          fi

  build-ipa-self-hosted:
    name: Build iOS IPA (self-hosted)
    if: needs.check-runner.outputs.runner-label == 'self-hosted'
    needs: check-runner
    runs-on: [self-hosted, macOS]

    steps:
      - name: 📦 Checkout code
        uses: actions/checkout@v6

      - name: 📱 Setup EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - name: Install dependencies
        run: pnpm install

      - name: 🔨 Build iOS IPA
        working-directory: apps/mobile
        run: eas build --platform ios --profile ${{ github.event.inputs.profile || 'preview' }} --non-interactive --local --output=./build.ipa
        env:
          SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
          CI: true

      # Optional: Upload artifact
      - name: 📤 Upload IPA
        uses: actions/upload-artifact@v7
        with:
          name: app-ios
          path: apps/mobile/build.ipa
          retention-days: 90

      - name: Clear Xcode cache
        run: |
          rm -rf ~/Library/Developer/Xcode/DerivedData

      - name: Submit to App Store
        if: github.event.inputs.profile == 'production'
        working-directory: apps/mobile
        run: eas submit --platform ios --path build.ipa --non-interactive

  build-ipa-github:
    name: Build iOS IPA (GitHub)
    if: needs.check-runner.outputs.runner-label == 'macos-latest'
    needs: check-runner
    runs-on: macos-latest

    steps:
      - name: 📦 Checkout code
        uses: actions/checkout@v6

      - name: 🔧 Setup Xcode
        uses: ./.github/actions/setup-xcode

      - name: 📦 Setup pnpm
        uses: pnpm/action-setup@v4

      - name: 🏗 Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: "pnpm"

      - name: 📱 Setup EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - name: Install dependencies
        run: pnpm install

      - name: 🔨 Build iOS IPA
        working-directory: apps/mobile
        run: eas build --platform ios --profile ${{ github.event.inputs.profile || 'preview' }} --non-interactive --local --output=./build.ipa
        env:
          CI: true
          SENTRY_AUTH_TOKEN: ${{ secrets.RN_SENTRY_AUTH_TOKEN }}
      # Optional: Upload artifact
      - name: 📤 Upload IPA
        uses: actions/upload-artifact@v7
        with:
          name: app-ios
          path: apps/mobile/build.ipa
          retention-days: 90

      - name: Submit to App Store
        if: github.event.inputs.profile == 'production'
        working-directory: apps/mobile
        run: eas submit --platform ios --path build.ipa --non-interactive


================================================
FILE: .github/workflows/build-web.yml
================================================
on:
  pull_request:
  push:
    branches: [main, dev]

name: 🌐 CI Build web and SSR server
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-ssr
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}
jobs:
  build:
    name: Build web and SSR server
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [lts/*]
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          lfs: true
      - name: Cache turbo build setup
        uses: actions/cache@v5
        with:
          path: .turbo
          key: ${{ runner.os }}-turbo-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-turbo-

      - name: Checkout LFS objects
        run: git lfs checkout

      - uses: pnpm/action-setup@v4

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v6
        with:
          node-version: ${{ matrix.node-version }}
          cache: "pnpm"
      - name: Install dependencies
        run: pnpm install
      - name: Build web and SSR server
        run: |
          npm exec turbo run Folo#build:web @follow/ssr#build


================================================
FILE: .github/workflows/deploy-cloudflare-desktop.yml
================================================
name: "\u2601\ufe0f Deploy Desktop to Cloudflare"

on:
  push:
    branches: [main, dev]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  deploy:
    name: Build & Deploy Desktop Web
    runs-on: ubuntu-latest
    env:
      VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
      VITE_FIREBASE_CONFIG: ${{ vars.VITE_FIREBASE_CONFIG }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          lfs: true

      - name: Checkout LFS objects
        run: git lfs checkout

      - name: Cache turbo build setup
        uses: actions/cache@v5
        with:
          path: .turbo
          key: ${{ runner.os }}-turbo-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-turbo-

      - uses: pnpm/action-setup@v4

      - name: Use Node.js LTS
        uses: actions/setup-node@v6
        with:
          node-version: lts/*
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Build desktop web (SPA)
        working-directory: apps/desktop
        env:
          VITE_WEB_URL: ${{ github.ref == 'refs/heads/dev' && 'https://dev.folo.is' || 'https://app.folo.is' }}
          VITE_API_URL: ${{ github.ref == 'refs/heads/dev' && 'https://api.dev.folo.is' || 'https://api.folo.is' }}
        run: pnpm run build:web

      - name: Deploy desktop web to Cloudflare (dev)
        if: github.ref == 'refs/heads/dev'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          workingDirectory: apps/desktop
          command: deploy --env dev

      - name: Deploy desktop web to Cloudflare (prod)
        if: github.ref == 'refs/heads/main'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          workingDirectory: apps/desktop
          command: 'deploy --env=""'


================================================
FILE: .github/workflows/deploy-cloudflare-landing.yml
================================================
name: "\u2601\ufe0f Deploy Landing to Cloudflare"

on:
  push:
    branches: [main, dev]
  workflow_dispatch:
    inputs:
      confirm_production_deploy:
        description: Deploy the selected branch to production (folo.is)
        required: true
        default: false
        type: boolean

concurrency:
  group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-prod-{0}', github.ref) || github.ref }}
  cancel-in-progress: true

jobs:
  deploy:
    name: Build & Deploy Landing Worker
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          lfs: true

      - name: Checkout LFS objects
        run: git lfs checkout

      - name: Cache turbo build setup
        uses: actions/cache@v5
        with:
          path: .turbo
          key: ${{ runner.os }}-turbo-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-turbo-

      - uses: pnpm/action-setup@v4

      - name: Use Node.js LTS
        uses: actions/setup-node@v6
        with:
          node-version: lts/*
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Resolve deployment target
        id: target
        run: |
          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
            if [[ "${{ inputs.confirm_production_deploy }}" != "true" ]]; then
              echo "Manual production deploy was not confirmed." >&2
              exit 1
            fi

            echo "environment=prod" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
            echo "environment=dev" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          echo "environment=prod" >> "$GITHUB_OUTPUT"

      - name: Build Landing Worker
        run: pnpm exec turbo run @follow/landing#cf:build

      - name: Deploy Landing to Cloudflare (dev)
        if: steps.target.outputs.environment == 'dev'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          workingDirectory: apps/landing
          command: deploy --env dev --name landing-next-dev --routes landing.dev.folo.is/*

      - name: Deploy Landing to Cloudflare (prod)
        if: steps.target.outputs.environment == 'prod'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          workingDirectory: apps/landing
          command: deploy


================================================
FILE: .github/workflows/deploy-cloudflare-ssr.yml
================================================
name: "\u2601\ufe0f Deploy SSR to Cloudflare"

on:
  push:
    branches: [main, dev]
  workflow_dispatch:
    inputs:
      confirm_production_deploy:
        description: Deploy the selected branch to production (app.folo.is)
        required: true
        default: false
        type: boolean

concurrency:
  group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-prod-{0}', github.ref) || github.ref }}
  cancel-in-progress: true

jobs:
  deploy:
    name: Build & Deploy SSR Worker
    runs-on: ubuntu-latest
    env:
      VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
      VITE_FIREBASE_CONFIG: ${{ vars.VITE_FIREBASE_CONFIG }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          lfs: true

      - name: Checkout LFS objects
        run: git lfs checkout

      - name: Cache turbo build setup
        uses: actions/cache@v5
        with:
          path: .turbo
          key: ${{ runner.os }}-turbo-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-turbo-

      - uses: pnpm/action-setup@v4

      - name: Use Node.js LTS
        uses: actions/setup-node@v6
        with:
          node-version: lts/*
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install

      - name: Resolve deployment target
        id: target
        run: |
          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
            if [[ "${{ inputs.confirm_production_deploy }}" != "true" ]]; then
              echo "Manual production deploy was not confirmed." >&2
              exit 1
            fi

            echo "environment=prod" >> "$GITHUB_OUTPUT"
            echo "vite_web_url=https://app.folo.is" >> "$GITHUB_OUTPUT"
            echo "vite_api_url=https://api.folo.is" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
            echo "environment=dev" >> "$GITHUB_OUTPUT"
            echo "vite_web_url=https://dev.folo.is" >> "$GITHUB_OUTPUT"
            echo "vite_api_url=https://api.dev.folo.is" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          echo "environment=prod" >> "$GITHUB_OUTPUT"
          echo "vite_web_url=https://app.folo.is" >> "$GITHUB_OUTPUT"
          echo "vite_api_url=https://api.folo.is" >> "$GITHUB_OUTPUT"

      - name: Build desktop web (SSR assets)
        working-directory: apps/desktop
        env:
          VITE_WEB_URL: ${{ steps.target.outputs.vite_web_url }}
          VITE_API_URL: ${{ steps.target.outputs.vite_api_url }}
        run: pnpm run build:web

      - name: Build SSR Worker
        working-directory: apps/ssr
        run: pnpm run build:worker

      - name: Copy WASM file
        run: cp node_modules/@resvg/resvg-wasm/index_bg.wasm apps/ssr/dist/worker/resvg.wasm

      - name: Deploy SSR Worker to Cloudflare (dev)
        if: steps.target.outputs.environment == 'dev'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          workingDirectory: apps/ssr
          command: deploy --env dev

      - name: Deploy SSR Worker to Cloudflare (prod)
        if: steps.target.outputs.environment == 'prod'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          workingDirectory: apps/ssr
          command: 'deploy --env=""'


================================================
FILE: .github/workflows/issue-labeler.yml
================================================
name: 🏷️ Issue labeler
on:
  issues:
    types: [opened]

permissions:
  contents: read

jobs:
  label-component:
    runs-on: ubuntu-latest

    permissions:
      # required for all workflows
      issues: write

      # only required for workflows in private repositories
      actions: read
      contents: read

    steps:
      - uses: actions/checkout@v6

      - name: Parse issue form
        uses: stefanbuck/github-issue-parser@v3
        id: issue-parser
        with:
          template-path: .github/ISSUE_TEMPLATE/bug_report.yml

      - name: Set labels based on platform field
        uses: redhat-plumbers-in-action/advanced-issue-labeler@v3
        with:
          issue-form: ${{ steps.issue-parser.outputs.jsonString }}
          token: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/lint.yml
================================================
on:
  pull_request:
  push:
    branches: [main, dev]

# Do not use secrets or variables to allow for public access
env:
  VITE_WEB_URL: https://app.folo.is
  VITE_API_URL: https://api.follow.is

name: ✨ CI Format, Typecheck and Lint
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-lint
  cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}
jobs:
  build:
    name: Format, Lint and Typecheck
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [lts/*]
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          lfs: true
      - name: Cache turbo build setup
        uses: actions/cache@v5
        with:
          path: .turbo
          key: ${{ runner.os }}-turbo-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-turbo-

      - name: Checkout LFS objects
        run: git lfs checkout

      - uses: pnpm/action-setup@v4

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v6
        with:
          node-version: ${{ matrix.node-version }}
          cache: "pnpm"
      - name: Install dependencies
        run: pnpm install
      - name: Build web and SSR server
        run: |
          npm exec turbo run Folo#build:web @follow/ssr#build
      - name: Format, Lint and Typecheck
        run: |
          export NODE_OPTIONS="--max_old_space_size=16384"
          npm exec turbo run format:check typecheck lint
      - name: Run test
        run: npm exec turbo run test


================================================
FILE: .github/workflows/pr-title-check.yml
================================================
name: ✅ PR Conventional Commit Validation

on:
  pull_request:
    types: [opened, synchronize, reopened, edited]

jobs:
  validate-pr-title:
    runs-on: ubuntu-latest
    steps:
      - name: PR Conventional Commit Validation
        uses: ytanikin/PRConventionalCommits@1.3.0
        with:
          task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert","release","build"]'
          add_label: false


================================================
FILE: .github/workflows/similar-issues.yml
================================================
name: Similar Issues via AI MCP

on:
  issues:
    types: [opened]

jobs:
  find-similar:
    permissions:
      contents: read
      issues: write
      models: read
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@v6

      - name: Prepare prompt variables
        id: prepare_input
        uses: actions/github-script@v8
        with:
          script: |
            const issue = context.payload.issue || {};
            const title = issue.title || '';
            const body = issue.body || '';
            const indent = '      ';
            // Indent subsequent lines so YAML block scalar indentation remains valid
            const bodyIndented = body.replace(/\n/g, '\n' + indent);
            core.setOutput('issue_title_json', JSON.stringify(title));
            core.setOutput('issue_body_indented_json', JSON.stringify(bodyIndented));

      - name: Find similar issues with AI (MCP)
        id: inference
        uses: actions/ai-inference@v2
        with:
          prompt-file: ./.github/prompts/similar_issues.prompt.yml
          input: |
            issue_title: ${{ steps.prepare_input.outputs.issue_title_json }}
            issue_body: ${{ steps.prepare_input.outputs.issue_body_indented_json }}
            repository: ${{ github.repository }}
          enable-github-mcp: true
          token: ${{ secrets.GITHUB_TOKEN }}
          github-mcp-token: ${{ secrets.USER_PAT }}
          endpoint: https://models.github.ai/orgs/RSSNext/inference

      - name: Prepare comment body
        id: prepare
        uses: actions/github-script@v8
        env:
          AI_RESPONSE: ${{ steps.inference.outputs.response }}
        with:
          script: |
            let data;
            try {
              data = JSON.parse(process.env.AI_RESPONSE || '{}');
            } catch (e) {
              core.setOutput('has_matches', 'false');
              return;
            }
            const matches = Array.isArray(data.matches) ? data.matches : [];
            const rawIssueNumber = context?.payload?.issue?.number;
            const issueNumber = typeof rawIssueNumber === 'string' ? Number(rawIssueNumber) : rawIssueNumber;
            const filteredMatches = Number.isFinite(issueNumber)
              ? matches.filter((m) => {
                  const matchNumber = typeof m?.number === 'string' ? Number(m.number) : m?.number;
                  if (Number.isFinite(matchNumber)) {
                    return matchNumber !== issueNumber;
                  }
                  if (typeof m?.url === 'string') {
                    const urlMatch = m.url.match(/\/issues\/(\d+)(?:$|[?#])/);
                    if (urlMatch) {
                      return Number(urlMatch[1]) !== issueNumber;
                    }
                  }
                  return true;
                })
              : matches;
            if (!filteredMatches.length) {
              core.setOutput('has_matches', 'false');
              return;
            }
            const lines = [];
            lines.push('I found similar issues that might help:');
            for (const m of filteredMatches.slice(0, 3)) {
              const num = m.number != null ? `#${m.number}` : '';
              const title = m.title || 'Untitled';
              const url = m.url || '';
              const score = typeof m.similarity_score === 'number' ? ` (similarity: ${m.similarity_score.toFixed(2)})` : '';
              lines.push(`- ${url}${score}`.trim());
            }
            core.setOutput('has_matches', 'true');
            core.setOutput('comment_body', lines.join('\n'));

      - name: Comment similar issues
        if: steps.prepare.outputs.has_matches == 'true'
        uses: actions/github-script@v8
        with:
          script: |
            const body = ${{ toJson(steps.prepare.outputs.comment_body) }};
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.issue.number,
              body
            });


================================================
FILE: .github/workflows/sync.yaml
================================================
name: 🔄 Sync Release Branches To Dev

on:
  push:
    branches:
      - main
      - mobile-main

permissions:
  contents: write

jobs:
  sync-to-dev:
    runs-on: ubuntu-latest
    if: |
      (github.ref == 'refs/heads/main' && contains(github.event.head_commit.message || '', 'release(desktop):')) ||
      (github.ref == 'refs/heads/mobile-main' && contains(github.event.head_commit.message || '', 'release(mobile):'))
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Set up Git
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

      - name: Merge source branch into dev
        run: |
          source_branch="${GITHUB_REF_NAME}"
          git fetch origin main mobile-main dev
          git checkout dev
          git merge --no-ff "origin/${source_branch}" -m "chore(sync): merge ${source_branch} into dev"
          git push origin dev


================================================
FILE: .github/workflows/tag.yml
================================================
name: 🏷️ Release Orchestrator

on:
  push:
    branches:
      - main
      - mobile-main

permissions:
  contents: write
  actions: write

jobs:
  create_tag:
    name: Create Release Tag
    runs-on: ubuntu-latest
    outputs:
      tag_version: ${{ steps.release_info.outputs.tag_version }}
      platform: ${{ steps.release_info.outputs.platform }}
      version: ${{ steps.release_info.outputs.version }}
      ref_name: ${{ steps.release_info.outputs.ref_name }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: lts/*

      - name: Make script executable
        run: |
          chmod +x .github/scripts/extract-release-info.mjs

      - name: Extract release information
        id: extract_info
        run: .github/scripts/extract-release-info.mjs
        continue-on-error: true

      - name: Expose release outputs
        id: release_info
        run: |
          echo "tag_version=${tag_version:-}" >> "$GITHUB_OUTPUT"
          echo "platform=${platform:-}" >> "$GITHUB_OUTPUT"
          echo "version=${version:-}" >> "$GITHUB_OUTPUT"
          echo "ref_name=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"

      - name: Validate git configuration
        if: steps.release_info.outputs.tag_version != ''
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

      - name: Create and push tag
        if: steps.release_info.outputs.tag_version != ''
        run: |
          git fetch --tags --force
          echo "Creating tag: ${{ steps.release_info.outputs.tag_version }}"
          if git rev-parse "${{ steps.release_info.outputs.tag_version }}" >/dev/null 2>&1; then
            echo "Tag ${{ steps.release_info.outputs.tag_version }} already exists, skipping creation"
            exit 0
          fi

          git tag "${{ steps.release_info.outputs.tag_version }}"
          git push origin "${{ steps.release_info.outputs.tag_version }}"
          echo "Successfully created and pushed tag: ${{ steps.release_info.outputs.tag_version }}"

  trigger_builds:
    name: Trigger Platform Builds
    needs: create_tag
    if: needs.create_tag.outputs.tag_version != ''
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        if: needs.create_tag.outputs.platform == 'desktop' && needs.create_tag.outputs.ref_name == 'main'
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Configure Git
        if: needs.create_tag.outputs.platform == 'desktop' && needs.create_tag.outputs.ref_name == 'main'
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

      - name: Resolve desktop build version
        id: desktop_build_version
        if: needs.create_tag.outputs.platform == 'desktop' && needs.create_tag.outputs.ref_name == 'main'
        run: |
          git fetch --tags --force
          latest_tag="$(git tag -l 'desktop-build/v*' --sort=-v:refname | head -n 1)"
          if [ -z "$latest_tag" ]; then
            last_build=110
          else
            last_build="${latest_tag#desktop-build/v}"
          fi

          next_build=$((last_build + 1))
          build_tag="desktop-build/v${next_build}"

          if git rev-parse "$build_tag" >/dev/null 2>&1; then
            echo "Build tag $build_tag already exists."
            exit 1
          fi

          git tag "$build_tag"
          git push origin "$build_tag"
          echo "build_version=${next_build}" >> "$GITHUB_OUTPUT"
          echo "Desktop build version: ${next_build}"

      - name: Trigger Desktop Tag Version Build
        if: needs.create_tag.outputs.platform == 'desktop' && needs.create_tag.outputs.ref_name == 'main'
        uses: actions/github-script@v8
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const response = await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'build-desktop.yml',
              ref: 'main',
              inputs: {
                tag_version: 'true',
                store: 'false'
              }
            });
            console.log('Desktop Tag Version build triggered successfully');

      - name: Trigger Desktop Store Build
        if: needs.create_tag.outputs.platform == 'desktop' && needs.create_tag.outputs.ref_name == 'main'
        uses: actions/github-script@v8
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const response = await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'build-desktop.yml',
              ref: 'main',
              inputs: {
                tag_version: 'false',
                store: 'true',
                build_version: '${{ steps.desktop_build_version.outputs.build_version }}'
              }
            });
            console.log('Desktop store build triggered successfully');

      - name: Trigger Mobile Preview Release Build
        if: needs.create_tag.outputs.platform == 'mobile' && needs.create_tag.outputs.ref_name == 'mobile-main'
        uses: actions/github-script@v8
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const response = await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'build-android.yml',
              ref: 'mobile-main',
              inputs: {
                profile: 'preview',
                release: 'true'
              }
            });
            console.log('Mobile preview release build triggered successfully');

      - name: Trigger Mobile Production Android Build
        if: needs.create_tag.outputs.platform == 'mobile' && needs.create_tag.outputs.ref_name == 'mobile-main'
        uses: actions/github-script@v8
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const response = await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'build-android.yml',
              ref: 'mobile-main',
              inputs: {
                profile: 'production',
                release: 'false'
              }
            });
            console.log('Mobile production Android build triggered successfully');

      - name: Trigger Mobile Production iOS Build
        if: needs.create_tag.outputs.platform == 'mobile' && needs.create_tag.outputs.ref_name == 'mobile-main'
        uses: actions/github-script@v8
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const response = await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'build-ios.yml',
              ref: 'mobile-main',
              inputs: {
                profile: 'production'
              }
            });
            console.log('Mobile production iOS build triggered successfully');


================================================
FILE: .github/workflows/translator.yml
================================================
name: "🌍 translator"
on:
  issues:
    types: [opened, edited]
  issue_comment:
    types: [created, edited]
  discussion:
    types: [created, edited]
  discussion_comment:
    types: [created, edited]

jobs:
  translate:
    permissions:
      issues: write
      discussions: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: lizheming/github-translate-action@c55aac477e98562d4faed9f77c54ab8306ae6ebf
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          IS_MODIFY_TITLE: true


================================================
FILE: .gitignore
================================================
node_modules
dist
out
.next
.open-next
next-env.d.ts
.DS_Store
*.log*
.env
.eslintcache
.env.*
!.env.example

# Sentry Config File
.env.sentry-build-plugin
.vercel
stats.html

electron.vite.config.*.mjs
vite.config.*.mjs

.generated
.turbo

apps/desktop/src/renderer/dev-dist
tsconfig.tsbuildinfo
buildServer.json

**/**/generated-routes.ts

apps/desktop/build/appxmanifest.xml
apps/desktop/resources/cli

.claude/settings.local.json
.serena

.wrangler

# Local agent artifacts
.codex/

# E2E outputs
/apps/desktop/e2e/playwright-report/
/apps/desktop/e2e/test-results/
/apps/mobile/e2e/artifacts/
/apps/mobile/report.xml
/report.xml

# Mobile local E2E build artifacts
apps/mobile/build-*.tar.gz


================================================
FILE: .npmrc
================================================
shamefully-hoist=true
node-linker=hoisted
save-exact=true


================================================
FILE: .nvmrc
================================================
stable


================================================
FILE: .prettierignore
================================================
pnpm-lock.yaml

CHANGELOG.md
.context

apps/external/postcss.config.cjs

apps/mobile/android
apps/mobile/ios
apps/mobile/native/example

apps/mobile/native/android
apps/mobile/native/ios
apps/mobile/.expo

generated-routes.ts


================================================
FILE: .prettierrc.mjs
================================================
/** @type {import("prettier").Config & import("prettier-plugin-tailwindcss").PluginOptions} */
export default {
  semi: false,
  singleQuote: false,
  printWidth: 100,
  tabWidth: 2,
  trailingComma: "all",
  objectWrap: "preserve",
  plugins: ["prettier-plugin-tailwindcss"],
  tailwindConfig: "./apps/desktop/tailwind.config.ts",
  overrides: [
    {
      files: "apps/mobile/**/*.{css,js,jsx,ts,tsx}",
      options: {
        tailwindConfig: "./apps/mobile/tailwind.config.ts",
      },
    },
    {
      files: "apps/mobile/web-app/html-renderer/**/*.{css,js,jsx,ts,tsx}",
      options: {
        tailwindConfig: "./apps/mobile/web-app/html-renderer/tailwind.config.ts",
      },
    },
    {
      files: "apps/ssr/**/*.{css,html,js,jsx,ts,tsx}",
      options: {
        tailwindConfig: "./apps/ssr/tailwind.config.ts",
      },
    },
  ],
}


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "johnsoncodehk.vscode-tsslint",
    "esbenp.prettier-vscode",
    "bradlc.vscode-tailwindcss"
  ]
}


================================================
FILE: .vscode/launch.json
================================================
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Main Process",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceRoot}",
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
      "windows": {
        "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
      },
      "runtimeArgs": ["--sourcemap"],
      "env": {
        "REMOTE_DEBUGGING_PORT": "9222"
      }
    },
    {
      "name": "Debug Renderer Process",
      "port": 9222,
      "request": "attach",
      "type": "chrome",
      "webRoot": "${workspaceFolder}/src/renderer",
      "timeout": 60000,
      "presentation": {
        "hidden": true
      }
    }
  ],
  "compounds": [
    {
      "name": "Debug All",
      "configurations": ["Debug Main Process", "Debug Renderer Process"],
      "presentation": {
        "order": 1
      }
    }
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[javascript][javascriptreact][typescript][typescriptreact][json][jsonc]": {
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": "explicit"
    }
  },
  "files.associations": {
    "*.css": "tailwindcss"
  },
  "tailwindCSS.experimental.classRegex": [
    ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
    ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
    ["tw`([^`]*)`", "([^`]*)"],
    ["[a-zA-Z]+[cC]lass[nN]ame[\"'`]?:\\s*[\"'`]([^\"'`]*)[\"'`]", "([^\"'`]*)"],
    ["[a-zA-Z]+[cC]lass[nN]ame\\s*=\\s*[\"'`]([^\"'`]*)[\"'`]", "([^\"'`]*)"]
  ],
  "github.copilot.chat.codeGeneration.useInstructionFiles": true,
  "tailwindCSS.experimental.configFile": {
    "apps/mobile/tailwind.config.ts": "apps/mobile/**",
    "apps/ssr/tailwind.config.ts": "apps/ssr/**",
    "apps/desktop/tailwind.config.ts": ["!apps/mobile/**", "!apps/ssr/**", "**"]
  },
  "typescript.tsserver.maxTsServerMemory": 8096,
  "typescript.tsserver.nodePath": "node",
  // If you do not want to autofix some rules on save
  // You can put this in your user settings or workspace settings
  "eslint.codeActionsOnSave.rules": [
    "!prefer-const",
    "!unused-imports/no-unused-imports",
    "!@stylistic/jsx-self-closing-comp",
    "!tailwindcss/classnames-order",
    "!arrow-body-style",
    "*"
  ],
  // If you want to silent stylistic rules
  // You can put this in your user settings or workspace settings
  "eslint.rules.customizations": [
    {
      "rule": "@stylistic/*",
      "severity": "off",
      "fixable": true
    },
    {
      "rule": "tailwindcss/classnames-order",
      "severity": "off"
    },
    {
      "rule": "antfu/consistent-list-newline",
      "severity": "off"
    },
    {
      "rule": "hyoban/jsx-attribute-spacing",
      "severity": "off"
    },
    {
      "rule": "simple-import-sort/*",
      "severity": "off"
    },
    {
      "rule": "prefer-const",
      "severity": "off"
    },
    {
      "rule": "unused-imports/no-unused-imports",
      "severity": "off"
    }
  ],
  "cSpell.words": ["Hydable", "rsshub", "Русский"],
  // "editor.foldingImportsByDefault": true,
  "commentTranslate.hover.enabled": false,
  "typescript.tsdk": "node_modules/typescript/lib",
  "i18n-ally.enabledFrameworks": ["i18next"],
  "i18n-ally.displayLanguage": "en",
  "i18n-ally.localesPaths": ["locales"],
  "i18n-ally.namespace": true,
  "i18n-ally.pathMatcher": "{namespaces}/{locale}.json",
  "lldb.library": "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB",
  "lldb.launch.expressions": "native",
  "[swift]": {
    "editor.defaultFormatter": "swiftlang.swift-vscode"
  },
  "npm.exclude": "**/packages/**"
}


================================================
FILE: AGENTS.md
================================================
# AGENTS.md

This file provides concise, agent-focused guidance for working in this monorepo. It consolidates the repository's CLAUDE.md guides, .cursor rules, Cursor rules improvements, and modern agent best practices.

## Project overview

- Monorepo managed by pnpm workspaces + Turbo.
- Apps:
  - `apps/desktop` – Electron app (Vite + React renderer is the primary web app)
  - `apps/mobile` – React Native app via Expo
  - `apps/ssr` – Minimal SSR site for external sharing
- Shared packages: `packages/internal` (components, atoms, hooks, store, utils, database, etc.).

## Setup commands

```bash
# Install deps
pnpm install

# Desktop – recommended (browser renderer)
cd apps/desktop && pnpm run dev:web

# Desktop – full Electron
cd apps/desktop && pnpm run dev:electron

# Mobile – Expo
cd apps/mobile && pnpm run dev
# or target platforms
cd apps/mobile && pnpm run ios
cd apps/mobile && pnpm run android

# SSR
cd apps/ssr && pnpm run dev

# Build web version (desktop renderer)
pnpm run build:web
```

## Quality gates (must-pass before commit/PR)

```bash
# 1) Typecheck first (required)
pnpm run typecheck

# 2) Lint and auto-fix
pnpm run lint:fix

# 3) Tests
pnpm run test
```

- Run the above at the root, or use per-package variants as needed.
- Follow this order strictly: typecheck → lint → test.
- After every modification, run the following checks to catch errors early:

```bash
npm exec turbo run format:check typecheck lint
npm exec turbo run test
```

## Code style and conventions

- TypeScript strict; avoid `any` (use precise types). Comments in English. Keep solutions simple and maintainable.
- Prefer CSS transitions/animations for simple UI interactions. Use JS-driven motion only when necessary to avoid frame drops.
- Imports: use `pathe` instead of `node:path` for cross‑platform paths.
- Organize shared, reusable UI in `packages/internal/components`; app-specific UI stays in its app.
- **Style extraction**: Avoid inline styles in JSX. Extract complex styles (especially those using CSS variables, gradients, or multiple properties) to external style objects similar to React Native's `StyleSheet.create`. Place style objects in a `styles.ts` file alongside the component, using `CSSProperties` type for type safety.

## Team preferences

- Prefer CSS transitions/animations over JS-based motion for simple interactions to avoid frame drops.
- Prefer simple, easy-to-maintain solutions.
- Avoid using the `any` type in TypeScript.
- Write code comments in English.

## Architecture quick reference

- State: Jotai for atoms, Zustand for complex stores, TanStack Query for server state.
- Database: Drizzle + SQLite (see `packages/internal/database`).
- Error handling: custom utils in `packages/internal/utils`; Sentry integrated.
- i18n: i18next with flat keys only; no `defaultValue`. Provide `en`, `zh-CN`, `ja` for each feature. Avoid conflicting dotted keys.

## UI system and design tokens

### Tailwind + Apple UIKit colors (Desktop/Web)

- Use Tailwind classes bound to Apple UIKit color tokens (light/dark adaptive). Prefix by CSS property:
  - System colors: `text-red`, `bg-blue`, `border-gray`, etc. for `red|orange|yellow|green|mint|teal|cyan|blue|indigo|purple|pink|brown|gray`.
  - Fill: `bg-fill[-secondary|-tertiary|-quaternary|-quinary]` and `bg-fill-vibrant[-secondary|-tertiary|-quaternary|-quinary]` (and `border-*` as needed).
  - Text: `text-text`, `text-text-secondary|tertiary|quaternary|quinary`, `text-text-vibrant(-secondary|-tertiary|-quaternary|-quinary)`.
  - Material: `bg-material-ultra-thick|thick|medium|thin|ultra-thin|opaque`.
  - Control: `bg-control-enabled|disabled`.
  - Interface: `bg-menu|popover|titlebar|sidebar|selection-focused|selection-focused-fill|selection-unfocused|selection-unfocused-fill|header-view|tooltip|under-window-background`.

These classes map to the UIKit color variables (see `.cursor rules/color` and `apps/desktop/AGENTS.md`).

### Icons (Desktop/Web)

- Prefer MingCute with `i-mgc-` prefix (e.g., `i-mgc-copy-cute-re`). Use `i-mingcute-` only if no `i-mgc-` exists.

### Motion (Desktop/Web)

- Use Framer Motion with LazyMotion via `m` from `motion/react` (e.g., `m.div`).
- Prefer spring presets from `@follow/components/constants/spring.js` (`Spring.presets.smooth|snappy|bouncy`).
- For simple micro-interactions, prefer CSS transitions first.

### React Native (Mobile)

- Styling: NativewindCSS; do not use external `StyleSheet.create` for new UI.
- Icons: use `@/apps/mobile/icons` only.
- Colors: use React Native UIKit color system (via `react-native-uikit-colors`) with Tailwind utilities:
  - Backgrounds: `system-background`, `secondary-system-background`, `tertiary-system-background`.
  - Grouped backgrounds: `system-grouped-background`, `secondary-system-grouped-background`, `tertiary-system-grouped-background`.
  - Labels: `label`, `secondary-label`, `tertiary-label`, `quaternary-label`.
  - Fills: `system-fill`, `secondary-system-fill`, `tertiary-system-fill`, `quaternary-system-fill`.
  - Separators: `separator`, `opaque-separator`, `non-opaque-separator`.
  - Semantic colors: `red|orange|yellow|green|mint|teal|cyan|blue|indigo|purple|pink|brown`, grays `gray..gray6`, and interactive `link`, `placeholder-text`.

## Component placement

1. Check existing components in `apps/desktop/layer/renderer/src/modules/renderer/components` for app-specific UI.
2. If generic and reusable, implement in `packages/internal/components` and export from the package index.

## Testing & CI tips

- Use Vitest for unit tests; co-locate tests near source files.
- After moving files or changing imports, run `pnpm lint` and `pnpm typecheck` for the affected package.
- CI expects `pnpm typecheck`, `pnpm lint`, and `pnpm test` to pass before merge.

## Agent workflow (Cursor-oriented improvements)

- Status updates: provide brief progress notes when running tool batches.
- Prefer semantic code search to explore unfamiliar areas; use exact grep only for symbols.
- Default to parallelizing independent searches/reads to reduce latency.
- Avoid multi-line speculative edits; keep edits minimal and targeted; preserve existing indentation.
- When editing TypeScript, do not introduce `any`; keep types precise.
- For UI, prefer CSS transitions for simple effects; use Framer Motion `m.*` only when needed.

## Context7 (up-to-date docs)

- Use Context7 to fetch current library docs before using APIs prone to change.
- Workflow:
  1. Resolve a library ID: resolve the library (e.g., React Native, Vite, TanStack Query).
  2. Fetch docs scoped to the topic (e.g., hooks, routing).
  3. Integrate code examples following our style rules.

## Sequential Thinking (step-by-step problem solving)

- Break work into small thought steps:
  1. Define immediate goal/assumption.
  2. Use suitable tool (search, code edit, error explainer, docs).
  3. Record the output/results.
  4. Decide next step or branch alternatives; compare trade-offs.
- Encourage rollback/iteration if new information contradicts prior steps.

## Subproject guides

- This root AGENTS.md sets global rules. Each app/package should include its own `AGENTS.md` (e.g., `apps/desktop/AGENTS.md`, `apps/mobile/AGENTS.md`). The closest guide to the edited file takes precedence when rules conflict.

## Quick checklists

- Implementation
  - [ ] Is code placed in the right package/app?
  - [ ] Type-safe (no `any`), readable names, English comments where needed.
  - [ ] Uses correct UIKit Tailwind tokens and icon sources.
  - [ ] For motion: CSS first; `m.*` only if necessary.

- Validation
  - [ ] `pnpm typecheck` passes
  - [ ] `pnpm lint:fix` passes cleanly
  - [ ] Tests updated and pass


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

- The use of sexualized language or imagery and unwelcome sexual attention or
  advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
  address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at follow@rss3.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Folo

Thank you for considering contributing to Folo! We welcome contributions from the community to help improve and expand the project.

## Getting Started

Before you start contributing, please ensure you have enabled [Corepack](https://nodejs.org/api/corepack.html). Corepack ensures you are using the correct version of the package manager specified in the `package.json`.

```sh
corepack enable && corepack prepare
```

### Installing Dependencies

To install the necessary dependencies, run:

```sh
pnpm install
```

## Development Setup

### Develop in the Browser

For a more convenient development experience, we recommend developing in the browser:

```sh
cd apps/desktop && pnpm run dev:web
```

This will open the browser at `https://app.folo.is/__debug_proxy`, allowing you to access the online API environment for development and debugging.

### Develop in Electron

If you prefer to develop in Electron, follow these steps:

0. Go to the `apps/desktop` directory:

   ```sh
   cd apps/desktop
   ```

1. Copy the example environment variables file:

   ```sh
   cp .env.example .env
   ```

2. Set `VITE_API_URL` to `https://api.follow.is` in your `.env` file.

3. Run the development server:

   ```sh
   pnpm run dev:electron
   ```

> **Tip:** If you encounter login issues, copy the `__Secure-better-auth.session_token` from your browser's cookies into the app.

### Develop in External SSR Web App

To develop in SSR, follow these steps:

1. Go to the `apps/ssr` directory:

   ```sh
   cd apps/ssr
   ```

2. Run the development server:

   ```sh
   pnpm run dev
   ```

### Develop in Mobile App

To develop in the mobile app, follow these steps:

> [!NOTE]
> You need to have a Mac device to develop in the mobile app.
>
> And already installed Xcode and the necessary dependencies.

1. Go to the `apps/mobile` directory:

   ```sh
   cd apps/mobile
   ```

2. Copy the example environment variables file:

   ```sh
   cp .env.example .env
   ```

   Then set the required environment variables in your `.env` file:

   ```sh
   echo 'EXPO_PUBLIC_APP_CHECK_DEBUG_TOKEN="xxx"' >> .env
   ```

   Or manually edit the `.env` file to add:

   ```
   EXPO_PUBLIC_APP_CHECK_DEBUG_TOKEN="xxx"
   ```

   the value is any string.

3. Build and install Folo(dev) app from source: (This step will take a while and only need to be done once)

   ```sh
   pnpm expo prebuild --clean # Optional
   pnpm run ios
   ```

4. Run the development server:

   ```sh
   pnpm run dev
   ```

#### Development Native Modules

To develop native iOS modules, follow these steps:

1. Go to the `apps/mobile` directory:

   ```sh
   cd apps/mobile/ios
   ```

2. Open project in Xcode:

   ```sh
   open Folo.xcworkspace
   ```

3. Open `Pods` in left sidebar and select `FollowNative`:

![](https://github.com/user-attachments/assets/a449c087-6d55-4cbd-bc4b-c61a08406e98)

4. Build and run the project.

## Contribution Guidelines

- Ensure your code follows the project's coding standards and conventions.
- Write clear, concise commit messages.
- Include relevant tests for your changes.
- Update documentation as necessary.

## Community

Join our community to discuss ideas, ask questions, and share your contributions:

- [Discord](https://discord.gg/AwWcAQ7euc)
- [Twitter](https://x.com/intent/follow?screen_name=folo_is)

We look forward to your contributions!

## License

By contributing to Folo, you agree that your contributions will be licensed under the GNU Affero General Public License version 3, with the special exceptions noted in the `README.md`.


================================================
FILE: LICENSE
================================================
                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 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 Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are 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.

  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.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  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 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 work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  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 AGPL, see
<https://www.gnu.org/licenses/>.

---

Folo is licensed under the GNU Affero General Public License version 3 with the addition of the following special exception:

All content in the `icons/mgc` directory is copyrighted by https://mgc.mingcute.com/ and cannot be redistributed.


================================================
FILE: README.md
================================================
<div align="center">
  <a href="https://github.com/RSSNext/Folo">
    <img src="https://github.com/RSSNext/Folo/raw/refs/heads/dev/apps/desktop/layer/renderer/public/icon.svg" alt="Logo" width="80" height="80">
  </a>

  <h3>Folo</h3>
  <p>
    <img src="https://github.com/user-attachments/assets/cbe924f2-d8b0-48b0-814e-7c06ccb1911c" height="60" />
    &nbsp;&nbsp;&nbsp;
    <img src="https://github.com/user-attachments/assets/6997a236-3df3-49d5-98a4-514f6d1a02c4" height="60" />
    <br />
    <br />
    <a href="https://github.com/RSSNext/Folo/stargazers"><img src="https://img.shields.io/github/stars/RSSNext/Follow?color=ffcb47&labelColor=black&style=flat-square&logo=github&label=Stars" /></a>
    <a href="https://github.com/RSSNext/Folo/graphs/contributors"><img src="https://img.shields.io/github/contributors/RSSNext/Folo?style=flat-square&logo=github&label=Contributors&labelColor=black" /></a>
    <a href="https://status.follow.is/" target="_blank"><img src="https://status.follow.is/api/badge/18/uptime?color=%2344CC10&labelColor=black&style=flat-square"/></a>
    <a href="https://github.com/RSSNext/Folo/releases"><img src="https://img.shields.io/github/downloads/RSSNext/Folo/total?color=369eff&labelColor=black&logo=github&style=flat-square&label=Downloads" /></a>
    <a href="https://x.com/intent/follow?screen_name=folo_is"><img src="https://img.shields.io/badge/Follow-blue?color=1d9bf0&logo=x&labelColor=black&style=flat-square" /></a>
    <a href="https://discord.gg/AwWcAQ7euc" target="_blank"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2Ffollowapp%3Fwith_counts%3Dtrue&query=approximate_member_count&color=5865F2&label=Discord&labelColor=black&logo=discord&logoColor=white&style=flat-square"/></a>
    <br />
    <a href="https://apps.apple.com/us/app/folo-follow-everything/id6739802604"><img src="https://img.shields.io/itunes/v/6739802604?style=flat-square&logo=apple&label=App%20Store&color=FF5C00&labelColor=black" /></a>
    <a href="https://play.google.com/store/apps/details?id=is.follow" target="_blank"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fplay.cuzi.workers.dev%2Fplay%3Fi%3Dis.follow%26gl%3DUS%26hl%3Den%26l%3DAndroid%26m%3D%24version&style=flat-square&logo=google-play&label=Google%20Play&labelColor=black&color=FF5C00"/></a>
    <a href="https://apps.apple.com/us/app/folo-follow-everything/id6739802604"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.folo.is%2Fupdates%2Fdistribution%2Fmas&query=data.storeVersion&prefix=v&style=flat-square&logo=apple&label=Mac%20App%20Store&labelColor=black&color=FF5C00&cacheSeconds=3600" /></a>
    <a href="https://apps.microsoft.com/detail/9nvfzpv0v0ht?mode=direct"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.folo.is%2Fupdates%2Fdistribution%2Fmss&query=data.storeVersion&style=flat-square&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMyAzaDguNTN2OC41M0gzek0xMi40NjkgM2g4LjUzdjguNTNoLTguNTN6TTMgMTIuNDdoOC41M1YyMUgzek0xMi40NjkgMTIuNDdoOC41M1YyMWgtOC41M3oiLz48L3N2Zz4%3D&logoColor=white&label=Microsoft%20Store&labelColor=black&color=FF5C00&cacheSeconds=3600&prefix=v" /></a>
    <br />
    <br />
    <!-- <a href="https://github.com/RSSNext/Folo" target="_blank"><img src="https://github.com/user-attachments/assets/59b957fb-59ed-4ef0-994e-f6a402a6fe2b" alt="GitHub Trending" height="55"/></a>
    <br />
    <br /> -->
    <a href="https://apps.apple.com/us/app/folo-follow-everything/id6739802604" target="_blank"><img src="https://github.com/user-attachments/assets/35747716-28bf-413a-822b-aa49d49f1aa0" alt="Folo Mobile" width="52%"/></a>
    <a href="https://apps.apple.com/us/app/folo-follow-everything/id6739802604" target="_blank"><img src="https://github.com/user-attachments/assets/198a0165-b8c9-45c1-9116-b473a13a8d0c" alt="Folo Desktop" width="46%"/></a>
    <br />
    <br />

  </p>
</div>

As they say, your thoughts are what you read—and we’ve been consuming noisy feeds for too long! Folo organizes content into one timeline, keeping you updated on what matters, noise-free. Share lists, explore collections, and enjoy distraction-free browsing.

## 👋🏻 Getting Started & Join Our Community

Whether for users or professional developers, Folo will be your open information playground. Please be aware that Folo is currently under active development, and feedback is welcome for any [issue](https://github.com/RSSNext/Folo/issues) encountered.

Feel free to try it using the following methods:

| Operating System | Source                                                                                                                                                                                                                                                                                                                                                                                                                                |
| :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Any              | <a href="https://app.folo.is" target="_blank"><img src="https://github.com/user-attachments/assets/51ef7800-b683-4493-83e8-eb4752366997" alt="Browser" height="55"/></a>                                                                                                                                                                                                                                                              |
| iOS              | <a href="https://apps.apple.com/us/app/folo-follow-everything/id6739802604" target="_blank"><img src="https://github.com/user-attachments/assets/a94d8698-2a11-4f43-9b0a-b756b17b61f7" alt="App Store" height="55"/></a>                                                                                                                                                                                                              |
| Android          | <a href="https://play.google.com/store/apps/details?id=is.follow" target="_blank"><img src="https://github.com/user-attachments/assets/0d178e0b-3ace-4f75-bbde-ab3c0a416ce8" alt="Google Play" height="55"/></a> <a href="https://github.com/RSSNext/Folo/releases/latest" target="_blank"><img src="https://github.com/user-attachments/assets/cf61e197-d756-4606-a8ad-fb591f79fdfc" alt="App Store" height="55"/></a>               |
| macOS            | <a href="https://apps.apple.com/us/app/folo-follow-everything/id6739802604" target="_blank"><img src="https://github.com/user-attachments/assets/0d47f902-7fa3-494e-ad28-9ab11af5e6d4" alt="Microsoft Store" height="55"/></a> <a href="https://github.com/RSSNext/Folo/releases/latest" target="_blank"><img src="https://github.com/user-attachments/assets/cf61e197-d756-4606-a8ad-fb591f79fdfc" alt="App Store" height="55"/></a> |
| Windows          | <a href="https://apps.microsoft.com/detail/9nvfzpv0v0ht?mode=direct" target="_blank"><img src="https://github.com/user-attachments/assets/b3112bab-9dd0-4893-9488-890dcb368f70" alt="Microsoft Store" height="55"/></a> <a href="https://github.com/RSSNext/Folo/releases/latest" target="_blank"><img src="https://github.com/user-attachments/assets/cf61e197-d756-4606-a8ad-fb591f79fdfc" alt="App Store" height="55"/></a>        |
| Linux            | <a href="https://github.com/RSSNext/Folo/releases/latest" target="_blank"><img src="https://github.com/user-attachments/assets/cf61e197-d756-4606-a8ad-fb591f79fdfc" alt="App Store" height="55"/></a>                                                                                                                                                                                                                                |

You can also install using the following methods maintained by our community:

- If you are using Arch Linux, you can install the package [folo-appimage](https://aur.archlinux.org/packages/folo-appimage) that is maintained by [timochan](https://github.com/ttimochan) and [grtsinry43](https://github.com/grtsinry43).
- If you are using Nix, you can install the package [follow](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/fo/follow/package.nix) that is maintained by [iosmanthus](https://github.com/iosmanthus).
- If you are using macOS with [Homebrew](https://brew.sh), you can install the cask [folo](https://formulae.brew.sh/cask/folo) that is maintained by [realSunyz](https://github.com/realSunyz).
- If you are using Windows with [Scoop](https://scoop.sh), you can install the manifest [folo](https://github.com/cscnk52/cetacea/blob/master/bucket/folo.json) that is maintained by [cscnk52](https://github.com/cscnk52).

| [![Discord](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Finvites%2Ffollowapp%3Fwith_counts%3Dtrue&query=approximate_member_count&color=5865F2&label=Discord&labelColor=black&logo=discord&logoColor=white&style=flat-square)](https://discord.gg/AwWcAQ7euc) | Join our Discord server to connect with developers, request features, and receive support. |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- |
| [![](https://img.shields.io/badge/any_text-Follow-blue?color=2CA5E0&label=_&logo=x&labelColor=black&style=flat-square)](https://x.com/intent/follow?screen_name=folo_is)                                                                                                                        | Follow us on X/Twitter for product updates and to join in on reward activities.            |

> \[!IMPORTANT]
>
> **Star Us**, You will receive all release notifications from GitHub without any delay \~

![Image](https://github.com/user-attachments/assets/a08f9437-b24c-4388-8f01-2826e09eeaf2)

<a href="https://next.ossinsight.io/widgets/official/compose-last-28-days-stats?repo_id=783512367" target="_blank" style="display: block" align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-last-28-days-stats/thumbnail.png?repo_id=783512367&image_size=auto&color_scheme=dark" width="655" height="auto">
    <img alt="Performance Stats of RSSNext/Folo - Last 28 days" src="https://next.ossinsight.io/widgets/official/compose-last-28-days-stats/thumbnail.png?repo_id=783512367&image_size=auto&color_scheme=light" width="655" height="auto">
  </picture>
</a>

## ✨ Features

### Customized Information Hub

Subscribe to a vast range of feeds and curated lists. Curate your favorites and keep track of what matters most to you.

![](https://github.com/user-attachments/assets/11dc7d21-f5d8-4e41-9269-24fc352aa02b)

### AI At Your Fingertips

A smarter and more efficient browsing with AI-powered features like translation, summary, and more.

![](https://github.com/user-attachments/assets/37cf4f2f-4c5e-4775-86e8-2fa1a1b2ecf5)

### Dynamic Content Support

Because we know content is more than just text. From articles to videos, images to audio — Folo gets it all covered.

![](https://github.com/user-attachments/assets/d1379fd6-8767-476e-b0dc-d61753715e26)

### More Than Just An App

This isn’t just another app. Folo is a community — introducing a new era of openness and community-driven experience.

![](https://github.com/user-attachments/assets/62004a04-eaea-4f5d-bfbf-4e68b6b90286)

## 🤝 Contributing

You are welcome to join the open source community to build together, please check our [Contributing Guide](./CONTRIBUTING.md) for more details.

## 🔏 Code signing policy

Folo for Windows uses free code signing provided by [SignPath.io](https://about.signpath.io/), a certificate by [SignPath Foundation](https://signpath.org/).

Folo for macOS and iOS is signed and notarized by [Apple Developer Program](https://developer.apple.com/programs/).

All released files are verified with [GitHub artifact attestations](https://github.com/RSSNext/Folo/attestations) to ensure their provenance and integrity.

## 📝 License

Folo is licensed under the GNU Affero General Public License version 3 with the addition of the following special exception:

All content in the `icons/mgc` directory is copyrighted by https://mgc.mingcute.com/ and cannot be redistributed.


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

We always recommend using the latest version of Follow to ensure you get all security updates.

## Reporting a Vulnerability

Please report security vulnerabilities to follow@rss3.io.


================================================
FILE: api/vercel_webhook.ts
================================================
import crypto from "node:crypto"

import type { VercelRequest, VercelResponse } from "@vercel/node"
import getRawBody from "raw-body"

export default async function handler(request: VercelRequest, response: VercelResponse) {
  const { WEBHOOK_SECRET: INTEGRATION_SECRET } = process.env

  if (typeof INTEGRATION_SECRET != "string") {
    return response.status(400).json({
      code: "invalid_secret",
      error: "No integration secret found",
    })
  }

  const rawBody = await getRawBody(request)
  const bodySignature = sha1(rawBody, INTEGRATION_SECRET)

  if (bodySignature !== request.headers["x-vercel-signature"]) {
    return response.status(403).json({
      code: "invalid_signature",
      error: "signature didn't match",
    })
  }

  const json = JSON.parse(rawBody.toString("utf-8"))

  switch (json.type) {
    // https://vercel.com/docs/observability/webhooks-overview/webhooks-api#deployment.succeeded
    case "deployment.succeeded": {
      const { target } = json.payload || json.data

      if (target === "production") {
        await purgeCloudflareCache()
      } else {
        console.info(`Skipping non-production deployment: ${target}`, json)
      }
    }
    // ...
  }

  return response.status(200).end("OK")
}

function sha1(data: Buffer, secret: string): string {
  return crypto.createHmac("sha1", secret).update(data).digest("hex")
}

export const config = {
  api: {
    bodyParser: false,
  },
}

async function purgeCloudflareCache() {
  const { CF_TOKEN, CF_ZONE_ID } = process.env

  if (typeof CF_TOKEN !== "string" || typeof CF_ZONE_ID !== "string") {
    throw new TypeError("No Cloudflare token or zone ID found")
  }

  const apiUrl = `https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/purge_cache`

  const manifestPath = await fetch(`https://app.folo.is/assets/manifest.txt?t=${Date.now()}`).then(
    (res) => res.text(),
  )

  try {
    await fetch(apiUrl, {
      method: "POST",
      headers: {
        Authorization: CF_TOKEN,
      },
      body: JSON.stringify({
        tags: ["follow-assets"],
      }),
    })

    console.info("Successfully purged Cloudflare cache")
  } catch {
    console.error("Failed to purge Cloudflare cache by tags, fallback to purge by files")
    const allPath = manifestPath.split("\n").map((path) => `https://app.folo.is/${path}`)

    // Function to delay execution
    const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

    const taskPromise = [] as Promise<Response>[]
    // Batch processing
    for (let i = 0; i < allPath.length; i += 30) {
      const batch = allPath.slice(i, i + 30)

      const r = fetch(apiUrl, {
        method: "POST",
        headers: {
          Authorization: CF_TOKEN,
        },
        body: JSON.stringify({
          files: batch,
        }),
      })

      taskPromise.push(r)

      // Delay for 0.5 seconds between batches
      if (i + 30 < allPath.length) {
        await delay(500)
      }
    }

    const result = await Promise.allSettled(taskPromise)
    console.info(`Success: ${result.filter((r) => r.status === "fulfilled").length}`)
    console.info(`Failed: ${result.filter((r) => r.status === "rejected").length}`)
  }
}


================================================
FILE: apps/cli/package.json
================================================
{
  "name": "folocli",
  "type": "module",
  "version": "0.0.1",
  "description": "Folo CLI for terminal workflows and automation",
  "author": "Folo Team",
  "license": "AGPL-3.0-only",
  "homepage": "https://github.com/RSSNext/Folo",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/RSSNext/Folo.git"
  },
  "keywords": [
    "folo",
    "rss",
    "reader",
    "cli",
    "automation"
  ],
  "bin": {
    "folo": "./dist/index.js"
  },
  "files": [
    "dist",
    "skill.md"
  ],
  "engines": {
    "node": ">=18"
  },
  "scripts": {
    "build": "tsup --config tsup.config.ts && chmod +x dist/index.js",
    "dev": "tsx src/index.ts",
    "start": "node dist/index.js",
    "test": "pnpm run build && vitest run",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@follow-app/client-sdk": "catalog:",
    "commander": "14.0.1",
    "pathe": "2.0.3"
  },
  "devDependencies": {
    "@follow/configs": "workspace:*",
    "@types/node": "25.2.3",
    "tsup": "8.5.0",
    "tsx": "4.21.0",
    "typescript": "catalog:"
  }
}


================================================
FILE: apps/cli/skill.md
================================================
# Folo CLI Skill

## Trigger Conditions

Use this skill when a user asks to:

- Manage RSS subscriptions
- Browse timeline entries
- Read entry details or readability content
- Mark entries as read/unread
- Search feeds/lists or trending sources
- Import/export OPML
- Check unread counts

## Preconditions

1. Node.js and npm are installed so the CLI can be executed with `npx`.
2. Authentication is configured:
   - `npx --yes folocli@latest login` (recommended, opens browser and auto-logins)
   - or `npx --yes folocli@latest login --token <session-token>`
   - or set `FOLO_TOKEN=<token>`

## Execution Policy

- Prefer `npx --yes folocli@latest ...` for all agent runs.
- Do not require `npm install -g folocli`.
- No separate update preflight is needed. Using `folocli@latest` is the update strategy.
- If a user already has a working global `folo` binary, it is acceptable, but `npx --yes folocli@latest` remains the recommended default in docs and automation.

## Output Contract

Default output is JSON with a stable envelope:

```json
{
  "ok": true,
  "data": {},
  "error": null
}
```

Errors return:

```json
{
  "ok": false,
  "data": null,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Token is invalid or expired."
  }
}
```

You can switch output mode:

- `--format json` (default)
- `--format table`
- `--format plain`

## Core Workflows

### 1. Timeline Reading

1. Fetch timeline:
   - `npx --yes folocli@latest timeline --limit 10`
2. Get entry detail:
   - `npx --yes folocli@latest entry get <entryId>`
3. Get readability content:
   - `npx --yes folocli@latest entry read <entryId>`

### 2. Subscription Management

1. Discover:
   - `npx --yes folocli@latest search discover <keyword>`
2. Add subscription:
   - `npx --yes folocli@latest subscription add --feed <url>`
   - or `npx --yes folocli@latest subscription add --list <listId>`
3. List subscriptions:
   - `npx --yes folocli@latest subscription list`

### 3. Unread Processing

1. Check unread total:
   - `npx --yes folocli@latest unread count`
2. List unread subscriptions:
   - `npx --yes folocli@latest unread list`
3. Read unread entries:
   - `npx --yes folocli@latest timeline --unread-only --limit 20`
4. Mark read:
   - `npx --yes folocli@latest entry mark-read <entryId>`
   - or batch: `npx --yes folocli@latest entry mark-all-read --view articles`

### 4. Collection Operations

- Add: `npx --yes folocli@latest collection add <entryId>`
- Remove: `npx --yes folocli@latest collection remove <entryId>`
- List: `npx --yes folocli@latest collection list --limit 20`

### 5. OPML Import / Export

- Export:
  - `npx --yes folocli@latest opml export --output backup.opml`
- Import:
  - `npx --yes folocli@latest opml import feeds.opml`

## Pagination Pattern

`npx --yes folocli@latest timeline` returns:

- `entries`
- `nextCursor`
- `hasNext`

Loop until `hasNext` is `false`:

1. `npx --yes folocli@latest timeline --limit 20`
2. Read `nextCursor`
3. `npx --yes folocli@latest timeline --limit 20 --cursor <nextCursor>`
4. Repeat

## Command Reference

- `npx --yes folocli@latest login [--timeout <seconds>] [--token <token>]`
- `npx --yes folocli@latest logout`
- `npx --yes folocli@latest whoami`
- `npx --yes folocli@latest auth login [--timeout <seconds>] [--token <token>]`
- `npx --yes folocli@latest auth logout`
- `npx --yes folocli@latest auth whoami`

- `npx --yes folocli@latest timeline [--view <type>] [--limit <n>] [--unread-only] [--cursor <datetime>]`
- `npx --yes folocli@latest timeline --feed <feedId> [--limit <n>] [--cursor <datetime>]`
- `npx --yes folocli@latest timeline --list <listId> [--limit <n>] [--cursor <datetime>]`
- `npx --yes folocli@latest timeline --category <name> [--view <type>] [--limit <n>]`

- `npx --yes folocli@latest subscription list [--view <type>] [--category <name>]`
- `npx --yes folocli@latest subscription add --feed <url> [--category <name>] [--view <type>] [--private]`
- `npx --yes folocli@latest subscription add --list <listId> [--category <name>] [--view <type>]`
- `npx --yes folocli@latest subscription remove <id> [--target feed|list|url]`
- `npx --yes folocli@latest subscription update <id> [--target feed|list] [--category <name>] [--title <title>] [--view <type>] [--private|--public]`

- `npx --yes folocli@latest entry get <entryId>`
- `npx --yes folocli@latest entry read <entryId>`
- `npx --yes folocli@latest entry mark-read <entryId>`
- `npx --yes folocli@latest entry mark-unread <entryId>`
- `npx --yes folocli@latest entry mark-all-read [--feed <feedId>] [--list <listId>] [--view <type>]`

- `npx --yes folocli@latest feed get <feedId|feedUrl>`
- `npx --yes folocli@latest feed refresh <feedId>`
- `npx --yes folocli@latest feed analytics <feedId>`

- `npx --yes folocli@latest list ls`
- `npx --yes folocli@latest list get <listId>`
- `npx --yes folocli@latest list create --title <title> [--description <desc>] [--view <type>] [--fee <n>]`
- `npx --yes folocli@latest list update <listId> [--title <title>] [--description <desc>] [--view <type>] [--fee <n>]`
- `npx --yes folocli@latest list delete <listId>`
- `npx --yes folocli@latest list add-feed <listId> --feed <feedId>`
- `npx --yes folocli@latest list remove-feed <listId> --feed <feedId>`

- `npx --yes folocli@latest search discover <keyword> [--type feeds|lists]`
- `npx --yes folocli@latest search rsshub <keyword> [--lang <lang>]`
- `npx --yes folocli@latest search trending [--range 1d|3d|7d|30d] [--view <type>] [--limit <n>] [--language eng|cmn] [--category <keyword>]`

- `npx --yes folocli@latest collection list [--limit <n>] [--cursor <datetime>]`
- `npx --yes folocli@latest collection add <entryId> [--view <type>]`
- `npx --yes folocli@latest collection remove <entryId>`

- `npx --yes folocli@latest opml export [--output <file>]`
- `npx --yes folocli@latest opml import <file> [--items <url1,url2,...>]`

- `npx --yes folocli@latest unread count`
- `npx --yes folocli@latest unread list [--view <type>]`

## Error Recovery

- `UNAUTHORIZED`
  - Re-login: `npx --yes folocli@latest login`
  - or `npx --yes folocli@latest login --token <token>`
  - Or set `FOLO_TOKEN`
- `HTTP_4xx` / `HTTP_5xx`
  - Retry with `--verbose` for request details
  - Verify `--api-url` if using non-default endpoint
- `INVALID_ARGUMENT`
  - Run `npx --yes folocli@latest <command> --help` to inspect accepted options


================================================
FILE: apps/cli/src/args.test.ts
================================================
import { describe, expect, it } from "vitest"

import { parseFormat, parseISODate, parseNonNegativeInt, parsePositiveInt, parseView } from "./args"

describe("args parsers", () => {
  it("parses named view values", () => {
    expect(parseView("articles")).toBe(0)
    expect(parseView("social")).toBe(1)
    expect(parseView("pictures")).toBe(2)
    expect(parseView("videos")).toBe(3)
    expect(parseView("audio")).toBe(4)
    expect(parseView("notifications")).toBe(5)
  })

  it("parses numeric view values", () => {
    expect(parseView("0")).toBe(0)
    expect(parseView("5")).toBe(5)
  })

  it("throws for invalid view values", () => {
    expect(() => parseView("foo")).toThrowError(/Invalid view/)
    expect(() => parseView("6")).toThrowError(/Invalid view/)
  })

  it("parses positive integers", () => {
    expect(parsePositiveInt("1")).toBe(1)
    expect(parsePositiveInt("99")).toBe(99)
  })

  it("throws for non-positive integers", () => {
    expect(() => parsePositiveInt("0")).toThrowError(/positive integer/)
    expect(() => parsePositiveInt("-1")).toThrowError(/positive integer/)
  })

  it("parses non-negative integers", () => {
    expect(parseNonNegativeInt("0")).toBe(0)
    expect(parseNonNegativeInt("3")).toBe(3)
  })

  it("throws for negative integers", () => {
    expect(() => parseNonNegativeInt("-1")).toThrowError(/non-negative integer/)
  })

  it("parses ISO datetime", () => {
    expect(parseISODate("2
Download .txt
gitextract_ni4pl4rv/

├── .agents/
│   ├── settings.local.json
│   └── skills/
│       ├── desktop-release/
│       │   └── SKILL.md
│       ├── installing-mobile-preview-builds/
│       │   └── SKILL.md
│       ├── mobile-e2e/
│       │   └── SKILL.md
│       ├── mobile-release/
│       │   └── SKILL.md
│       ├── mobile-self-test/
│       │   └── SKILL.md
│       └── update-deps/
│           └── SKILL.md
├── .cursorignore
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   ├── i18n.yml
│   │   └── typo.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   ├── setup-version/
│   │   │   └── action.yml
│   │   └── setup-xcode/
│   │       └── action.yml
│   ├── advanced-issue-labeler.yml
│   ├── copilot-instructions.md
│   ├── dependabot.yaml
│   ├── prompts/
│   │   └── similar_issues.prompt.yml
│   ├── scripts/
│   │   └── extract-release-info.mjs
│   └── workflows/
│       ├── build-android.yml
│       ├── build-desktop.yml
│       ├── build-ios-development.yml
│       ├── build-ios.yml
│       ├── build-web.yml
│       ├── deploy-cloudflare-desktop.yml
│       ├── deploy-cloudflare-landing.yml
│       ├── deploy-cloudflare-ssr.yml
│       ├── issue-labeler.yml
│       ├── lint.yml
│       ├── pr-title-check.yml
│       ├── similar-issues.yml
│       ├── sync.yaml
│       ├── tag.yml
│       └── translator.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc.mjs
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── AGENTS.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── api/
│   └── vercel_webhook.ts
├── apps/
│   ├── cli/
│   │   ├── package.json
│   │   ├── skill.md
│   │   ├── src/
│   │   │   ├── args.test.ts
│   │   │   ├── args.ts
│   │   │   ├── browser-login.test.ts
│   │   │   ├── browser-login.ts
│   │   │   ├── cli.e2e.test.ts
│   │   │   ├── client.ts
│   │   │   ├── command.ts
│   │   │   ├── commands/
│   │   │   │   ├── auth.ts
│   │   │   │   ├── collection.ts
│   │   │   │   ├── entry.ts
│   │   │   │   ├── feed.ts
│   │   │   │   ├── list.ts
│   │   │   │   ├── opml.ts
│   │   │   │   ├── search.ts
│   │   │   │   ├── subscription.ts
│   │   │   │   ├── timeline.ts
│   │   │   │   └── unread.ts
│   │   │   ├── config.ts
│   │   │   ├── index.ts
│   │   │   ├── output.test.ts
│   │   │   └── output.ts
│   │   ├── tsconfig.json
│   │   ├── tsup.config.ts
│   │   └── vitest.config.ts
│   ├── desktop/
│   │   ├── .env.example
│   │   ├── AGENTS.md
│   │   ├── build/
│   │   │   ├── appxmanifest-template.xml
│   │   │   ├── dev.pfx
│   │   │   ├── entitlements.mac.plist
│   │   │   ├── entitlements.mas.child.plist
│   │   │   └── entitlements.mas.plist
│   │   ├── bump.config.ts
│   │   ├── bump.hotfix.config.js
│   │   ├── changelog/
│   │   │   ├── 0.1.2.md
│   │   │   ├── 0.2.0.md
│   │   │   ├── 0.2.1.md
│   │   │   ├── 0.2.2.md
│   │   │   ├── 0.2.3.md
│   │   │   ├── 0.2.4.md
│   │   │   ├── 0.2.5.md
│   │   │   ├── 0.2.6.md
│   │   │   ├── 0.2.7.md
│   │   │   ├── 0.2.8.md
│   │   │   ├── 0.2.9.md
│   │   │   ├── 0.3.0.md
│   │   │   ├── 0.3.1.md
│   │   │   ├── 0.3.10.md
│   │   │   ├── 0.3.11.md
│   │   │   ├── 0.3.12.md
│   │   │   ├── 0.3.13.md
│   │   │   ├── 0.3.2.md
│   │   │   ├── 0.3.3.md
│   │   │   ├── 0.3.4.md
│   │   │   ├── 0.3.5.md
│   │   │   ├── 0.3.6.md
│   │   │   ├── 0.3.7.md
│   │   │   ├── 0.3.8.md
│   │   │   ├── 0.3.9.md
│   │   │   ├── 0.4.0.md
│   │   │   ├── 0.4.1.md
│   │   │   ├── 0.4.2.md
│   │   │   ├── 0.4.3.md
│   │   │   ├── 0.4.4.md
│   │   │   ├── 0.4.5.md
│   │   │   ├── 0.4.6.md
│   │   │   ├── 0.4.8.md
│   │   │   ├── 0.5.0.md
│   │   │   ├── 0.6.0.md
│   │   │   ├── 0.6.1.md
│   │   │   ├── 0.6.2.md
│   │   │   ├── 0.6.3.md
│   │   │   ├── 0.7.0.md
│   │   │   ├── 0.8.0.md
│   │   │   ├── 0.9.0.md
│   │   │   ├── 1.0.0.md
│   │   │   ├── 1.1.0.md
│   │   │   ├── 1.2.2.md
│   │   │   ├── 1.2.6.md
│   │   │   ├── 1.3.0.md
│   │   │   ├── 1.3.1.md
│   │   │   ├── 1.4.0.md
│   │   │   ├── next.md
│   │   │   └── next.template.md
│   │   ├── configs/
│   │   │   ├── vite.electron-render.config.ts
│   │   │   └── vite.render.config.ts
│   │   ├── dev-only/
│   │   │   └── dev-app-update.yml
│   │   ├── e2e/
│   │   │   ├── playwright.config.ts
│   │   │   ├── scripts/
│   │   │   │   └── capture-ui-audit.ts
│   │   │   ├── support/
│   │   │   │   ├── account.ts
│   │   │   │   ├── app.ts
│   │   │   │   ├── auth-bootstrap.ts
│   │   │   │   ├── electron.ts
│   │   │   │   └── env.ts
│   │   │   └── tests/
│   │   │       ├── electron/
│   │   │       │   └── core.spec.ts
│   │   │       └── web/
│   │   │           ├── core.spec.ts
│   │   │           └── settings-sync.spec.ts
│   │   ├── electron.vite.config.ts
│   │   ├── forge.config.cts
│   │   ├── layer/
│   │   │   ├── main/
│   │   │   │   ├── export.ts
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── package.json
│   │   │   │   ├── preload/
│   │   │   │   │   ├── index.d.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── src/
│   │   │   │   │   ├── @types/
│   │   │   │   │   │   ├── constants.ts
│   │   │   │   │   │   ├── i18next.d.ts
│   │   │   │   │   │   └── resources.ts
│   │   │   │   │   ├── before-bootstrap.ts
│   │   │   │   │   ├── bootstrap.ts
│   │   │   │   │   ├── constants/
│   │   │   │   │   │   ├── app.ts
│   │   │   │   │   │   └── system.ts
│   │   │   │   │   ├── env.ts
│   │   │   │   │   ├── helper.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── ipc/
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── services/
│   │   │   │   │   │       ├── app.ts
│   │   │   │   │   │       ├── auth.ts
│   │   │   │   │   │       ├── cli.ts
│   │   │   │   │   │       ├── debug.ts
│   │   │   │   │   │       ├── dock.ts
│   │   │   │   │   │       ├── integration.ts
│   │   │   │   │   │       ├── menu.ts
│   │   │   │   │   │       ├── reader.ts
│   │   │   │   │   │       └── setting.ts
│   │   │   │   │   ├── lib/
│   │   │   │   │   │   ├── api-client.ts
│   │   │   │   │   │   ├── auth-cookie-migration.ts
│   │   │   │   │   │   ├── cleaner.ts
│   │   │   │   │   │   ├── cli-session-sync.ts
│   │   │   │   │   │   ├── dock.ts
│   │   │   │   │   │   ├── download.ts
│   │   │   │   │   │   ├── i18n.ts
│   │   │   │   │   │   ├── proxy.test.ts
│   │   │   │   │   │   ├── proxy.ts
│   │   │   │   │   │   ├── router.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── tray.ts
│   │   │   │   │   │   ├── user.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── logger.ts
│   │   │   │   │   ├── manager/
│   │   │   │   │   │   ├── app.ts
│   │   │   │   │   │   ├── bootstrap.ts
│   │   │   │   │   │   ├── lifecycle.ts
│   │   │   │   │   │   └── window.ts
│   │   │   │   │   ├── menu.ts
│   │   │   │   │   ├── modules/
│   │   │   │   │   │   └── language-detection/
│   │   │   │   │   │       └── index.ts
│   │   │   │   │   ├── shims/
│   │   │   │   │   │   └── utf-8-validate.cjs
│   │   │   │   │   └── updater/
│   │   │   │   │       ├── api.ts
│   │   │   │   │       ├── configs.ts
│   │   │   │   │       ├── follow-update-provider.ts
│   │   │   │   │       ├── hot-updater.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── logger.ts
│   │   │   │   │       └── windows-updater.ts
│   │   │   │   ├── tsconfig.json
│   │   │   │   └── vitest.config.ts
│   │   │   └── renderer/
│   │   │       ├── debug_proxy.html
│   │   │       ├── global.d.ts
│   │   │       ├── index.html
│   │   │       ├── package.json
│   │   │       ├── pwa-assets.config.ts
│   │   │       ├── setup-file.ts
│   │   │       ├── src/
│   │   │       │   ├── @types/
│   │   │       │   │   ├── constants.ts
│   │   │       │   │   ├── default-resource.electron.ts
│   │   │       │   │   ├── default-resource.ts
│   │   │       │   │   └── i18next.d.ts
│   │   │       │   ├── App.tsx
│   │   │       │   ├── atoms/
│   │   │       │   │   ├── ai-summary.ts
│   │   │       │   │   ├── ai-translation.ts
│   │   │       │   │   ├── app.ts
│   │   │       │   │   ├── context-menu.ts
│   │   │       │   │   ├── corner-player.ts
│   │   │       │   │   ├── debug-feature.ts
│   │   │       │   │   ├── dom.ts
│   │   │       │   │   ├── lang.ts
│   │   │       │   │   ├── network.ts
│   │   │       │   │   ├── player.ts
│   │   │       │   │   ├── popover.ts
│   │   │       │   │   ├── preview.ts
│   │   │       │   │   ├── readability.ts
│   │   │       │   │   ├── server-configs.ts
│   │   │       │   │   ├── settings/
│   │   │       │   │   │   ├── ai.ts
│   │   │       │   │   │   ├── general.ts
│   │   │       │   │   │   ├── integration.ts
│   │   │       │   │   │   └── ui.ts
│   │   │       │   │   ├── sidebar.ts
│   │   │       │   │   ├── source-content.tsx
│   │   │       │   │   ├── updater.ts
│   │   │       │   │   └── user.ts
│   │   │       │   ├── components/
│   │   │       │   │   ├── common/
│   │   │       │   │   │   ├── AppErrorBoundary.tsx
│   │   │       │   │   │   ├── ErrorBoundary.tsx
│   │   │       │   │   │   ├── ErrorElement.tsx
│   │   │       │   │   │   ├── ErrorTooltip.tsx
│   │   │       │   │   │   ├── ExPromise.tsx
│   │   │       │   │   │   ├── Focusable.tsx
│   │   │       │   │   │   ├── Fragment.tsx
│   │   │       │   │   │   ├── ImpressionTracker.tsx
│   │   │       │   │   │   ├── LCPEndDetector.tsx
│   │   │       │   │   │   ├── LoadMoreIndicator.tsx
│   │   │       │   │   │   ├── LoadRemixAsyncComponent.tsx
│   │   │       │   │   │   ├── Motion.tsx
│   │   │       │   │   │   ├── NotFound.tsx
│   │   │       │   │   │   ├── PoweredByFooter.tsx
│   │   │       │   │   │   ├── ProviderComposer.tsx
│   │   │       │   │   │   ├── ReloadPrompt.tsx
│   │   │       │   │   │   ├── ShadowDOM.tsx
│   │   │       │   │   │   ├── SharePanel.tsx
│   │   │       │   │   │   └── withAppErrorBoundary.tsx
│   │   │       │   │   ├── errors/
│   │   │       │   │   │   ├── EntryNotFound.tsx
│   │   │       │   │   │   ├── FeedNotFound.tsx
│   │   │       │   │   │   ├── ModalError.tsx
│   │   │       │   │   │   ├── PageError.tsx
│   │   │       │   │   │   ├── RSSHubError.tsx
│   │   │       │   │   │   ├── enum.ts
│   │   │       │   │   │   ├── helper.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── mobile/
│   │   │       │   │   │   └── button.tsx
│   │   │       │   │   ├── ui/
│   │   │       │   │   │   ├── ai-summary-card/
│   │   │       │   │   │   │   ├── AISummaryCardBase.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── auto-completion/
│   │   │       │   │   │   │   ├── AutoCompletion.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── background/
│   │   │       │   │   │   │   ├── WindowUnderBlur.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── button/
│   │   │       │   │   │   │   ├── AnimatedCommandButton.tsx
│   │   │       │   │   │   │   ├── CommandActionButton.tsx
│   │   │       │   │   │   │   ├── CopyButton.tsx
│   │   │       │   │   │   │   ├── GlassButton.tsx
│   │   │       │   │   │   │   └── HeaderActionButton.tsx
│   │   │       │   │   │   ├── code-highlighter/
│   │   │       │   │   │   │   ├── constants/
│   │   │       │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   └── shiki/
│   │   │       │   │   │   │       ├── Shiki.tsx
│   │   │       │   │   │   │       ├── hooks.ts
│   │   │       │   │   │   │       ├── index.ts
│   │   │       │   │   │   │       ├── shared.ts
│   │   │       │   │   │   │       └── shiki.module.css
│   │   │       │   │   │   ├── crop/
│   │   │       │   │   │   │   └── AvatarUploadModal.tsx
│   │   │       │   │   │   ├── datetime/
│   │   │       │   │   │   │   └── index.tsx
│   │   │       │   │   │   ├── dropdown-menu/
│   │   │       │   │   │   │   └── dropdown-menu.tsx
│   │   │       │   │   │   ├── fab/
│   │   │       │   │   │   │   ├── FABContainer.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── hover-preview/
│   │   │       │   │   │   │   ├── EntryPreviewCard.tsx
│   │   │       │   │   │   │   ├── FeedPreviewCard.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── keyboard-recorder/
│   │   │       │   │   │   │   ├── KeyRecorder.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── markdown/
│   │   │       │   │   │   │   ├── HTML.tsx
│   │   │       │   │   │   │   ├── Markdown.tsx
│   │   │       │   │   │   │   ├── components/
│   │   │       │   │   │   │   │   ├── Toc.tsx
│   │   │       │   │   │   │   │   ├── TocItem.tsx
│   │   │       │   │   │   │   │   └── hooks.tsx
│   │   │       │   │   │   │   ├── context.tsx
│   │   │       │   │   │   │   ├── renderers/
│   │   │       │   │   │   │   │   ├── BlockErrorBoundary.tsx
│   │   │       │   │   │   │   │   ├── BlockImage.tsx
│   │   │       │   │   │   │   │   ├── Heading.tsx
│   │   │       │   │   │   │   │   ├── InlineImage.tsx
│   │   │       │   │   │   │   │   ├── MarkdownLink.tsx
│   │   │       │   │   │   │   │   ├── MarkdownP.tsx
│   │   │       │   │   │   │   │   ├── ctx.tsx
│   │   │       │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   ├── media/
│   │   │       │   │   │   │   ├── Media.tsx
│   │   │       │   │   │   │   ├── MediaContainerWidthContext.tsx
│   │   │       │   │   │   │   ├── MediaContainerWidthProvider.tsx
│   │   │       │   │   │   │   ├── MediaInfoRecord.tsx
│   │   │       │   │   │   │   ├── MediaInfoRecordContext.tsx
│   │   │       │   │   │   │   ├── MediaInfoRecordProvider.tsx
│   │   │       │   │   │   │   ├── PreviewMediaContent.tsx
│   │   │       │   │   │   │   ├── SwipeMedia.tsx
│   │   │       │   │   │   │   ├── VideoPlayer.tsx
│   │   │       │   │   │   │   ├── VolumeSlider.tsx
│   │   │       │   │   │   │   └── hooks.tsx
│   │   │       │   │   │   ├── modal/
│   │   │       │   │   │   │   ├── components/
│   │   │       │   │   │   │   │   └── close.tsx
│   │   │       │   │   │   │   ├── helper/
│   │   │       │   │   │   │   │   ├── useAsyncModal.tsx
│   │   │       │   │   │   │   │   └── useModalStackCalculationAndEffect.tsx
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   ├── inspire/
│   │   │       │   │   │   │   │   ├── InPeekModal.tsx
│   │   │       │   │   │   │   │   └── PeekModal.tsx
│   │   │       │   │   │   │   └── stacked/
│   │   │       │   │   │   │       ├── AsyncModalContent.tsx
│   │   │       │   │   │   │       ├── atom.ts
│   │   │       │   │   │   │       ├── bus.ts
│   │   │       │   │   │   │       ├── components.tsx
│   │   │       │   │   │   │       ├── constants.ts
│   │   │       │   │   │   │       ├── context.tsx
│   │   │       │   │   │   │       ├── custom-modal.tsx
│   │   │       │   │   │   │       ├── declarative-modal.tsx
│   │   │       │   │   │   │       ├── helper.tsx
│   │   │       │   │   │   │       ├── hooks.tsx
│   │   │       │   │   │   │       ├── index.ts
│   │   │       │   │   │   │       ├── internal/
│   │   │       │   │   │   │       │   ├── use-animate.ts
│   │   │       │   │   │   │       │   ├── use-drag.ts
│   │   │       │   │   │   │       │   ├── use-select.ts
│   │   │       │   │   │   │       │   └── use-subscriber.ts
│   │   │       │   │   │   │       ├── modal-stack.tsx
│   │   │       │   │   │   │       ├── modal.tsx
│   │   │       │   │   │   │       ├── overlay.tsx
│   │   │       │   │   │   │       ├── provider.tsx
│   │   │       │   │   │   │       └── types.tsx
│   │   │       │   │   │   ├── paper/
│   │   │       │   │   │   │   ├── Paper.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   └── peek-modal/
│   │   │       │   │   │       ├── EntryModalPreview.tsx
│   │   │       │   │   │       ├── EntryMoreActions.tsx
│   │   │       │   │   │       └── EntryToastPreview.tsx
│   │   │       │   │   └── ux/
│   │   │       │   │       ├── pull-to-refresh/
│   │   │       │   │       │   └── index.tsx
│   │   │       │   │       └── transition/
│   │   │       │   │           └── icon.tsx
│   │   │       │   ├── constants/
│   │   │       │   │   ├── app.tsx
│   │   │       │   │   ├── copy.ts
│   │   │       │   │   ├── dom.ts
│   │   │       │   │   ├── env.ts
│   │   │       │   │   ├── hotkeys.ts
│   │   │       │   │   ├── index.ts
│   │   │       │   │   └── ui.ts
│   │   │       │   ├── env.d.ts
│   │   │       │   ├── errors/
│   │   │       │   │   └── CustomSafeError.ts
│   │   │       │   ├── global.d.ts
│   │   │       │   ├── hooks/
│   │   │       │   │   ├── biz/
│   │   │       │   │   │   ├── useAsRead.ts
│   │   │       │   │   │   ├── useContextMenuActionShortCutTrigger.ts
│   │   │       │   │   │   ├── useDiscoverRSSHubRoute.tsx
│   │   │       │   │   │   ├── useEntryActions.tsx
│   │   │       │   │   │   ├── useEntryContextMenu.ts
│   │   │       │   │   │   ├── useFeature.ts
│   │   │       │   │   │   ├── useFeedActions.tsx
│   │   │       │   │   │   ├── useFollow.tsx
│   │   │       │   │   │   ├── useNavigateEntry.ts
│   │   │       │   │   │   ├── usePeekModal.tsx
│   │   │       │   │   │   ├── useProxySetting.ts
│   │   │       │   │   │   ├── useReduceMotion.ts
│   │   │       │   │   │   ├── useRenderStyle.tsx
│   │   │       │   │   │   ├── useRouteParams.ts
│   │   │       │   │   │   ├── useShowEntryDetailsColumn.ts
│   │   │       │   │   │   ├── useSubscriptionActions.tsx
│   │   │       │   │   │   ├── useTimelineList.ts
│   │   │       │   │   │   └── useTraySetting.ts
│   │   │       │   │   └── common/
│   │   │       │   │       ├── index.ts
│   │   │       │   │       ├── useBizQuery.ts
│   │   │       │   │       ├── useContextMenu.tsx
│   │   │       │   │       ├── useFeedSafeUrl.ts
│   │   │       │   │       ├── useI18n.ts
│   │   │       │   │       ├── useLoginModal.tsx
│   │   │       │   │       ├── usePreventOverscrollBounce.ts
│   │   │       │   │       ├── useRecaptchaToken.ts
│   │   │       │   │       ├── useRequireLogin.ts
│   │   │       │   │       └── useSyncTheme.ts
│   │   │       │   ├── i18n.ts
│   │   │       │   ├── initialize/
│   │   │       │   │   ├── analytics.ts
│   │   │       │   │   ├── global-shortcuts.ts
│   │   │       │   │   ├── helper.ts
│   │   │       │   │   ├── history.ts
│   │   │       │   │   ├── index.ts
│   │   │       │   │   ├── migrates/
│   │   │       │   │   │   ├── helper.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   └── v/
│   │   │       │   │   │       └── v1.ts
│   │   │       │   │   ├── queue.ts
│   │   │       │   │   └── settings.ts
│   │   │       │   ├── lib/
│   │   │       │   │   ├── __tests__/
│   │   │       │   │   │   └── parse-html.test.ts
│   │   │       │   │   ├── api-client.ts
│   │   │       │   │   ├── app.ts
│   │   │       │   │   ├── auth.ts
│   │   │       │   │   ├── avatar-upload.ts
│   │   │       │   │   ├── client-session.ts
│   │   │       │   │   ├── client.ts
│   │   │       │   │   ├── clipboard.ts
│   │   │       │   │   ├── defineQuery.ts
│   │   │       │   │   ├── dev.tsx
│   │   │       │   │   ├── error-parser.ts
│   │   │       │   │   ├── export.ts
│   │   │       │   │   ├── features.tsx
│   │   │       │   │   ├── ga4.ts
│   │   │       │   │   ├── img-proxy.ts
│   │   │       │   │   ├── issues.ts
│   │   │       │   │   ├── jotai.ts
│   │   │       │   │   ├── language.ts
│   │   │       │   │   ├── load-language.ts
│   │   │       │   │   ├── log.ts
│   │   │       │   │   ├── native-menu.ts
│   │   │       │   │   ├── observe-resize.ts
│   │   │       │   │   ├── parse-html.ts
│   │   │       │   │   ├── parse-markdown.ts
│   │   │       │   │   ├── parsers.ts
│   │   │       │   │   ├── query-client.ts
│   │   │       │   │   ├── simple-text-selection.ts
│   │   │       │   │   ├── url-builder.ts
│   │   │       │   │   └── utils.ts
│   │   │       │   ├── main.tsx
│   │   │       │   ├── modules/
│   │   │       │   │   ├── action/
│   │   │       │   │   │   ├── action-setting.tsx
│   │   │       │   │   │   ├── constants.tsx
│   │   │       │   │   │   ├── rule-card.tsx
│   │   │       │   │   │   ├── rule-summary.ts
│   │   │       │   │   │   ├── then-section.tsx
│   │   │       │   │   │   ├── utils.ts
│   │   │       │   │   │   └── when-section.tsx
│   │   │       │   │   ├── ai-chat/
│   │   │       │   │   │   ├── atoms/
│   │   │       │   │   │   │   └── session.ts
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── 3d-models/
│   │   │       │   │   │   │   │   ├── AISpline.ts
│   │   │       │   │   │   │   │   └── AISplineLoader.tsx
│   │   │       │   │   │   │   ├── context-bar/
│   │   │       │   │   │   │   │   ├── MentionButton.tsx
│   │   │       │   │   │   │   │   ├── blocks/
│   │   │       │   │   │   │   │   │   ├── ContextBlock.tsx
│   │   │       │   │   │   │   │   │   ├── TitleComponents.tsx
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   └── menus/
│   │   │       │   │   │   │   │       ├── ShortcutsMenuContent.tsx
│   │   │       │   │   │   │   │       └── index.ts
│   │   │       │   │   │   │   ├── displays/
│   │   │       │   │   │   │   │   ├── AIChainOfThought.tsx
│   │   │       │   │   │   │   │   ├── AIDisplayFlowPart.tsx
│   │   │       │   │   │   │   │   ├── AIReasoningPart.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── share.tsx
│   │   │       │   │   │   │   │   └── shared/
│   │   │       │   │   │   │   │       ├── AnalyticsMetrics.tsx
│   │   │       │   │   │   │   │       ├── CategoryTag.tsx
│   │   │       │   │   │   │   │       ├── DisplayHeader.tsx
│   │   │       │   │   │   │   │       ├── EmptyState.tsx
│   │   │       │   │   │   │   │       ├── FeedItemCard.tsx
│   │   │       │   │   │   │   │       ├── GroupedContent.tsx
│   │   │       │   │   │   │   │       ├── StatCard.tsx
│   │   │       │   │   │   │   │       └── index.ts
│   │   │       │   │   │   │   ├── file/
│   │   │       │   │   │   │   │   └── GlobalFileDropZone.tsx
│   │   │       │   │   │   │   ├── layouts/
│   │   │       │   │   │   │   │   ├── AIChatContextBar.tsx
│   │   │       │   │   │   │   │   ├── AIChatRoot.tsx
│   │   │       │   │   │   │   │   ├── AIChatSendButton.tsx
│   │   │       │   │   │   │   │   ├── AIErrorFallback.tsx
│   │   │       │   │   │   │   │   ├── AIModelIndicator.tsx
│   │   │       │   │   │   │   │   ├── AISmartSidebar.css
│   │   │       │   │   │   │   │   ├── AISmartSidebar.tsx
│   │   │       │   │   │   │   │   ├── ChatBottomPanel.tsx
│   │   │       │   │   │   │   │   ├── ChatHeader.tsx
│   │   │       │   │   │   │   │   ├── ChatHistoryDropdown.tsx
│   │   │       │   │   │   │   │   ├── ChatInput.tsx
│   │   │       │   │   │   │   │   ├── ChatInterface.tsx
│   │   │       │   │   │   │   │   ├── ChatMessageContainer.tsx
│   │   │       │   │   │   │   │   ├── ChatMoreDropdown.tsx
│   │   │       │   │   │   │   │   ├── ChatShortcutsRow.tsx
│   │   │       │   │   │   │   │   ├── ChatTitle.tsx
│   │   │       │   │   │   │   │   ├── Messages.tsx
│   │   │       │   │   │   │   │   ├── RateLimitNotice.tsx
│   │   │       │   │   │   │   │   ├── ScrollToBottomButton.tsx
│   │   │       │   │   │   │   │   ├── TaskReportDropdown.tsx
│   │   │       │   │   │   │   │   ├── WelcomeScreen.tsx
│   │   │       │   │   │   │   │   └── shared/
│   │   │       │   │   │   │   │       ├── ChatSessionComponents.tsx
│   │   │       │   │   │   │   │       ├── index.ts
│   │   │       │   │   │   │   │       ├── useChatSessionHandlers.tsx
│   │   │       │   │   │   │   │       └── utils.ts
│   │   │       │   │   │   │   ├── message/
│   │   │       │   │   │   │   │   ├── AIChatMessage.tsx
│   │   │       │   │   │   │   │   ├── AIDataBlockPart.tsx
│   │   │       │   │   │   │   │   ├── AIMarkdownMessage.tsx
│   │   │       │   │   │   │   │   ├── AIMessageIdContext.tsx
│   │   │       │   │   │   │   │   ├── AIMessageParts.tsx
│   │   │       │   │   │   │   │   ├── BlockTitleComponents.tsx
│   │   │       │   │   │   │   │   ├── EditableMessage.tsx
│   │   │       │   │   │   │   │   ├── ErrorMessage.tsx
│   │   │       │   │   │   │   │   ├── ImageThumbnail.tsx
│   │   │       │   │   │   │   │   ├── TokenUsagePill.tsx
│   │   │       │   │   │   │   │   ├── ToolInvocationComponent.tsx
│   │   │       │   │   │   │   │   ├── UserChatMessage.tsx
│   │   │       │   │   │   │   │   ├── UserMessageParts.tsx
│   │   │       │   │   │   │   │   ├── UserRichTextMessage.tsx
│   │   │       │   │   │   │   │   ├── ai-block-constants.ts
│   │   │       │   │   │   │   │   ├── animated/
│   │   │       │   │   │   │   │   │   ├── AnimatedMarkdown.tsx
│   │   │       │   │   │   │   │   │   ├── TokenizedText.tsx
│   │   │       │   │   │   │   │   │   └── constants.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── parse-incomplete-markdown.ts
│   │   │       │   │   │   │   │   └── useContextBlockPresentation.tsx
│   │   │       │   │   │   │   ├── shared/
│   │   │       │   │   │   │   │   └── common-states.tsx
│   │   │       │   │   │   │   ├── ui/
│   │   │       │   │   │   │   │   ├── AIShortcutButton.tsx
│   │   │       │   │   │   │   │   ├── ShortcutTooltip.tsx
│   │   │       │   │   │   │   │   └── UploadProgress.tsx
│   │   │       │   │   │   │   └── welcome/
│   │   │       │   │   │   │       ├── DefaultWelcomeContent.tsx
│   │   │       │   │   │   │       ├── EntrySummaryCard.tsx
│   │   │       │   │   │   │       ├── EntryWelcomeContent.tsx
│   │   │       │   │   │   │       └── index.ts
│   │   │       │   │   │   ├── constants/
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── editor/
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   └── plugins/
│   │   │       │   │   │   │       ├── file-upload/
│   │   │       │   │   │   │       │   ├── FileAttachmentNode.tsx
│   │   │       │   │   │   │       │   ├── FileUploadPlugin.tsx
│   │   │       │   │   │   │       │   ├── components/
│   │   │       │   │   │   │       │   │   └── FileDropZone.tsx
│   │   │       │   │   │   │       │   ├── hooks/
│   │   │       │   │   │   │       │   │   ├── useFileAttachmentBlockSync.ts
│   │   │       │   │   │   │       │   │   └── useFileUploadIntegration.ts
│   │   │       │   │   │   │       │   ├── index.ts
│   │   │       │   │   │   │       │   ├── types.ts
│   │   │       │   │   │   │       │   └── utils/
│   │   │       │   │   │   │       │       └── file-handling.ts
│   │   │       │   │   │   │       ├── index.tsx
│   │   │       │   │   │   │       ├── mention/
│   │   │       │   │   │   │       │   ├── MentionNode.tsx
│   │   │       │   │   │   │       │   ├── MentionPlugin.tsx
│   │   │       │   │   │   │       │   ├── components/
│   │   │       │   │   │   │       │   │   ├── MentionComponent.tsx
│   │   │       │   │   │   │       │   │   ├── MentionDropdown.tsx
│   │   │       │   │   │   │       │   │   └── shared/
│   │   │       │   │   │   │       │   │       └── MentionTypeIcon.tsx
│   │   │       │   │   │   │       │   ├── constants.ts
│   │   │       │   │   │   │       │   ├── hooks/
│   │   │       │   │   │   │       │   │   ├── dateMentionConfig.ts
│   │   │       │   │   │   │       │   │   ├── dateMentionParsers.ts
│   │   │       │   │   │   │       │   │   ├── dateMentionSearch.ts
│   │   │       │   │   │   │       │   │   ├── dateMentionUtils.ts
│   │   │       │   │   │   │       │   │   ├── useMentionKeyboard.ts
│   │   │       │   │   │   │       │   │   ├── useMentionSearch.ts
│   │   │       │   │   │   │       │   │   ├── useMentionSearchService.ts
│   │   │       │   │   │   │       │   │   ├── useMentionSelection.ts
│   │   │       │   │   │   │       │   │   └── useMentionTrigger.ts
│   │   │       │   │   │   │       │   ├── index.ts
│   │   │       │   │   │   │       │   ├── types.ts
│   │   │       │   │   │   │       │   └── utils/
│   │   │       │   │   │   │       │       ├── mentionTextValue.ts
│   │   │       │   │   │   │       │       ├── parseNaturalLanguageDate.ts
│   │   │       │   │   │   │       │       ├── textReplacement.ts
│   │   │       │   │   │   │       │       └── triggerDetection.ts
│   │   │       │   │   │   │       ├── selection/
│   │   │       │   │   │   │       │   ├── SelectedTextNode.tsx
│   │   │       │   │   │   │       │   ├── SelectedTextNodeComponent.tsx
│   │   │       │   │   │   │       │   ├── SelectedTextPlugin.tsx
│   │   │       │   │   │   │       │   ├── index.ts
│   │   │       │   │   │   │       │   ├── insertSelectedTextNode.ts
│   │   │       │   │   │   │       │   └── selectedTextBridge.ts
│   │   │       │   │   │   │       ├── shared/
│   │   │       │   │   │   │       │   ├── components/
│   │   │       │   │   │   │       │   │   ├── MentionLikePill.tsx
│   │   │       │   │   │   │       │   │   ├── TypeaheadDropdown.tsx
│   │   │       │   │   │   │       │   │   └── index.ts
│   │   │       │   │   │   │       │   ├── hooks/
│   │   │       │   │   │   │       │   │   ├── useListKeyboardNavigation.ts
│   │   │       │   │   │   │       │   │   ├── useTextTrigger.ts
│   │   │       │   │   │   │       │   │   └── useTypeaheadSelection.ts
│   │   │       │   │   │   │       │   └── utils/
│   │   │       │   │   │   │       │       └── positioning.ts
│   │   │       │   │   │   │       └── shortcut/
│   │   │       │   │   │   │           ├── ShortcutNode.tsx
│   │   │       │   │   │   │           ├── ShortcutPlugin.tsx
│   │   │       │   │   │   │           ├── components/
│   │   │       │   │   │   │           │   ├── ShortcutComponent.tsx
│   │   │       │   │   │   │           │   └── ShortcutDropdown.tsx
│   │   │       │   │   │   │           ├── constants.ts
│   │   │       │   │   │   │           ├── hooks/
│   │   │       │   │   │   │           │   ├── useShortcutKeyboard.ts
│   │   │       │   │   │   │           │   ├── useShortcutSearch.ts
│   │   │       │   │   │   │           │   ├── useShortcutSearchService.ts
│   │   │       │   │   │   │           │   ├── useShortcutSelection.ts
│   │   │       │   │   │   │           │   └── useShortcutTrigger.ts
│   │   │       │   │   │   │           ├── index.ts
│   │   │       │   │   │   │           ├── types.ts
│   │   │       │   │   │   │           └── utils/
│   │   │       │   │   │   │               ├── index.ts
│   │   │       │   │   │   │               ├── positioning.ts
│   │   │       │   │   │   │               ├── shortcutTextValue.ts
│   │   │       │   │   │   │               ├── textReplacement.ts
│   │   │       │   │   │   │               └── triggerDetection.ts
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── useAIConfiguration.ts
│   │   │       │   │   │   │   ├── useAIModel.ts
│   │   │       │   │   │   │   ├── useAIShortcut.ts
│   │   │       │   │   │   │   ├── useAttachScrollBeyond.tsx
│   │   │       │   │   │   │   ├── useAutoScroll.tsx
│   │   │       │   │   │   │   ├── useAutoTimelineSummaryShortcut.ts
│   │   │       │   │   │   │   ├── useChatHistory.ts
│   │   │       │   │   │   │   ├── useDisplayBlocks.ts
│   │   │       │   │   │   │   ├── useFeedEntrySearchService.ts
│   │   │       │   │   │   │   ├── useFileUpload.ts
│   │   │       │   │   │   │   ├── useLoadMessages.ts
│   │   │       │   │   │   │   ├── useMainEntryId.ts
│   │   │       │   │   │   │   ├── useSendAIShortcut.ts
│   │   │       │   │   │   │   └── useTimelineSummaryAutoContext.ts
│   │   │       │   │   │   ├── services/
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── store/
│   │   │       │   │   │   │   ├── AIChatContext.ts
│   │   │       │   │   │   │   ├── chat-core/
│   │   │       │   │   │   │   │   ├── chat-actions.ts
│   │   │       │   │   │   │   │   ├── chat-instance.ts
│   │   │       │   │   │   │   │   ├── chat-state.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   ├── event-system/
│   │   │       │   │   │   │   │   ├── event-emitter.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   ├── hooks.ts
│   │   │       │   │   │   │   ├── slices/
│   │   │       │   │   │   │   │   ├── block.slice.ts
│   │   │       │   │   │   │   │   ├── chat.slice.ts
│   │   │       │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   ├── store.ts
│   │   │       │   │   │   │   ├── transport.ts
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   ├── types/
│   │   │       │   │   │   │   └── ChatSession.ts
│   │   │       │   │   │   └── utils/
│   │   │       │   │   │       ├── error.ts
│   │   │       │   │   │       ├── extractor.ts
│   │   │       │   │   │       ├── file-processing.ts
│   │   │       │   │   │       ├── file-validation.ts
│   │   │       │   │   │       ├── mentionDate.ts
│   │   │       │   │   │       ├── rate-limit.ts
│   │   │       │   │   │       ├── shortcut.ts
│   │   │       │   │   │       └── titleGeneration.ts
│   │   │       │   │   ├── ai-chat-session/
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── query.ts
│   │   │       │   │   │   ├── service.ts
│   │   │       │   │   │   └── store.ts
│   │   │       │   │   ├── ai-onboarding/
│   │   │       │   │   │   ├── ai-chat-pane.tsx
│   │   │       │   │   │   ├── ai-onboarding-modal-content.tsx
│   │   │       │   │   │   ├── feeds-selection-list.tsx
│   │   │       │   │   │   ├── modal.tsx
│   │   │       │   │   │   └── store.ts
│   │   │       │   │   ├── ai-task/
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── ai-item-actions.tsx
│   │   │       │   │   │   │   ├── ai-task-modal.tsx
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   ├── notify-channels-config.tsx
│   │   │       │   │   │   │   ├── schedule-config.tsx
│   │   │       │   │   │   │   ├── task-item.tsx
│   │   │       │   │   │   │   └── task-list.tsx
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── query.ts
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── app/
│   │   │       │   │   │   ├── EnvironmentIndicator.tsx
│   │   │       │   │   │   ├── NetworkStatusIndicator.tsx
│   │   │       │   │   │   └── Titlebar.tsx
│   │   │       │   │   ├── app-layout/
│   │   │       │   │   │   ├── LAYOUT_ARCHITECTURE.md
│   │   │       │   │   │   ├── MainDestopLayout.tsx
│   │   │       │   │   │   ├── ai/
│   │   │       │   │   │   │   ├── AIChatFixedPanel.tsx
│   │   │       │   │   │   │   ├── AIChatFloatingPanel.tsx
│   │   │       │   │   │   │   └── AISplineButton.tsx
│   │   │       │   │   │   ├── ai-enhanced-timeline/
│   │   │       │   │   │   │   ├── AIEnhancedTimelineLayout.tsx
│   │   │       │   │   │   │   ├── MobileTimelineLayout.tsx
│   │   │       │   │   │   │   └── index.tsx
│   │   │       │   │   │   ├── entry-content/
│   │   │       │   │   │   │   └── EntryContentPlaceholder.tsx
│   │   │       │   │   │   ├── subscription-column/
│   │   │       │   │   │   │   ├── SubscriptionColumn.tsx
│   │   │       │   │   │   │   ├── components/
│   │   │       │   │   │   │   │   └── PodcastButton.tsx
│   │   │       │   │   │   │   └── index.tsx
│   │   │       │   │   │   └── subview/
│   │   │       │   │   │       ├── SubviewLayout.tsx
│   │   │       │   │   │       ├── hooks.ts
│   │   │       │   │   │       └── index.tsx
│   │   │       │   │   ├── app-tip/
│   │   │       │   │   │   ├── AICopilotMedia.tsx
│   │   │       │   │   │   ├── AppTipDialog.tsx
│   │   │       │   │   │   ├── AppTipMediaPreview.tsx
│   │   │       │   │   │   ├── AppTipModalContent.tsx
│   │   │       │   │   │   ├── OverviewMedia.tsx
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   ├── types.ts
│   │   │       │   │   │   └── useNewUserGuideState.ts
│   │   │       │   │   ├── auth/
│   │   │       │   │   │   ├── Form.tsx
│   │   │       │   │   │   ├── LoginModalContent.tsx
│   │   │       │   │   │   └── TokenModal.tsx
│   │   │       │   │   ├── claim/
│   │   │       │   │   │   ├── feed-claim-modal.tsx
│   │   │       │   │   │   ├── hooks.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── command/
│   │   │       │   │   │   ├── README.md
│   │   │       │   │   │   ├── command-button.test-d.ts
│   │   │       │   │   │   ├── command-button.tsx
│   │   │       │   │   │   ├── command-manager.ts
│   │   │       │   │   │   ├── commands/
│   │   │       │   │   │   │   ├── entry-render.tsx
│   │   │       │   │   │   │   ├── entry.tsx
│   │   │       │   │   │   │   ├── global.tsx
│   │   │       │   │   │   │   ├── id.ts
│   │   │       │   │   │   │   ├── integration.tsx
│   │   │       │   │   │   │   ├── layout.tsx
│   │   │       │   │   │   │   ├── list.tsx
│   │   │       │   │   │   │   ├── settings.tsx
│   │   │       │   │   │   │   ├── subscription.tsx
│   │   │       │   │   │   │   ├── timeline.tsx
│   │   │       │   │   │   │   └── types.ts
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── use-command-binding.ts
│   │   │       │   │   │   │   ├── use-command.test-d.ts
│   │   │       │   │   │   │   ├── use-command.ts
│   │   │       │   │   │   │   ├── use-register-command.test-d.ts
│   │   │       │   │   │   │   ├── use-register-command.ts
│   │   │       │   │   │   │   ├── use-register-hotkey.test-d.ts
│   │   │       │   │   │   │   └── use-register-hotkey.ts
│   │   │       │   │   │   ├── mutation-command-ids.ts
│   │   │       │   │   │   ├── registry/
│   │   │       │   │   │   │   ├── command.test-d.ts
│   │   │       │   │   │   │   ├── command.ts
│   │   │       │   │   │   │   └── registry.ts
│   │   │       │   │   │   ├── shortcuts/
│   │   │       │   │   │   │   └── SettingShortcuts.tsx
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── customize-toolbar/
│   │   │       │   │   │   ├── constant.ts
│   │   │       │   │   │   ├── dnd.tsx
│   │   │       │   │   │   ├── hooks.ts
│   │   │       │   │   │   └── modal.tsx
│   │   │       │   │   ├── debug/
│   │   │       │   │   │   └── registry.ts
│   │   │       │   │   ├── discover/
│   │   │       │   │   │   ├── DiscoverFeedCard.tsx
│   │   │       │   │   │   ├── DiscoverFeedForm.tsx
│   │   │       │   │   │   ├── DiscoverForm.tsx
│   │   │       │   │   │   ├── DiscoverImport.tsx
│   │   │       │   │   │   ├── DiscoverInboxList.tsx
│   │   │       │   │   │   ├── DiscoverTransform.tsx
│   │   │       │   │   │   ├── DiscoverUser.tsx
│   │   │       │   │   │   ├── DiscoveryContent.tsx
│   │   │       │   │   │   ├── FeedForm.tsx
│   │   │       │   │   │   ├── FeedSummary.tsx
│   │   │       │   │   │   ├── Inbox/
│   │   │       │   │   │   │   ├── ConfirmDestroyModalContent.tsx
│   │   │       │   │   │   │   ├── InboxActions.tsx
│   │   │       │   │   │   │   ├── InboxEmail.tsx
│   │   │       │   │   │   │   ├── InboxSecret.tsx
│   │   │       │   │   │   │   ├── InboxTable.tsx
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   ├── InboxForm.tsx
│   │   │       │   │   │   ├── ListForm.tsx
│   │   │       │   │   │   ├── OpmlAbstractGraphic.tsx
│   │   │       │   │   │   ├── OpmlSelectionModal.tsx
│   │   │       │   │   │   ├── RecommendationContent.tsx
│   │   │       │   │   │   ├── TrendingFeedCard.tsx
│   │   │       │   │   │   ├── UnifiedDiscoverForm.tsx
│   │   │       │   │   │   ├── atoms/
│   │   │       │   │   │   │   └── discover.ts
│   │   │       │   │   │   ├── example-data.json
│   │   │       │   │   │   ├── recommendations.tsx
│   │   │       │   │   │   ├── types.ts
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── download/
│   │   │       │   │   │   └── index.tsx
│   │   │       │   │   ├── editor/
│   │   │       │   │   │   └── css-editor.tsx
│   │   │       │   │   ├── entry-column/
│   │   │       │   │   │   ├── EntryColumnShortcutHandler.tsx
│   │   │       │   │   │   ├── EntryItemSkeleton.tsx
│   │   │       │   │   │   ├── Items/
│   │   │       │   │   │   │   ├── all-item.tsx
│   │   │       │   │   │   │   ├── article-item.tsx
│   │   │       │   │   │   │   ├── audio-item.tsx
│   │   │       │   │   │   │   ├── getItemComponentByView.ts
│   │   │       │   │   │   │   ├── getSkeletonItemComponentByView.ts
│   │   │       │   │   │   │   ├── list-item.tsx
│   │   │       │   │   │   │   ├── media-gallery.tsx
│   │   │       │   │   │   │   ├── notification-item.tsx
│   │   │       │   │   │   │   ├── picture-item-skeleton.tsx
│   │   │       │   │   │   │   ├── picture-item-stateless.tsx
│   │   │       │   │   │   │   ├── picture-item.tsx
│   │   │       │   │   │   │   ├── picture-masonry.tsx
│   │   │       │   │   │   │   ├── social-media-item.tsx
│   │   │       │   │   │   │   └── video-item.tsx
│   │   │       │   │   │   ├── atoms/
│   │   │       │   │   │   │   ├── ai-timeline.ts
│   │   │       │   │   │   │   └── social-media-content-width.ts
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── DateItem.tsx
│   │   │       │   │   │   │   ├── FooterMarkItem.tsx
│   │   │       │   │   │   │   ├── VirtualRowItem.tsx
│   │   │       │   │   │   │   ├── ai-timeline-loading/
│   │   │       │   │   │   │   │   ├── AITimelineLoadingOverlay.css
│   │   │       │   │   │   │   │   └── AITimelineLoadingOverlay.tsx
│   │   │       │   │   │   │   ├── entry-column-wrapper/
│   │   │       │   │   │   │   │   ├── EntryColumnWrapper.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   └── types.tsx
│   │   │       │   │   │   │   └── mark-all-button.tsx
│   │   │       │   │   │   ├── context/
│   │   │       │   │   │   │   └── EntriesContext.tsx
│   │   │       │   │   │   ├── grid.tsx
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── useAttachScrollBeyond.tsx
│   │   │       │   │   │   │   ├── useEntriesByView.ts
│   │   │       │   │   │   │   ├── useEntryIdListSnap.ts
│   │   │       │   │   │   │   ├── useEntryMarkReadHandler.tsx
│   │   │       │   │   │   │   ├── useEntryVirtualization.ts
│   │   │       │   │   │   │   ├── useIsPreviewFeed.ts
│   │   │       │   │   │   │   ├── useLocalEntries.ts
│   │   │       │   │   │   │   ├── useMarkAll.ts
│   │   │       │   │   │   │   ├── useNavigateFirstEntry.tsx
│   │   │       │   │   │   │   └── useWheelGestureClose.ts
│   │   │       │   │   │   ├── index.tsx
│   │   │       │   │   │   ├── item-stateless.tsx
│   │   │       │   │   │   ├── item.tsx
│   │   │       │   │   │   ├── layouts/
│   │   │       │   │   │   │   ├── AppendTaildingDivider.tsx
│   │   │       │   │   │   │   ├── EntryItemWrapper.tsx
│   │   │       │   │   │   │   ├── EntryListHeader.tsx
│   │   │       │   │   │   │   └── buttons/
│   │   │       │   │   │   │       └── SwitchToMasonryButton.tsx
│   │   │       │   │   │   ├── list.tsx
│   │   │       │   │   │   ├── star-icon.tsx
│   │   │       │   │   │   ├── store/
│   │   │       │   │   │   │   └── EntryColumnContext.ts
│   │   │       │   │   │   ├── styles.ts
│   │   │       │   │   │   ├── templates/
│   │   │       │   │   │   │   ├── grid-item-template.tsx
│   │   │       │   │   │   │   └── list-item-template.tsx
│   │   │       │   │   │   ├── translation.tsx
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── entry-content/
│   │   │       │   │   │   ├── EntryContent.tsx
│   │   │       │   │   │   ├── EntryContentForPreview.tsx
│   │   │       │   │   │   ├── actions/
│   │   │       │   │   │   │   ├── header-actions.tsx
│   │   │       │   │   │   │   ├── more-actions.tsx
│   │   │       │   │   │   │   └── picture-gallery.tsx
│   │   │       │   │   │   ├── atoms.tsx
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   ├── AISummary.tsx
│   │   │       │   │   │   │   ├── ApplyEntryActions.tsx
│   │   │       │   │   │   │   ├── EntryAttachments.tsx
│   │   │       │   │   │   │   ├── EntryPlaceholderLogo.tsx
│   │   │       │   │   │   │   ├── EntryTimelineSidebar.tsx
│   │   │       │   │   │   │   ├── EntryTitle.tsx
│   │   │       │   │   │   │   ├── ImageGalleryContent.tsx
│   │   │       │   │   │   │   ├── SourceContentView.tsx
│   │   │       │   │   │   │   ├── WarnGoToExternalLink.tsx
│   │   │       │   │   │   │   ├── entry-content/
│   │   │       │   │   │   │   │   ├── EntryCommandShortcutRegister.tsx
│   │   │       │   │   │   │   │   ├── EntryContentFallback.tsx
│   │   │       │   │   │   │   │   ├── EntryContentLoading.tsx
│   │   │       │   │   │   │   │   ├── EntryNoContent.tsx
│   │   │       │   │   │   │   │   ├── EntryRenderError.tsx
│   │   │       │   │   │   │   │   ├── EntryScrollingAndNavigationHandler.tsx
│   │   │       │   │   │   │   │   ├── EntryTitleMetaHandler.tsx
│   │   │       │   │   │   │   │   ├── ReadabilityNotice.tsx
│   │   │       │   │   │   │   │   ├── accessories/
│   │   │       │   │   │   │   │   │   ├── ContainerToc.tsx
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   └── types.tsx
│   │   │       │   │   │   │   ├── entry-header/
│   │   │       │   │   │   │   │   ├── AIEntryHeader.tsx
│   │   │       │   │   │   │   │   ├── EntryHeader.tsx
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── internal/
│   │   │       │   │   │   │   │   │   ├── EntryHeaderActionsContainer.tsx
│   │   │       │   │   │   │   │   │   ├── EntryHeaderBreadcrumb.tsx
│   │   │       │   │   │   │   │   │   ├── EntryHeaderMeta.tsx
│   │   │       │   │   │   │   │   │   ├── EntryHeaderReadHistory.tsx
│   │   │       │   │   │   │   │   │   └── context.tsx
│   │   │       │   │   │   │   │   └── types.tsx
│   │   │       │   │   │   │   ├── entry-read-history/
│   │   │       │   │   │   │   │   ├── EntryReadHistory.tsx
│   │   │       │   │   │   │   │   ├── EntryUser.tsx
│   │   │       │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   ├── layouts/
│   │   │       │   │   │   │   │   ├── ArticleLayout.tsx
│   │   │       │   │   │   │   │   ├── MediaLayout.tsx
│   │   │       │   │   │   │   │   ├── PicturesLayout.tsx
│   │   │       │   │   │   │   │   ├── SocialMediaLayout.tsx
│   │   │       │   │   │   │   │   ├── VideosLayout.tsx
│   │   │       │   │   │   │   │   ├── factory.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── shared/
│   │   │       │   │   │   │   │   │   ├── AudioPlayer.tsx
│   │   │       │   │   │   │   │   │   ├── AuthorHeader.tsx
│   │   │       │   │   │   │   │   │   ├── ContentBody.tsx
│   │   │       │   │   │   │   │   │   ├── MediaTranscript.tsx
│   │   │       │   │   │   │   │   │   ├── TranscriptToggle.tsx
│   │   │       │   │   │   │   │   │   ├── VideoPlayer.tsx
│   │   │       │   │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   │   └── useTranscription.ts
│   │   │       │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   └── selection/
│   │   │       │   │   │   │       ├── GlassButton.tsx
│   │   │       │   │   │   │       ├── SharePosterModal.tsx
│   │   │       │   │   │   │       └── TextSelectionToolbar.tsx
│   │   │       │   │   │   ├── constants/
│   │   │       │   │   │   │   └── navigation-hints.ts
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   └── useEntryNavigationHints.ts
│   │   │       │   │   │   └── hooks.tsx
│   │   │       │   │   ├── feed/
│   │   │       │   │   │   ├── feed-certification.tsx
│   │   │       │   │   │   ├── feed-icon.tsx
│   │   │       │   │   │   ├── feed-summary.tsx
│   │   │       │   │   │   ├── feed-title.tsx
│   │   │       │   │   │   └── view-select-content.tsx
│   │   │       │   │   ├── integration/
│   │   │       │   │   │   ├── CustomIntegrationPreview.tsx
│   │   │       │   │   │   ├── CustomIntegrationValidator.tsx
│   │   │       │   │   │   ├── PlaceholderHelp.tsx
│   │   │       │   │   │   ├── URLSchemePreview.tsx
│   │   │       │   │   │   ├── custom-integration-manager.ts
│   │   │       │   │   │   ├── fetch-adapter.ts
│   │   │       │   │   │   └── url-scheme-handler.ts
│   │   │       │   │   ├── modal/
│   │   │       │   │   │   ├── ConfirmDestroyModalContent.tsx
│   │   │       │   │   │   ├── ShortcutModalContent.tsx
│   │   │       │   │   │   └── hooks/
│   │   │       │   │   │       ├── useConfirmUnsubscribeSubscriptionModal.tsx
│   │   │       │   │   │       └── useShortcutsModal.tsx
│   │   │       │   │   ├── new-user-guide/
│   │   │       │   │   │   ├── ai-chat-pane.tsx
│   │   │       │   │   │   ├── discover-import-step.tsx
│   │   │       │   │   │   ├── feeds-selection-list.tsx
│   │   │       │   │   │   ├── pre-finish.tsx
│   │   │       │   │   │   └── store.ts
│   │   │       │   │   ├── panel/
│   │   │       │   │   │   ├── cmdf.tsx
│   │   │       │   │   │   ├── cmdk.module.css
│   │   │       │   │   │   ├── cmdk.tsx
│   │   │       │   │   │   └── cmdn.tsx
│   │   │       │   │   ├── plan/
│   │   │       │   │   │   ├── UpgradePlanModalContent.tsx
│   │   │       │   │   │   └── index.tsx
│   │   │       │   │   ├── player/
│   │   │       │   │   │   ├── corner-player.tsx
│   │   │       │   │   │   └── entry-tts.ts
│   │   │       │   │   ├── power/
│   │   │       │   │   │   ├── my-wallet-section/
│   │   │       │   │   │   │   ├── create-wallet.tsx
│   │   │       │   │   │   │   ├── index.tsx
│   │   │       │   │   │   │   └── withdraw.tsx
│   │   │       │   │   │   └── transaction-section/
│   │   │       │   │   │       ├── TransactionsSection.tsx
│   │   │       │   │   │       ├── index.ts
│   │   │       │   │   │       └── tx-table/
│   │   │       │   │   │           ├── TxTable.tsx
│   │   │       │   │   │           ├── components.tsx
│   │   │       │   │   │           └── index.ts
│   │   │       │   │   ├── profile/
│   │   │       │   │   │   ├── account-management.tsx
│   │   │       │   │   │   ├── email-management.tsx
│   │   │       │   │   │   ├── hooks.ts
│   │   │       │   │   │   ├── profile-setting-form.tsx
│   │   │       │   │   │   ├── two-factor.tsx
│   │   │       │   │   │   ├── update-password-form.tsx
│   │   │       │   │   │   ├── user-profile-modal/
│   │   │       │   │   │   │   ├── UserProfileModalContent.tsx
│   │   │       │   │   │   │   ├── constants.ts
│   │   │       │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   └── shared.tsx
│   │   │       │   │   │   └── user-profile-modal.constants.ts
│   │   │       │   │   ├── renderer/
│   │   │       │   │   │   ├── components/
│   │   │       │   │   │   │   └── TimeStamp.tsx
│   │   │       │   │   │   ├── context.tsx
│   │   │       │   │   │   ├── html.tsx
│   │   │       │   │   │   ├── markdown.tsx
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   ├── review-prompt/
│   │   │       │   │   │   ├── ReviewPromptModalContent.tsx
│   │   │       │   │   │   ├── debug.ts
│   │   │       │   │   │   ├── provider.tsx
│   │   │       │   │   │   ├── use-review-prompt-state.ts
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── rsshub/
│   │   │       │   │   │   ├── add-modal-content.tsx
│   │   │       │   │   │   ├── delete-modal-content.tsx
│   │   │       │   │   │   └── set-modal-content.tsx
│   │   │       │   │   ├── settings/
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── context.tsx
│   │   │       │   │   │   ├── control.tsx
│   │   │       │   │   │   ├── helper/
│   │   │       │   │   │   │   ├── EnhancedIndicator.tsx
│   │   │       │   │   │   │   ├── SyncIndicator.tsx
│   │   │       │   │   │   │   ├── builder.ts
│   │   │       │   │   │   │   ├── setting-builder.tsx
│   │   │       │   │   │   │   ├── sync-queue.ts
│   │   │       │   │   │   │   └── withSettingEnable.tsx
│   │   │       │   │   │   ├── hooks/
│   │   │       │   │   │   │   ├── use-setting-ctx.ts
│   │   │       │   │   │   │   └── useWrapEnhancedSettingItem.ts
│   │   │       │   │   │   ├── modal/
│   │   │       │   │   │   │   ├── SettingModalContent.tsx
│   │   │       │   │   │   │   ├── context.tsx
│   │   │       │   │   │   │   ├── hooks.ts
│   │   │       │   │   │   │   ├── layout.tsx
│   │   │       │   │   │   │   ├── use-setting-modal-hack.ts
│   │   │       │   │   │   │   └── useSettingModal.ts
│   │   │       │   │   │   ├── section.tsx
│   │   │       │   │   │   ├── sections/
│   │   │       │   │   │   │   └── fonts.tsx
│   │   │       │   │   │   ├── settings-glob.ts
│   │   │       │   │   │   ├── tabs/
│   │   │       │   │   │   │   ├── about.tsx
│   │   │       │   │   │   │   ├── ai/
│   │   │       │   │   │   │   │   ├── PanelStyleSection.tsx
│   │   │       │   │   │   │   │   ├── PersonalizePromptSection.tsx
│   │   │       │   │   │   │   │   ├── TimelinePromptSection.tsx
│   │   │       │   │   │   │   │   ├── byok/
│   │   │       │   │   │   │   │   │   ├── ByokProviderItem.tsx
│   │   │       │   │   │   │   │   │   ├── ByokProviderModalContent.tsx
│   │   │       │   │   │   │   │   │   ├── ByokSection.tsx
│   │   │       │   │   │   │   │   │   ├── constants.ts
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   ├── index.ts
│   │   │       │   │   │   │   │   ├── mcp/
│   │   │       │   │   │   │   │   │   ├── MCPPresetCard.tsx
│   │   │       │   │   │   │   │   │   ├── MCPPresetSelectionModal.tsx
│   │   │       │   │   │   │   │   │   ├── MCPServiceItem.tsx
│   │   │       │   │   │   │   │   │   ├── MCPServiceModalContent.tsx
│   │   │       │   │   │   │   │   │   ├── MCPServicesSection.tsx
│   │   │       │   │   │   │   │   │   └── types.ts
│   │   │       │   │   │   │   │   ├── shortcuts/
│   │   │       │   │   │   │   │   │   ├── AIShortcutsSection.tsx
│   │   │       │   │   │   │   │   │   ├── ShortcutItem.tsx
│   │   │       │   │   │   │   │   │   ├── ShortcutModalContent.tsx
│   │   │       │   │   │   │   │   │   ├── hooks.tsx
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   ├── tasks/
│   │   │       │   │   │   │   │   │   ├── TaskSchedulingSection.tsx
│   │   │       │   │   │   │   │   │   └── index.ts
│   │   │       │   │   │   │   │   └── usage/
│   │   │       │   │   │   │   │       ├── UsageAnalysisSection.tsx
│   │   │       │   │   │   │   │       ├── components/
│   │   │       │   │   │   │   │       │   ├── DetailedUsageModal.tsx
│   │   │       │   │   │   │   │       │   ├── EfficiencyTab.tsx
│   │   │       │   │   │   │   │       │   ├── HistoryTab.tsx
│   │   │       │   │   │   │   │       │   ├── OverviewTab.tsx
│   │   │       │   │   │   │   │       │   ├── PatternsTab.tsx
│   │   │       │   │   │   │   │       │   ├── UsageProgressRing.tsx
│   │   │       │   │   │   │   │       │   ├── UsageWarningBanner.tsx
│   │   │       │   │   │   │   │       │   ├── charts/
│   │   │       │   │   │   │   │       │   │   ├── BarList.tsx
│   │   │       │   │   │   │   │       │   │   ├── Sparkline.tsx
│   │   │       │   │   │   │   │       │   │   ├── TinyBars.tsx
│   │   │       │   │   │   │   │       │   │   └── index.ts
│   │   │       │   │   │   │   │       │   └── index.ts
│   │   │       │   │   │   │   │       ├── index.ts
│   │   │       │   │   │   │   │       ├── types.ts
│   │   │       │   │   │   │   │       └── utils.ts
│   │   │       │   │   │   │   ├── ai.tsx
│   │   │       │   │   │   │   ├── appearance.tsx
│   │   │       │   │   │   │   ├── cli.tsx
│   │   │       │   │   │   │   ├── data-control.tsx
│   │   │       │   │   │   │   ├── feeds.tsx
│   │   │       │   │   │   │   ├── general.tsx
│   │   │       │   │   │   │   ├── integration/
│   │   │       │   │   │   │   │   ├── CustomIntegrationModal.tsx
│   │   │       │   │   │   │   │   ├── CustomIntegrationSection.tsx
│   │   │       │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   ├── lists/
│   │   │       │   │   │   │   │   ├── hooks.tsx
│   │   │       │   │   │   │   │   ├── index.tsx
│   │   │       │   │   │   │   │   └── modals.tsx
│   │   │       │   │   │   │   ├── notifications.tsx
│   │   │       │   │   │   │   ├── plan.tsx
│   │   │       │   │   │   │   └── shortcut.tsx
│   │   │       │   │   │   ├── title.tsx
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── shared/
│   │   │       │   │   │   └── ViewSelectorRadioGroup.tsx
│   │   │       │   │   ├── subscription-column/
│   │   │       │   │   │   ├── CategoryRemoveDialogContent.tsx
│   │   │       │   │   │   ├── CategoryUnsubscribeDialogContent.tsx
│   │   │       │   │   │   ├── FeedCategory.tsx
│   │   │       │   │   │   ├── FeedItem.tsx
│   │   │       │   │   │   ├── RenameCategoryForm.tsx
│   │   │       │   │   │   ├── SimpleDiscoverModal.tsx
│   │   │       │   │   │   ├── SortedFeedItems.tsx
│   │   │       │   │   │   ├── SubscriptionColumnHeader.tsx
│   │   │       │   │   │   ├── SubscriptionTabButton.tsx
│   │   │       │   │   │   ├── TimelineTabsSettingsModal.tsx
│   │   │       │   │   │   ├── UnreadNumber.tsx
│   │   │       │   │   │   ├── atom.ts
│   │   │       │   │   │   ├── context.ts
│   │   │       │   │   │   ├── hook.ts
│   │   │       │   │   │   ├── index.tsx
│   │   │       │   │   │   ├── sort-by/
│   │   │       │   │   │   │   ├── SortByAlphabeticalList.tsx
│   │   │       │   │   │   │   ├── SortByUnreadList.tsx
│   │   │       │   │   │   │   ├── index.tsx
│   │   │       │   │   │   │   └── types.tsx
│   │   │       │   │   │   ├── styles.ts
│   │   │       │   │   │   └── subscription-list/
│   │   │       │   │   │       ├── EmptyFeedList.tsx
│   │   │       │   │   │       ├── ListHeader.tsx
│   │   │       │   │   │       ├── SortButton.tsx
│   │   │       │   │   │       ├── StarredItem.tsx
│   │   │       │   │   │       ├── SubscriptionList.tsx
│   │   │       │   │   │       ├── SubscriptionListGuard.tsx
│   │   │       │   │   │       └── index.ts
│   │   │       │   │   ├── trending/
│   │   │       │   │   │   └── index.tsx
│   │   │       │   │   ├── update-notice/
│   │   │       │   │   │   └── UpdateNotice.tsx
│   │   │       │   │   ├── upgrade/
│   │   │       │   │   │   ├── container.tsx
│   │   │       │   │   │   ├── lazy/
│   │   │       │   │   │   │   ├── index.electron.ts
│   │   │       │   │   │   │   └── index.ts
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   ├── user/
│   │   │       │   │   │   ├── LoginButton.tsx
│   │   │       │   │   │   ├── ProfileButton.tsx
│   │   │       │   │   │   ├── UserAvatar.tsx
│   │   │       │   │   │   ├── UserGallery.tsx
│   │   │       │   │   │   ├── UserProBadge.tsx
│   │   │       │   │   │   └── utils.ts
│   │   │       │   │   └── wallet/
│   │   │       │   │       └── balance.tsx
│   │   │       │   ├── pages/
│   │   │       │   │   ├── (main)/
│   │   │       │   │   │   ├── (layer)/
│   │   │       │   │   │   │   ├── (ai)/
│   │   │       │   │   │   │   │   └── ai/
│   │   │       │   │   │   │   │       └── index.tsx
│   │   │       │   │   │   │   ├── (subview)/
│   │   │       │   │   │   │   │   ├── action/
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   ├── discover/
│   │   │       │   │   │   │   │   │   ├── category/
│   │   │       │   │   │   │   │   │   │   └── [category].tsx
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   ├── layout.tsx
│   │   │       │   │   │   │   │   ├── power/
│   │   │       │   │   │   │   │   │   └── index.tsx
│   │   │       │   │   │   │   │   └── rsshub/
│   │   │       │   │   │   │   │       └── index.tsx
│   │   │       │   │   │   │   └── timeline/
│   │   │       │   │   │   │       └── [timelineId]/
│   │   │       │   │   │   │           ├── [feedId]/
│   │   │       │   │   │   │           │   ├── [entryId]/
│   │   │       │   │   │   │           │   │   └── index.tsx
│   │   │       │   │   │   │           │   ├── index.tsx
│   │   │       │   │   │   │           │   └── layout.tsx
│   │   │       │   │   │   │           └── layout.tsx
│   │   │       │   │   │   ├── index.sync.tsx
│   │   │       │   │   │   └── layout.tsx
│   │   │       │   │   └── settings/
│   │   │       │   │       ├── (settings)/
│   │   │       │   │       │   ├── about.tsx
│   │   │       │   │       │   ├── ai.tsx
│   │   │       │   │       │   ├── appearance.tsx
│   │   │       │   │       │   ├── cli.tsx
│   │   │       │   │       │   ├── data-control.tsx
│   │   │       │   │       │   ├── feeds.tsx
│   │   │       │   │       │   ├── general.tsx
│   │   │       │   │       │   ├── integration.tsx
│   │   │       │   │       │   ├── list.tsx
│   │   │       │   │       │   ├── notifications.tsx
│   │   │       │   │       │   ├── plan.tsx
│   │   │       │   │       │   ├── profile.tsx
│   │   │       │   │       │   └── shortcuts.tsx
│   │   │       │   │       ├── index.tsx
│   │   │       │   │       └── layout.tsx
│   │   │       │   ├── providers/
│   │   │       │   │   ├── app-grid-layout-container-provider.tsx
│   │   │       │   │   ├── context-menu-provider.tsx
│   │   │       │   │   ├── extension-expose-provider.tsx
│   │   │       │   │   ├── external-jump-in-provider.tsx
│   │   │       │   │   ├── global-focusable-provider.tsx
│   │   │       │   │   ├── global-hotkeys-provider.tsx
│   │   │       │   │   ├── hotkey-provider.tsx
│   │   │       │   │   ├── i18n-provider.tsx
│   │   │       │   │   ├── inject-styles-provider.tsx
│   │   │       │   │   ├── invalidate-query-provider.tsx
│   │   │       │   │   ├── lazy/
│   │   │       │   │   │   ├── index.electron.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── main-view-hotkeys-provider.tsx
│   │   │       │   │   ├── popover-provider.tsx
│   │   │       │   │   ├── root-providers.tsx
│   │   │       │   │   ├── server-configs-provider.tsx
│   │   │       │   │   ├── setting-sync.tsx
│   │   │       │   │   ├── user-provider.tsx
│   │   │       │   │   └── wrapped-element-provider.tsx
│   │   │       │   ├── push-notification.ts
│   │   │       │   ├── queries/
│   │   │       │   │   ├── _.ts
│   │   │       │   │   ├── auth.ts
│   │   │       │   │   ├── discover.ts
│   │   │       │   │   ├── entries.ts
│   │   │       │   │   ├── feed.ts
│   │   │       │   │   ├── index.ts
│   │   │       │   │   ├── mcp.ts
│   │   │       │   │   ├── messaging.ts
│   │   │       │   │   ├── rsshub.ts
│   │   │       │   │   ├── server-configs.ts
│   │   │       │   │   ├── settings.ts
│   │   │       │   │   ├── types.d.ts
│   │   │       │   │   ├── users.ts
│   │   │       │   │   └── wallet.tsx
│   │   │       │   ├── router.tsx
│   │   │       │   ├── router.web.tsx
│   │   │       │   ├── store/
│   │   │       │   │   ├── feed/
│   │   │       │   │   │   └── hooks.ts
│   │   │       │   │   ├── image/
│   │   │       │   │   │   ├── db.ts
│   │   │       │   │   │   └── index.ts
│   │   │       │   │   ├── search/
│   │   │       │   │   │   ├── constants.ts
│   │   │       │   │   │   ├── helper.ts
│   │   │       │   │   │   ├── index.ts
│   │   │       │   │   │   └── types.ts
│   │   │       │   │   └── utils/
│   │   │       │   │       ├── clear.ts
│   │   │       │   │       ├── helper.test.ts
│   │   │       │   │       └── helper.ts
│   │   │       │   ├── styles/
│   │   │       │   │   ├── additional.css
│   │   │       │   │   ├── base.css
│   │   │       │   │   ├── cursor.css
│   │   │       │   │   ├── font.css
│   │   │       │   │   ├── main.css
│   │   │       │   │   └── scrollbar.css
│   │   │       │   ├── sw.ts
│   │   │       │   ├── wdyr.ts
│   │   │       │   └── workers/
│   │   │       │       └── sw/
│   │   │       │           ├── index.ts
│   │   │       │           └── pusher.ts
│   │   │       ├── tsconfig.json
│   │   │       └── vitest.config.ts
│   │   ├── package.json
│   │   ├── plugins/
│   │   │   └── vite/
│   │   │       ├── ast.ts
│   │   │       ├── cleanup.ts
│   │   │       ├── compress.ts
│   │   │       ├── deps.ts
│   │   │       ├── generate-main-hash.ts
│   │   │       ├── hmr.ts
│   │   │       ├── html-inject.ts
│   │   │       ├── i18n-hmr.ts
│   │   │       ├── locales-json.ts
│   │   │       ├── locales.ts
│   │   │       ├── manifest.ts
│   │   │       ├── specific-import.ts
│   │   │       └── utils/
│   │   │           └── i18n-completeness.ts
│   │   ├── postcss.config.cjs
│   │   ├── resources/
│   │   │   ├── app-update.yml
│   │   │   ├── icon-staging.icns
│   │   │   └── icon.icns
│   │   ├── scripts/
│   │   │   ├── apply-changelog.ts
│   │   │   ├── generate-appx-manifest.ts
│   │   │   ├── merge-yml.ts
│   │   │   └── update-windows-yml.ts
│   │   ├── static/
│   │   │   └── dmg-icon.icns
│   │   ├── tailwind.config.ts
│   │   ├── vite.config.electron-render.ts
│   │   ├── vite.config.ts
│   │   └── wrangler.jsonc
│   ├── landing/
│   │   ├── .prettierrc.mjs
│   │   ├── components.json
│   │   ├── eslint.config.mjs
│   │   ├── global.d.ts
│   │   ├── next.config.mjs
│   │   ├── package.json
│   │   ├── plugins/
│   │   │   └── eslint-recursive-sort.mjs
│   │   ├── postcss.config.mjs
│   │   ├── public/
│   │   │   ├── discover-sources.json
│   │   │   └── manifest.json
│   │   ├── src/
│   │   │   ├── app/
│   │   │   │   ├── ClientInit.tsx
│   │   │   │   ├── InitInClient.ts
│   │   │   │   ├── [locale]/
│   │   │   │   │   ├── download/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── error.tsx
│   │   │   │   │   ├── layout.tsx
│   │   │   │   │   ├── page.tsx
│   │   │   │   │   ├── pricing/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   ├── privacy-policy/
│   │   │   │   │   │   └── page.tsx
│   │   │   │   │   └── terms-of-service/
│   │   │   │   │       └── page.tsx
│   │   │   │   ├── apple-app-site-association/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── discover-sources/
│   │   │   │   │   └── route.ts
│   │   │   │   ├── globals.css
│   │   │   │   ├── init.ts
│   │   │   │   ├── layout.tsx
│   │   │   │   └── robots.ts
│   │   │   ├── atoms/
│   │   │   │   ├── css-media.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── is-interactive.ts
│   │   │   │   └── viewport.ts
│   │   │   ├── components/
│   │   │   │   ├── brand/
│   │   │   │   │   ├── Folo.tsx
│   │   │   │   │   └── Logo.tsx
│   │   │   │   ├── common/
│   │   │   │   │   ├── ClientOnly.tsx
│   │   │   │   │   ├── ErrorBoundary.tsx
│   │   │   │   │   ├── GithubTrending.tsx
│   │   │   │   │   ├── HydrationEndDetector.tsx
│   │   │   │   │   ├── Lazyload.tsx
│   │   │   │   │   ├── LightRays.tsx
│   │   │   │   │   ├── ProviderComposer.tsx
│   │   │   │   │   ├── QueryHydrate.tsx
│   │   │   │   │   └── ScrollTop.tsx
│   │   │   │   ├── hoc/
│   │   │   │   │   └── with-no-ssr.tsx
│   │   │   │   ├── layout/
│   │   │   │   │   ├── container/
│   │   │   │   │   │   └── Normal.tsx
│   │   │   │   │   ├── content/
│   │   │   │   │   │   ├── Content.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── footer/
│   │   │   │   │   │   └── Footer.tsx
│   │   │   │   │   └── root/
│   │   │   │   │       └── Root.tsx
│   │   │   │   ├── ui/
│   │   │   │   │   ├── 3d-models/
│   │   │   │   │   │   ├── AISpline.tsx
│   │   │   │   │   │   └── AISplineLoader.tsx
│   │   │   │   │   ├── accordion/
│   │   │   │   │   │   └── Accordion.tsx
│   │   │   │   │   ├── border-beam.tsx
│   │   │   │   │   ├── button/
│   │   │   │   │   │   ├── Button.tsx
│   │   │   │   │   │   ├── MotionButton.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── checkbox/
│   │   │   │   │   │   ├── Checkbox.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── collapse/
│   │   │   │   │   │   ├── CollapseCss.tsx
│   │   │   │   │   │   ├── hooks.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── dialog/
│   │   │   │   │   │   ├── Dialog.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── divider/
│   │   │   │   │   │   ├── Divider.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── dropdown-menu/
│   │   │   │   │   │   └── DropdownMenu.tsx
│   │   │   │   │   ├── effects/
│   │   │   │   │   │   ├── GridGuides.tsx
│   │   │   │   │   │   ├── ParticlesAura.tsx
│   │   │   │   │   │   └── TiltCard.tsx
│   │   │   │   │   ├── glass/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── highlighter.tsx
│   │   │   │   │   ├── hover-card/
│   │   │   │   │   │   ├── HoverCard.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── Input.tsx
│   │   │   │   │   │   ├── TextArea.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── json-highlighter/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── label/
│   │   │   │   │   │   └── Label.tsx
│   │   │   │   │   ├── light-rays.tsx
│   │   │   │   │   ├── loading/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── magic-card.tsx
│   │   │   │   │   ├── markdown/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── modal/
│   │   │   │   │   │   ├── ModalContainer.tsx
│   │   │   │   │   │   ├── ModalManager.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── panel/
│   │   │   │   │   │   └── PanelSplitter.tsx
│   │   │   │   │   ├── portal/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── provider.tsx
│   │   │   │   │   ├── prompts/
│   │   │   │   │   │   ├── BasePrompt.tsx
│   │   │   │   │   │   ├── InputPrompt.tsx
│   │   │   │   │   │   ├── Prompt.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── radio/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── relative-time/
│   │   │   │   │   │   ├── RelativeTime.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── scroll-areas/
│   │   │   │   │   │   ├── ScrollArea.tsx
│   │   │   │   │   │   ├── ctx.ts
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── segment-tab/
│   │   │   │   │   │   ├── SegmentTab.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── select/
│   │   │   │   │   │   ├── ComboboxSelect.tsx
│   │   │   │   │   │   ├── MultiSelect.tsx
│   │   │   │   │   │   ├── Select.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── sheet/
│   │   │   │   │   │   ├── Sheet.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── skeleton/
│   │   │   │   │   │   ├── Skeleton.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── switch/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── theme-switcher/
│   │   │   │   │   │   ├── ThemeSwitcher.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── tooltip/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── styles.ts
│   │   │   │   │   ├── transition/
│   │   │   │   │   │   ├── BottomToUpSoftScaleTransitionView.tsx
│   │   │   │   │   │   ├── BottomToUpTransitionView.tsx
│   │   │   │   │   │   ├── FadeInOutTransitionView.tsx
│   │   │   │   │   │   ├── IconTransiton.tsx
│   │   │   │   │   │   ├── LeftToRightTransitionView.tsx
│   │   │   │   │   │   ├── RightToLeftTransitionView.tsx
│   │   │   │   │   │   ├── ScaleTransitionView.tsx
│   │   │   │   │   │   ├── TextUpTransitionView.tsx
│   │   │   │   │   │   ├── factor.tsx
│   │   │   │   │   │   └── typings.ts
│   │   │   │   │   └── viewport/
│   │   │   │   │       ├── OnlyDesktop.tsx
│   │   │   │   │       ├── OnlyMobile.tsx
│   │   │   │   │       └── index.ts
│   │   │   │   └── widgets/
│   │   │   │       ├── download/
│   │   │   │       │   ├── DownloadHero.tsx
│   │   │   │       │   └── PlatformDownloads.tsx
│   │   │   │       ├── landing/
│   │   │   │       │   ├── Audience.tsx
│   │   │   │       │   ├── BuiltOpen.tsx
│   │   │   │       │   ├── Features.tsx
│   │   │   │       │   ├── Header.tsx
│   │   │   │       │   ├── Hero.tsx
│   │   │   │       │   ├── PromptDemo.tsx
│   │   │   │       │   ├── RepoStats.tsx
│   │   │   │       │   ├── SocialProof.tsx
│   │   │   │       │   ├── TrustedBy.tsx
│   │   │   │       │   ├── ViewsShowcase.tsx
│   │   │   │       │   └── WindowChrome.tsx
│   │   │   │       ├── pricing/
│   │   │   │       │   └── PricingPlans.tsx
│   │   │   │       └── simulators/
│   │   │   │           ├── EntryChatPanel.tsx
│   │   │   │           ├── EntryPage.tsx
│   │   │   │           ├── ListDemo.tsx
│   │   │   │           ├── TimelineChatDemo.tsx
│   │   │   │           ├── components/
│   │   │   │           │   ├── EntryPageOverlay.tsx
│   │   │   │           │   ├── ai/
│   │   │   │           │   │   ├── AIChainOfThought.tsx
│   │   │   │           │   │   ├── AIMarkdownMessage.tsx
│   │   │   │           │   │   ├── AIReasoningPart.tsx
│   │   │   │           │   │   ├── ToolInvocationComponent.tsx
│   │   │   │           │   │   ├── animated/
│   │   │   │           │   │   │   ├── AnimatedMarkdown.tsx
│   │   │   │           │   │   │   ├── TokenizedText.tsx
│   │   │   │           │   │   │   └── constants.ts
│   │   │   │           │   │   ├── mocks.ts
│   │   │   │           │   │   ├── parse-incomplete-markdown.ts
│   │   │   │           │   │   ├── reasoning-mock.json
│   │   │   │           │   │   └── shiny-text/
│   │   │   │           │   │       ├── ShinyText.tsx
│   │   │   │           │   │       └── index.module.css
│   │   │   │           │   └── chat/
│   │   │   │           │       ├── AiMessageContextBar.tsx
│   │   │   │           │       ├── AiMockMessage.tsx
│   │   │   │           │       ├── AiUserMessage.tsx
│   │   │   │           │       ├── ListChatPlayer.tsx
│   │   │   │           │       ├── MarkdownMessage.tsx
│   │   │   │           │       └── stream.ts
│   │   │   │           └── mocks.tsx
│   │   │   ├── constants/
│   │   │   │   ├── download.ts
│   │   │   │   ├── env.ts
│   │   │   │   ├── site.ts
│   │   │   │   └── spring.ts
│   │   │   ├── hooks/
│   │   │   │   ├── biz/
│   │   │   │   │   └── use-github-star.ts
│   │   │   │   ├── common/
│   │   │   │   │   ├── use-before-mounted.ts
│   │   │   │   │   ├── use-click-away.ts
│   │   │   │   │   ├── use-debounce-value.ts
│   │   │   │   │   ├── use-event-callback.ts
│   │   │   │   │   ├── use-input-composition.ts
│   │   │   │   │   ├── use-is-active.ts
│   │   │   │   │   ├── use-is-client.ts
│   │   │   │   │   ├── use-is-dark.ts
│   │   │   │   │   ├── use-is-mounted.ts
│   │   │   │   │   ├── use-is-unmounted.ts
│   │   │   │   │   ├── use-previous.ts
│   │   │   │   │   ├── use-ref-value.ts
│   │   │   │   │   ├── use-safe-setState.ts
│   │   │   │   │   ├── use-state-ref.ts
│   │   │   │   │   ├── use-sync-effect.ts
│   │   │   │   │   └── useMeasure.ts
│   │   │   │   └── shared/
│   │   │   │       └── use-mask-scrollarea.ts
│   │   │   ├── i18n/
│   │   │   │   ├── request.ts
│   │   │   │   └── routing.ts
│   │   │   ├── legal/
│   │   │   │   ├── privacy.md
│   │   │   │   └── tos.md
│   │   │   ├── lib/
│   │   │   │   ├── apple-app-site-association.ts
│   │   │   │   ├── cn.ts
│   │   │   │   ├── color.ts
│   │   │   │   ├── cookie.ts
│   │   │   │   ├── datetime.ts
│   │   │   │   ├── dom.ts
│   │   │   │   ├── env.ts
│   │   │   │   ├── fonts.ts
│   │   │   │   ├── helper.ts
│   │   │   │   ├── jotai.ts
│   │   │   │   ├── landing-data.ts
│   │   │   │   ├── noop.ts
│   │   │   │   ├── platform.ts
│   │   │   │   ├── pricing-data.ts
│   │   │   │   ├── query-client.server.ts
│   │   │   │   ├── scroller.ts
│   │   │   │   ├── sleep.ts
│   │   │   │   ├── spring.ts
│   │   │   │   └── store.ts
│   │   │   ├── messages/
│   │   │   │   ├── en.json
│   │   │   │   ├── jp.json
│   │   │   │   └── zh.json
│   │   │   ├── providers/
│   │   │   │   ├── root/
│   │   │   │   │   ├── debug-provider.tsx
│   │   │   │   │   ├── event-provider.tsx
│   │   │   │   │   ├── framer-lazy-feature.ts
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── jotai-provider.tsx
│   │   │   │   │   ├── page-scroll-info-provider.tsx
│   │   │   │   │   ├── react-query-provider.tsx
│   │   │   │   │   └── sonner.tsx
│   │   │   │   └── shared/
│   │   │   │       ├── LayoutRightSideProvider.tsx
│   │   │   │       └── WrappedElementProvider.tsx
│   │   │   ├── proxy.ts
│   │   │   └── styles/
│   │   │       ├── globals.css
│   │   │       └── pastel-theme-oklch.css
│   │   ├── tsconfig.json
│   │   ├── vite.config.ts
│   │   ├── worker/
│   │   │   └── index.js
│   │   └── wrangler.jsonc
│   ├── mobile/
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .watchmanconfig
│   │   ├── AGENTS.md
│   │   ├── README.md
│   │   ├── app.config.ts
│   │   ├── assets/
│   │   │   └── font/
│   │   │       └── sn-pro/
│   │   │           ├── SNPro-Black.otf
│   │   │           ├── SNPro-BlackItalic.otf
│   │   │           ├── SNPro-Bold.otf
│   │   │           ├── SNPro-BoldItalic.otf
│   │   │           ├── SNPro-Book.otf
│   │   │           ├── SNPro-BookItalic.otf
│   │   │           ├── SNPro-Heavy.otf
│   │   │           ├── SNPro-HeavyItalic.otf
│   │   │           ├── SNPro-Light.otf
│   │   │           ├── SNPro-LightItalic.otf
│   │   │           ├── SNPro-Medium.otf
│   │   │           ├── SNPro-MediumItalic.otf
│   │   │           ├── SNPro-Regular.otf
│   │   │           ├── SNPro-RegularItalic.otf
│   │   │           ├── SNPro-Semibold.otf
│   │   │           ├── SNPro-SemiboldItalic.otf
│   │   │           ├── SNPro-Thin.otf
│   │   │           └── SNPro-ThinItalic.otf
│   │   ├── babel.config.js
│   │   ├── build/
│   │   │   ├── GoogleService-Info.plist
│   │   │   └── google-services.json
│   │   ├── bump.config.ts
│   │   ├── changelog/
│   │   │   ├── 0.1.3.md
│   │   │   ├── 0.1.4.md
│   │   │   ├── 0.1.5.md
│   │   │   ├── 0.1.6.md
│   │   │   ├── 0.1.7.md
│   │   │   ├── 0.1.8.md
│   │   │   ├── 0.1.9.md
│   │   │   ├── 0.2.0.md
│   │   │   ├── 0.2.1.md
│   │   │   ├── 0.2.10.md
│   │   │   ├── 0.2.2.md
│   │   │   ├── 0.2.3.md
│   │   │   ├── 0.2.4.md
│   │   │   ├── 0.2.5.md
│   │   │   ├── 0.2.6.md
│   │   │   ├── 0.2.8.md
│   │   │   ├── 0.3.0.md
│   │   │   ├── 0.4.0.md
│   │   │   ├── next.md
│   │   │   └── next.template.md
│   │   ├── e2e/
│   │   │   ├── README.md
│   │   │   ├── flows/
│   │   │   │   ├── ios/
│   │   │   │   │   ├── auth.yaml
│   │   │   │   │   ├── content.yaml
│   │   │   │   │   ├── core.yaml
│   │   │   │   │   ├── dismiss-overlays.yaml
│   │   │   │   │   ├── ensure-onboarding-unfollowed.yaml
│   │   │   │   │   ├── follow-onboarding.yaml
│   │   │   │   │   ├── login.yaml
│   │   │   │   │   ├── register.yaml
│   │   │   │   │   ├── sign-out.yaml
│   │   │   │   │   ├── timeline-entry.yaml
│   │   │   │   │   └── unfollow-onboarding.yaml
│   │   │   │   └── shared/
│   │   │   │       ├── core.yaml
│   │   │   │       ├── dismiss-ios-system-modal.yaml
│   │   │   │       ├── ensure-onboarding-unfollowed.yaml
│   │   │   │       ├── follow-onboarding.yaml
│   │   │   │       ├── login.yaml
│   │   │   │       ├── open-auth.yaml
│   │   │   │       ├── register.yaml
│   │   │   │       ├── sign-out.yaml
│   │   │   │       ├── timeline-entry.yaml
│   │   │   │       └── unfollow-onboarding.yaml
│   │   │   └── run-maestro.sh
│   │   ├── eas.json
│   │   ├── global.d.ts
│   │   ├── ios/
│   │   │   ├── .gitignore
│   │   │   ├── .xcode.env
│   │   │   ├── Assets.xcassets/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── black_board_2_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── black_board_2_cute_re.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── home_5_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── home_5_cute_re.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── search_3_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── search_3_cute_re.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── settings_1_cute_fi.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── settings_1_cute_re.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── Folo/
│   │   │   │   ├── AppDelegate.swift
│   │   │   │   ├── Folo-Bridging-Header.h
│   │   │   │   ├── Folo.entitlements
│   │   │   │   ├── Images.xcassets/
│   │   │   │   │   ├── AppIcon.appiconset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   ├── Contents.json
│   │   │   │   │   └── SplashScreenBackground.colorset/
│   │   │   │   │       └── Contents.json
│   │   │   │   ├── Info.plist
│   │   │   │   ├── PrivacyInfo.xcprivacy
│   │   │   │   ├── SplashScreen.storyboard
│   │   │   │   └── Supporting/
│   │   │   │       └── Expo.plist
│   │   │   ├── Folo - Follow everything.storekit
│   │   │   ├── Folo.xcodeproj/
│   │   │   │   ├── project.pbxproj
│   │   │   │   └── xcshareddata/
│   │   │   │       └── xcschemes/
│   │   │   │           └── Folo.xcscheme
│   │   │   ├── Folo.xcworkspace/
│   │   │   │   └── contents.xcworkspacedata
│   │   │   ├── Podfile
│   │   │   └── Podfile.properties.json
│   │   ├── metro.config.js
│   │   ├── native/
│   │   │   ├── .eslintrc.js
│   │   │   ├── .gitignore
│   │   │   ├── .npmignore
│   │   │   ├── README.md
│   │   │   ├── expo-module.config.json
│   │   │   ├── ios/
│   │   │   │   ├── Controllers/
│   │   │   │   │   ├── ModalWebViewController.swift
│   │   │   │   │   ├── RNSViewController.swift
│   │   │   │   │   └── WebViewController.swift
│   │   │   │   ├── Extensions/
│   │   │   │   │   ├── UIColor+Hex.swift
│   │   │   │   │   ├── UIImage+asActivityItemSource.swift
│   │   │   │   │   ├── UIImage.swift
│   │   │   │   │   └── UIWindow.swift
│   │   │   │   ├── FollowNative.podspec
│   │   │   │   ├── Models/
│   │   │   │   │   ├── ProfileData.swift
│   │   │   │   │   └── UserData.swift
│   │   │   │   ├── Modules/
│   │   │   │   │   ├── AppleIntelligenceGlowEffect/
│   │   │   │   │   │   ├── AppleIntelligenceGlowEffectModule.swift
│   │   │   │   │   │   ├── IntelligenceAnimationController.swift
│   │   │   │   │   │   └── IntelligenceAnimationView.swift
│   │   │   │   │   ├── Helper/
│   │   │   │   │   │   ├── Helper+Image.swift
│   │   │   │   │   │   └── HelperModule.swift
│   │   │   │   │   ├── ItemPressable/
│   │   │   │   │   │   └── ItemPressableModule.swift
│   │   │   │   │   ├── PagerView/
│   │   │   │   │   │   ├── EnhancePageViewModule.swift
│   │   │   │   │   │   ├── EnhancePagerController.swift
│   │   │   │   │   │   └── EnhancePagerViewModule.swift
│   │   │   │   │   ├── SharedWebView/
│   │   │   │   │   │   ├── FOWebView.swift
│   │   │   │   │   │   ├── FollowImageURLSchemeHandler.swift
│   │   │   │   │   │   ├── Injected/
│   │   │   │   │   │   │   ├── at_end.js
│   │   │   │   │   │   │   └── at_start.js
│   │   │   │   │   │   ├── SharedWebView+BridgeData.swift
│   │   │   │   │   │   ├── SharedWebView.swift
│   │   │   │   │   │   ├── SharedWebViewModule.swift
│   │   │   │   │   │   ├── WebViewManager.swift
│   │   │   │   │   │   └── WebViewState.swift
│   │   │   │   │   ├── StoreKitTestHelper/
│   │   │   │   │   │   └── StoreKitTestHelperModule.swift
│   │   │   │   │   ├── TabBar/
│   │   │   │   │   │   ├── TabBarBottomAccessoryModule.swift
│   │   │   │   │   │   ├── TabBarModule.swift
│   │   │   │   │   │   ├── TabBarPortalModule.swift
│   │   │   │   │   │   ├── TabBarRootView.swift
│   │   │   │   │   │   ├── TabScreenModule.swift
│   │   │   │   │   │   └── TabScreenView.swift
│   │   │   │   │   └── Toaster/
│   │   │   │   │       ├── Toast.swift
│   │   │   │   │       └── ToasterModule.swift
│   │   │   │   ├── Packages/
│   │   │   │   │   ├── ImageViewer_swift/
│   │   │   │   │   │   ├── ImageCarouselViewController.swift
│   │   │   │   │   │   ├── ImageCarouselViewControllerProtocol.swift
│   │   │   │   │   │   ├── ImageItem.swift
│   │   │   │   │   │   ├── ImageLoader.swift
│   │   │   │   │   │   ├── ImageViewerController.swift
│   │   │   │   │   │   ├── ImageViewerOption.swift
│   │   │   │   │   │   ├── ImageViewerTransitionPresentationManager.swift
│   │   │   │   │   │   ├── ImageViewer_swift.h
│   │   │   │   │   │   ├── LISENCE
│   │   │   │   │   │   ├── SimpleImageDatasource.swift
│   │   │   │   │   │   ├── UIImageView_Extensions.swift
│   │   │   │   │   │   ├── UINavigationBar_Extensions.swift
│   │   │   │   │   │   └── UIView_Extensions.swift
│   │   │   │   │   └── SPIndicator/
│   │   │   │   │       └── LICENSE
│   │   │   │   └── Utils/
│   │   │   │       └── Utils.swift
│   │   │   └── package.json
│   │   ├── nativewind-env.d.ts
│   │   ├── package.json
│   │   ├── plugins/
│   │   │   ├── android-trust-user-certs.js
│   │   │   ├── network_security_config.xml
│   │   │   ├── with-android-jdk-21.js
│   │   │   ├── with-android-manifest-plugin.js
│   │   │   ├── with-follow-app-delegate.js
│   │   │   ├── with-follow-assets.js
│   │   │   └── with-gradle-jvm-heap-size-increase.js
│   │   ├── postcss.config.js
│   │   ├── scripts/
│   │   │   ├── apply-changelog.ts
│   │   │   ├── e2e-prod-ios-auth-bootstrap.ts
│   │   │   └── expo-update.ts
│   │   ├── shim-env.d.ts
│   │   ├── src/
│   │   │   ├── @types/
│   │   │   │   ├── constants.ts
│   │   │   │   ├── default-resource.ts
│   │   │   │   └── i18next.d.ts
│   │   │   ├── App.tsx
│   │   │   ├── atoms/
│   │   │   │   ├── app.ts
│   │   │   │   ├── hooks/
│   │   │   │   │   └── useDeviceType.ts
│   │   │   │   ├── server-configs.ts
│   │   │   │   └── settings/
│   │   │   │       ├── data.ts
│   │   │   │       ├── general.ts
│   │   │   │       ├── internal/
│   │   │   │       │   └── helper.ts
│   │   │   │       └── ui.ts
│   │   │   ├── components/
│   │   │   │   ├── common/
│   │   │   │   │   ├── AnimatedComponents.tsx
│   │   │   │   │   ├── Balance.tsx
│   │   │   │   │   ├── BlurEffect.tsx
│   │   │   │   │   ├── CopyButton.tsx
│   │   │   │   │   ├── ErrorBoundary.tsx
│   │   │   │   │   ├── FullWindowOverlay.ios.tsx
│   │   │   │   │   ├── FullWindowOverlay.tsx
│   │   │   │   │   ├── Link.tsx
│   │   │   │   │   ├── NoLoginInfo.tsx
│   │   │   │   │   ├── RefreshControl.tsx
│   │   │   │   │   ├── RotateableLoading.tsx
│   │   │   │   │   ├── SubmitButton.tsx
│   │   │   │   │   ├── SwipeableItem.tsx
│   │   │   │   │   └── ThemedBlurView.tsx
│   │   │   │   ├── errors/
│   │   │   │   │   ├── GlobalErrorScreen.tsx
│   │   │   │   │   ├── ListErrorView.tsx
│   │   │   │   │   └── ScreenErrorScreen.tsx
│   │   │   │   ├── icons/
│   │   │   │   │   ├── OouiUserAnonymous.tsx
│   │   │   │   │   └── PhUsersBold.tsx
│   │   │   │   ├── layouts/
│   │   │   │   │   ├── contexts/
│   │   │   │   │   │   └── ModalScrollViewContext.ts
│   │   │   │   │   ├── header/
│   │   │   │   │   │   ├── FakeNativeHeaderTitle.tsx
│   │   │   │   │   │   ├── HeaderElements.tsx
│   │   │   │   │   │   ├── NavigationHeader.tsx
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── tabbar/
│   │   │   │   │   │   ├── BottomTabHeightProvider.tsx
│   │   │   │   │   │   ├── BottomTabProvider.tsx
│   │   │   │   │   │   ├── BottomTabs.tsx
│   │   │   │   │   │   ├── ReactNativeTab.ios.tsx
│   │   │   │   │   │   ├── ReactNativeTab.tsx
│   │   │   │   │   │   ├── Tabbar.tsx
│   │   │   │   │   │   ├── contexts/
│   │   │   │   │   │   │   ├── BottomTabBarBackgroundContext.tsx
│   │   │   │   │   │   │   ├── BottomTabBarHeightContext.tsx
│   │   │   │   │   │   │   └── BottomTabBarVisibleContext.tsx
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── utils/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   └── views/
│   │   │   │   │       ├── NavigationHeaderContext.tsx
│   │   │   │   │       └── SafeNavigationScrollView.tsx
│   │   │   │   ├── native/
│   │   │   │   │   ├── PagerView/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── specs.ts
│   │   │   │   │   └── webview/
│   │   │   │   │       ├── DebugPanel.tsx
│   │   │   │   │       ├── EntryContentWebView.tsx
│   │   │   │   │       ├── atom.ts
│   │   │   │   │       ├── constants.ts
│   │   │   │   │       ├── hooks.ts
│   │   │   │   │       ├── index.android.ts
│   │   │   │   │       ├── index.ts
│   │   │   │   │       ├── injected-js.ts
│   │   │   │   │       ├── native-webview.android.tsx
│   │   │   │   │       ├── native-webview.tsx
│   │   │   │   │       └── webview-manager.ts
│   │   │   │   └── ui/
│   │   │   │       ├── accordion/
│   │   │   │       │   └── AccordionItem.tsx
│   │   │   │       ├── action-bar/
│   │   │   │       │   └── ActionBarItem.tsx
│   │   │   │       ├── avatar/
│   │   │   │       │   └── UserAvatar.tsx
│   │   │   │       ├── button/
│   │   │   │       │   └── UIBarButton.tsx
│   │   │   │       ├── carousel/
│   │   │   │       │   └── MediaCarousel.tsx
│   │   │   │       ├── context-menu/
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── datetime/
│   │   │   │       │   └── RelativeDateTime.tsx
│   │   │   │       ├── form/
│   │   │   │       │   ├── FormProvider.tsx
│   │   │   │       │   ├── Label.tsx
│   │   │   │       │   ├── PickerIos.tsx
│   │   │   │       │   ├── Select.android.tsx
│   │   │   │       │   ├── Select.tsx
│   │   │   │       │   ├── Slider.tsx
│   │   │   │       │   ├── Switch.tsx
│   │   │   │       │   └── TextField.tsx
│   │   │   │       ├── grid/
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── grouped/
│   │   │   │       │   ├── GroupedInsetListCardItemStyle.tsx
│   │   │   │       │   ├── GroupedList.tsx
│   │   │   │       │   └── constants.ts
│   │   │   │       ├── icon/
│   │   │   │       │   ├── fallback-icon.tsx
│   │   │   │       │   └── feed-icon.tsx
│   │   │   │       ├── image/
│   │   │   │       │   ├── Image.tsx
│   │   │   │       │   ├── ImageContextMenu.tsx
│   │   │   │       │   └── utils.ts
│   │   │   │       ├── lightbox/
│   │   │   │       │   ├── ImageViewing/
│   │   │   │       │   │   ├── @types/
│   │   │   │       │   │   │   └── index.ts
│   │   │   │       │   │   ├── components/
│   │   │   │       │   │   │   ├── ImageDefaultHeader.tsx
│   │   │   │       │   │   │   └── ImageItem/
│   │   │   │       │   │   │       ├── ImageItem.android.tsx
│   │   │   │       │   │   │       ├── ImageItem.ios.tsx
│   │   │   │       │   │   │       └── ImageItem.tsx
│   │   │   │       │   │   ├── index.tsx
│   │   │   │       │   │   └── transforms.ts
│   │   │   │       │   ├── Lightbox.tsx
│   │   │   │       │   └── lightboxState.tsx
│   │   │   │       ├── loading/
│   │   │   │       │   └── PlatformActivityIndicator.tsx
│   │   │   │       ├── logo/
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── modal/
│   │   │   │       │   ├── BottomModal.tsx
│   │   │   │       │   └── imperative-modal/
│   │   │   │       │       ├── index.tsx
│   │   │   │       │       ├── modal.tsx
│   │   │   │       │       └── templates.tsx
│   │   │   │       ├── overlay/
│   │   │   │       │   └── Overlay.tsx
│   │   │   │       ├── pressable/
│   │   │   │       │   ├── IosItemPressable.ios.tsx
│   │   │   │       │   ├── IosItemPressable.tsx
│   │   │   │       │   ├── ItemPressable.ios.tsx
│   │   │   │       │   ├── ItemPressable.tsx
│   │   │   │       │   ├── NativePressable.ios.tsx
│   │   │   │       │   ├── NativePressable.tsx
│   │   │   │       │   ├── NativePressable.types.tsx
│   │   │   │       │   └── enum.ts
│   │   │   │       ├── qrcode/
│   │   │   │       │   ├── LICENSE
│   │   │   │       │   ├── QRCode.tsx
│   │   │   │       │   ├── SVGPieces.tsx
│   │   │   │       │   ├── SVGRadialGradient.tsx
│   │   │   │       │   ├── adapter.ts
│   │   │   │       │   ├── constants.ts
│   │   │   │       │   ├── helper.ts
│   │   │   │       │   ├── types.ts
│   │   │   │       │   └── useQRCodeData.ts
│   │   │   │       ├── slider/
│   │   │   │       │   ├── Slider.tsx
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── switch/
│   │   │   │       │   └── Switch.tsx
│   │   │   │       ├── tabview/
│   │   │   │       │   ├── TabBar.tsx
│   │   │   │       │   ├── TabView.tsx
│   │   │   │       │   └── types.ts
│   │   │   │       ├── toast/
│   │   │   │       │   ├── CenteredToast.tsx
│   │   │   │       │   ├── ToastContainer.tsx
│   │   │   │       │   ├── constants.ts
│   │   │   │       │   ├── ctx.tsx
│   │   │   │       │   ├── manager.tsx
│   │   │   │       │   └── types.ts
│   │   │   │       ├── typography/
│   │   │   │       │   ├── HtmlWeb.tsx
│   │   │   │       │   ├── MarkdownNative.tsx
│   │   │   │       │   ├── MonoText.tsx
│   │   │   │       │   └── Text.tsx
│   │   │   │       └── video/
│   │   │   │           ├── PlayerAction.tsx
│   │   │   │           └── VideoPlayer.tsx
│   │   │   ├── constants/
│   │   │   │   ├── native-images.ts
│   │   │   │   ├── spring.ts
│   │   │   │   ├── ui.ts
│   │   │   │   └── views.tsx
│   │   │   ├── database/
│   │   │   │   └── index.ts
│   │   │   ├── global.css
│   │   │   ├── hooks/
│   │   │   │   ├── useBackHandler.ts
│   │   │   │   ├── useDefaultHeaderHeight.ts
│   │   │   │   ├── useIntentHandler.ts
│   │   │   │   ├── useLoadingCallback.tsx
│   │   │   │   ├── useMessaging.ts
│   │   │   │   ├── useOnboarding.ts
│   │   │   │   ├── useUnreadCountBadge.ts
│   │   │   │   └── useWebViewNavigation.tsx
│   │   │   ├── icons/
│   │   │   │   ├── AZ_sort_ascending_letters_cute_re.tsx
│   │   │   │   ├── AZ_sort_descending_letters_cute_re.tsx
│   │   │   │   ├── VIP_2_cute_fi.tsx
│   │   │   │   ├── VIP_2_cute_re.tsx
│   │   │   │   ├── add_cute_fi.tsx
│   │   │   │   ├── add_cute_re.tsx
│   │   │   │   ├── ai_cute_fi.tsx
│   │   │   │   ├── ai_cute_re.tsx
│   │   │   │   ├── alert_cute_fi.tsx
│   │   │   │   ├── align_justify_cute_re.tsx
│   │   │   │   ├── align_left_cute_re.tsx
│   │   │   │   ├── announcement_cute_fi.tsx
│   │   │   │   ├── apple_cute_fi.tsx
│   │   │   │   ├── arrow_left_cute_re.tsx
│   │   │   │   ├── arrow_right_circle_cute_fi.tsx
│   │   │   │   ├── arrow_right_up_cute_re.tsx
│   │   │   │   ├── arrow_up_circle_cute_fi.tsx
│   │   │   │   ├── at_cute_re.tsx
│   │   │   │   ├── attachment_cute_re.tsx
│   │   │   │   ├── back_2_cute_re.tsx
│   │   │   │   ├── black_board_2_cute_fi.tsx
│   │   │   │   ├── black_board_2_cute_re.tsx
│   │   │   │   ├── book_6_cute_re.tsx
│   │   │   │   ├── bookmark_cute_re.tsx
│   │   │   │   ├── bubble_cute_fi.tsx
│   │   │   │   ├── bug_cute_re.tsx
│   │   │   │   ├── calendar_time_add_cute_re.tsx
│   │   │   │   ├── celebrate_cute_re.tsx
│   │   │   │   ├── certificate_cute_fi.tsx
│   │   │   │   ├── certificate_cute_re.tsx
│   │   │   │   ├── check_circle_cute_re.tsx
│   │   │   │   ├── check_circle_filled.tsx
│   │   │   │   ├── check_cute_re.tsx
│   │   │   │   ├── check_filled.tsx
│   │   │   │   ├── check_line.tsx
│   │   │   │   ├── classify_2_cute_re.tsx
│   │   │   │   ├── close_circle_fill.tsx
│   │   │   │   ├── close_cute_re.tsx
│   │   │   │   ├── comment_2_cute_re.tsx
│   │   │   │   ├── comment_cute_fi.tsx
│   │   │   │   ├── comment_cute_li.tsx
│   │   │   │   ├── comment_cute_re.tsx
│   │   │   │   ├── compass_3_cute_re.tsx
│   │   │   │   ├── compass_cute_fi.tsx
│   │   │   │   ├── copy_2_cute_re.tsx
│   │   │   │   ├── copy_cute_re.tsx
│   │   │   │   ├── cursor_3_cute_re.tsx
│   │   │   │   ├── danmaku_cute_fi.tsx
│   │   │   │   ├── database.tsx
│   │   │   │   ├── delete_2_cute_re.tsx
│   │   │   │   ├── department_cute_re.tsx
│   │   │   │   ├── discord_cute_fi.tsx
│   │   │   │   ├── docment_cute_fi.tsx
│   │   │   │   ├── docment_cute_re.tsx
│   │   │   │   ├── documents_cute_re.tsx
│   │   │   │   ├── download_2_cute_fi.tsx
│   │   │   │   ├── download_2_cute_re.tsx
│   │   │   │   ├── edit_cute_re.tsx
│   │   │   │   ├── emoji_2_cute_re.tsx
│   │   │   │   ├── exit_cute_fi.tsx
│   │   │   │   ├── exit_cute_re.tsx
│   │   │   │   ├── external_link_cute_re.tsx
│   │   │   │   ├── eye_2_cute_re.tsx
│   │   │   │   ├── eye_close_cute_re.tsx
│   │   │   │   ├── facebook_cute_fi.tsx
│   │   │   │   ├── facebook_cute_re.tsx
│   │   │   │   ├── fast_forward_cute_re.tsx
│   │   │   │   ├── file_import_cute_re.tsx
│   │   │   │   ├── file_upload_cute_re.tsx
│   │   │   │   ├── filter_cute_re.tsx
│   │   │   │   ├── finger_press_cute_re.tsx
│   │   │   │   ├── fire_cute_fi.tsx
│   │   │   │   ├── fire_cute_re.tsx
│   │   │   │   ├── flag_1_cute_fi.tsx
│   │   │   │   ├── folder_open_cute_re.tsx
│   │   │   │   ├── forward_2_cute_re.tsx
│   │   │   │   ├── fullscreen_2_cute_re.tsx
│   │   │   │   ├── fullscreen_cute_re.tsx
│   │   │   │   ├── fullscreen_exit_cute_re.tsx
│   │   │   │   ├── ghost_cute_re.tsx
│   │   │   │   ├── gift_cute_re.tsx
│   │   │   │   ├── github_2_cute_fi.tsx
│   │   │   │   ├── github_cute_fi.tsx
│   │   │   │   ├── google_cute_fi.tsx
│   │   │   │   ├── grid_2_cute_re.tsx
│   │   │   │   ├── grid_cute_re.tsx
│   │   │   │   ├── hammer_cute_re.tsx
│   │   │   │   ├── heart_cute_fi.tsx
│   │   │   │   ├── history_cute_re.tsx
│   │   │   │   ├── home_5_cute_fi.tsx
│   │   │   │   ├── home_5_cute_re.tsx
│   │   │   │   ├── hotkey_cute_re.tsx
│   │   │   │   ├── inbox_cute_fi.tsx
│   │   │   │   ├── inbox_cute_re.tsx
│   │   │   │   ├── info_circle_fill.tsx
│   │   │   │   ├── information_cute_re.tsx
│   │   │   │   ├── instagram_cute_fi.tsx
│   │   │   │   ├── key_2_cute_re.tsx
│   │   │   │   ├── layout_4_cute_re.tsx
│   │   │   │   ├── layout_leftbar_close_cute_re.tsx
│   │   │   │   ├── layout_leftbar_open_cute_re.tsx
│   │   │   │   ├── left_cute_fi.tsx
│   │   │   │   ├── left_small_sharp.tsx
│   │   │   │   ├── line_cute_re.tsx
│   │   │   │   ├── link_cute_re.tsx
│   │   │   │   ├── list_check_2_cute_re.tsx
│   │   │   │   ├── list_check_3_cute_re.tsx
│   │   │   │   ├── list_check_cute_re.tsx
│   │   │   │   ├── list_collapse_cute_fi.tsx
│   │   │   │   ├── list_collapse_cute_re.tsx
│   │   │   │   ├── list_expansion_cute_fi.tsx
│   │   │   │   ├── list_expansion_cute_re.tsx
│   │   │   │   ├── loading_3_cute_li.tsx
│   │   │   │   ├── loading_3_cute_re.tsx
│   │   │   │   ├── love_cute_fi.tsx
│   │   │   │   ├── love_cute_re.tsx
│   │   │   │   ├── magic_2_cute_fi.tsx
│   │   │   │   ├── magic_2_cute_re.tsx
│   │   │   │   ├── mail_cute_re.tsx
│   │   │   │   ├── mic_cute_fi.tsx
│   │   │   │   ├── mic_cute_re.tsx
│   │   │   │   ├── mind_map_cute_re.tsx
│   │   │   │   ├── mingcute_down_line.tsx
│   │   │   │   ├── mingcute_left_line.tsx
│   │   │   │   ├── mingcute_right_line.tsx
│   │   │   │   ├── more_1_cute_re.tsx
│   │   │   │   ├── music_2_cute_fi.tsx
│   │   │   │   ├── notification_cute_re.tsx
│   │   │   │   ├── numbers_09_sort_ascending_cute_re.tsx
│   │   │   │   ├── numbers_09_sort_descending_cute_re.tsx
│   │   │   │   ├── numbers_90_sort_ascending_cute_re.tsx
│   │   │   │   ├── numbers_90_sort_descending_cute_re.tsx
│   │   │   │   ├── palette_cute_fi.tsx
│   │   │   │   ├── palette_cute_re.tsx
│   │   │   │   ├── paper_cute_fi.tsx
│   │   │   │   ├── paste_cute_re.tsx
│   │   │   │   ├── pause_cute_fi.tsx
│   │   │   │   ├── pause_cute_re.tsx
│   │   │   │   ├── pdf_cute_re.tsx
│   │   │   │   ├── photo_album_cute_fi.tsx
│   │   │   │   ├── photo_album_cute_re.tsx
│   │   │   │   ├── pic_cute_fi.tsx
│   │   │   │   ├── pic_cute_re.tsx
│   │   │   │   ├── play_cute_fi.tsx
│   │   │   │   ├── play_cute_re.tsx
│   │   │   │   ├── plugin_2_cute_re.tsx
│   │   │   │   ├── polygon_cute_re.tsx
│   │   │   │   ├── power.tsx
│   │   │   │   ├── power_mono.tsx
│   │   │   │   ├── power_outline.tsx
│   │   │   │   ├── question_cute_re.tsx
│   │   │   │   ├── quill_pen_cute_re.tsx
│   │   │   │   ├── rada_cute_fi.tsx
│   │   │   │   ├── rada_cute_re.tsx
│   │   │   │   ├── refresh_2_cute_re.tsx
│   │   │   │   ├── rewind_backward_15_cute_re.tsx
│   │   │   │   ├── rewind_forward_30_cute_re.tsx
│   │   │   │   ├── right_cute_fi.tsx
│   │   │   │   ├── right_cute_li.tsx
│   │   │   │   ├── right_cute_re.tsx
│   │   │   │   ├── right_small_sharp.tsx
│   │   │   │   ├── rocket_cute_fi.tsx
│   │   │   │   ├── rocket_cute_re.tsx
│   │   │   │   ├── round_cute_fi.tsx
│   │   │   │   ├── round_cute_re.tsx
│   │   │   │   ├── rss_2_cute_fi.tsx
│   │   │   │   ├── rss_cute_fi.tsx
│   │   │   │   ├── sad_cute_re.tsx
│   │   │   │   ├── safe_alert_cute_re.tsx
│   │   │   │   ├── safe_lock_filled.tsx
│   │   │   │   ├── safety_certificate_cute_re.tsx
│   │   │   │   ├── save_cute_re.tsx
│   │   │   │   ├── search_2_cute_re.tsx
│   │   │   │   ├── search_3_cute_fi.tsx
│   │   │   │   ├── search_3_cute_re.tsx
│   │   │   │   ├── search_cute_re.tsx
│   │   │   │   ├── send_plane_cute_fi.tsx
│   │   │   │   ├── send_plane_cute_re.tsx
│   │   │   │   ├── settings_1_cute_fi.tsx
│   │   │   │   ├── settings_1_cute_re.tsx
│   │   │   │   ├── settings_7_cute_re.tsx
│   │   │   │   ├── share_forward_cute_re.tsx
│   │   │   │   ├── shuffle_2_cute_re.tsx
│   │   │   │   ├── social_x_cute_li.tsx
│   │   │   │   ├── social_x_cute_re.tsx
│   │   │   │   ├── sort_ascending_cute_re.tsx
│   │   │   │   ├── sort_descending_cute_re.tsx
│   │   │   │   ├── star_cute_fi.tsx
│   │   │   │   ├── star_cute_re.tsx
│   │   │   │   ├── stop_circle_cute_fi.tsx
│   │   │   │   ├── telegram_cute_fi.tsx
│   │   │   │   ├── telegram_cute_re.tsx
│   │   │   │   ├── thought_cute_fi.tsx
│   │   │   │   ├── time_cute_re.tsx
│   │   │   │   ├── tool_cute_re.tsx
│   │   │   │   ├── train_cute_fi.tsx
│   │   │   │   ├── translate_2_ai_cute_re.tsx
│   │   │   │   ├── translate_2_cute_re.tsx
│   │   │   │   ├── trending_up_cute_re.tsx
│   │   │   │   ├── trophy_cute_fi.tsx
│   │   │   │   ├── trophy_cute_re.tsx
│   │   │   │   ├── twitter_cute_fi.tsx
│   │   │   │   ├── up_cute_re.tsx
│   │   │   │   ├── user_3_cute_fi.tsx
│   │   │   │   ├── user_3_cute_re.tsx
│   │   │   │   ├── user_4_cute_fi.tsx
│   │   │   │   ├── user_4_cute_re.tsx
│   │   │   │   ├── user_add_2_cute_fi.tsx
│   │   │   │   ├── user_heart_cute_fi.tsx
│   │   │   │   ├── user_heart_cute_re.tsx
│   │   │   │   ├── user_setting_cute_fi.tsx
│   │   │   │   ├── user_setting_cute_re.tsx
│   │   │   │   ├── video_cute_fi.tsx
│   │   │   │   ├── video_cute_re.tsx
│   │   │   │   ├── voice_cute_re.tsx
│   │   │   │   ├── volume_cute_re.tsx
│   │   │   │   ├── volume_mute_cute_re.tsx
│   │   │   │   ├── volume_off_cute_re.tsx
│   │   │   │   ├── wallet_2_cute_fi.tsx
│   │   │   │   ├── warning_cute_re.tsx
│   │   │   │   ├── web_cute_re.tsx
│   │   │   │   ├── webhook_cute_re.tsx
│   │   │   │   ├── weibo_cute_re.tsx
│   │   │   │   ├── wifi_off_cute_re.tsx
│   │   │   │   ├── world_2_cute_fi.tsx
│   │   │   │   ├── world_2_cute_re.tsx
│   │   │   │   └── youtube_cute_fi.tsx
│   │   │   ├── initialize/
│   │   │   │   ├── analytics.ts
│   │   │   │   ├── app-check.ts
│   │   │   │   ├── background.ts
│   │   │   │   ├── dayjs.ts
│   │   │   │   ├── device.ts
│   │   │   │   ├── hydrate.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── migration.ts
│   │   │   │   └── player.ts
│   │   │   ├── interfaces/
│   │   │   │   └── settings/
│   │   │   │       └── data.ts
│   │   │   ├── lib/
│   │   │   │   ├── api-client.ts
│   │   │   │   ├── auth-cookie-migration.ts
│   │   │   │   ├── auth.ts
│   │   │   │   ├── client-session.ts
│   │   │   │   ├── dialog-state.ts
│   │   │   │   ├── dialog.tsx
│   │   │   │   ├── e2e-config.ts
│   │   │   │   ├── error-parser.ts
│   │   │   │   ├── event-bus.ts
│   │   │   │   ├── ga4.ts
│   │   │   │   ├── i18n.ts
│   │   │   │   ├── image.ts
│   │   │   │   ├── img-proxy.ts
│   │   │   │   ├── jotai.ts
│   │   │   │   ├── kv.ts
│   │   │   │   ├── loading.tsx
│   │   │   │   ├── markdown.tsx
│   │   │   │   ├── native/
│   │   │   │   │   ├── index.ios.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── picker.ts
│   │   │   │   │   └── user-agent.ts
│   │   │   │   ├── navigation/
│   │   │   │   │   ├── AttachNavigationScrollViewContext.tsx
│   │   │   │   │   ├── ChainNavigationContext.tsx
│   │   │   │   │   ├── GroupedNavigationRouteContext.ts
│   │   │   │   │   ├── Navigation.ts
│   │   │   │   │   ├── NavigationInstanceContext.ts
│   │   │   │   │   ├── NavigationLink.tsx
│   │   │   │   │   ├── ScreenItemContext.ts
│   │   │   │   │   ├── ScreenNameContext.tsx
│   │   │   │   │   ├── ScreenOptionsContext.ts
│   │   │   │   │   ├── StackNavigation.tsx
│   │   │   │   │   ├── StackScreenHeaderPortal.tsx
│   │   │   │   │   ├── WrappedScreenItem.tsx
│   │   │   │   │   ├── __internal/
│   │   │   │   │   │   └── hooks.ts
│   │   │   │   │   ├── biz/
│   │   │   │   │   │   └── Destination.ts
│   │   │   │   │   ├── bottom-tab/
│   │   │   │   │   │   ├── BottomTabContext.tsx
│   │   │   │   │   │   ├── TabBarPortal.tsx
│   │   │   │   │   │   ├── TabRoot.tsx
│   │   │   │   │   │   ├── TabScreen.tsx
│   │   │   │   │   │   ├── TabScreenContext.tsx
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── native.ios.tsx
│   │   │   │   │   │   ├── native.tsx
│   │   │   │   │   │   ├── shared.tsx
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── config.ts
│   │   │   │   │   ├── debug/
│   │   │   │   │   │   └── DebugButtonGroup.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   ├── readme.md
│   │   │   │   │   ├── sitemap/
│   │   │   │   │   │   └── registry.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── onboarding.ts
│   │   │   │   ├── parse-api-error.ts
│   │   │   │   ├── payment.ts
│   │   │   │   ├── permission.ts
│   │   │   │   ├── platform.ts
│   │   │   │   ├── player.ts
│   │   │   │   ├── proxy-env.ts
│   │   │   │   ├── query-client.ts
│   │   │   │   ├── responsive.ts
│   │   │   │   ├── secure-store.ts
│   │   │   │   ├── toast.tsx
│   │   │   │   ├── token.ts
│   │   │   │   ├── url-builder.ts
│   │   │   │   └── volume.ts
│   │   │   ├── main.tsx
│   │   │   ├── modules/
│   │   │   │   ├── ai/
│   │   │   │   │   └── summary.tsx
│   │   │   │   ├── context-menu/
│   │   │   │   │   ├── entry.tsx
│   │   │   │   │   ├── feeds.tsx
│   │   │   │   │   ├── inbox.tsx
│   │   │   │   │   ├── lists.tsx
│   │   │   │   │   └── video.tsx
│   │   │   │   ├── debug/
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── dialogs/
│   │   │   │   │   ├── ConfirmPasswordDialog.tsx
│   │   │   │   │   ├── ConfirmTOTPCodeDialog.tsx
│   │   │   │   │   ├── MarkAllAsReadDialog.tsx
│   │   │   │   │   └── UpgradeRequiredDialog.tsx
│   │   │   │   ├── discover/
│   │   │   │   │   ├── Category.tsx
│   │   │   │   │   ├── Content.tsx
│   │   │   │   │   ├── DiscoverContent.tsx
│   │   │   │   │   ├── FeedSummary.tsx
│   │   │   │   │   ├── RecommendationListItem.tsx
│   │   │   │   │   ├── Recommendations.tsx
│   │   │   │   │   ├── SearchContent.tsx
│   │   │   │   │   ├── SearchTabBar.tsx
│   │   │   │   │   ├── Trending.tsx
│   │   │   │   │   ├── api.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── ctx.tsx
│   │   │   │   │   ├── search-tabs/
│   │   │   │   │   │   ├── SearchFeed.tsx
│   │   │   │   │   │   ├── SearchFeedCard.tsx
│   │   │   │   │   │   ├── SearchList.tsx
│   │   │   │   │   │   ├── __base.tsx
│   │   │   │   │   │   └── hooks.tsx
│   │   │   │   │   └── search.tsx
│   │   │   │   ├── entry-content/
│   │   │   │   │   ├── EntryAISummary.tsx
│   │   │   │   │   ├── EntryContentHeaderRightActions.tsx
│   │   │   │   │   ├── EntryGridFooter.tsx
│   │   │   │   │   ├── EntryNavigationHeader.tsx
│   │   │   │   │   ├── EntryReadHistory.tsx
│   │   │   │   │   ├── EntryTitle.tsx
│   │   │   │   │   ├── ctx.ts
│   │   │   │   │   └── pull-up-navigation/
│   │   │   │   │       ├── PullUpIndicatorAndroid.tsx
│   │   │   │   │       ├── PullUpIndicatorIos.tsx
│   │   │   │   │       ├── types.ts
│   │   │   │   │       ├── use-pull-up-navigation.android.tsx
│   │   │   │   │       └── use-pull-up-navigation.tsx
│   │   │   │   ├── entry-list/
│   │   │   │   │   ├── EntryListContentArticle.tsx
│   │   │   │   │   ├── EntryListContentPicture.tsx
│   │   │   │   │   ├── EntryListContentSocial.tsx
│   │   │   │   │   ├── EntryListContentVideo.tsx
│   │   │   │   │   ├── EntryListContext.tsx
│   │   │   │   │   ├── EntryListEmpty.tsx
│   │   │   │   │   ├── EntryListFooter.tsx
│   │   │   │   │   ├── EntryListSelector.tsx
│   │   │   │   │   ├── ItemSeparator.tsx
│   │   │   │   │   ├── hooks.ts
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   ├── templates/
│   │   │   │   │   │   ├── EntryNormalItem.tsx
│   │   │   │   │   │   ├── EntryPictureItem.tsx
│   │   │   │   │   │   ├── EntrySocialItem.tsx
│   │   │   │   │   │   ├── EntryTranslation.tsx
│   │   │   │   │   │   └── EntryVideoItem.tsx
│   │   │   │   │   └── types.ts
│   │   │   │   ├── feed/
│   │   │   │   │   ├── FollowFeed.tsx
│   │   │   │   │   └── view-selector.tsx
│   │   │   │   ├── list/
│   │   │   │   │   └── FollowList.tsx
│   │   │   │   ├── login/
│   │   │   │   │   ├── email.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── social.tsx
│   │   │   │   ├── onboarding/
│   │   │   │   │   ├── feeds-english.json
│   │   │   │   │   ├── feeds.json
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   └── use-reading-behavior.ts
│   │   │   │   │   ├── preset.ts
│   │   │   │   │   ├── shared.tsx
│   │   │   │   │   ├── step-finished.tsx
│   │   │   │   │   ├── step-interests.tsx
│   │   │   │   │   ├── step-preferences.tsx
│   │   │   │   │   └── step-welcome.tsx
│   │   │   │   ├── player/
│   │   │   │   │   ├── GlassPlayerTabBar.tsx
│   │   │   │   │   ├── PlayerTabBar.tsx
│   │   │   │   │   ├── context.ts
│   │   │   │   │   ├── control.tsx
│   │   │   │   │   └── hooks.ts
│   │   │   │   ├── review-prompt/
│   │   │   │   │   ├── debug.ts
│   │   │   │   │   ├── provider.tsx
│   │   │   │   │   ├── use-review-prompt-state.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── rsshub/
│   │   │   │   │   └── preview-url.tsx
│   │   │   │   ├── screen/
│   │   │   │   │   ├── PagerList.ios.tsx
│   │   │   │   │   ├── PagerList.tsx
│   │   │   │   │   ├── PagerListContext.ts
│   │   │   │   │   ├── TimelineSelectorList.tsx
│   │   │   │   │   ├── TimelineSelectorProvider.tsx
│   │   │   │   │   ├── TimelineViewSelector.tsx
│   │   │   │   │   ├── TimelineViewSelectorContextMenu.tsx
│   │   │   │   │   ├── action.tsx
│   │   │   │   │   ├── atoms.ts
│   │   │   │   │   └── hooks/
│   │   │   │   │       └── useHeaderHeight.tsx
│   │   │   │   ├── settings/
│   │   │   │   │   ├── SettingsList.tsx
│   │   │   │   │   ├── UserHeaderBanner.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   └── OTPWindow.tsx
│   │   │   │   │   ├── hooks/
│   │   │   │   │   │   ├── useShareSubscription.tsx
│   │   │   │   │   │   └── useTOTPModalWrapper.tsx
│   │   │   │   │   ├── routes/
│   │   │   │   │   │   ├── 2FASetting.tsx
│   │   │   │   │   │   ├── About.tsx
│   │   │   │   │   │   ├── Account.tsx
│   │   │   │   │   │   ├── Achievement.tsx
│   │   │   │   │   │   ├── Actions.tsx
│   │   │   │   │   │   ├── Appearance.tsx
│   │   │   │   │   │   ├── Data.tsx
│   │   │   │   │   │   ├── EditCondition.tsx
│   │   │   │   │   │   ├── EditProfile.tsx
│   │   │   │   │   │   ├── EditRewriteRules.tsx
│   │   │   │   │   │   ├── EditRule.tsx
│   │   │   │   │   │   ├── EditWebhooks.tsx
│   │   │   │   │   │   ├── Feeds.tsx
│   │   │   │   │   │   ├── General.tsx
│   │   │   │   │   │   ├── Lists.tsx
│   │   │   │   │   │   ├── ManageList.tsx
│   │   │   │   │   │   ├── Notifications.tsx
│   │   │   │   │   │   ├── Plan.tsx
│   │   │   │   │   │   ├── Privacy.tsx
│   │   │   │   │   │   ├── ResetPassword.tsx
│   │   │   │   │   │   └── navigateToPlanScreen.ts
│   │   │   │   │   ├── sync-queue.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   └── subscription/
│   │   │   │       ├── CategoryGrouped.tsx
│   │   │   │       ├── ItemSeparator.tsx
│   │   │   │       ├── SubscriptionLists.tsx
│   │   │   │       ├── UnGroupedList.tsx
│   │   │   │       ├── atoms.ts
│   │   │   │       ├── constants.ts
│   │   │   │       ├── ctx.ts
│   │   │   │       ├── header-actions.tsx
│   │   │   │       └── items/
│   │   │   │           ├── InboxItem.tsx
│   │   │   │           ├── ListSubscriptionItem.tsx
│   │   │   │           ├── SubscriptionItem.tsx
│   │   │   │           ├── UnreadCount.tsx
│   │   │   │           └── types.tsx
│   │   │   ├── polyfill/
│   │   │   │   ├── index.ts
│   │   │   │   └── promise-with-resolvers.ts
│   │   │   ├── providers/
│   │   │   │   ├── AppleIAPProvider.tsx
│   │   │   │   ├── FontScalingProvider.tsx
│   │   │   │   ├── ServerConfigsLoader.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── migration.tsx
│   │   │   ├── screens/
│   │   │   │   ├── (headless)/
│   │   │   │   │   ├── (debug)/
│   │   │   │   │   │   ├── markdown.tsx
│   │   │   │   │   │   └── text.tsx
│   │   │   │   │   └── DebugScreen.tsx
│   │   │   │   ├── (modal)/
│   │   │   │   │   ├── DiscoverSettingsScreen.tsx
│   │   │   │   │   ├── EditEmailScreen.tsx
│   │   │   │   │   ├── FollowScreen.tsx
│   │   │   │   │   ├── ForgetPasswordScreen.tsx
│   │   │   │   │   ├── ListScreen.tsx
│   │   │   │   │   ├── LoginScreen.tsx
│   │   │   │   │   ├── ProfileScreen.tsx
│   │   │   │   │   ├── RsshubFormScreen.tsx
│   │   │   │   │   ├── TwoFactorAuthScreen.tsx
│   │   │   │   │   └── onboarding/
│   │   │   │   │       ├── EditProfileScreen.tsx
│   │   │   │   │       └── SelectReadingModeScreen.tsx
│   │   │   │   ├── (stack)/
│   │   │   │   │   ├── (tabs)/
│   │   │   │   │   │   ├── discover.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   ├── settings.tsx
│   │   │   │   │   │   └── subscriptions.tsx
│   │   │   │   │   ├── entries/
│   │   │   │   │   │   └── [entryId]/
│   │   │   │   │   │       └── EntryDetailScreen.tsx
│   │   │   │   │   ├── feeds/
│   │   │   │   │   │   └── [feedId]/
│   │   │   │   │   │       └── FeedScreen.tsx
│   │   │   │   │   └── recommendation/
│   │   │   │   │       └── RecommendationCategoryScreen.tsx
│   │   │   │   ├── +native-intent.tsx
│   │   │   │   ├── OnboardingScreen.tsx
│   │   │   │   └── PlayerScreen.tsx
│   │   │   ├── sitemap.tsx
│   │   │   ├── spec/
│   │   │   │   └── typography.ts
│   │   │   ├── store/
│   │   │   │   └── image/
│   │   │   │       ├── hooks.ts
│   │   │   │       └── store.ts
│   │   │   └── theme/
│   │   │       ├── colors.ts
│   │   │       ├── utils.ts
│   │   │       └── web.ts
│   │   ├── tailwind.config.ts
│   │   ├── tailwind.dom.config.ts
│   │   ├── tsconfig.json
│   │   └── web-app/
│   │       ├── html-renderer/
│   │       │   ├── global.d.ts
│   │       │   ├── index.html
│   │       │   ├── package.json
│   │       │   ├── postcss.config.cjs
│   │       │   ├── src/
│   │       │   │   ├── App.tsx
│   │       │   │   ├── HTML.tsx
│   │       │   │   ├── atoms/
│   │       │   │   │   └── index.ts
│   │       │   │   ├── common/
│   │       │   │   │   ├── ProviderComposer.tsx
│   │       │   │   │   └── WrappedElementProvider.tsx
│   │       │   │   ├── components/
│   │       │   │   │   ├── __internal/
│   │       │   │   │   │   ├── calculateDimensions.tsx
│   │       │   │   │   │   └── ctx.ts
│   │       │   │   │   ├── heading.tsx
│   │       │   │   │   ├── image.tsx
│   │       │   │   │   ├── index.ts
│   │       │   │   │   ├── link.tsx
│   │       │   │   │   ├── math.tsx
│   │       │   │   │   ├── p.tsx
│   │       │   │   │   └── shiki/
│   │       │   │   │       ├── Shiki.tsx
│   │       │   │   │       ├── hooks.ts
│   │       │   │   │       ├── index.ts
│   │       │   │   │       ├── shared.ts
│   │       │   │   │       └── shiki.module.css
│   │       │   │   ├── index.css
│   │       │   │   ├── index.ts
│   │       │   │   ├── managers/
│   │       │   │   │   └── webview-bridge.ts
│   │       │   │   ├── parser.tsx
│   │       │   │   ├── test.txt
│   │       │   │   └── utils.ts
│   │       │   ├── tailwind.config.ts
│   │       │   ├── tsconfig.json
│   │       │   ├── types/
│   │       │   │   └── index.ts
│   │       │   └── vite.config.mts
│   │       └── package.json
│   └── ssr/
│       ├── .env.example
│       ├── api/
│       │   └── index.ts
│       ├── client/
│       │   ├── @types/
│       │   │   ├── constants.ts
│       │   │   ├── default-resource.ts
│       │   │   └── i18next.d.ts
│       │   ├── App.tsx
│       │   ├── atoms/
│       │   │   ├── server-configs.ts
│       │   │   ├── settings/
│       │   │   │   ├── general.ts
│       │   │   │   └── helper.ts
│       │   │   └── user.ts
│       │   ├── components/
│       │   │   ├── common/
│       │   │   │   ├── 404.tsx
│       │   │   │   └── PoweredByFooter.tsx
│       │   │   ├── items/
│       │   │   │   ├── grid.tsx
│       │   │   │   ├── index.tsx
│       │   │   │   ├── normal.tsx
│       │   │   │   └── picture.tsx
│       │   │   ├── layout/
│       │   │   │   └── header/
│       │   │   │       └── index.tsx
│       │   │   └── ui/
│       │   │       ├── feed-certification.tsx
│       │   │       ├── feed-icon.tsx
│       │   │       ├── image.tsx
│       │   │       └── user-avatar.tsx
│       │   ├── configs.ts
│       │   ├── global.d.ts
│       │   ├── hooks/
│       │   │   └── useRecaptchaToken.ts
│       │   ├── i18n.ts
│       │   ├── index.tsx
│       │   ├── initialize/
│       │   │   ├── helper.ts
│       │   │   ├── index.ts
│       │   │   └── sentry.ts
│       │   ├── lib/
│       │   │   ├── api-fetch.ts
│       │   │   ├── auth.ts
│       │   │   ├── helper.ts
│       │   │   ├── query-client.ts
│       │   │   ├── store.ts
│       │   │   └── url-builder.ts
│       │   ├── modules/
│       │   │   └── login/
│       │   │       └── index.tsx
│       │   ├── pages/
│       │   │   ├── (login)/
│       │   │   │   ├── forget-password.tsx
│       │   │   │   ├── layout.tsx
│       │   │   │   ├── login/
│       │   │   │   │   ├── index.tsx
│       │   │   │   │   └── metadata.ts
│       │   │   │   ├── register.tsx
│       │   │   │   └── reset-password.tsx
│       │   │   ├── (main)/
│       │   │   │   ├── index.tsx
│       │   │   │   ├── layout.tsx
│       │   │   │   └── share/
│       │   │   │       ├── feeds/
│       │   │   │       │   └── [id]/
│       │   │   │       │       ├── index.tsx
│       │   │   │       │       └── metadata.ts
│       │   │   │       ├── lists/
│       │   │   │       │   └── [id]/
│       │   │   │       │       ├── index.tsx
│       │   │   │       │       └── metadata.ts
│       │   │   │       └── users/
│       │   │   │           └── [id]/
│       │   │   │               ├── index.tsx
│       │   │   │               └── metadata.ts
│       │   │   └── layout.tsx
│       │   ├── providers/
│       │   │   ├── root-providers.tsx
│       │   │   ├── server-configs-provider.tsx
│       │   │   └── user-provider.tsx
│       │   ├── query/
│       │   │   ├── auth.ts
│       │   │   ├── entries.ts
│       │   │   ├── feed.ts
│       │   │   ├── list.ts
│       │   │   └── users.ts
│       │   ├── router.tsx
│       │   └── styles/
│       │       └── index.css
│       ├── global.ts
│       ├── helper/
│       │   └── meta-map.ts
│       ├── index.html
│       ├── index.ts
│       ├── note.md
│       ├── package.json
│       ├── postcss.config.cjs
│       ├── public/
│       │   └── manifest.json
│       ├── scripts/
│       │   ├── check-fonts.ts
│       │   ├── cleanup-vercel-build.ts
│       │   ├── generate-font-data.ts
│       │   ├── patch-worker-build.ts
│       │   ├── prepare-vercel-build.ts
│       │   ├── skip-ssr-app-vercel-build.sh
│       │   └── upload-fonts-to-r2.ts
│       ├── src/
│       │   ├── global.d.ts
│       │   ├── lib/
│       │   │   ├── api-client.ts
│       │   │   ├── dev-vite.ts
│       │   │   ├── load-env.ts
│       │   │   ├── load-env.worker.ts
│       │   │   ├── not-found.ts
│       │   │   ├── og/
│       │   │   │   ├── fonts.ts
│       │   │   │   ├── fonts.worker.ts
│       │   │   │   ├── render-to-image.ts
│       │   │   │   ├── render-to-image.worker.ts
│       │   │   │   └── resvg-wasm-shim.ts
│       │   │   ├── seo.ts
│       │   │   └── worker-request-context.ts
│       │   ├── meta-handler.map.ts
│       │   ├── meta-handler.ts
│       │   └── router/
│       │       ├── global.ts
│       │       └── og/
│       │           ├── __base.tsx
│       │           ├── feed.tsx
│       │           ├── index.ts
│       │           ├── list.tsx
│       │           └── user.tsx
│       ├── tailwind.config.ts
│       ├── tsconfig.json
│       ├── tsdown.config.ts
│       ├── tsdown.worker.config.ts
│       ├── vercel.json
│       ├── vite.config.mts
│       ├── worker-app.ts
│       ├── worker-entry.ts
│       └── wrangler.jsonc
├── changelogithub.config.ts
├── conductor.json
├── eslint.config.mjs
├── locales/
│   ├── ai/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── app/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── common/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── errors/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── external/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── lang/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── mobile/
│   │   └── default/
│   │       ├── en.json
│   │       ├── fr-FR.json
│   │       ├── ja.json
│   │       ├── zh-CN.json
│   │       └── zh-TW.json
│   ├── native/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   ├── settings/
│   │   ├── en.json
│   │   ├── fr-FR.json
│   │   ├── ja.json
│   │   ├── zh-CN.json
│   │   └── zh-TW.json
│   └── shortcuts/
│       ├── en.json
│       ├── fr-FR.json
│       ├── ja.json
│       ├── zh-CN.json
│       └── zh-TW.json
├── package.json
├── packages/
│   ├── configs/
│   │   ├── package.json
│   │   ├── tailwindcss/
│   │   │   ├── ratio-mixing-plugin.js
│   │   │   ├── tailwind-extend.css
│   │   │   ├── tw-css-plugin.js
│   │   │   └── web.ts
│   │   └── tsconfig.extend.json
│   ├── internal/
│   │   ├── AGENTS.md
│   │   ├── atoms/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── atoms/
│   │   │   │   │   └── user.ts
│   │   │   │   └── helper/
│   │   │   │       └── setting.ts
│   │   │   └── tsconfig.json
│   │   ├── components/
│   │   │   ├── assets/
│   │   │   │   ├── colors-media.css
│   │   │   │   ├── colors.css
│   │   │   │   ├── font.css
│   │   │   │   ├── index.css
│   │   │   │   └── tailwind.css
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── atoms/
│   │   │   │   │   ├── mouse.ts
│   │   │   │   │   ├── route.ts
│   │   │   │   │   └── viewport.ts
│   │   │   │   ├── common/
│   │   │   │   │   ├── Focusable/
│   │   │   │   │   │   ├── Focusable.tsx
│   │   │   │   │   │   ├── GlobalFocusableProvider.tsx
│   │   │   │   │   │   ├── context.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── Fragment.ts
│   │   │   │   │   ├── MemoedDangerousHTMLStyle.tsx
│   │   │   │   │   ├── MotionProvider.tsx
│   │   │   │   │   └── ReparentPortal.tsx
│   │   │   │   ├── constants/
│   │   │   │   │   └── spring.ts
│   │   │   │   ├── hooks/
│   │   │   │   │   ├── useMedia.ts
│   │   │   │   │   ├── useMobile.ts
│   │   │   │   │   ├── useMouse.ts
│   │   │   │   │   └── useViewport.ts
│   │   │   │   ├── icons/
│   │   │   │   │   ├── Database.tsx
│   │   │   │   │   ├── Meditation.tsx
│   │   │   │   │   ├── MynauiInboxArchive.tsx
│   │   │   │   │   ├── OouiUserAnonymous.tsx
│   │   │   │   │   ├── PhCloudCheck.tsx
│   │   │   │   │   ├── PhCloudWarning.tsx
│   │   │   │   │   ├── PhCloudX.tsx
│   │   │   │   │   ├── Progress.tsx
│   │   │   │   │   ├── empty.tsx
│   │   │   │   │   ├── follow.tsx
│   │   │   │   │   ├── folo.tsx
│   │   │   │   │   ├── infinify.tsx
│   │   │   │   │   ├── logo.tsx
│   │   │   │   │   ├── nft.tsx
│   │   │   │   │   ├── resize.tsx
│   │   │   │   │   ├── user.tsx
│   │   │   │   │   └── users.tsx
│   │   │   │   ├── providers/
│   │   │   │   │   ├── event-provider.tsx
│   │   │   │   │   └── stable-router-provider.tsx
│   │   │   │   ├── ui/
│   │   │   │   │   ├── auto-resize-height/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── avatar/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── avatar-group/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── button/
│   │   │   │   │   │   ├── action-button.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   ├── interface.ts
│   │   │   │   │   │   └── variants.tsx
│   │   │   │   │   ├── card/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── checkbox/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── collapse/
│   │   │   │   │   │   ├── Collapse.tsx
│   │   │   │   │   │   ├── CollapseCss.tsx
│   │   │   │   │   │   ├── hooks.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── context-menu/
│   │   │   │   │   │   ├── context-menu.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── datetime/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── utils.tsx
│   │   │   │   │   ├── divider/
│   │   │   │   │   │   ├── Divider.tsx
│   │   │   │   │   │   ├── PanelSplitter.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── drop-zone/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── effect/
│   │   │   │   │   │   └── MagneticHoverEffect.tsx
│   │   │   │   │   ├── form/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── hover-card/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── icon/
│   │   │   │   │   │   └── SiteIcon.tsx
│   │   │   │   │   ├── input/
│   │   │   │   │   │   ├── DateTimePicker.tsx
│   │   │   │   │   │   ├── Input.tsx
│   │   │   │   │   │   ├── InputV2.tsx
│   │   │   │   │   │   ├── OTP.tsx
│   │   │   │   │   │   ├── TextArea.tsx
│   │   │   │   │   │   ├── TextAreaWrapper.tsx
│   │   │   │   │   │   ├── TimeSelect.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── json-highlighter/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── katex/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── lazy.tsx
│   │   │   │   │   ├── kbd/
│   │   │   │   │   │   └── Kbd.tsx
│   │   │   │   │   ├── key-value-editor/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── label/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── lexical-rich-editor/
│   │   │   │   │   │   ├── LexicalRichEditor.tsx
│   │   │   │   │   │   ├── LexicalRichEditorTextArea.tsx
│   │   │   │   │   │   ├── editor.tsx
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── nodes.ts
│   │   │   │   │   │   ├── plugins/
│   │   │   │   │   │   │   ├── code-highlighting/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   ├── exit-code/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   │   ├── keyboard/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   ├── string-length-change/
│   │   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   │   └── triple-backtick-toggle/
│   │   │   │   │   │   │       └── index.tsx
│   │   │   │   │   │   ├── theme.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── link/
│   │   │   │   │   │   ├── LinkWithTooltip.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── loading/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── markdown/
│   │   │   │   │   │   └── html.tsx
│   │   │   │   │   ├── marquee/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── masonry/
│   │   │   │   │   │   ├── contexts.tsx
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── navigation-menu/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── style.ts
│   │   │   │   │   ├── platform-icon/
│   │   │   │   │   │   ├── collections/
│   │   │   │   │   │   │   ├── cubox.tsx
│   │   │   │   │   │   │   ├── eagle.tsx
│   │   │   │   │   │   │   ├── instapaper.tsx
│   │   │   │   │   │   │   ├── obsidian.tsx
│   │   │   │   │   │   │   ├── outline.tsx
│   │   │   │   │   │   │   ├── readeck.tsx
│   │   │   │   │   │   │   ├── readwise.tsx
│   │   │   │   │   │   │   ├── rss3.tsx
│   │   │   │   │   │   │   ├── rsshub.tsx
│   │   │   │   │   │   │   └── zotero.tsx
│   │   │   │   │   │   ├── icons.ts
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── utils.tsx
│   │   │   │   │   ├── popover/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── portal/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── provider.tsx
│   │   │   │   │   ├── progress/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── progressive-blur/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── radio-group/
│   │   │   │   │   │   ├── RadioCard.tsx
│   │   │   │   │   │   ├── RadioGroup.tsx
│   │   │   │   │   │   ├── context.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── motion.tsx
│   │   │   │   │   ├── scroll-area/
│   │   │   │   │   │   ├── ScrollArea.tsx
│   │   │   │   │   │   ├── ctx.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── index.module.css
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── segment/
│   │   │   │   │   │   ├── ctx.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── select/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── responsive.tsx
│   │   │   │   │   ├── sheet/
│   │   │   │   │   │   ├── Sheet.tsx
│   │   │   │   │   │   ├── context.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── shiny-text/
│   │   │   │   │   │   ├── ShinyText.tsx
│   │   │   │   │   │   └── index.module.css
│   │   │   │   │   ├── shrinking-focus-border/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── skeleton/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── slider/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── switch/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── table/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── variants.tsx
│   │   │   │   │   ├── tabs/
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── toast/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── styles.ts
│   │   │   │   │   ├── tooltip/
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── styles.ts
│   │   │   │   │   ├── typography/
│   │   │   │   │   │   ├── EllipsisWithTooltip.tsx
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── z-index/
│   │   │   │   │       ├── ctx.tsx
│   │   │   │   │       └── index.tsx
│   │   │   │   └── utils/
│   │   │   │       ├── dayjs.ts
│   │   │   │       ├── icon.ts
│   │   │   │       ├── parse-markdown.tsx
│   │   │   │       └── selector.tsx
│   │   │   └── tsconfig.json
│   │   ├── constants/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── app.ts
│   │   │   │   ├── auth-providers.ts
│   │   │   │   ├── enums.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── rsshub.ts
│   │   │   │   ├── social.ts
│   │   │   │   └── tabs.tsx
│   │   │   └── tsconfig.json
│   │   ├── database/
│   │   │   ├── drizzle.config.ts
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── DatabaseSource.js
│   │   │   │   ├── ResourceLock.ts
│   │   │   │   ├── constant.ts
│   │   │   │   ├── db.desktop.ts
│   │   │   │   ├── db.rn.ts
│   │   │   │   ├── db.ts
│   │   │   │   ├── drizzle/
│   │   │   │   │   ├── 0000_harsh_shiva.sql
│   │   │   │   │   ├── 0001_bored_hobgoblin.sql
│   │   │   │   │   ├── 0002_smart_power_man.sql
│   │   │   │   │   ├── 0003_known_roland_deschain.sql
│   │   │   │   │   ├── 0004_majestic_thunderbolt_ross.sql
│   │   │   │   │   ├── 0005_tense_sleepwalker.sql
│   │   │   │   │   ├── 0006_exotic_kid_colt.sql
│   │   │   │   │   ├── 0007_curvy_tarantula.sql
│   │   │   │   │   ├── 0008_last_the_santerians.sql
│   │   │   │   │   ├── 0009_lucky_power_man.sql
│   │   │   │   │   ├── 0010_legal_ben_grimm.sql
│   │   │   │   │   ├── 0011_mysterious_stark_industries.sql
│   │   │   │   │   ├── 0012_magenta_thing.sql
│   │   │   │   │   ├── 0013_chunky_stephen_strange.sql
│   │   │   │   │   ├── 0014_chemical_shocker.sql
│   │   │   │   │   ├── 0015_colorful_warbird.sql
│   │   │   │   │   ├── 0016_curious_carnage.sql
│   │   │   │   │   ├── 0017_talented_captain_cross.sql
│   │   │   │   │   ├── 0018_dashing_the_fury.sql
│   │   │   │   │   ├── 0019_wonderful_shape.sql
│   │   │   │   │   ├── 0020_little_marauders.sql
│   │   │   │   │   ├── 0021_wakeful_onslaught.sql
│   │   │   │   │   ├── 0022_tiny_northstar.sql
│   │   │   │   │   ├── 0023_pink_namor.sql
│   │   │   │   │   ├── 0024_spooky_alex_power.sql
│   │   │   │   │   ├── 0025_colorful_valkyrie.sql
│   │   │   │   │   ├── 0026_numerous_slyde.sql
│   │   │   │   │   ├── 0027_nostalgic_human_torch.sql
│   │   │   │   │   ├── 0028_chief_cyclops.sql
│   │   │   │   │   ├── 0029_flaky_gorgon.sql
│   │   │   │   │   ├── 0030_common_gabe_jones.sql
│   │   │   │   │   ├── 0031_kind_ikaris.sql
│   │   │   │   │   ├── 0032_orange_prima.sql
│   │   │   │   │   ├── 0033_shiny_sebastian_shaw.sql
│   │   │   │   │   ├── 0034_curly_darkstar.sql
│   │   │   │   │   ├── 0035_last_valeria_richards.sql
│   │   │   │   │   ├── 0036_entry_tag_summary.sql
│   │   │   │   │   ├── 0037_bored_the_leader.sql
│   │   │   │   │   ├── meta/
│   │   │   │   │   │   ├── 0000_snapshot.json
│   │   │   │   │   │   ├── 0001_snapshot.json
│   │   │   │   │   │   ├── 0002_snapshot.json
│   │   │   │   │   │   ├── 0003_snapshot.json
│   │   │   │   │   │   ├── 0004_snapshot.json
│   │   │   │   │   │   ├── 0005_snapshot.json
│   │   │   │   │   │   ├── 0006_snapshot.json
│   │   │   │   │   │   ├── 0007_snapshot.json
│   │   │   │   │   │   ├── 0008_snapshot.json
│   │   │   │   │   │   ├── 0009_snapshot.json
│   │   │   │   │   │   ├── 0010_snapshot.json
│   │   │   │   │   │   ├── 0011_snapshot.json
│   │   │   │   │   │   ├── 0012_snapshot.json
│   │   │   │   │   │   ├── 0013_snapshot.json
│   │   │   │   │   │   ├── 0014_snapshot.json
│   │   │   │   │   │   ├── 0015_snapshot.json
│   │   │   │   │   │   ├── 0016_snapshot.json
│   │   │   │   │   │   ├── 0017_snapshot.json
│   │   │   │   │   │   ├── 0018_snapshot.json
│   │   │   │   │   │   ├── 0019_snapshot.json
│   │   │   │   │   │   ├── 0020_snapshot.json
│   │   │   │   │   │   ├── 0021_snapshot.json
│   │   │   │   │   │   ├── 0022_snapshot.json
│   │   │   │   │   │   ├── 0023_snapshot.json
│   │   │   │   │   │   ├── 0024_snapshot.json
│   │   │   │   │   │   ├── 0025_snapshot.json
│   │   │   │   │   │   ├── 0026_snapshot.json
│   │   │   │   │   │   ├── 0027_snapshot.json
│   │   │   │   │   │   ├── 0028_snapshot.json
│   │   │   │   │   │   ├── 0029_snapshot.json
│   │   │   │   │   │   ├── 0030_snapshot.json
│   │   │   │   │   │   ├── 0031_snapshot.json
│   │   │   │   │   │   ├── 0032_snapshot.json
│   │   │   │   │   │   ├── 0033_snapshot.json
│   │   │   │   │   │   ├── 0034_snapshot.json
│   │   │   │   │   │   ├── 0035_snapshot.json
│   │   │   │   │   │   ├── 0036_snapshot.json
│   │   │   │   │   │   ├── 0037_snapshot.json
│   │   │   │   │   │   └── _journal.json
│   │   │   │   │   └── migrations.js
│   │   │   │   ├── migrator.ts
│   │   │   │   ├── schemas/
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── services/
│   │   │   │   │   ├── collection.ts
│   │   │   │   │   ├── entry.ts
│   │   │   │   │   ├── feed.ts
│   │   │   │   │   ├── image.ts
│   │   │   │   │   ├── inbox.ts
│   │   │   │   │   ├── internal/
│   │   │   │   │   │   ├── base.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── list.ts
│   │   │   │   │   ├── subscription.ts
│   │   │   │   │   ├── summary.ts
│   │   │   │   │   ├── translation.ts
│   │   │   │   │   ├── unread.ts
│   │   │   │   │   └── user.ts
│   │   │   │   └── types.ts
│   │   │   └── tsconfig.json
│   │   ├── hooks/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── factory/
│   │   │   │   │   └── createHTMLMediaHook.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── internal/
│   │   │   │   │   └── for-theme.ts
│   │   │   │   ├── optimistic/
│   │   │   │   │   ├── config.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── strategies.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── useOptimisticMutation.ts
│   │   │   │   ├── useAnyPointDown.ts
│   │   │   │   ├── useControlled.ts
│   │   │   │   ├── useCountDown.ts
│   │   │   │   ├── useDark.ts
│   │   │   │   ├── useElementWidth.ts
│   │   │   │   ├── useInputComposition.ts
│   │   │   │   ├── useInterval.ts
│   │   │   │   ├── useIsOnline.ts
│   │   │   │   ├── useLongPress.ts
│   │   │   │   ├── useMeasure.ts
│   │   │   │   ├── useOnce.ts
│   │   │   │   ├── usePageVisibility.ts
│   │   │   │   ├── usePrevious.ts
│   │   │   │   ├── useRefValue.ts
│   │   │   │   ├── useSetState.ts
│   │   │   │   ├── useSmoothScroll.ts
│   │   │   │   ├── useSyncTheme.ts
│   │   │   │   ├── useTitle.ts
│   │   │   │   ├── useTriangleMenu.ts
│   │   │   │   ├── useTypescriptHappyCallback.ts
│   │   │   │   └── useVideo.ts
│   │   │   └── tsconfig.json
│   │   ├── logger/
│   │   │   ├── electron.ts
│   │   │   ├── package.json
│   │   │   ├── tsconfig.json
│   │   │   └── web.ts
│   │   ├── models/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── index.ts
│   │   │   │   └── rsshub.ts
│   │   │   └── tsconfig.json
│   │   ├── shared/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── auth.ts
│   │   │   │   ├── bridge.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── electron.ts
│   │   │   │   ├── env.common.ts
│   │   │   │   ├── env.desktop.ts
│   │   │   │   ├── env.rn.ts
│   │   │   │   ├── env.ssr.ts
│   │   │   │   ├── event.ts
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── language.ts
│   │   │   │   ├── queue.ts
│   │   │   │   ├── review-prompt.test.ts
│   │   │   │   ├── review-prompt.ts
│   │   │   │   └── settings/
│   │   │   │       ├── constants.ts
│   │   │   │       ├── defaults.ts
│   │   │   │       ├── hook.ts
│   │   │   │       └── interface.ts
│   │   │   └── tsconfig.json
│   │   ├── store/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── @types/
│   │   │   │   │   ├── default-resource.ts
│   │   │   │   │   └── i18next.d.ts
│   │   │   │   ├── constants/
│   │   │   │   │   ├── app.ts
│   │   │   │   │   └── onboarding.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── hydrate.ts
│   │   │   │   ├── lib/
│   │   │   │   │   ├── base.ts
│   │   │   │   │   ├── helper.ts
│   │   │   │   │   └── stream.ts
│   │   │   │   ├── modules/
│   │   │   │   │   ├── action/
│   │   │   │   │   │   ├── constant.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   └── store.ts
│   │   │   │   │   ├── collection/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── entry/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── feed/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── selectors.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── image/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   └── store.ts
│   │   │   │   │   ├── inbox/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── list/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── subscription/
│   │   │   │   │   │   ├── getter.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── selectors.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── summary/
│   │   │   │   │   │   ├── enum.ts
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   ├── translation/
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── unread/
│   │   │   │   │   │   ├── getters.ts
│   │   │   │   │   │   ├── hooks.ts
│   │   │   │   │   │   ├── selectors.ts
│   │   │   │   │   │   ├── store.ts
│   │   │   │   │   │   ├── types.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   └── user/
│   │   │   │   │       ├── constants.ts
│   │   │   │   │       ├── getters.ts
│   │   │   │   │       ├── hooks.ts
│   │   │   │   │       ├── store.ts
│   │   │   │   │       └── types.ts
│   │   │   │   ├── morph/
│   │   │   │   │   ├── api.ts
│   │   │   │   │   ├── db-store.ts
│   │   │   │   │   └── store-db.ts
│   │   │   │   ├── reset.ts
│   │   │   │   └── types.ts
│   │   │   └── tsconfig.json
│   │   ├── tracker/
│   │   │   ├── package.json
│   │   │   ├── src/
│   │   │   │   ├── adapters/
│   │   │   │   │   ├── base.ts
│   │   │   │   │   ├── firebase.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── posthog.ts
│   │   │   │   │   └── proxy.ts
│   │   │   │   ├── enums.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   ├── track-manager.ts
│   │   │   │   ├── tracker-points.ts
│   │   │   │   ├── types.ts
│   │   │   │   └── utils.ts
│   │   │   └── tsconfig.json
│   │   ├── types/
│   │   │   ├── global.d.ts
│   │   │   ├── package.json
│   │   │   ├── react-global.d.ts
│   │   │   └── vite-env.d.ts
│   │   └── utils/
│   │       ├── package.json
│   │       ├── src/
│   │       │   ├── attribution.ts
│   │       │   ├── bind-this.ts
│   │       │   ├── chain.ts
│   │       │   ├── cjk.ts
│   │       │   ├── color.ts
│   │       │   ├── data-structure/
│   │       │   │   ├── index.ts
│   │       │   │   └── set.ts
│   │       │   ├── dom.ts
│   │       │   ├── duration.ts
│   │       │   ├── environment.ts
│   │       │   ├── event-bus.rn.ts
│   │       │   ├── event-bus.ts
│   │       │   ├── headers.ts
│   │       │   ├── html.ts
│   │       │   ├── img-proxy.ts
│   │       │   ├── index.ts
│   │       │   ├── jotai.ts
│   │       │   ├── json-codec.ts
│   │       │   ├── language.ts
│   │       │   ├── link-parser.ts
│   │       │   ├── lru-cache.test.ts
│   │       │   ├── lru-cache.ts
│   │       │   ├── noop.ts
│   │       │   ├── ns.ts
│   │       │   ├── path-parser.test.ts
│   │       │   ├── path-parser.ts
│   │       │   ├── react.ts
│   │       │   ├── resize.ts
│   │       │   ├── scroller.ts
│   │       │   ├── url-builder.ts
│   │       │   ├── url-for-video.ts
│   │       │   ├── utils.spec.ts
│   │       │   └── utils.ts
│   │       ├── tsconfig.json
│   │       └── vitest.config.ts
│   └── readability/
│       ├── bump.config.ts
│       ├── package.json
│       ├── src/
│       │   └── index.ts
│       ├── tsconfig.json
│       └── tsdown.config.ts
├── patches/
│   ├── @microflash__remark-callout-directives.patch
│   ├── @mozilla__readability@0.6.0.patch
│   ├── @pengx17__electron-forge-maker-appimage.patch
│   ├── daisyui@4.12.24.patch
│   ├── re-resizable@6.11.2.patch
│   ├── react-native-sheet-transitions.patch
│   ├── react-native-track-player@4.1.1.patch
│   └── workbox-precaching.patch
├── plugins/
│   ├── eslint/
│   │   ├── eslint-check-i18n-json.js
│   │   ├── eslint-no-debug.js
│   │   ├── eslint-package-json.js
│   │   └── eslint-recursive-sort.js
│   └── utils.js
├── pnpm-workspace.yaml
├── scripts/
│   ├── copy-translation.ts
│   ├── increment-build-id.sh
│   ├── lib.ts
│   ├── mitproxy.py
│   ├── run-proxy.sh
│   ├── skip-main-app-vercel-build.sh
│   ├── svg-to-rn.ts
│   └── update-icon.ts
├── tsconfig.json
├── tsslint.config.ts
├── turbo.json
├── vercel.json
├── vitest.workspace.js
├── vitest.workspace.ts
└── wiki/
    └── contribute-i18n.md
Download .txt
Showing preview only (400K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3942 symbols across 1441 files)

FILE: .github/scripts/extract-release-info.mjs
  constant RELEASE_PATTERNS (line 12) | const RELEASE_PATTERNS = {
  constant EXIT_CODES (line 17) | const EXIT_CODES = {
  function setGitHubEnv (line 29) | function setGitHubEnv(key, value) {
  function setGitHubOutput (line 46) | function setGitHubOutput(key, value) {
  function getLatestCommitMessage (line 62) | function getLatestCommitMessage() {
  function extractReleaseInfo (line 76) | function extractReleaseInfo(commitMessage) {
  function main (line 97) | function main() {

FILE: api/vercel_webhook.ts
  function handler (line 6) | async function handler(request: VercelRequest, response: VercelResponse) {
  function sha1 (line 45) | function sha1(data: Buffer, secret: string): string {
  function purgeCloudflareCache (line 55) | async function purgeCloudflareCache() {

FILE: apps/cli/src/browser-login.ts
  constant LOCAL_CALLBACK_HOST (line 8) | const LOCAL_CALLBACK_HOST = "127.0.0.1"
  constant LOCAL_CALLBACK_PATH (line 9) | const LOCAL_CALLBACK_PATH = "/callback"
  constant DEFAULT_TIMEOUT_MS (line 10) | const DEFAULT_TIMEOUT_MS = 3 * 60 * 1000
  type BrowserLoginOptions (line 100) | interface BrowserLoginOptions {
  type BrowserLoginResult (line 106) | interface BrowserLoginResult {

FILE: apps/cli/src/cli.e2e.test.ts
  type CLIExecution (line 15) | type CLIExecution = {

FILE: apps/cli/src/client.ts
  type GlobalOptions (line 27) | interface GlobalOptions {
  type ResolvedGlobalOptions (line 34) | interface ResolvedGlobalOptions extends GlobalOptions {
  type CommandContext (line 38) | interface CommandContext {

FILE: apps/cli/src/command.ts
  type RunCommandOptions (line 7) | interface RunCommandOptions {

FILE: apps/cli/src/commands/auth.ts
  type AuthLoginOptions (line 10) | interface AuthLoginOptions {

FILE: apps/cli/src/commands/collection.ts
  type CollectionListOptions (line 7) | interface CollectionListOptions {
  type CollectionAddOptions (line 12) | interface CollectionAddOptions {

FILE: apps/cli/src/commands/entry.ts
  type MarkAllReadOptions (line 8) | interface MarkAllReadOptions {

FILE: apps/cli/src/commands/list.ts
  type ListCreateOptions (line 7) | interface ListCreateOptions {
  type ListUpdateOptions (line 15) | interface ListUpdateOptions {
  type ListFeedOptions (line 23) | interface ListFeedOptions {

FILE: apps/cli/src/commands/opml.ts
  type OpmlExportOptions (line 8) | interface OpmlExportOptions {
  type OpmlImportOptions (line 12) | interface OpmlImportOptions {

FILE: apps/cli/src/commands/search.ts
  type DiscoverTarget (line 6) | type DiscoverTarget = "feeds" | "lists"
  type TrendingRange (line 7) | type TrendingRange = "1d" | "3d" | "7d" | "30d"
  type TrendingLanguage (line 8) | type TrendingLanguage = "eng" | "cmn"
  type SearchDiscoverOptions (line 31) | interface SearchDiscoverOptions {
  type SearchRsshubOptions (line 35) | interface SearchRsshubOptions {
  type SearchTrendingOptions (line 39) | interface SearchTrendingOptions {

FILE: apps/cli/src/commands/subscription.ts
  type SubscriptionTarget (line 12) | type SubscriptionTarget = "feed" | "list" | "url"
  type UpdateTarget (line 13) | type UpdateTarget = "feed" | "list"
  type SubscriptionListOptions (line 29) | interface SubscriptionListOptions {
  type SubscriptionAddOptions (line 34) | interface SubscriptionAddOptions {
  type SubscriptionRemoveOptions (line 43) | interface SubscriptionRemoveOptions {
  type SubscriptionUpdateOptions (line 47) | interface SubscriptionUpdateOptions {

FILE: apps/cli/src/commands/timeline.ts
  type TimelineQuery (line 8) | type TimelineQuery = EntryListRequest & {
  type TimelineOptions (line 12) | interface TimelineOptions {

FILE: apps/cli/src/commands/unread.ts
  type UnreadListOptions (line 11) | interface UnreadListOptions {

FILE: apps/cli/src/config.ts
  type FoloCLIConfig (line 6) | interface FoloCLIConfig {

FILE: apps/cli/src/output.ts
  type OutputFormat (line 5) | type OutputFormat = "json" | "table" | "plain"
  type OutputError (line 7) | interface OutputError {
  class CLIError (line 12) | class CLIError extends Error {
    method constructor (line 15) | constructor(code: string, message: string) {

FILE: apps/desktop/configs/vite.electron-render.config.ts
  constant VITE_ROOT (line 13) | const VITE_ROOT = resolve(root, "layer/renderer")

FILE: apps/desktop/configs/vite.render.config.ts
  method transform (line 53) | transform(code, id) {

FILE: apps/desktop/e2e/scripts/capture-ui-audit.ts
  constant SETTING_TABS (line 17) | const SETTING_TABS = [
  constant SUBVIEW_ROUTES (line 33) | const SUBVIEW_ROUTES = ["discover", "power", "action", "rsshub", "ai"] a...
  function main (line 60) | async function main() {

FILE: apps/desktop/e2e/support/account.ts
  type TestAccount (line 5) | interface TestAccount {

FILE: apps/desktop/e2e/support/app.ts
  constant ONBOARDING_FEED_URL (line 8) | const ONBOARDING_FEED_URL = "folo://onboarding"
  method get (line 58) | get(currentTarget, property, receiver) {
  method set (line 65) | set(currentTarget, property, value, receiver) {
  method ownKeys (line 72) | ownKeys(currentTarget) {
  method getOwnPropertyDescriptor (line 75) | getOwnPropertyDescriptor(currentTarget, property) {
  method get (line 92) | get() {
  method set (line 95) | set() {}

FILE: apps/desktop/e2e/support/auth-bootstrap.ts
  type AuthBootstrapResponse (line 9) | type AuthBootstrapResponse = {
  type ParsedCookie (line 16) | type ParsedCookie = {

FILE: apps/desktop/e2e/support/env.ts
  type DesktopE2EProfile (line 5) | type DesktopE2EProfile = "local" | "prod"
  constant DESKTOP_E2E_PROFILES (line 7) | const DESKTOP_E2E_PROFILES = {
  type DesktopE2EEnv (line 22) | interface DesktopE2EEnv {

FILE: apps/desktop/layer/main/preload/index.d.ts
  type Window (line 4) | interface Window {

FILE: apps/desktop/layer/main/src/@types/constants.ts
  type MainSupportedLanguages (line 3) | type MainSupportedLanguages = (typeof langs)[number]

FILE: apps/desktop/layer/main/src/@types/i18next.d.ts
  type CustomTypeOptions (line 5) | interface CustomTypeOptions {

FILE: apps/desktop/layer/main/src/constants/app.ts
  constant UNREAD_BACKGROUND_POLLING_INTERVAL (line 4) | const UNREAD_BACKGROUND_POLLING_INTERVAL = 1000 * 60 * 5
  constant HOTUPDATE_RENDER_ENTRY_DIR (line 6) | const HOTUPDATE_RENDER_ENTRY_DIR = path.resolve(app.getPath("userData"),...
  constant GITHUB_OWNER (line 8) | const GITHUB_OWNER = process.env.GITHUB_OWNER || "RSSNext"
  constant GITHUB_REPO (line 9) | const GITHUB_REPO = process.env.GITHUB_REPO || "follow"
  constant START_IN_TRAY_ARGS (line 12) | const START_IN_TRAY_ARGS = "--start-in-tray"
  constant BETTER_AUTH_COOKIE_NAME_SESSION_TOKEN (line 14) | const BETTER_AUTH_COOKIE_NAME_SESSION_TOKEN = "better-auth.session_token"

FILE: apps/desktop/layer/main/src/constants/system.ts
  constant DEVICE_ID (line 3) | const DEVICE_ID = machineIdSync()

FILE: apps/desktop/layer/main/src/ipc/index.ts
  type IpcServices (line 27) | type IpcServices = MergeIpcService<typeof services>
  function initializeIpcServices (line 30) | function initializeIpcServices() {

FILE: apps/desktop/layer/main/src/ipc/services/app.ts
  type WindowActionInput (line 24) | interface WindowActionInput {
  type SearchInput (line 28) | interface SearchInput {
  type Sender (line 33) | interface Sender extends Electron.WebContents {
  class AppService (line 37) | class AppService extends IpcService {
    method getAppVersion (line 41) | getAppVersion(): string {
    method checkForUpdates (line 46) | async checkForUpdates(): Promise<{ hasUpdate: boolean; error?: string ...
    method switchAppLocale (line 51) | switchAppLocale(context: IpcContext, input: string): void {
    method rendererUpdateReload (line 60) | rendererUpdateReload(): void {
    method openExternal (line 85) | async openExternal(_context: IpcContext, url: string): Promise<void> {
    method windowAction (line 92) | windowAction(context: IpcContext, input: WindowActionInput): void {
    method quitAndInstall (line 119) | quitAndInstall(_context: IpcContext): void {
    method readClipboard (line 124) | readClipboard(_context: IpcContext): string {
    method search (line 129) | async search(context: IpcContext, input: SearchInput): Promise<Electro...
    method clearSearch (line 143) | clearSearch(context: IpcContext): void {
    method download (line 148) | async download(context: IpcContext, input: string): Promise<void> {
    method getAppPath (line 175) | getAppPath(_context: IpcContext): string {
    method resolveAppAsarPath (line 180) | resolveAppAsarPath(context: IpcContext, input: string): string {
    method readyToShowMainWindow (line 193) | readyToShowMainWindow(_context: IpcContext) {
    method openCacheFolder (line 203) | openCacheFolder(_context: IpcContext): void {
    method getCacheLimit (line 209) | getCacheLimit(_context: IpcContext): number {
    method clearCache (line 214) | async clearCache(_context: IpcContext): Promise<void> {
    method limitCacheSize (line 237) | limitCacheSize(_context: IpcContext, input: number): void {
    method revealLogFile (line 246) | revealLogFile(_context: IpcContext) {
    method getCacheSize (line 251) | getCacheSize(_context: IpcContext) {

FILE: apps/desktop/layer/main/src/ipc/services/auth.ts
  class AuthService (line 14) | class AuthService extends IpcService {
    method applySessionToken (line 17) | private async applySessionToken(token: string): Promise<void> {
    method clearSessionToken (line 50) | private async clearSessionToken(): Promise<void> {
    method requestCredentialAuth (line 65) | private async requestCredentialAuth(
    method sessionChanged (line 109) | async sessionChanged(_context: IpcContext): Promise<void> {
    method signOut (line 120) | async signOut(_context: IpcContext): Promise<void> {
    method signOutRemote (line 130) | async signOutRemote(_context: IpcContext, token?: string): Promise<voi...
    method signInWithCredential (line 147) | async signInWithCredential(
    method signUpWithCredential (line 162) | async signUpWithCredential(
    method setSessionToken (line 185) | async setSessionToken(_context: IpcContext, token: string): Promise<vo...

FILE: apps/desktop/layer/main/src/ipc/services/cli.ts
  type CliInstallStatus (line 15) | interface CliInstallStatus {
  class CliService (line 25) | class CliService extends IpcService {
    method getInstallStatus (line 29) | async getInstallStatus(_context: IpcContext): Promise<CliInstallStatus> {
    method installCli (line 48) | async installCli(_context: IpcContext): Promise<{ success: boolean; er...
    method uninstallCli (line 70) | async uninstallCli(_context: IpcContext): Promise<{ success: boolean; ...

FILE: apps/desktop/layer/main/src/ipc/services/debug.ts
  type InspectElementInput (line 4) | interface InspectElementInput {
  class DebugService (line 9) | class DebugService extends IpcService {
    method inspectElement (line 13) | inspectElement(context: IpcContext, input: InspectElementInput): void {

FILE: apps/desktop/layer/main/src/ipc/services/dock.ts
  class PollingManager (line 8) | class PollingManager {
    method startPolling (line 12) | async startPolling(pollingFn: () => Promise<void>, interval: number): ...
    method stopPolling (line 37) | stopPolling(): void {
    method active (line 43) | get active(): boolean {
    method sleepWithAbortSignal (line 47) | private async sleepWithAbortSignal(ms: number, signal: AbortSignal): P...
  class DockService (line 59) | class DockService extends IpcService {
    method pollingUpdateUnreadCount (line 65) | async pollingUpdateUnreadCount(): Promise<void> {
    method cancelPollingUpdateUnreadCount (line 73) | async cancelPollingUpdateUnreadCount(): Promise<void> {
    method updateUnreadCount (line 78) | async updateUnreadCount(): Promise<void> {
    method setDockBadge (line 85) | setDockBadge(_context: IpcContext, count: number): void {

FILE: apps/desktop/layer/main/src/ipc/services/integration.ts
  constant INVALID_CHAR_REGEX (line 15) | const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$%&*+,:;<=>?[\]^`{|}\u007F]/g
  constant DRIVE_LETTER_REGEX (line 16) | const DRIVE_LETTER_REGEX = /^[a-z]:/i
  function sanitizeFileName (line 18) | function sanitizeFileName(name: string): string {
  type SaveToEagleInput (line 28) | interface SaveToEagleInput {
  type LoginToQBittorrentInput (line 33) | interface LoginToQBittorrentInput {
  type CheckQBittorrentAuthInput (line 39) | interface CheckQBittorrentAuthInput {
  type AddMagnetInput (line 43) | interface AddMagnetInput {
  type CustomFetchInput (line 48) | interface CustomFetchInput {
  class IntegrationService (line 56) | class IntegrationService extends IpcService {
    method saveToObsidian (line 60) | async saveToObsidian(
    method saveToEagle (line 104) | async saveToEagle(context: IpcContext, input: SaveToEagleInput): Promi...
    method loginToQBittorrent (line 128) | async loginToQBittorrent(context: IpcContext, input: LoginToQBittorren...
    method checkQBittorrentAuth (line 161) | async checkQBittorrentAuth(context: IpcContext, input: CheckQBittorren...
    method addMagnet (line 181) | async addMagnet(context: IpcContext, input: AddMagnetInput) {
    method customFetch (line 207) | async customFetch(context: IpcContext, input: CustomFetchInput) {
    method openURLScheme (line 350) | async openURLScheme(context: IpcContext, scheme: string) {

FILE: apps/desktop/layer/main/src/ipc/services/menu.ts
  type SerializableMenuItem (line 6) | type SerializableMenuItem = Omit<MenuItemConstructorOptions, "click" | "...
  type ShowContextMenuInput (line 10) | interface ShowContextMenuInput {
  type ShowConfirmDialogInput (line 14) | interface ShowConfirmDialogInput {
  class MenuService (line 20) | class MenuService extends IpcService {
    method normalizeMenuItems (line 23) | private normalizeMenuItems(
    method showContextMenu (line 44) | async showContextMenu(context: IpcContext, input: ShowContextMenuInput...
    method showConfirmDialog (line 56) | async showConfirmDialog(_context: IpcContext, input: ShowConfirmDialog...
    method showShareMenu (line 67) | async showShareMenu(context: IpcContext, input: string): Promise<void> {

FILE: apps/desktop/layer/main/src/ipc/services/reader.ts
  type ReadabilityInput (line 16) | interface ReadabilityInput {
  type TtsInput (line 21) | interface TtsInput {
  type DetectCodeStringLanguageInput (line 27) | interface DetectCodeStringLanguageInput {
  class ReaderService (line 31) | class ReaderService extends IpcService {
    method readability (line 35) | async readability(_context: IpcContext, input: ReadabilityInput) {
    method tts (line 47) | async tts(context: IpcContext, input: TtsInput): Promise<string | null> {
    method getVoices (line 87) | async getVoices(context: IpcContext) {
    method detectCodeStringLanguage (line 104) | async detectCodeStringLanguage(

FILE: apps/desktop/layer/main/src/ipc/services/setting.ts
  type SetLoginItemSettingsInput (line 15) | interface SetLoginItemSettingsInput {
  class SettingService (line 22) | class SettingService extends IpcService {
    method getLoginItemSettings (line 26) | getLoginItemSettings(_context: IpcContext): Electron.LoginItemSettings {
    method setLoginItemSettings (line 31) | setLoginItemSettings(_context: IpcContext, input: SetLoginItemSettings...
    method openSettingWindow (line 36) | openSettingWindow(_context: IpcContext): void {
    method getSystemFonts (line 41) | async getSystemFonts(_context: IpcContext): Promise<string[]> {
    method getAppearance (line 47) | getAppearance(_context: IpcContext): "light" | "dark" | "system" {
    method setAppearance (line 52) | setAppearance(_context: IpcContext, appearance: "light" | "dark" | "sy...
    method getMinimizeToTray (line 58) | getMinimizeToTray(_context: IpcContext): boolean {
    method setMinimizeToTray (line 63) | setMinimizeToTray(_context: IpcContext, minimize: boolean): void {
    method getProxyConfig (line 68) | getProxyConfig(_context: IpcContext) {
    method setProxyConfig (line 74) | setProxyConfig(_context: IpcContext, config: string) {
    method getMessagingToken (line 81) | getMessagingToken(_context: IpcContext): string | null {

FILE: apps/desktop/layer/main/src/lib/auth-cookie-migration.ts
  constant LEGACY_PROD_API_URL (line 7) | const LEGACY_PROD_API_URL = "https://api.follow.is"
  constant BETTER_AUTH_SESSION_DATA_COOKIE_NAME (line 8) | const BETTER_AUTH_SESSION_DATA_COOKIE_NAME = "better-auth.session_data"

FILE: apps/desktop/layer/main/src/lib/cli-session-sync.ts
  constant CLI_NPM_PACKAGE_NAME (line 15) | const CLI_NPM_PACKAGE_NAME = "folocli"
  constant CLI_NPX_PACKAGE_SPEC (line 16) | const CLI_NPX_PACKAGE_SPEC = `${CLI_NPM_PACKAGE_NAME}@latest`
  constant CLI_CONFIG_DIR (line 17) | const CLI_CONFIG_DIR = join(homedir(), ".folo")
  constant CLI_CONFIG_PATH (line 18) | const CLI_CONFIG_PATH = join(CLI_CONFIG_DIR, "config.json")
  type CliConfig (line 21) | interface CliConfig {

FILE: apps/desktop/layer/main/src/lib/download.ts
  type DownloadOptions (line 12) | interface DownloadOptions {
  function downloadFile (line 20) | async function downloadFile(url: string, dest: string) {
  function downloadFileWithProgress (line 33) | async function downloadFileWithProgress(options: DownloadOptions): Promi...

FILE: apps/desktop/layer/main/src/lib/proxy.ts
  constant URL_SCHEME (line 37) | const URL_SCHEME = new Set(["http:", "https:", "ftp:", "socks:", "socks4...
  constant BYPASS_RULES (line 59) | const BYPASS_RULES = ["<local>"].join(";")

FILE: apps/desktop/layer/main/src/lib/store.ts
  type StoreData (line 5) | type StoreData = {
  type StoreKey (line 23) | enum StoreKey {

FILE: apps/desktop/layer/main/src/lib/tray.ts
  constant DEFAULT_MINIMIZE_TO_TRAY (line 116) | const DEFAULT_MINIMIZE_TO_TRAY = false

FILE: apps/desktop/layer/main/src/lib/utils.ts
  function refreshBound (line 6) | function refreshBound(window: BrowserWindow, timeout = 0) {

FILE: apps/desktop/layer/main/src/logger.ts
  function getLogFilePath (line 7) | function getLogFilePath() {
  function revealLogFile (line 11) | async function revealLogFile() {

FILE: apps/desktop/layer/main/src/manager/app.ts
  class AppManagerStatic (line 25) | class AppManagerStatic {
    method getInstance (line 28) | public static getInstance(): AppManagerStatic {
    method init (line 35) | public init() {
    method onReady (line 40) | private onReady() {
    method registerProtocols (line 65) | private registerProtocols() {
    method setupAppVisuals (line 81) | private setupAppVisuals() {
    method setupSystemConfigs (line 87) | private setupSystemConfigs() {
    method runCronJobs (line 94) | private runCronJobs() {
    method registerPushNotifications (line 99) | private async registerPushNotifications() {
    method registerMenuAndContextMenu (line 177) | public registerMenuAndContextMenu() {

FILE: apps/desktop/layer/main/src/manager/bootstrap.ts
  class BootstrapManager (line 31) | class BootstrapManager {
    method start (line 32) | public static start() {
    method registerAppEvents (line 44) | private static registerAppEvents() {
    method installDevTools (line 166) | private static installDevTools() {
    method handleOpen (line 188) | private static async handleOpen(url: string) {

FILE: apps/desktop/layer/main/src/manager/lifecycle.ts
  class LifecycleManagerStatic (line 5) | class LifecycleManagerStatic {
    method constructor (line 8) | private constructor() {
    method getInstance (line 12) | public static getInstance(): LifecycleManagerStatic {
    method registerListeners (line 19) | private registerListeners() {
    method onWindowAllClosed (line 24) | private onWindowAllClosed() {
    method onActivate (line 30) | private onActivate() {
    method onReady (line 36) | public onReady(callback: () => void) {

FILE: apps/desktop/layer/main/src/manager/window.ts
  class WindowManagerStatic (line 23) | class WindowManagerStatic {
    method mainWindowDefaultSize (line 24) | static get mainWindowDefaultSize() {
    method bindEvents (line 77) | private bindEvents(window: BrowserWindow) {
    method setupDevToolsFont (line 181) | private setupDevToolsFont(window: BrowserWindow) {
    method bindWindowStateEvents (line 218) | private bindWindowStateEvents(window: BrowserWindow) {
    method bindMainWindowCloseHandlers (line 241) | private bindMainWindowCloseHandlers(window: BrowserWindow) {
    method getPlatformSpecificWindowConfig (line 288) | private getPlatformSpecificWindowConfig(): Partial<BrowserWindowConstr...
    method ensureWindowBoundsInScreen (line 379) | private ensureWindowBoundsInScreen(windowState?: {

FILE: apps/desktop/layer/main/src/menu.ts
  method click (line 100) | click(_e, window) {
  method click (line 214) | click() {

FILE: apps/desktop/layer/main/src/updater/follow-update-provider.ts
  type FollowProviderOptions (line 19) | interface FollowProviderOptions {
  type FollowProviderContext (line 23) | type FollowProviderContext = {
  class FollowUpdateProvider (line 28) | class FollowUpdateProvider extends Provider<UpdateInfo> {
    method setContext (line 31) | static setContext(context: FollowProviderContext) {
    method clearContext (line 35) | static clearContext() {
    method getContext (line 39) | static getContext() {
    method constructor (line 43) | constructor(
    method getLatestVersion (line 51) | async getLatestVersion(): Promise<UpdateInfo> {
    method resolveFiles (line 56) | resolveFiles(updateInfo: UpdateInfo): Array<ResolvedUpdateFileInfo> {
    method buildUpdateInfo (line 63) | private buildUpdateInfo(context: FollowProviderContext): UpdateInfo {
    method mapFiles (line 104) | private mapFiles(files: PlatformUpdate["files"]): UpdateFileInfo[] {
    method mapFile (line 112) | private mapFile(file: PlatformUpdateFile): UpdateFileInfo | null {
    method safeParseUrl (line 130) | private safeParseUrl(value: string): URL | null {
    method ensureContext (line 139) | private async ensureContext(): Promise<FollowProviderContext> {
    method fetchContext (line 148) | private async fetchContext(): Promise<FollowProviderContext> {
    method pickPlatform (line 170) | private pickPlatform(appDecision: AppUpdate): PlatformUpdate | null {
    method resolvePlatformCandidates (line 185) | private resolvePlatformCandidates(): string[] {

FILE: apps/desktop/layer/main/src/updater/hot-updater.ts
  type RendererManifest (line 21) | type RendererManifest = RendererUpdate & {
  type RendererEligibilityStatus (line 26) | enum RendererEligibilityStatus {
  type RendererEligibilityResult (line 33) | interface RendererEligibilityResult {
  class RendererHotUpdater (line 39) | class RendererHotUpdater {
    method extractManifest (line 44) | extractManifest(payload: LatestReleasePayload | null): RendererManifes...
    method extractManifestFromRendererUpdate (line 55) | extractManifestFromRendererUpdate(renderer: RendererUpdate | null): Re...
    method evaluateManifest (line 59) | evaluateManifest(manifest: RendererManifest | null): RendererEligibili...
    method toManifest (line 113) | private toManifest(renderer: RendererUpdate | null): RendererManifest ...
    method applyManifest (line 140) | async applyManifest(manifest: RendererManifest): Promise<void> {
    method getCurrentManifest (line 183) | getCurrentManifest(): RendererManifest | null {
    method cleanup (line 201) | async cleanup(): Promise<void> {
    method loadDynamicEntry (line 229) | loadDynamicEntry() {
    method downloadArchive (line 243) | private async downloadArchive(manifest: RendererManifest) {
    method writeManifest (line 264) | private async writeManifest(manifest: RendererManifest) {

FILE: apps/desktop/layer/main/src/updater/index.ts
  type UpdateCheckOptions (line 29) | type UpdateCheckOptions = {
  type UpdateCheckResult (line 33) | type UpdateCheckResult = {
  class FollowUpdater (line 38) | class FollowUpdater {
    method constructor (line 45) | constructor(
    method register (line 52) | register() {
    method checkForUpdates (line 100) | async checkForUpdates(options: UpdateCheckOptions = {}): Promise<Updat...
    method handleDirectAppDecision (line 129) | async handleDirectAppDecision(payload: LatestReleasePayload): Promise<...
    method downloadAppUpdate (line 151) | async downloadAppUpdate(): Promise<void> {
    method quitAndInstall (line 168) | quitAndInstall() {
    method resolvePlatformCandidates (line 179) | private resolvePlatformCandidates() {
    method pickPlatformUpdate (line 198) | private pickPlatformUpdate(
    method handleAppDecision (line 219) | private async handleAppDecision(payload: LatestReleasePayload): Promis...
    method handleDistributionAppDecision (line 265) | private async handleDistributionAppDecision(): Promise<UpdateCheckResu...
    method tryDistributionRendererUpdate (line 302) | private async tryDistributionRendererUpdate(
    method shouldPromptDistributionStoreUpdate (line 372) | private shouldPromptDistributionStoreUpdate(info: DistributionStatusPa...
    method notifyDistributionUpdate (line 418) | private async notifyDistributionUpdate(
    method handleRendererDecision (line 444) | private async handleRendererDecision(payload: LatestReleasePayload): P...
    method registerAutoUpdaterEvents (line 505) | private registerAutoUpdaterEvents() {

FILE: apps/desktop/layer/main/src/updater/logger.ts
  function logObject (line 17) | function logObject(logger: typeof updaterLogger, prefix: string, obj: Re...

FILE: apps/desktop/layer/main/src/updater/windows-updater.ts
  class WindowsUpdater (line 5) | class WindowsUpdater extends NsisUpdater {

FILE: apps/desktop/layer/renderer/global.d.ts
  type Window (line 4) | interface Window {

FILE: apps/desktop/layer/renderer/setup-file.ts
  method navigator (line 10) | get navigator() {

FILE: apps/desktop/layer/renderer/src/@types/constants.ts
  type RendererSupportedLanguages (line 4) | type RendererSupportedLanguages = (typeof langs)[number]

FILE: apps/desktop/layer/renderer/src/@types/i18next.d.ts
  type CustomTypeOptions (line 5) | interface CustomTypeOptions {

FILE: apps/desktop/layer/renderer/src/App.tsx
  function App (line 18) | function App() {

FILE: apps/desktop/layer/renderer/src/atoms/context-menu.ts
  type ContextMenuState (line 14) | type ContextMenuState =
  type FollowMenuItem (line 55) | type FollowMenuItem = MenuItemText | MenuItemSeparator
  type MenuItemInput (line 57) | type MenuItemInput = MenuItemText | MenuItemSeparator | NilValue
  function sortShortcutsString (line 59) | function sortShortcutsString(shortcut: string) {
  function filterNullableMenuItems (line 74) | function filterNullableMenuItems(items: MenuItemInput[]): FollowMenuItem...
  function transformMenuItemsForNative (line 94) | function transformMenuItemsForNative(nextItems: FollowMenuItem[]): Elect...
  function withDebugMenu (line 115) | function withDebugMenu(menuItems: Array<FollowMenuItem>, e: MouseEvent |...
  type MenuItemType (line 133) | enum MenuItemType {
  class MenuItemSeparator (line 180) | class MenuItemSeparator {
    method constructor (line 182) | constructor(public hide = false) {}
  type BaseMenuItemTextConfig (line 187) | type BaseMenuItemTextConfig = {
  class BaseMenuItemText (line 199) | class BaseMenuItemText {
    method constructor (line 204) | constructor(private configs: BaseMenuItemTextConfig) {
    method label (line 210) | public get label() {
    method click (line 214) | public get click() {
    method onClick (line 218) | public get onClick() {
    method icon (line 221) | public get icon() {
    method shortcut (line 225) | public get shortcut() {
    method disabled (line 229) | public get disabled() {
    method checked (line 233) | public get checked() {
    method supportMultipleSelection (line 237) | public get supportMultipleSelection() {
    method requiresLogin (line 241) | public get requiresLogin() {
  type MenuItemTextConfig (line 246) | type MenuItemTextConfig = Prettify<
  class MenuItemText (line 253) | class MenuItemText extends BaseMenuItemText {
    method constructor (line 255) | constructor(protected config: MenuItemTextConfig) {
    method submenu (line 261) | public get submenu() {
    method hide (line 265) | public get hide() {
    method extend (line 269) | extend(config: Partial<MenuItemTextConfig>) {
  constant MENU_ITEM_SEPARATOR (line 276) | const MENU_ITEM_SEPARATOR = MenuItemSeparator.default

FILE: apps/desktop/layer/renderer/src/atoms/corner-player.ts
  type CornerPlayerAtomValue (line 6) | type CornerPlayerAtomValue = {

FILE: apps/desktop/layer/renderer/src/atoms/network.ts
  type NetworkStatus (line 5) | enum NetworkStatus {

FILE: apps/desktop/layer/renderer/src/atoms/player.ts
  type PlayerAtomValue (line 10) | type PlayerAtomValue = {
  method get (line 72) | get() {
  method mount (line 75) | mount(v: Omit<PlayerAtomValue, "show" | "status" | "playedSeconds" | "du...
  method teardown (line 129) | teardown() {
  method play (line 133) | play() {
  method pause (line 148) | pause() {
  method togglePlayAndPause (line 163) | togglePlayAndPause() {
  method close (line 182) | close() {
  method seek (line 192) | seek(time: number) {
  method setPlaybackRate (line 202) | setPlaybackRate(speed: number) {
  method back (line 212) | back(time: number) {
  method forward (line 218) | forward(time: number) {
  method toggleMute (line 224) | toggleMute() {
  method setVolume (line 231) | setVolume(volume: number) {

FILE: apps/desktop/layer/renderer/src/atoms/popover.ts
  type PopoverProps (line 9) | interface PopoverProps extends Omit<PopoverContentProps, "children"> {
  type PopoverState (line 16) | type PopoverState =

FILE: apps/desktop/layer/renderer/src/atoms/readability.ts
  type ReadabilityStatus (line 10) | enum ReadabilityStatus {

FILE: apps/desktop/layer/renderer/src/atoms/server-configs.ts
  type ServerConfigs (line 19) | type ServerConfigs = ExtractResponseData<GetStatusConfigsResponse>
  type PaymentPlan (line 20) | type PaymentPlan = ServerConfigs["PAYMENT_PLAN_LIST"][number]
  type PaymentFeature (line 21) | type PaymentFeature = PaymentPlan["limit"]

FILE: apps/desktop/layer/renderer/src/atoms/settings/ai.ts
  type WebAISettings (line 17) | interface WebAISettings extends AISettings {
  type ServerShortcutConfig (line 22) | type ServerShortcutConfig = ExtractResponseData<GetStatusConfigsResponse...
  constant FALLBACK_SHORTCUT_ICON (line 24) | const FALLBACK_SHORTCUT_ICON = "i-mgc-hotkey-cute-re"
  constant VALID_SHORTCUT_TARGETS (line 25) | const VALID_SHORTCUT_TARGETS = new Set<AIShortcutTarget>(DEFAULT_SHORTCU...
  type AIChatPanelStyle (line 141) | enum AIChatPanelStyle {
  type FloatingPanelState (line 153) | interface FloatingPanelState {
  constant DEFAULT_FLOATING_PANEL_WIDTH (line 160) | const DEFAULT_FLOATING_PANEL_WIDTH = 500
  constant DEFAULT_FLOATING_PANEL_HEIGHT (line 161) | const DEFAULT_FLOATING_PANEL_HEIGHT = clamp(window.innerHeight * 0.9, 60...
  constant DEFAULT_FLOATING_PANEL_X (line 162) | const DEFAULT_FLOATING_PANEL_X = window.innerWidth - DEFAULT_FLOATING_PA...
  constant DEFAULT_FLOATING_PANEL_Y (line 163) | const DEFAULT_FLOATING_PANEL_Y = window.innerHeight - DEFAULT_FLOATING_P...

FILE: apps/desktop/layer/renderer/src/atoms/settings/general.ts
  constant DEFAULT_ACTION_LANGUAGE (line 10) | const DEFAULT_ACTION_LANGUAGE = "default"
  function useActionLanguage (line 61) | function useActionLanguage() {
  function getActionLanguage (line 69) | function getActionLanguage() {
  function useHideAllReadSubscriptions (line 76) | function useHideAllReadSubscriptions() {

FILE: apps/desktop/layer/renderer/src/atoms/updater.ts
  type UpdaterStatus (line 6) | type UpdaterStatus = "ready"
  type UpdaterStatusKind (line 7) | type UpdaterStatusKind = "app" | "renderer" | "pwa" | "distribution"
  type BaseUpdaterStatus (line 9) | type BaseUpdaterStatus<T extends UpdaterStatusKind> = {
  type AppUpdaterStatus (line 15) | type AppUpdaterStatus = BaseUpdaterStatus<"app">
  type RendererUpdaterStatus (line 17) | type RendererUpdaterStatus = BaseUpdaterStatus<"renderer">
  type PwaUpdaterStatus (line 19) | type PwaUpdaterStatus = BaseUpdaterStatus<"pwa">
  type DistributionUpdaterStatus (line 21) | type DistributionUpdaterStatus = BaseUpdaterStatus<"distribution"> & {
  type UpdaterStatusAtom (line 28) | type UpdaterStatusAtom =

FILE: apps/desktop/layer/renderer/src/components/common/AppErrorBoundary.tsx
  type AppErrorBoundaryProps (line 10) | interface AppErrorBoundaryProps extends PropsWithChildren {
  type ErrorFallbackProps (line 38) | type ErrorFallbackProps = Parameters<FallbackRender>["0"]
  type AppErrorFallbackProps (line 39) | type AppErrorFallbackProps = ErrorFallbackProps & {}

FILE: apps/desktop/layer/renderer/src/components/common/ErrorBoundary.tsx
  type ErrorFallbackProps (line 6) | type ErrorFallbackProps = Omit<FallbackProps, "resetErrorBoundary"> &
  type FallbackRender (line 10) | type FallbackRender = (props: ErrorFallbackProps) => ReactNode
  type ErrorBoundaryProps (line 12) | interface ErrorBoundaryProps extends PropsWithChildren {

FILE: apps/desktop/layer/renderer/src/components/common/ErrorElement.tsx
  function ErrorElement (line 14) | function ErrorElement() {

FILE: apps/desktop/layer/renderer/src/components/common/ErrorTooltip.tsx
  function ErrorTooltip (line 10) | function ErrorTooltip({

FILE: apps/desktop/layer/renderer/src/components/common/ExPromise.tsx
  constant NOT_RESOLVED (line 4) | const NOT_RESOLVED = Symbol("NOT_RESOLVED")

FILE: apps/desktop/layer/renderer/src/components/common/Focusable.tsx
  type BizFocusableProps (line 6) | interface BizFocusableProps extends Omit<FocusableProps, "scope"> {

FILE: apps/desktop/layer/renderer/src/components/common/ImpressionTracker.tsx
  type ImpressionProps (line 6) | type ImpressionProps<T extends AllTrackers> = {
  function ImpressionView (line 14) | function ImpressionView<T extends keyof typeof tracker>(
  function ImpressionViewImpl (line 24) | function ImpressionViewImpl<T extends keyof typeof tracker>(props: Impre...

FILE: apps/desktop/layer/renderer/src/components/common/LoadMoreIndicator.tsx
  method onChange (line 9) | onChange(inView) {

FILE: apps/desktop/layer/renderer/src/components/common/Motion.tsx
  type WithLCPOptimization (line 9) | type WithLCPOptimization<P> = P & { lcpOptimization?: boolean }
  type MotionProxy (line 11) | type MotionProxy = {
  method get (line 19) | get(target, p: string) {

FILE: apps/desktop/layer/renderer/src/components/common/NotFound.tsx
  class AccessNotFoundError (line 13) | class AccessNotFoundError extends Error {
    method constructor (line 14) | constructor(
    method toString (line 23) | override toString() {

FILE: apps/desktop/layer/renderer/src/components/common/ReloadPrompt.tsx
  function ReloadPrompt (line 9) | function ReloadPrompt() {
  function registerPeriodicSync (line 45) | function registerPeriodicSync(period: number, swUrl: string, r: ServiceW...

FILE: apps/desktop/layer/renderer/src/components/common/ShadowDOM.tsx
  function getLinkedStaticStyleSheets (line 146) | function getLinkedStaticStyleSheets() {

FILE: apps/desktop/layer/renderer/src/components/common/SharePanel.tsx
  type SharePanelProps (line 11) | interface SharePanelProps {
  type ShareOption (line 15) | interface ShareOption {
  type SocialShareOption (line 24) | interface SocialShareOption {

FILE: apps/desktop/layer/renderer/src/components/common/withAppErrorBoundary.tsx
  type WithErrorBoundaryOptions (line 7) | interface WithErrorBoundaryOptions {
  function withAppErrorBoundary (line 18) | function withAppErrorBoundary<P extends object>(

FILE: apps/desktop/layer/renderer/src/components/errors/EntryNotFound.tsx
  class EntryNotFound (line 47) | class EntryNotFound extends CustomSafeError {
    method constructor (line 48) | constructor() {

FILE: apps/desktop/layer/renderer/src/components/errors/FeedNotFound.tsx
  class FeedNotFound (line 46) | class FeedNotFound extends CustomSafeError {
    method constructor (line 47) | constructor() {

FILE: apps/desktop/layer/renderer/src/components/errors/enum.ts
  type ErrorComponentType (line 1) | enum ErrorComponentType {

FILE: apps/desktop/layer/renderer/src/components/ui/ai-summary-card/AISummaryCardBase.tsx
  type AISummaryCardBaseProps (line 14) | interface AISummaryCardBaseProps {

FILE: apps/desktop/layer/renderer/src/components/ui/auto-completion/AutoCompletion.tsx
  type Suggestion (line 11) | type Suggestion = {
  type AutocompleteProps (line 15) | interface AutocompleteProps extends React.InputHTMLAttributes<HTMLInputE...

FILE: apps/desktop/layer/renderer/src/components/ui/background/WindowUnderBlur.tsx
  type Props (line 8) | type Props<T extends ElementType = "div"> = {

FILE: apps/desktop/layer/renderer/src/components/ui/button/AnimatedCommandButton.tsx
  type AnimatedCommandButtonProps (line 31) | interface AnimatedCommandButtonProps extends VariantProps<typeof animate...

FILE: apps/desktop/layer/renderer/src/components/ui/button/CommandActionButton.tsx
  type CommandActionButtonProps (line 7) | interface CommandActionButtonProps extends ActionButtonProps {

FILE: apps/desktop/layer/renderer/src/components/ui/button/GlassButton.tsx
  type GlassButtonProps (line 13) | interface GlassButtonProps {

FILE: apps/desktop/layer/renderer/src/components/ui/button/HeaderActionButton.tsx
  type HeaderActionButtonProps (line 5) | interface HeaderActionButtonProps {
  type HeaderActionGroupProps (line 84) | interface HeaderActionGroupProps {

FILE: apps/desktop/layer/renderer/src/components/ui/code-highlighter/shiki/Shiki.tsx
  type ShikiProps (line 22) | interface ShikiProps {
  function guessLanguage (line 59) | function guessLanguage() {
  function loadShikiLanguage (line 82) | async function loadShikiLanguage(language: string, languageModule: any) {
  function loadShikiTheme (line 88) | async function loadShikiTheme(theme: string, themeModule: any) {
  function register (line 95) | async function register() {

FILE: apps/desktop/layer/renderer/src/components/ui/crop/AvatarUploadModal.tsx
  type AvatarUploadModalProps (line 7) | interface AvatarUploadModalProps {

FILE: apps/desktop/layer/renderer/src/components/ui/fab/FABContainer.tsx
  type FABConfig (line 16) | interface FABConfig {

FILE: apps/desktop/layer/renderer/src/components/ui/hover-preview/EntryPreviewCard.tsx
  type EntryPreviewCardProps (line 16) | interface EntryPreviewCardProps {

FILE: apps/desktop/layer/renderer/src/components/ui/hover-preview/FeedPreviewCard.tsx
  type FeedPreviewCardProps (line 14) | interface FeedPreviewCardProps {

FILE: apps/desktop/layer/renderer/src/components/ui/keyboard-recorder/KeyRecorder.tsx
  type KeyRecorderProps (line 12) | interface KeyRecorderProps {
  function FamiconsArrowUndoCircle (line 83) | function FamiconsArrowUndoCircle(props: SVGProps<SVGSVGElement>) {
  constant MODIFIER_KEYS_MAP (line 101) | const MODIFIER_KEYS_MAP = {
  constant MODIFIER_KEYS_SET (line 108) | const MODIFIER_KEYS_SET = new Set<string>(Object.values(MODIFIER_KEYS_MAP))
  constant F_KEY_REGEX (line 110) | const F_KEY_REGEX = /^F(?:[1-9]|1[0-2])$/

FILE: apps/desktop/layer/renderer/src/components/ui/markdown/HTML.tsx
  type HTMLProps (line 23) | type HTMLProps<A extends keyof JSX.IntrinsicElements = "div"> = {
  constant HTML (line 98) | const HTML = memo(HTMLImpl)

FILE: apps/desktop/layer/renderer/src/components/ui/markdown/components/Toc.tsx
  type ITocItem (line 21) | interface ITocItem {
  type TocProps (line 29) | interface TocProps {
  type TocRef (line 36) | interface TocRef {
  type TocContainerProps (line 248) | interface TocContainerProps {
  type TocHoverCardProps (line 256) | interface TocHoverCardProps extends TocContainerProps {}

FILE: apps/desktop/layer/renderer/src/components/ui/markdown/components/TocItem.tsx
  type ITocItem (line 6) | interface ITocItem {
  type TocItemProps (line 15) | interface TocItemProps {

FILE: apps/desktop/layer/renderer/src/components/ui/markdown/components/hooks.tsx
  type DebouncedFuncLeading (line 64) | type DebouncedFuncLeading<T extends (..._args: any[]) => any> = T & {

FILE: apps/desktop/layer/renderer/src/components/ui/markdown/types.ts
  type MarkdownImage (line 1) | type MarkdownImage = {
  type MarkdownRenderActions (line 9) | interface MarkdownRenderActions {

FILE: apps/desktop/layer/renderer/src/components/ui/media/Media.tsx
  type BaseProps (line 19) | type BaseProps = {
  type MediaProps (line 31) | type MediaProps = BaseProps &

FILE: apps/desktop/layer/renderer/src/components/ui/media/MediaInfoRecord.tsx
  type MediaInfoRecord (line 1) | type MediaInfoRecord = Record<string, { width?: number; height?: number }>

FILE: apps/desktop/layer/renderer/src/components/ui/media/PreviewMediaContent.tsx
  constant GLASS_BUTTON_CLASS (line 207) | const GLASS_BUTTON_CLASS = tw`group-hover/left:opacity-100 opacity-0`
  type PreviewMediaProps (line 259) | interface PreviewMediaProps extends EntryMedia {

FILE: apps/desktop/layer/renderer/src/components/ui/media/SwipeMedia.tsx
  function SwipeMedia (line 16) | function SwipeMedia({

FILE: apps/desktop/layer/renderer/src/components/ui/media/VideoPlayer.tsx
  type VideoPlayerProps (line 31) | type VideoPlayerProps = {
  type VideoPlayerRef (line 37) | type VideoPlayerRef = {
  type VideoPlayerContextValue (line 53) | interface VideoPlayerContextValue {
  method onClick (line 102) | onClick(e) {
  method onDoubleClick (line 107) | onDoubleClick(e) {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/helper/useAsyncModal.tsx
  type AsyncModalOptions (line 13) | type AsyncModalOptions<T> = {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/inspire/PeekModal.tsx
  type PeekModalProps (line 14) | interface PeekModalProps {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/AsyncModalContent.tsx
  type UseAsyncFetcher (line 12) | interface UseAsyncFetcher<T> {
  type AsyncModalContentProps (line 18) | interface AsyncModalContentProps<T> {
  function useCountdown (line 24) | function useCountdown(durationInSeconds: number): boolean {
  function AsyncModalContent (line 38) | function AsyncModalContent<T>({

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/bus.ts
  type ModalDisposeEvent (line 8) | type ModalDisposeEvent = {
  type ModalRePresentEvent (line 12) | type ModalRePresentEvent = {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/constants.ts
  constant MODAL_STACK_Z_INDEX (line 27) | const MODAL_STACK_Z_INDEX = 1001

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/context.tsx
  type CurrentModalContentProps (line 7) | type CurrentModalContentProps = ModalActionsInternal & {
  type ModalContentComponent (line 36) | type ModalContentComponent<T = object> = FC<ModalActionsInternal & T>
  type ModalActionsInternal (line 37) | type ModalActionsInternal = {
  type Disposer (line 43) | type Disposer = () => void
  type PresentModalContextInternalFn (line 44) | type PresentModalContextInternalFn = (props: ModalProps & { id?: string ...

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/custom-modal.tsx
  type ModalTemplateType (line 29) | type ModalTemplateType = {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/declarative-modal.tsx
  type DeclarativeModalProps (line 12) | interface DeclarativeModalProps extends Omit<ModalProps, "content"> {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/helper.tsx
  function resizableOnly (line 10) | function resizableOnly(...positions: (keyof Enable)[]) {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/hooks.tsx
  method getTopModalStack (line 32) | getTopModalStack() {
  method getModalStackById (line 35) | getModalStackById(id: string) {
  method dismiss (line 38) | dismiss(id: string) {
  method dismissTop (line 43) | dismissTop() {
  method dismissAll (line 49) | dismissAll() {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/internal/use-animate.ts
  type ModalAnimateControls (line 10) | interface ModalAnimateControls {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/provider.tsx
  type Window (line 17) | interface Window {

FILE: apps/desktop/layer/renderer/src/components/ui/modal/stacked/types.tsx
  type ModalOverlayOptions (line 5) | interface ModalOverlayOptions {
  type ModalProps (line 9) | interface ModalProps {
  type DialogInstance (line 37) | interface DialogInstance {

FILE: apps/desktop/layer/renderer/src/components/ux/pull-to-refresh/index.tsx
  type PullToRefreshProps (line 7) | interface PullToRefreshProps {
  constant THRESHOLD (line 13) | const THRESHOLD = 80
  constant MAX_PULL_DISTANCE (line 14) | const MAX_PULL_DISTANCE = 120
  function PullToRefresh (line 16) | function PullToRefresh({

FILE: apps/desktop/layer/renderer/src/components/ux/transition/icon.tsx
  type TransitionType (line 8) | type TransitionType = {
  type IconTransitionProps (line 14) | type IconTransitionProps = {

FILE: apps/desktop/layer/renderer/src/constants/app.tsx
  constant FEED_COLLECTION_LIST (line 4) | const FEED_COLLECTION_LIST = "collections"
  constant QUERY_PERSIST_KEY (line 6) | const QUERY_PERSIST_KEY = getStorageNS("REACT_QUERY_OFFLINE_CACHE")
  constant I18N_LOCALE_KEY (line 7) | const I18N_LOCALE_KEY = getStorageNS("I18N_LOCALE")
  constant ROUTE_VIEW_ALL (line 10) | const ROUTE_VIEW_ALL = "all"
  constant ROUTE_FEED_PENDING (line 11) | const ROUTE_FEED_PENDING = "all"
  constant ROUTE_ENTRY_PENDING (line 12) | const ROUTE_ENTRY_PENDING = "pending"
  constant ROUTE_FEED_IN_FOLDER (line 13) | const ROUTE_FEED_IN_FOLDER = "folder-"
  constant ROUTE_FEED_IN_LIST (line 14) | const ROUTE_FEED_IN_LIST = "list-"
  constant ROUTE_FEED_IN_INBOX (line 15) | const ROUTE_FEED_IN_INBOX = "inbox-"
  constant ROUTE_TIMELINE_OF_VIEW (line 16) | const ROUTE_TIMELINE_OF_VIEW = "view-"

FILE: apps/desktop/layer/renderer/src/constants/copy.ts
  constant COPY_MAP (line 8) | const COPY_MAP = {

FILE: apps/desktop/layer/renderer/src/constants/dom.ts
  constant ENTRY_CONTENT_RENDER_CONTAINER_ID (line 1) | const ENTRY_CONTENT_RENDER_CONTAINER_ID = "follow-entry-render"
  constant LOGO_MOBILE_ID (line 3) | const LOGO_MOBILE_ID = "follow-logo-mobile"
  constant ENTRY_COLUMN_LIST_SCROLLER_ID (line 5) | const ENTRY_COLUMN_LIST_SCROLLER_ID = "entry-column-scroller"
  constant APP_GRID_CONTAINER_ID (line 7) | const APP_GRID_CONTAINER_ID = "follow-app-grid-container"
  constant ROOT_CONTAINER_ID (line 9) | const ROOT_CONTAINER_ID = "follow-root-container"

FILE: apps/desktop/layer/renderer/src/constants/env.ts
  constant WEB_URL (line 3) | const WEB_URL = env.VITE_WEB_URL

FILE: apps/desktop/layer/renderer/src/constants/hotkeys.ts
  type HotkeyScope (line 1) | enum HotkeyScope {

FILE: apps/desktop/layer/renderer/src/errors/CustomSafeError.ts
  class CustomSafeError (line 5) | class CustomSafeError extends Error {
    method constructor (line 6) | constructor(message: string, toast?: boolean) {

FILE: apps/desktop/layer/renderer/src/global.d.ts
  type Id (line 12) | type Id = string
  type FeedId (line 13) | type FeedId = Id
  type EntryId (line 14) | type EntryId = Id
  type Window (line 21) | interface Window {
  type I18nKeys (line 33) | type I18nKeys = OmitStringType<Parameters<typeof t>[0]>
  type I18nKeysForSettings (line 34) | type I18nKeysForSettings = OmitStringType<Parameters<typeof settingsT>[0]>
  type I18nKeysForShortcuts (line 35) | type I18nKeysForShortcuts = OmitStringType<Parameters<typeof shortcutsT>...
  type I18nKeysForAi (line 36) | type I18nKeysForAi = OmitStringType<Parameters<typeof aiT>[0]>

FILE: apps/desktop/layer/renderer/src/hooks/biz/useAsRead.ts
  function useEntryIsRead (line 8) | function useEntryIsRead(entryId?: string) {

FILE: apps/desktop/layer/renderer/src/hooks/biz/useDiscoverRSSHubRoute.tsx
  type ResponseType (line 15) | type ResponseType = Awaited<ReturnType<ReturnType<typeof useDataFetcher>...

FILE: apps/desktop/layer/renderer/src/hooks/biz/useEntryActions.tsx
  type EntryActionMenuItemConfig (line 71) | interface EntryActionMenuItemConfig {
  class EntryActionMenuItem (line 83) | class EntryActionMenuItem extends MenuItemText {
    method constructor (line 86) | constructor(config: EntryActionMenuItemConfig) {
    method id (line 103) | public get id() {
    method active (line 107) | public get active() {
    method notice (line 111) | public get notice() {
    method entryId (line 115) | public get entryId() {
    method extend (line 119) | public override extend(config: Partial<EntryActionMenuItemConfig>) {
  class EntryActionDropdownItem (line 127) | class EntryActionDropdownItem extends MenuItemText {
    method constructor (line 131) | constructor(config: EntryActionMenuItemConfig & { children?: EntryActi...
    method id (line 149) | public get id() {
    method active (line 153) | public get active() {
    method notice (line 157) | public get notice() {
    method entryId (line 161) | public get entryId() {
    method hasChildren (line 165) | public get hasChildren() {
    method enabledChildren (line 169) | public get enabledChildren() {
    method addChild (line 173) | public addChild(child: EntryActionMenuItem) {
    method removeChild (line 177) | public removeChild(childId: string) {
    method extend (line 181) | public override extend(
  type EntryActionItem (line 191) | type EntryActionItem = EntryActionMenuItem | EntryActionDropdownItem | M...
  constant HIDE_ACTIONS_IN_ENTRY_CONTEXT_MENU (line 220) | const HIDE_ACTIONS_IN_ENTRY_CONTEXT_MENU: FollowCommandId[] = [
  constant HIDE_ACTIONS_IN_ENTRY_TOOLBAR_ACTIONS (line 234) | const HIDE_ACTIONS_IN_ENTRY_TOOLBAR_ACTIONS: FollowCommandId[] = [

FILE: apps/desktop/layer/renderer/src/hooks/biz/useEntryContextMenu.ts
  function useEntryContextMenu (line 16) | function useEntryContextMenu({

FILE: apps/desktop/layer/renderer/src/hooks/biz/useFeature.ts
  type DebugFeatureValue (line 6) | interface DebugFeatureValue {
  type FeatureKey (line 12) | type FeatureKey = keyof typeof featureConfigMap

FILE: apps/desktop/layer/renderer/src/hooks/biz/useFeedActions.tsx
  method click (line 148) | click() {
  method click (line 176) | click() {
  method click (line 199) | click() {
  method click (line 213) | click() {
  method onError (line 452) | async onError() {
  method onError (line 472) | async onError() {

FILE: apps/desktop/layer/renderer/src/hooks/biz/useFollow.tsx
  type FollowOptions (line 15) | interface FollowOptions {

FILE: apps/desktop/layer/renderer/src/hooks/biz/useNavigateEntry.ts
  type NavigateEntryOptions (line 25) | type NavigateEntryOptions = Partial<{
  type ParsedNavigateEntryOptions (line 52) | type ParsedNavigateEntryOptions = {
  function getNavigateEntryPath (line 97) | function getNavigateEntryPath(options: NavigateEntryOptions | ParsedNavi...

FILE: apps/desktop/layer/renderer/src/hooks/biz/useRenderStyle.tsx
  function useRenderStyle (line 5) | function useRenderStyle({

FILE: apps/desktop/layer/renderer/src/hooks/biz/useRouteParams.ts
  type BizRouteParams (line 33) | interface BizRouteParams {
  constant VIEW_SLUG_BY_VIEW (line 46) | const VIEW_SLUG_BY_VIEW: Record<FeedViewType, string> = {
  constant VIEW_PARAM_ALIAS_MAP (line 56) | const VIEW_PARAM_ALIAS_MAP: Record<string, FeedViewType> = Object.entrie...
  constant FEED_VIEW_VALUES (line 67) | const FEED_VIEW_VALUES = new Set<FeedViewType>(
  function parseView (line 77) | function parseView(input: string | undefined): FeedViewType | undefined {

FILE: apps/desktop/layer/renderer/src/hooks/biz/useSubscriptionActions.tsx
  method onMutate (line 74) | onMutate(variables) {

FILE: apps/desktop/layer/renderer/src/hooks/biz/useTimelineList.ts
  constant ALL_TIMELINE_IDS (line 11) | const ALL_TIMELINE_IDS = getViewList({ includeAll: true }).map((view) =>

FILE: apps/desktop/layer/renderer/src/hooks/common/useBizQuery.ts
  type SafeReturnType (line 9) | type SafeReturnType<T> = T extends (...args: any[]) => infer R ? R : never
  type CombinedObject (line 11) | type CombinedObject<T, U> = T & U
  function useAuthQuery (line 12) | function useAuthQuery<

FILE: apps/desktop/layer/renderer/src/hooks/common/useContextMenu.tsx
  type UseContextMenuOptions (line 3) | interface UseContextMenuOptions {

FILE: apps/desktop/layer/renderer/src/hooks/common/useI18n.ts
  function useI18n (line 9) | function useI18n() {

FILE: apps/desktop/layer/renderer/src/hooks/common/usePreventOverscrollBounce.ts
  constant PREVENT_SPRING_CLASS (line 3) | const PREVENT_SPRING_CLASS = "prevent-spring"

FILE: apps/desktop/layer/renderer/src/hooks/common/useRecaptchaToken.ts
  type FoloE2EWindow (line 4) | type FoloE2EWindow = Window &

FILE: apps/desktop/layer/renderer/src/i18n.ts
  class LocaleCache (line 20) | class LocaleCache {
    method getKey (line 22) | private getKey(lang: string) {
    method get (line 25) | get(lang: string) {
    method set (line 31) | set(lang: string) {
  type CustomEvent (line 97) | interface CustomEvent {

FILE: apps/desktop/layer/renderer/src/initialize/global-shortcuts.ts
  type ShortcutDefinition (line 4) | interface ShortcutDefinition {

FILE: apps/desktop/layer/renderer/src/initialize/history.ts
  type History (line 10) | interface History {
  method get (line 23) | get() {
  method set (line 26) | set(value) {
  method get (line 55) | get() {
  method get (line 83) | get() {

FILE: apps/desktop/layer/renderer/src/initialize/index.ts
  type Window (line 24) | interface Window {
  method onWindowClose (line 69) | onWindowClose() {
  method onWindowShow (line 72) | onWindowShow() {

FILE: apps/desktop/layer/renderer/src/initialize/migrates/helper.ts
  type DefineMigrationOptions (line 1) | interface DefineMigrationOptions {

FILE: apps/desktop/layer/renderer/src/initialize/migrates/index.ts
  type Window (line 12) | interface Window {

FILE: apps/desktop/layer/renderer/src/initialize/migrates/v/v1.ts
  function hasSettingsChanged (line 12) | function hasSettingsChanged(

FILE: apps/desktop/layer/renderer/src/lib/avatar-upload.ts
  function uploadAvatarBlob (line 12) | async function uploadAvatarBlob(blob: Blob): Promise<string> {

FILE: apps/desktop/layer/renderer/src/lib/client-session.ts
  constant CLIENT_ID_KEY (line 4) | const CLIENT_ID_KEY = getStorageNS("client_id")
  constant SESSION_ID_KEY (line 5) | const SESSION_ID_KEY = getStorageNS("session_id")
  constant AUTH_SESSION_TOKEN_KEY (line 6) | const AUTH_SESSION_TOKEN_KEY = getStorageNS("auth_session_token")

FILE: apps/desktop/layer/renderer/src/lib/defineQuery.ts
  type ValidRecipeReturnDraftType (line 7) | type ValidRecipeReturnDraftType<T> = ReturnType<Producer<T>>
  type DefinedQuery (line 9) | type DefinedQuery<TQueryKey extends QueryKey, TData> = Readonly<{
  type DefinedQueryOptions (line 49) | type DefinedQueryOptions<TData> = {
  function defineQuery (line 72) | function defineQuery<

FILE: apps/desktop/layer/renderer/src/lib/ga4.ts
  class Analytics4 (line 7) | class Analytics4 {
    method constructor (line 13) | constructor(clientID: string = getClientId(), sessionID = getSessionId...
    method setUserId (line 18) | async setUserId(id: string) {
    method setUserProperties (line 22) | async setUserProperties(upValue?: Record<string, unknown>) {
    method logEvent (line 32) | async logEvent(eventName: string, params?: Record<string, unknown>): P...

FILE: apps/desktop/layer/renderer/src/lib/issues.ts
  type IssueOptions (line 4) | interface IssueOptions {

FILE: apps/desktop/layer/renderer/src/lib/native-menu.ts
  type ElectronMenuItem (line 5) | type ElectronMenuItem = Omit<MenuItemConstructorOptions, "click" | "subm...
  function getMenuItemByPath (line 28) | function getMenuItemByPath(
  function removeClick (line 48) | function removeClick(items: Array<ElectronMenuItem>): Array<ElectronMenu...

FILE: apps/desktop/layer/renderer/src/lib/observe-resize.ts
  type ObserveResize (line 5) | type ObserveResize = {

FILE: apps/desktop/layer/renderer/src/lib/parse-html.ts
  function markInlineImage (line 18) | function markInlineImage(node?: Element) {
  function extractCodeFromHtml (line 240) | function extractCodeFromHtml(htmlString: string) {

FILE: apps/desktop/layer/renderer/src/lib/query-client.ts
  constant DO_NOT_RETRY_CODES (line 11) | const DO_NOT_RETRY_CODES = new Set([400, 401, 403, 404, 422, 402])
  method retry (line 18) | retry(failureCount, error) {
  type Meta (line 42) | interface Meta {
  type Register (line 46) | interface Register extends Meta {}

FILE: apps/desktop/layer/renderer/src/lib/simple-text-selection.ts
  type SelectionRect (line 5) | interface SelectionRect {
  type TextSelectionEvent (line 14) | interface TextSelectionEvent {
  function addTextSelectionListener (line 23) | function addTextSelectionListener(
  function normalizeRect (line 69) | function normalizeRect(rect: DOMRect | DOMRectReadOnly): SelectionRect {

FILE: apps/desktop/layer/renderer/src/lib/url-builder.ts
  class WebUrlBuilder (line 6) | class WebUrlBuilder extends UrlBuilderClass {
    method constructor (line 7) | constructor() {
    method shareEntry (line 11) | shareEntry(

FILE: apps/desktop/layer/renderer/src/lib/utils.ts
  function getEntriesParams (line 7) | function getEntriesParams({

FILE: apps/desktop/layer/renderer/src/modules/action/constants.tsx
  function AiTargetLanguageSelector (line 29) | function AiTargetLanguageSelector() {

FILE: apps/desktop/layer/renderer/src/modules/action/rule-card.tsx
  type RuleCardProps (line 18) | type RuleCardProps = {

FILE: apps/desktop/layer/renderer/src/modules/action/then-section.tsx
  type ThenSectionProps (line 23) | type ThenSectionProps = {

FILE: apps/desktop/layer/renderer/src/modules/action/when-section.tsx
  type WhenSectionProps (line 21) | type WhenSectionProps = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat-session/service.ts
  constant MAX_PAGES (line 17) | const MAX_PAGES = 10
  class AIChatSessionServiceStatic (line 22) | class AIChatSessionServiceStatic {
    method syncSessionsAndMessagesFromServer (line 31) | async syncSessionsAndMessagesFromServer(filters?: ListSessionsQuery): ...
    method loadSessionsFromDb (line 70) | async loadSessionsFromDb() {
    method fetchAndPersistMessages (line 82) | async fetchAndPersistMessages(
    method syncSessionMessages (line 129) | async syncSessionMessages(chatId: string) {
    method fetchUnseenRemoteMessages (line 159) | private async fetchUnseenRemoteMessages(

FILE: apps/desktop/layer/renderer/src/modules/ai-chat-session/store.ts
  type AIChatSessionSyncStats (line 6) | interface AIChatSessionSyncStats {
  type AIChatSessionViewModelState (line 12) | interface AIChatSessionViewModelState {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/atoms/session.ts
  type AIModelState (line 12) | interface AIModelState {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/blocks/ContextBlock.tsx
  type MainViewBlock (line 79) | type MainViewBlock = AbstractValueContextBlock<"mainView">
  type MainFeedBlock (line 80) | type MainFeedBlock = AbstractValueContextBlock<"mainFeed">
  type UnreadOnlyBlock (line 81) | type UnreadOnlyBlock = AbstractValueContextBlock<"unreadOnly">

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/context-bar/menus/ShortcutsMenuContent.tsx
  type ShortcutsMenuContentProps (line 13) | interface ShortcutsMenuContentProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/AIChainOfThought.tsx
  type ChainReasoningPart (line 15) | type ChainReasoningPart = ReasoningUIPart | ToolUIPart<BizUITools>
  type AIChainOfThoughtProps (line 16) | interface AIChainOfThoughtProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/AIReasoningPart.tsx
  type AIReasoningPartProps (line 8) | interface AIReasoningPartProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/share.tsx
  type PartWithState (line 7) | interface PartWithState {
  function withDisplayStateHandler (line 20) | function withDisplayStateHandler<T>(config: {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/shared/AnalyticsMetrics.tsx
  type AnalyticsMetric (line 3) | interface AnalyticsMetric {
  type AnalyticsMetricsProps (line 8) | interface AnalyticsMetricsProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/shared/CategoryTag.tsx
  type CategoryTagProps (line 3) | interface CategoryTagProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/shared/DisplayHeader.tsx
  type DisplayHeaderProps (line 4) | interface DisplayHeaderProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/shared/EmptyState.tsx
  type EmptyStateProps (line 3) | interface EmptyStateProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/shared/FeedItemCard.tsx
  type FeedItemCardProps (line 11) | interface FeedItemCardProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/shared/GroupedContent.tsx
  type GroupedContentProps (line 3) | interface GroupedContentProps<T> {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/displays/shared/StatCard.tsx
  type StatCardProps (line 9) | interface StatCardProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/file/GlobalFileDropZone.tsx
  type GlobalFileDropZoneProps (line 9) | interface GlobalFileDropZoneProps extends PropsWithChildren {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/AIChatContextBar.tsx
  constant MAX_VISIBLE_BLOCKS (line 18) | const MAX_VISIBLE_BLOCKS = 4

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/AIChatRoot.tsx
  type AIChatRootProps (line 21) | interface AIChatRootProps extends PropsWithChildren {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/AIChatSendButton.tsx
  type AIChatSendButtonProps (line 5) | interface AIChatSendButtonProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/AIModelIndicator.tsx
  type AIModelIndicatorProps (line 19) | interface AIModelIndicatorProps {
  type ProviderType (line 24) | type ProviderType = "openai" | "google" | "auto" | "deepseek" | "anthrop...
  constant MODEL_PAID_LEVELS (line 35) | const MODEL_PAID_LEVELS = ["basic", "plus", "pro"] as const
  type ModelPaidLevel (line 36) | type ModelPaidLevel = (typeof MODEL_PAID_LEVELS)[number]

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatBottomPanel.tsx
  type ChatBottomPanelProps (line 14) | interface ChatBottomPanelProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatHistoryDropdown.tsx
  type ChatHistoryDropdownProps (line 30) | interface ChatHistoryDropdownProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInput.tsx
  type ChatInputProps (line 44) | interface ChatInputProps extends VariantProps<typeof chatInputVariants> {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx
  type ChatInterfaceProps (line 305) | interface ChatInterfaceProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatMessageContainer.tsx
  constant SCROLL_BOTTOM_THRESHOLD (line 13) | const SCROLL_BOTTOM_THRESHOLD = 100
  type ChatMessageContainerProps (line 15) | interface ChatMessageContainerProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatShortcutsRow.tsx
  type ChatShortcutsRowProps (line 24) | interface ChatShortcutsRowProps {
  type ShortcutMenuButtonProps (line 123) | interface ShortcutMenuButtonProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatTitle.tsx
  type AIHeaderTitleProps (line 4) | interface AIHeaderTitleProps extends Omit<ButtonHTMLAttributes<HTMLButto...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/Messages.tsx
  type MessagesProps (line 10) | interface MessagesProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/RateLimitNotice.tsx
  type RateLimitNoticeProps (line 8) | interface RateLimitNoticeProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ScrollToBottomButton.tsx
  type ScrollToBottomButtonProps (line 3) | interface ScrollToBottomButtonProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/TaskReportDropdown.tsx
  type TaskReportDropdownProps (line 28) | interface TaskReportDropdownProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/WelcomeScreen.tsx
  type WelcomeScreenProps (line 13) | interface WelcomeScreenProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/shared/ChatSessionComponents.tsx
  type SessionItemProps (line 11) | interface SessionItemProps {
  type EmptyStateProps (line 19) | interface EmptyStateProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/shared/useChatSessionHandlers.tsx
  type UseChatSessionHandlersProps (line 19) | interface UseChatSessionHandlersProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIChatMessage.tsx
  type ChatMessage (line 17) | interface ChatMessage {
  type AIChatMessageProps (line 24) | interface AIChatMessageProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIDataBlockPart.tsx
  type AIDataBlockPartProps (line 7) | interface AIDataBlockPartProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIMessageParts.tsx
  type AIMessagePartsProps (line 20) | interface AIMessagePartsProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/BlockTitleComponents.tsx
  type TitleProps (line 5) | interface TitleProps {
  type EntryTitleProps (line 9) | interface EntryTitleProps extends TitleProps {
  type FeedTitleProps (line 13) | interface FeedTitleProps extends TitleProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/EditableMessage.tsx
  type EditableMessageProps (line 17) | interface EditableMessageProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ErrorMessage.tsx
  type ErrorMessageProps (line 9) | interface ErrorMessageProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ImageThumbnail.tsx
  type AttachmentImageProps (line 11) | type AttachmentImageProps = {
  type ImageThumbnailProps (line 17) | type ImageThumbnailProps = AttachmentImageProps

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/TokenUsagePill.tsx
  type TokenUsagePillProps (line 15) | interface TokenUsagePillProps {
  type ModelInfoSectionProps (line 26) | interface ModelInfoSectionProps {
  type FreeUserTokenUsageProps (line 65) | interface FreeUserTokenUsageProps {
  type NormalUserTokenUsageProps (line 94) | interface NormalUserTokenUsageProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ToolInvocationComponent.tsx
  type ToolInvocationComponentProps (line 9) | interface ToolInvocationComponentProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/UserChatMessage.tsx
  type UserChatMessageProps (line 18) | interface UserChatMessageProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/UserMessageParts.tsx
  type UserMessagePartsProps (line 8) | interface UserMessagePartsProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/UserRichTextMessage.tsx
  function onError (line 16) | function onError(error: Error) {
  function replaceShortcutTagsWithMarkdown (line 20) | function replaceShortcutTagsWithMarkdown(state: string): string {
  type UserRichTextMessageProps (line 50) | interface UserRichTextMessageProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/ai-block-constants.ts
  constant BLOCK_STYLES (line 12) | const BLOCK_STYLES = {
  constant DEFAULT_BLOCK_STYLES (line 38) | const DEFAULT_BLOCK_STYLES = {
  constant BLOCK_ICONS (line 47) | const BLOCK_ICONS = {
  constant BLOCK_LABELS (line 57) | const BLOCK_LABELS = {
  constant FILE_STATUS_LABELS (line 68) | const FILE_STATUS_LABELS = {
  function getBlockStyles (line 78) | function getBlockStyles(type: AIChatContextBlock["type"]) {
  function getBlockIcon (line 85) | function getBlockIcon(block: AIChatContextBlock): string {
  function getBlockLabel (line 102) | function getBlockLabel(type: AIChatContextBlock["type"]): string {
  function getImageUrl (line 109) | function getImageUrl(attachment: FileAttachment): string | null {
  function isImageAttachment (line 116) | function isImageAttachment(block: AIChatContextBlock): boolean {
  function getFileDisplayContent (line 127) | function getFileDisplayContent(attachment: FileAttachment): string {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/animated/AnimatedMarkdown.tsx
  type MarkdownAnimateTextProps (line 23) | interface MarkdownAnimateTextProps {
  type BaseInlineFoloReferenceProps (line 185) | type BaseInlineFoloReferenceProps = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/animated/TokenizedText.tsx
  type TokenWithSource (line 10) | interface TokenWithSource {
  type TokenType (line 18) | type TokenType = string | TokenWithSource | ReactElement

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/animated/constants.ts
  constant DEFAULT_ANIMATION (line 1) | const DEFAULT_ANIMATION = "mask-left-to-right 0.5s ease-in-out"
  constant ANIMATION_STYLE (line 2) | const ANIMATION_STYLE = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/message/useContextBlockPresentation.tsx
  type ContextBlockPresentation (line 19) | interface ContextBlockPresentation {
  function useContextBlockPresentation (line 28) | function useContextBlockPresentation(block: AIChatContextBlock): Context...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/shared/common-states.tsx
  type LoadingStateProps (line 1) | interface LoadingStateProps {
  type ErrorStateProps (line 5) | interface ErrorStateProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/ui/AIShortcutButton.tsx
  type AIShortcutButtonProps (line 40) | interface AIShortcutButtonProps extends VariantProps<typeof aiShortcutBu...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/ui/ShortcutTooltip.tsx
  type ShortcutTooltipProps (line 10) | interface ShortcutTooltipProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/ui/UploadProgress.tsx
  type UploadProgressProps (line 6) | interface UploadProgressProps {
  type CircularProgressProps (line 69) | interface CircularProgressProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntrySummaryCard.tsx
  type EntrySummaryCardProps (line 10) | interface EntrySummaryCardProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntryWelcomeContent.tsx
  type EntryWelcomeContentProps (line 5) | interface EntryWelcomeContentProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/constants/index.ts
  constant SCROLLED_BEYOND_THRESHOLD (line 1) | const SCROLLED_BEYOND_THRESHOLD = 100
  constant AI_CHAT_SPECIAL_ID_PREFIX (line 3) | const AI_CHAT_SPECIAL_ID_PREFIX = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/file-upload/FileAttachmentNode.tsx
  type SerializedFileAttachmentNode (line 19) | type SerializedFileAttachmentNode = Spread<
  function convertFileAttachmentElement (line 26) | function convertFileAttachmentElement(domNode: Node): null | DOMConversi...
  class FileAttachmentNode (line 36) | class FileAttachmentNode extends DecoratorNode<React.ReactElement> {
    method getType (line 39) | static override getType(): string {
    method clone (line 43) | static override clone(node: FileAttachmentNode): FileAttachmentNode {
    method importJSON (line 47) | static override importJSON(serializedNode: SerializedFileAttachmentNod...
    method importDOM (line 53) | static override importDOM(): DOMConversionMap | null {
    method constructor (line 62) | constructor(attachmentId: string, key?: NodeKey) {
    method exportJSON (line 67) | override exportJSON(): SerializedFileAttachmentNode {
    method exportDOM (line 75) | override exportDOM(): DOMExportOutput {
    method createDOM (line 82) | override createDOM(): HTMLElement {
    method updateDOM (line 89) | override updateDOM(): false {
    method getAttachmentId (line 93) | getAttachmentId(): string {
    method setAttachmentId (line 97) | setAttachmentId(attachmentId: string): void {
    method decorate (line 102) | override decorate(): React.ReactElement {
    method isInline (line 106) | override isInline(): boolean {
  type FileAttachmentComponentProps (line 111) | interface FileAttachmentComponentProps {
  function FileAttachmentPill (line 115) | function FileAttachmentPill({ attachment }: { attachment: FileAttachment...
  function MissingFilePill (line 141) | function MissingFilePill() {
  function BlockBasedAttachment (line 150) | function BlockBasedAttachment({ attachmentId }: { attachmentId: string }) {
  function MessageBasedAttachment (line 164) | function MessageBasedAttachment({
  function FileAttachmentComponent (line 184) | function FileAttachmentComponent({ node }: FileAttachmentComponentProps) {
  function $createFileAttachmentNode (line 195) | function $createFileAttachmentNode(attachmentId: string): FileAttachment...
  function $isFileAttachmentNode (line 199) | function $isFileAttachmentNode(

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/file-upload/components/FileDropZone.tsx
  type FileDropZoneProps (line 6) | interface FileDropZoneProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/file-upload/hooks/useFileAttachmentBlockSync.ts
  type FileAttachmentBlockReference (line 11) | interface FileAttachmentBlockReference {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/file-upload/hooks/useFileUploadIntegration.ts
  function useFileUploadIntegration (line 14) | function useFileUploadIntegration(

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/file-upload/types.ts
  type FileUploadPluginConfig (line 1) | interface FileUploadPluginConfig {
  type FileDropZoneState (line 12) | interface FileDropZoneState {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/file-upload/utils/file-handling.ts
  function dragEventHasFiles (line 4) | function dragEventHasFiles(event: DragEvent): boolean {
  function clipboardEventHasFiles (line 11) | function clipboardEventHasFiles(event: ClipboardEvent): boolean {
  function preventDefaultDrag (line 18) | function preventDefaultDrag(event: DragEvent): void {
  function getFilesFromDrop (line 26) | function getFilesFromDrop(event: DragEvent): FileList | null {
  function getFilesFromPaste (line 33) | function getFilesFromPaste(event: ClipboardEvent): FileList | null {
  function createDragCounter (line 40) | function createDragCounter() {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/MentionNode.tsx
  type SerializedMentionNode (line 19) | type SerializedMentionNode = Spread<
  class MentionNode (line 26) | class MentionNode extends DecoratorNode<React.JSX.Element> {
    method getType (line 29) | static override getType(): string {
    method clone (line 33) | static override clone(node: MentionNode): MentionNode {
    method constructor (line 37) | constructor(mentionData: MentionData, key?: NodeKey) {
    method getMentionData (line 42) | getMentionData(): MentionData {
    method setMentionData (line 46) | setMentionData(mentionData: MentionData): void {
    method createDOM (line 51) | override createDOM(config: EditorConfig): HTMLElement {
    method updateDOM (line 60) | override updateDOM(): false {
    method importDOM (line 64) | static override importDOM(): DOMConversionMap | null {
    method importJSON (line 72) | static override importJSON(serializedNode: SerializedMentionNode): Men...
    method exportDOM (line 78) | override exportDOM(): DOMExportOutput {
    method exportJSON (line 88) | override exportJSON(): SerializedMentionNode {
    method getTextContent (line 99) | override getTextContent(): string {
    method decorate (line 103) | override decorate(editor: LexicalEditor): React.JSX.Element {
    method isInline (line 122) | override isInline(): boolean {
    method isKeyboardSelectable (line 126) | override isKeyboardSelectable(): boolean {
    method canInsertTextBefore (line 130) | canInsertTextBefore(): boolean {
    method canInsertTextAfter (line 134) | canInsertTextAfter(): boolean {
    method canBeEmpty (line 138) | canBeEmpty(): boolean {
    method isSegmented (line 142) | isSegmented(): boolean {
    method extractWithChild (line 146) | extractWithChild(): boolean {
  function $createMentionNode (line 151) | function $createMentionNode(mentionData: MentionData): MentionNode {
  function $isMentionNode (line 156) | function $isMentionNode(node: LexicalNode | null | undefined): node is M...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/MentionPlugin.tsx
  function MentionPlugin (line 13) | function MentionPlugin() {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/components/MentionComponent.tsx
  type MentionComponentProps (line 28) | interface MentionComponentProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/components/MentionDropdown.tsx
  type MentionDropdownProps (line 13) | interface MentionDropdownProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/components/shared/MentionTypeIcon.tsx
  type MentionTypeIconProps (line 6) | interface MentionTypeIconProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/constants.ts
  constant MENTION_COMMAND (line 6) | const MENTION_COMMAND = createCommand<MentionData>("MENTION_COMMAND")
  constant MENTION_TYPEAHEAD_COMMAND (line 7) | const MENTION_TYPEAHEAD_COMMAND = createCommand<string>("MENTION_TYPEAHE...
  constant DEFAULT_MAX_SUGGESTIONS (line 11) | const DEFAULT_MAX_SUGGESTIONS = 10
  constant MENTION_TRIGGER_PATTERN (line 15) | const MENTION_TRIGGER_PATTERN =

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/dateMentionConfig.ts
  constant MAX_INLINE_DATE_SUGGESTIONS (line 6) | const MAX_INLINE_DATE_SUGGESTIONS = 2
  type DateRangeFactory (line 8) | type DateRangeFactory = (today: Dayjs) => DateRange
  type RelativeDateDefinition (line 10) | interface RelativeDateDefinition {
  constant RELATIVE_DATE_DEFINITIONS (line 17) | const RELATIVE_DATE_DEFINITIONS: readonly RelativeDateDefinition[] = [
  type WeekdayPrefix (line 184) | type WeekdayPrefix = "auto" | "this" | "last"
  type WeekdayTranslationDescriptor (line 186) | interface WeekdayTranslationDescriptor {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/dateMentionParsers.ts
  type AiTFunction (line 17) | type AiTFunction = TFunction<"ai">
  type DateMentionBuilderContext (line 19) | interface DateMentionBuilderContext {
  type RelativeDateCandidate (line 24) | interface RelativeDateCandidate {
  constant FUSE_OPTIONS (line 30) | const FUSE_OPTIONS: IFuseOptions<RelativeDateCandidate> = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/dateMentionUtils.ts
  type DateRange (line 11) | interface DateRange {
  type LabelTranslator (line 42) | type LabelTranslator = TFunction<"ai", undefined>

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/useMentionKeyboard.ts
  type UseMentionKeyboardOptions (line 6) | interface UseMentionKeyboardOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/useMentionSearch.ts
  type UseMentionSearchOptions (line 6) | interface UseMentionSearchOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/useMentionSelection.ts
  type UseMentionSelectionOptions (line 6) | interface UseMentionSelectionOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/hooks/useMentionTrigger.ts
  type UseMentionTriggerOptions (line 7) | interface UseMentionTriggerOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/types.ts
  type MentionLabelValue (line 3) | type MentionLabelValue = string | number | boolean | MentionLabelDescriptor
  type MentionLabelDescriptor (line 5) | interface MentionLabelDescriptor {
  type MentionBaseData (line 10) | interface MentionBaseData {
  type EntryMentionData (line 17) | interface EntryMentionData extends MentionBaseData {
  type FeedMentionData (line 22) | interface FeedMentionData extends MentionBaseData {
  type DateMentionData (line 27) | interface DateMentionData extends MentionBaseData {
  type CategoryMentionData (line 35) | interface CategoryMentionData extends MentionBaseData {
  type ViewMentionData (line 40) | interface ViewMentionData extends MentionBaseData {
  type MentionData (line 45) | type MentionData =
  type MentionType (line 52) | type MentionType = MentionData["type"]
  type MentionMatch (line 54) | interface MentionMatch {
  type MentionDropdownPosition (line 60) | interface MentionDropdownPosition {
  type MentionSearchState (line 65) | interface MentionSearchState {
  type MentionTriggerState (line 71) | interface MentionTriggerState {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/mention/utils/mentionTextValue.ts
  function getMentionTextValue (line 13) | function getMentionTextValue(mentionData: {
  function getMentionDisplayTextValue (line 32) | function getMentionDisplayTextValue(

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/selection/SelectedTextNode.tsx
  type SelectedTextNodePayload (line 15) | type SelectedTextNodePayload = {
  type SerializedSelectedTextNode (line 21) | type SerializedSelectedTextNode = Spread<SelectedTextNodePayload, Serial...
  class SelectedTextNode (line 23) | class SelectedTextNode extends DecoratorNode<React.JSX.Element> {
    method getType (line 28) | static override getType(): string {
    method clone (line 32) | static override clone(node: SelectedTextNode): SelectedTextNode {
    method constructor (line 36) | constructor(text: string, sourceEntryId?: string, timestamp?: number, ...
    method getText (line 43) | getText(): string {
    method setText (line 47) | setText(text: string): void {
    method getSourceEntryId (line 52) | getSourceEntryId(): string | undefined {
    method getTimestamp (line 56) | getTimestamp(): number | undefined {
    method createDOM (line 60) | override createDOM(): HTMLElement {
    method updateDOM (line 66) | override updateDOM(): false {
    method importDOM (line 70) | static override importDOM(): DOMConversionMap | null {
    method importJSON (line 74) | static override importJSON(serializedNode: SerializedSelectedTextNode)...
    method exportJSON (line 79) | override exportJSON(): SerializedSelectedTextNode {
    method exportDOM (line 89) | override exportDOM(): DOMExportOutput {
    method decorate (line 96) | override decorate(_editor: LexicalEditor): React.JSX.Element {
    method isInline (line 106) | override isInline(): boolean {
    method isKeyboardSelectable (line 110) | override isKeyboardSelectable(): boolean {
    method getTextContent (line 114) | override getTextContent(): string {
  function $createSelectedTextNode (line 119) | function $createSelectedTextNode(payload: SelectedTextNodePayload): Sele...
  function $isSelectedTextNode (line 123) | function $isSelectedTextNode(
  function escapeXML (line 128) | function escapeXML(text: string): string {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/selection/SelectedTextNodeComponent.tsx
  type SelectedTextNodeComponentProps (line 3) | interface SelectedTextNodeComponentProps {
  function SelectedTextNodeComponent (line 9) | function SelectedTextNodeComponent({ text }: SelectedTextNodeComponentPr...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/selection/SelectedTextPlugin.tsx
  function SelectedTextPlugin (line 8) | function SelectedTextPlugin() {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/selection/insertSelectedTextNode.ts
  function insertSelectedTextNode (line 13) | function insertSelectedTextNode(editor: LexicalEditor, payload: Selected...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/selection/selectedTextBridge.ts
  type Listener (line 3) | type Listener = (payload: SelectedTextNodePayload) => void
  function queueSelectedTextInsertion (line 8) | function queueSelectedTextInsertion(payload: SelectedTextNodePayload) {
  function subscribeSelectedTextInsertion (line 18) | function subscribeSelectedTextInsertion(listener: Listener) {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/components/MentionLikePill.tsx
  type MentionLikePillProps (line 4) | interface MentionLikePillProps extends React.HTMLAttributes<HTMLSpanElem...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/components/TypeaheadDropdown.tsx
  type TypeaheadGroup (line 19) | interface TypeaheadGroup<TItem, TGroupKey = string> {
  type TypeaheadDropdownProps (line 24) | interface TypeaheadDropdownProps<TItem, TGroupKey = string> {
  function useOptionalLexicalEditor (line 51) | function useOptionalLexicalEditor() {
  function TypeaheadDropdown (line 60) | function TypeaheadDropdown<TItem, TGroupKey = string>({

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/hooks/useListKeyboardNavigation.ts
  type UseListKeyboardNavigationOptions (line 13) | interface UseListKeyboardNavigationOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/hooks/useTextTrigger.ts
  type TriggerMatch (line 6) | interface TriggerMatch {
  type UseTextTriggerOptions (line 12) | interface UseTextTriggerOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shared/hooks/useTypeaheadSelection.ts
  type ReplaceHandlerResult (line 6) | interface ReplaceHandlerResult {
  type UseTypeaheadSelectionOptions (line 11) | interface UseTypeaheadSelectionOptions<TMatch, TItem> {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/ShortcutNode.tsx
  type SerializedShortcutNode (line 18) | type SerializedShortcutNode = Spread<
  class ShortcutNode (line 25) | class ShortcutNode extends DecoratorNode<React.JSX.Element> {
    method getType (line 28) | static override getType(): string {
    method clone (line 32) | static override clone(node: ShortcutNode): ShortcutNode {
    method constructor (line 36) | constructor(shortcutData: ShortcutData, key?: NodeKey) {
    method getShortcutData (line 41) | getShortcutData(): ShortcutData {
    method setShortcutData (line 45) | setShortcutData(shortcutData: ShortcutData): void {
    method createDOM (line 50) | override createDOM(config: EditorConfig): HTMLElement {
    method updateDOM (line 58) | override updateDOM(): false {
    method importDOM (line 62) | static override importDOM(): DOMConversionMap | null {
    method importJSON (line 70) | static override importJSON(serializedNode: SerializedShortcutNode): Sh...
    method exportDOM (line 75) | override exportDOM(): DOMExportOutput {
    method exportJSON (line 84) | override exportJSON(): SerializedShortcutNode {
    method getTextContent (line 92) | override getTextContent(): string {
    method decorate (line 96) | override decorate(_editor: LexicalEditor): React.JSX.Element {
    method isInline (line 110) | override isInline(): boolean {
    method isKeyboardSelectable (line 114) | override isKeyboardSelectable(): boolean {
    method canInsertTextBefore (line 118) | canInsertTextBefore(): boolean {
    method canInsertTextAfter (line 122) | canInsertTextAfter(): boolean {
    method canBeEmpty (line 126) | canBeEmpty(): boolean {
    method isSegmented (line 130) | isSegmented(): boolean {
    method extractWithChild (line 134) | extractWithChild(): boolean {
  function $createShortcutNode (line 139) | function $createShortcutNode(shortcutData: ShortcutData): ShortcutNode {
  function $isShortcutNode (line 144) | function $isShortcutNode(node: LexicalNode | null | undefined): node is ...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/ShortcutPlugin.tsx
  function ShortcutPlugin (line 13) | function ShortcutPlugin() {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/components/ShortcutComponent.tsx
  type ShortcutComponentProps (line 9) | interface ShortcutComponentProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/components/ShortcutDropdown.tsx
  type ShortcutDropdownProps (line 10) | interface ShortcutDropdownProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/constants.ts
  constant SHORTCUT_COMMAND (line 5) | const SHORTCUT_COMMAND = createCommand<ShortcutData>("SHORTCUT_COMMAND")
  constant DEFAULT_MAX_SHORTCUT_SUGGESTIONS (line 7) | const DEFAULT_MAX_SHORTCUT_SUGGESTIONS = 10
  constant SHORTCUT_TRIGGER_PATTERN (line 9) | const SHORTCUT_TRIGGER_PATTERN =

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutKeyboard.ts
  type UseShortcutKeyboardOptions (line 6) | interface UseShortcutKeyboardOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutSearch.ts
  type UseShortcutSearchOptions (line 7) | interface UseShortcutSearchOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutSelection.ts
  type UseShortcutSelectionOptions (line 6) | interface UseShortcutSelectionOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/hooks/useShortcutTrigger.ts
  type UseShortcutTriggerOptions (line 7) | interface UseShortcutTriggerOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/types.ts
  type ShortcutData (line 3) | interface ShortcutData {
  type ShortcutMetadata (line 12) | interface ShortcutMetadata {
  type ShortcutMatch (line 17) | interface ShortcutMatch {
  type ShortcutSearchState (line 23) | interface ShortcutSearchState {
  type ShortcutTriggerState (line 29) | interface ShortcutTriggerState {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/editor/plugins/shortcut/utils/shortcutTextValue.ts
  function getShortcutTextValue (line 5) | function getShortcutTextValue(shortcutData: ShortcutData): string {
  function getShortcutDisplayTextValue (line 14) | function getShortcutDisplayTextValue(shortcutData: ShortcutData): string {
  function getShortcutMarkdownValue (line 20) | function getShortcutMarkdownValue(shortcutId: string): string {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useAutoScroll.tsx
  constant BOTTOM_THRESHOLD (line 5) | const BOTTOM_THRESHOLD = 50

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useAutoTimelineSummaryShortcut.ts
  constant ONE_HOUR (line 24) | const ONE_HOUR = 60 * 60 * 1000

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useDisplayBlocks.ts
  type ValueBlockOf (line 5) | type ValueBlockOf<Type extends ValueContextBlock["type"]> = Omit<ValueCo...
  type DisplayBlockItem (line 9) | type DisplayBlockItem =

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useFeedEntrySearchService.ts
  type SearchItem (line 15) | interface SearchItem {
  type SearchServiceOptions (line 24) | interface SearchServiceOptions {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useFileUpload.ts
  type UseFileUploadOptions (line 10) | interface UseFileUploadOptions extends ProcessFileOptions {
  type FileUploadHandlers (line 29) | interface FileUploadHandlers {
  function useFileUpload (line 51) | function useFileUpload(
  function useFileUploadWithDefaults (line 217) | function useFileUploadWithDefaults(): FileUploadHandlers {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useSendAIShortcut.ts
  type ShortcutLike (line 25) | type ShortcutLike = ShortcutData | AIShortcut
  type ShortcutResolver (line 27) | type ShortcutResolver =
  type SendAIShortcutOptions (line 37) | type SendAIShortcutOptions = ShortcutResolver & {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/hooks/useTimelineSummaryAutoContext.ts
  type TimelineSummaryContextParams (line 5) | type TimelineSummaryContextParams = Pick<BizRouteParams, "entryId">

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/services/index.ts
  class AIPersistServiceStatic (line 13) | class AIPersistServiceStatic {
    method markSessionExists (line 18) | private markSessionExists(chatId: string, exists: boolean) {
    method getSessionExistsFromCache (line 22) | private getSessionExistsFromCache(chatId: string): boolean | undefined {
    method clearSessionCache (line 26) | private clearSessionCache(chatId?: string) {
    method loadMessages (line 34) | async loadMessages(chatId: string): Promise<AiChatMessagesModel[]> {
    method hasPersistedMessages (line 41) | async hasPersistedMessages(chatId: string): Promise<boolean> {
    method hasAssistantMessagesMissingMetadata (line 52) | async hasAssistantMessagesMissingMetadata(chatId: string): Promise<boo...
    method convertToUIMessage (line 70) | private convertToUIMessage(dbMessage: AiChatMessagesModel): BizUIMessa...
    method loadUIMessages (line 89) | async loadUIMessages(chatId: string): Promise<BizUIMessage[]> {
    method loadSessionWithMessages (line 98) | async loadSessionWithMessages(chatId: string): Promise<{
    method replaceAllMessages (line 142) | async replaceAllMessages(chatId: string, messages: BizUIMessage[]) {
    method upsertMessages (line 151) | async upsertMessages(chatId: string, messages: BizUIMessage[]) {
    method deleteMessages (line 235) | async deleteMessages(chatId: string, messageIds: string[]) {
    method resolveSessionTitle (line 245) | private resolveSessionTitle(
    method getDefaultSessionTitle (line 258) | private getDefaultSessionTitle(
    method formatDateTime (line 293) | private formatDateTime(date: Date, locale?: string): string {
    method ensureSession (line 320) | async ensureSession(
    method createSession (line 369) | async createSession(
    method findTimelineSummarySession (line 385) | async findTimelineSummarySession(criteria: {
    method getChatSession (line 408) | async getChatSession(chatId: string) {
    method getChatSessions (line 422) | async getChatSessions(limit = 20) {
    method deleteSession (line 472) | async deleteSession(chatId: string) {
    method updateSessionTitle (line 482) | async updateSessionTitle(chatId: string, title: string) {
    method updateSessionTime (line 492) | async updateSessionTime(chatId: string, date: Date = new Date()) {
    method markSessionSynced (line 501) | async markSessionSynced(chatId: string) {
    method cleanupEmptySessions (line 505) | async cleanupEmptySessions() {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/AIChatContext.ts
  type AIPanelRefs (line 9) | type AIPanelRefs = {
  type AIRootStateContext (line 27) | type AIRootStateContext = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/chat-core/chat-actions.ts
  class ChatSliceActions (line 15) | class ChatSliceActions {
    method getActiveInstance (line 25) | static getActiveInstance(): ChatSliceActions | null {
    method setActiveInstance (line 33) | static setActiveInstance(instance: ChatSliceActions | null) {
    method constructor (line 38) | constructor(
    method set (line 53) | get set() {
    method get (line 57) | get get() {
    method computeSyncStatus (line 61) | private computeSyncStatus(isLocal: boolean): "local" | "synced" {
    method setSyncState (line 65) | private setSyncState(isLocal: boolean) {
    method markSessionSynced (line 78) | async markSessionSynced() {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/chat-core/chat-instance.ts
  class ZustandChat (line 9) | class ZustandChat extends AbstractChat<BizUIMessage> {
    method constructor (line 13) | constructor(
    method chatState (line 34) | get chatState() {
    method destroy (line 39) | async destroy(): Promise<void> {
    method setStatus (line 48) | protected override setStatus({ status, error }: { status: ChatStatus; ...

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/chat-core/chat-state.ts
  class ZustandChatState (line 13) | class ZustandChatState implements ChatState<BizUIMessage> {
    method constructor (line 20) | constructor(
    method #setupEventHandlers (line 32) | #setupEventHandlers(): void {
    method status (line 103) | get status(): ChatStatus {
    method status (line 107) | set status(newStatus: ChatStatus) {
    method error (line 114) | get error(): Error | undefined {
    method error (line 118) | set error(newError: Error | undefined) {
    method messages (line 125) | get messages(): BizUIMessage[] {
    method messages (line 129) | set messages(newMessages: BizUIMessage[]) {
    method #fillMessageCreatedAt (line 181) | #fillMessageCreatedAt(message: SendingUIMessage | BizUIMessage): BizUI...
    method destroy (line 201) | destroy(): void {
    method setResumingStream (line 205) | setResumingStream(isResuming: boolean) {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/chat-core/types.ts
  type ChatSlice (line 8) | interface ChatSlice {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/event-system/event-emitter.ts
  class ChatStateEventEmitter (line 5) | class ChatStateEventEmitter {
    method on (line 8) | on<T extends ChatStateEventType>(
    method emit (line 23) | emit<T extends ChatStateEventType>(event: T, payload: ChatStateEvents<...
    method clear (line 33) | clear(): void {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/event-system/types.ts
  type ChatStateEvents (line 6) | interface ChatStateEvents<UI_MESSAGE extends BizUIMessage> {
  type ChatStateEventType (line 12) | type ChatStateEventType = keyof ChatStateEvents<any>

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/slices/block.slice.ts
  type BlockSlice (line 9) | interface BlockSlice {
  class BlockSliceAction (line 27) | class BlockSliceAction {
    method constructor (line 28) | constructor(private params: Parameters<StateCreator<BlockSlice, [], []...
    method set (line 38) | get set() {
    method get (line 42) | get get() {
    method addBlock (line 45) | addBlock(block: AIChatContextBlockInput) {
    method removeBlock (line 63) | removeBlock(id: string) {
    method toggleBlockDisabled (line 75) | toggleBlockDisabled(id: string, disabled?: boolean) {
    method updateBlock (line 91) | updateBlock(id: string, updates: Partial<AIChatContextBlock>) {
    method addOrUpdateBlock (line 110) | addOrUpdateBlock(block: AIChatContextBlock) {
    method clearBlocks (line 119) | clearBlocks({ keepSpecialTypes = false }: { keepSpecialTypes?: boolean...
    method resetContext (line 137) | resetContext() {
    method getBlocks (line 151) | getBlocks() {
    method addFileAttachment (line 156) | addFileAttachment(fileAttachment: FileAttachment) {
    method updateFileAttachment (line 165) | updateFileAttachment(attachmentId: string, updatedAttachment: FileAtta...
    method updateFileAttachmentStatus (line 178) | updateFileAttachmentStatus(
    method removeFileAttachment (line 196) | removeFileAttachment(fileId: string) {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/store.ts
  type AiChatStore (line 9) | type AiChatStore = BlockSlice &

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/transport.ts
  type TitleHandlerPersistOption (line 9) | type TitleHandlerPersistOption = boolean | ((title: string) => void | Pr...
  type TitleHandlerOptions (line 11) | interface TitleHandlerOptions {
  type CreateChatTransportOptions (line 18) | interface CreateChatTransportOptions {
  type CreateChatTitleHandlerOptions (line 23) | interface CreateChatTitleHandlerOptions {
  function createChatTitleHandler (line 30) | function createChatTitleHandler(
  function createChatTransport (line 47) | function createChatTransport({ onValue, titleHandler }: CreateChatTransp...
  type UIMessageChunkParseResult (line 64) | type UIMessageChunkParseResult =
  class ExtendChatTransport (line 91) | class ExtendChatTransport extends HttpChatTransport<BizUIMessage> {
    method constructor (line 92) | constructor(
    method processResponseStream (line 101) | protected processResponseStream(
    method handleGeneratedTitle (line 125) | private async handleGeneratedTitle(chunk: UIMessageChunk) {
    method reconnectToStream (line 163) | override reconnectToStream(

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/store/types.ts
  type FileAttachment (line 4) | interface FileAttachment {
  type BaseContextBlock (line 18) | interface BaseContextBlock {
  type ValueContextBlockType (line 23) | type ValueContextBlockType = "mainView" | "mainEntry" | "mainFeed" | "un...
  type AbstractValueContextBlock (line 24) | interface AbstractValueContextBlock<T extends string> extends BaseContex...
  type ValueContextBlock (line 29) | type ValueContextBlock = AbstractValueContextBlock<ValueContextBlockType>
  type FileAttachmentContextBlock (line 31) | interface FileAttachmentContextBlock extends BaseContextBlock {
  type AIChatContextBlock (line 36) | type AIChatContextBlock = ValueContextBlock | FileAttachmentContextBlock
  type AIChatContextBlockInput (line 39) | type AIChatContextBlockInput =
  type AIChatContextBlockType (line 43) | type AIChatContextBlockType = AIChatContextBlock["type"]
  type AIChatStoreInitial (line 45) | interface AIChatStoreInitial {
  type AIChatContextBlocks (line 53) | interface AIChatContextBlocks {
  type AIDisplayFlowTool (line 57) | type AIDisplayFlowTool = ToolWithState<BizUITools["display_flow_chart"]>
  type BizUIDataTypes (line 60) | type BizUIDataTypes = {
  type BizUIMessage (line 67) | type BizUIMessage = UIMessage<BizUIMetadata, BizUIDataTypes, BizUITools>...
  type BizUIMessagePart (line 71) | type BizUIMessagePart = UIMessagePart<BizUIDataTypes, BizUITools>
  type SendingUIMessage (line 73) | type SendingUIMessage = Omit<BizUIMessage, "createdAt">

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/types/ChatSession.ts
  type ChatSession (line 3) | interface ChatSession {
  type RichTextPart (line 12) | type RichTextPart = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/utils/error.ts
  type ParsedErrorData (line 5) | interface ParsedErrorData {
  type ParsedError (line 12) | interface ParsedError {
  function parseAIError (line 25) | function parseAIError(error: Error | string | undefined): ParsedError {
  function isRateLimitError (line 70) | function isRateLimitError(error: Error | string | undefined): boolean {
  function getErrorMessage (line 79) | function getErrorMessage(error: ParsedError): string {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/utils/extractor.ts
  type AIMessageDataBlockPart (line 3) | type AIMessageDataBlockPart = {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/utils/file-processing.ts
  type ProcessFileOptions (line 7) | interface ProcessFileOptions {
  type ProcessFileResult (line 15) | interface ProcessFileResult {
  function processFile (line 21) | async function processFile(
  type ProcessImageResult (line 78) | interface ProcessImageResult {
  function processImage (line 83) | async function processImage(
  function fileToDataUrl (line 144) | function fileToDataUrl(file: File): Promise<string> {
  function cleanupFileAttachment (line 161) | function cleanupFileAttachment(fileAttachment: FileAttachment) {
  function uploadFileAttachment (line 167) | async function uploadFileAttachment(
  function processAndUploadFile (line 244) | async function processAndUploadFile(

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/utils/file-validation.ts
  constant MAX_IMAGE_ALLOWED_SIZE (line 1) | const MAX_IMAGE_ALLOWED_SIZE = 3 * 1024 * 1024
  constant MAX_DOCUMENT_ALLOWED_SIZE (line 2) | const MAX_DOCUMENT_ALLOWED_SIZE = 1 * 1024 * 1024
  constant SUPPORTED_MIME_ACCEPT (line 3) | const SUPPORTED_MIME_ACCEPT = "image/*,.pdf,.txt,.md"
  constant SUPPORTED_FILE_TYPES (line 4) | const SUPPORTED_FILE_TYPES = {
  type SupportedFileType (line 18) | type SupportedFileType = keyof typeof SUPPORTED_FILE_TYPES
  type FileCategory (line 19) | type FileCategory = (typeof SUPPORTED_FILE_TYPES)[SupportedFileType]["ca...
  type FileValidationError (line 21) | interface FileValidationError {
  type FileValidationResult (line 26) | interface FileValidationResult {
  function validateFile (line 37) | function validateFile(file: File): FileValidationResult {
  function formatFileSize (line 84) | function formatFileSize(bytes: number): string {
  function getFileCategoryFromMimeType (line 94) | function getFileCategoryFromMimeType(mimeType: string): FileCategory {
  function getFileIconName (line 114) | function getFileIconName(category: FileCategory): string {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/utils/mentionDate.ts
  constant MENTION_DATE_VALUE_FORMAT (line 3) | const MENTION_DATE_VALUE_FORMAT = "YYYY-MM-DDTHH:mm:ssZ"
  constant LEGACY_MENTION_DATE_VALUE_FORMAT (line 5) | const LEGACY_MENTION_DATE_VALUE_FORMAT = "YYYY-MM-DD"
  type MentionDateDisplay (line 7) | interface MentionDateDisplay {

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/utils/rate-limit.ts
  type AIConfigLike (line 7) | interface AIConfigLike {
  type RateLimitMessageOptions (line 12) | interface RateLimitMessageOptions {
  function computeIsRateLimited (line 41) | function computeIsRateLimited(
  function computeRateLimitMessage (line 60) | function computeRateLimitMessage(

FILE: apps/desktop/layer/renderer/src/modules/ai-chat/utils/shortcut.ts
  type SerializedNodeWithChildren (line 6) | type SerializedNodeWithChildren = SerializedLexicalNode & {

FILE: apps/desktop/layer/renderer/src/modules/ai-onboarding/ai-chat-pane.tsx
  constant SUGGESTION_KEYS (line 50) | const SUGGESTION_KEYS = [
  constant SUGGESTION_SAMPLE_SIZE (line 67) | const SUGGESTION_SAMPLE_SIZE = 5
  type SuggestionKey (line 69) | type SuggestionKey = (typeof SUGGESTION_KEYS)[number]
  function pickSuggestionKeys (line 71) | function pickSuggestionKeys(previous?: readonly SuggestionKey[]): Sugges...
  function AIChatPane (line 104) | function AIChatPane() {
  function AIChatPaneImpl (line 112) | function AIChatPaneImpl() {
  type WelcomeProps (line 173) | interface WelcomeProps {
  function Welcome (line 177) | function Welcome({ onSuggestionClick }: WelcomeProps) {
  function FinishListener (line 246) | function FinishListener() {
  constant SCROLL_BOTTOM_THRESHOLD (line 261) | const SCROLL_BOTTOM_THRESHOLD = 100
  type AIChatInterfaceProps (line 263) | interface AIChatInterfaceProps {
  function AIChatInterface (line 267) | function AIChatInterface({ inputRef }: AIChatInterfaceProps) {
  constant GRADIENT_COLORS (line 552) | const GRADIENT_COLORS = [
  function gradientByIndex (line 575) | function gradientByIndex(index: number, isDark: boolean) {

FILE: apps/desktop/layer/renderer/src/modules/ai-onboarding/ai-onboarding-modal-content.tsx
  function AiOnboardingModalContent (line 12) | function AiOnboardingModalContent({ onClose }: { onClose: () => void }) {

FILE: apps/desktop/layer/renderer/src/modules/ai-onboarding/feeds-selection-list.tsx
  type FeedToSelect (line 25) | type FeedToSelect = Omit<FeedSelection, "selected">
  function FeedsSelectionList (line 27) | function FeedsSelectionList() {
  function FeedSelectionOperationScreen (line 50) | function FeedSelectionOperationScreen() {
  function FeedSelectionItem (line 140) | function FeedSelectionItem({ feedAtom }: { feedAtom: PrimitiveAtom<FeedS...
  function FeedSelectionFirstScreen (line 201) | function FeedSelectionFirstScreen() {

FILE: apps/desktop/layer/renderer/src/modules/ai-onboarding/store.ts
  type FeedSelection (line 8) | type FeedSelection = {

FILE: apps/desktop/layer/renderer/src/modules/ai-task/components/ai-item-actions.tsx
  type ActionButton (line 5) | interface ActionButton {
  type ItemActionsProps (line 13) | interface ItemActionsProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-task/components/ai-task-modal.tsx
  type AITaskModalProps (line 33) | interface AITaskModalProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-task/components/notify-channels-config.tsx
  type NotifyChannelsConfigProps (line 8) | interface NotifyChannelsConfigProps {
  constant EMAIL_CHANNEL (line 14) | const EMAIL_CHANNEL = {

FILE: apps/desktop/layer/renderer/src/modules/ai-task/components/schedule-config.tsx
  type ScheduleConfigProps (line 18) | interface ScheduleConfigProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-task/components/task-list.tsx
  type TaskListProps (line 8) | interface TaskListProps {

FILE: apps/desktop/layer/renderer/src/modules/ai-task/query.ts
  constant MAX_AI_TASKS (line 13) | const MAX_AI_TASKS = 10
  type OptimisticAITask (line 16) | type OptimisticAITask = WithOptimistic<AITask>

FILE: apps/desktop/layer/renderer/src/modules/ai-task/types.ts
  constant MAX_PROMPT_LENGTH (line 4) | const MAX_PROMPT_LENGTH = 2000
  type ScheduleType (line 28) | type ScheduleType = z.infer<typeof scheduleSchema>
  type AITaskOptions (line 38) | type AITaskOptions = z.infer<typeof aiTaskOptionsSchema>
  type TaskFormData (line 66) | type TaskFormData = z.infer<typeof taskSchema>

FILE: apps/desktop/layer/renderer/src/modules/app-layout/MainDestopLayout.tsx
  function MainDestopLayout (line 139) | function MainDestopLayout() {

FILE: apps/desktop/layer/renderer/src/modules/app-layout/ai-enhanced-timeline/AIEnhancedTimelineLayout.tsx
  constant MIN_ENTRY_WIDTH (line 31) | const MIN_ENTRY_WIDTH = isSafari() ? 356 : 300
  method onResizeStart (line 82) | onResizeStart({ position }) {
  method onResizeEnd (line 85) | onResizeEnd({ position }) {
  method onResizeStart (line 122) | onResizeStart({ position }) {
  method onResizeEnd (line 125) | onResizeEnd({ position }) {

FILE: apps/desktop/layer/renderer/src/modules/app-layout/ai-enhanced-timeline/MobileTimelineLayout.tsx
  type MobileView (line 14) | type MobileView = "list" | "entry"
  type MobileTimelineLayoutProps (line 16) | interface MobileTimelineLayoutProps {

FILE: apps/desktop/layer/renderer/src/modules/app-layout/ai/AIChatFixedPanel.tsx
  type AIChatFixedPanelProps (line 9) | interface AIChatFixedPanelProps extends React.DetailedHTMLProps<

FILE: apps/desktop/layer/renderer/src/modules/app-layout/ai/AIChatFloatingPanel.tsx
  type AIChatFloatingPanelProps (line 20) | interface AIChatFloatingPanelProps extends React.DetailedHTMLProps<

FILE: apps/desktop/layer/renderer/src/modules/app-layout/subscription-column/SubscriptionColumn.tsx
  method onResizeEnd (line 99) | onResizeEnd({ position }) {

FILE: apps/desktop/layer/renderer/src/modules/app-layout/subview/SubviewLayout.tsx
  function SubviewLayout (line 53) | function SubviewLayout() {
  function SubviewLayoutInner (line 74) | function SubviewLayoutInner() {

FILE: apps/desktop/layer/renderer/src/modules/app-layout/subview/hooks.ts
  function useSubViewTitle (line 15) | function useSubViewTitle(title: I18nKeys | ReactNode, fallbackTitleStrin...

FILE: apps/desktop/layer/renderer/src/modules/app-tip/AICopilotMedia.tsx
  type PreviewMessage (line 14) | type PreviewMessage = {

FILE: apps/desktop/layer/renderer/src/modules/app-tip/AppTipDialog.tsx
  type AppTipDialogProps (line 12) | type AppTipDialogProps = {
  function AppTipDialog (line 22) | function AppTipDialog({

FILE: apps/desktop/layer/renderer/src/modules/app-tip/AppTipMediaPreview.tsx
  type AppTipMediaPreviewProps (line 8) | type AppTipMediaPreviewProps = {
  function AppTipMediaPreview (line 12) | function AppTipMediaPreview({ media }: AppTipMediaPreviewProps) {

FILE: apps/desktop/layer/renderer/src/modules/app-tip/AppTipModalContent.tsx
  type AppTipModalContentProps (line 21) | type AppTipModalContentProps = {
  function AppTipModalContent (line 25) | function AppTipModalContent({ initialStep = 0 }: AppTipModalContentProps) {

FILE: apps/desktop/layer/renderer/src/modules/app-tip/constants.ts
  constant APP_TIP_STORAGE_PREFIX (line 1) | const APP_TIP_STORAGE_PREFIX = "follow:ai-onboarding:dismissed"
  constant APP_TIP_DEBUG_EVENT (line 3) | const APP_TIP_DEBUG_EVENT = "follow:ai-onboarding:debug-open"
  constant APP_TIP_DISMISS_EVENT (line 5) | const APP_TIP_DISMISS_EVENT = "follow:ai-onboarding:dismiss-change"

FILE: apps/desktop/layer/renderer/src/modules/app-tip/types.ts
  type AppTipDebugOpenEventDetail (line 1) | type AppTipDebugOpenEventDetail = {
  type AppTipStepMedia (line 6) | type AppTipStepMedia = {
  type AppTipStep (line 15) | type AppTipStep = {

FILE: apps/desktop/layer/renderer/src/modules/app-tip/useNewUserGuideState.ts
  type AppTipDismissChangeDetail (line 9) | type AppTipDismissChangeDetail = {
  function readDismissed (line 76) | function readDismissed(key: string | null) {

FILE: apps/desktop/layer/renderer/src/modules/auth/Form.tsx
  type ElectronAuthResult (line 64) | type ElectronAuthResult = {
  function LoginWithPassword (line 116) | function LoginWithPassword({
  function RegisterForm (line 321) | function RegisterForm({

FILE: apps/desktop/layer/renderer/src/modules/auth/LoginModalContent.tsx
  type LoginModalContentProps (line 23) | interface LoginModalContentProps {

FILE: apps/desktop/layer/renderer/src/modules/auth/TokenModal.tsx
  function onSubmit (line 31) | async function onSubmit(values: z.infer<typeof formSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/command/command-button.tsx
  type CommandButtonProps (line 7) | interface CommandButtonProps<T extends FollowCommand> extends ActionButt...
  type CommandIdButtonProps (line 13) | interface CommandIdButtonProps<

FILE: apps/desktop/layer/renderer/src/modules/command/commands/entry-render.tsx
  type EventBusMap (line 9) | interface EventBusMap {
  type EntryScrollDownCommand (line 70) | type EntryScrollDownCommand = Command<{
  type EntryScrollUpCommand (line 75) | type EntryScrollUpCommand = Command<{
  type EntryNextEntryCommand (line 80) | type EntryNextEntryCommand = Command<{
  type EntryPreviousEntryCommand (line 85) | type EntryPreviousEntryCommand = Command<{
  type EntryRenderCommand (line 90) | type EntryRenderCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/commands/entry.tsx
  type StarCommand (line 370) | type StarCommand = Command<{
  type DeleteCommand (line 375) | type DeleteCommand = Command<{
  type CopyLinkCommand (line 380) | type CopyLinkCommand = Command<{
  type ExportAsPDFCommand (line 385) | type ExportAsPDFCommand = Command<{
  type CopyTitleCommand (line 390) | type CopyTitleCommand = Command<{
  type OpenInBrowserCommand (line 395) | type OpenInBrowserCommand = Command<{
  type ViewSourceContentCommand (line 400) | type ViewSourceContentCommand = Command<{
  type ShareCommand (line 405) | type ShareCommand = Command<{
  type ReadCommand (line 410) | type ReadCommand = Command<{
  type ReadAboveCommand (line 415) | type ReadAboveCommand = Command<{
  type ReadBelowCommand (line 420) | type ReadBelowCommand = Command<{
  type ToggleAITranslationCommand (line 425) | type ToggleAITranslationCommand = Command<{
  type ImageGalleryCommand (line 430) | type ImageGalleryCommand = Command<{
  type TTSCommand (line 435) | type TTSCommand = Command<{
  type ReadabilityCommand (line 440) | type ReadabilityCommand = Command<{
  type EntryCommand (line 445) | type EntryCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/commands/global.tsx
  type EventBusMap (line 13) | interface EventBusMap {
  type ShowShortcutsCommand (line 88) | type ShowShortcutsCommand = Command<{
  type ToggleCornerPlayCommand (line 93) | type ToggleCornerPlayCommand = Command<{
  type QuickAddCommand (line 98) | type QuickAddCommand = Command<{
  type ToggleAIChatCommand (line 103) | type ToggleAIChatCommand = Command<{
  type QuickSearchCommand (line 108) | type QuickSearchCommand = Command<{
  type GlobalCommand (line 113) | type GlobalCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/commands/id.ts
  constant COMMAND_ID (line 1) | const COMMAND_ID = {

FILE: apps/desktop/layer/renderer/src/modules/command/commands/integration.tsx
  function extractQBittorrentUrls (line 707) | function extractQBittorrentUrls(entry: EntryModel) {
  type SaveToEagleCommand (line 845) | type SaveToEagleCommand = Command<{
  type SaveToReadwiseCommand (line 850) | type SaveToReadwiseCommand = Command<{
  type SaveToInstapaperCommand (line 855) | type SaveToInstapaperCommand = Command<{
  type SaveToObsidianCommand (line 860) | type SaveToObsidianCommand = Command<{
  type SaveToOutlineCommand (line 865) | type SaveToOutlineCommand = Command<{
  type SaveToReadeckCommand (line 870) | type SaveToReadeckCommand = Command<{
  type SaveToCuboxCommand (line 875) | type SaveToCuboxCommand = Command<{
  type SaveToZoteroCommand (line 880) | type SaveToZoteroCommand = Command<{
  type SaveToQBittorrentCommand (line 885) | type SaveToQBittorrentCommand = Command<{
  type CustomIntegrationCommand (line 890) | type CustomIntegrationCommand = Command<{
  type IntegrationCommand (line 895) | type IntegrationCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/commands/layout.tsx
  type FocusEvent (line 10) | interface FocusEvent {
  type EventBusMap (line 14) | interface EventBusMap {
  type FocusToSubscriptionCommand (line 63) | type FocusToSubscriptionCommand = Command<{
  type ToggleTimelineColumnCommand (line 68) | type ToggleTimelineColumnCommand = Command<{
  type FocusToTimelineCommand (line 73) | type FocusToTimelineCommand = Command<{
  type FocusToEntryRenderCommand (line 77) | type FocusToEntryRenderCommand = Command<{
  type LayoutCommand (line 82) | type LayoutCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/commands/settings.tsx
  method run (line 26) | run() {
  method run (line 45) | run() {
  method run (line 55) | run() {
  method run (line 65) | run() {
  type CustomizeToolbarCommand (line 72) | type CustomizeToolbarCommand = Command<{
  type SettingsCommand (line 77) | type SettingsCommand = CustomizeToolbarCommand

FILE: apps/desktop/layer/renderer/src/modules/command/commands/subscription.tsx
  type EventBusMap (line 12) | interface EventBusMap {
  type SwitchTabToNextCommand (line 190) | type SwitchTabToNextCommand = Command<{
  type SwitchTabToPreviousCommand (line 195) | type SwitchTabToPreviousCommand = Command<{
  type SwitchTabToArticleCommand (line 200) | type SwitchTabToArticleCommand = Command<{
  type SwitchTabToSocialCommand (line 205) | type SwitchTabToSocialCommand = Command<{
  type SwitchTabToPictureCommand (line 210) | type SwitchTabToPictureCommand = Command<{
  type SwitchTabToVideoCommand (line 215) | type SwitchTabToVideoCommand = Command<{
  type SwitchTabToAudioCommand (line 220) | type SwitchTabToAudioCommand = Command<{
  type SwitchTabToNotificationCommand (line 225) | type SwitchTabToNotificationCommand = Command<{
  type NextSubscriptionCommand (line 230) | type NextSubscriptionCommand = Command<{
  type PreviousSubscriptionCommand (line 235) | type PreviousSubscriptionCommand = Command<{
  type ToggleFolderCollapseCommand (line 240) | type ToggleFolderCollapseCommand = Command<{
  type MarkAllAsReadCommand (line 245) | type MarkAllAsReadCommand = Command<{
  type OpenInBrowserCommand (line 250) | type OpenInBrowserCommand = Command<{
  type OpenSiteInBrowserCommand (line 255) | type OpenSiteInBrowserCommand = Command<{
  type SubscriptionCommand (line 260) | type SubscriptionCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/commands/timeline.tsx
  type EventBusMap (line 11) | interface EventBusMap {
  type SwitchToNextTimelineCommand (line 71) | type SwitchToNextTimelineCommand = Command<{
  type SwitchToPreviousTimelineCommand (line 76) | type SwitchToPreviousTimelineCommand = Command<{
  type RefetchTimelineCommand (line 81) | type RefetchTimelineCommand = Command<{
  type UnreadOnlyTimelineCommand (line 86) | type UnreadOnlyTimelineCommand = Command<{
  type TimelineCommand (line 91) | type TimelineCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/commands/types.ts
  type BasicCommand (line 12) | type BasicCommand =

FILE: apps/desktop/layer/renderer/src/modules/command/hooks/use-command-binding.ts
  type ExtractSetType (line 105) | type ExtractSetType<T extends Set<unknown>> = T extends Set<infer U> ? U...
  type AllowCustomizeCommandId (line 106) | type AllowCustomizeCommandId = ExtractSetType<typeof allowCustomizeComma...
  type BindingCommandId (line 107) | type BindingCommandId = keyof typeof defaultCommandShortcuts

FILE: apps/desktop/layer/renderer/src/modules/command/hooks/use-command.ts
  function useCommand (line 20) | function useCommand<T extends FollowCommandId>(id: T): FollowCommandMap[...
  function useRunCommandFn (line 38) | function useRunCommandFn() {

FILE: apps/desktop/layer/renderer/src/modules/command/hooks/use-register-command.ts
  type RegisterOptions (line 7) | type RegisterOptions = {
  function useRegisterFollowCommand (line 62) | function useRegisterFollowCommand(

FILE: apps/desktop/layer/renderer/src/modules/command/hooks/use-register-hotkey.ts
  type HotkeyOptions (line 9) | interface HotkeyOptions {
  type RegisterHotkeyOptions (line 12) | interface RegisterHotkeyOptions<T extends FollowCommandId> {
  constant SPECIAL_KEYS_MAPPINGS (line 21) | const SPECIAL_KEYS_MAPPINGS = {

FILE: apps/desktop/layer/renderer/src/modules/command/mutation-command-ids.ts
  constant MUTATION_COMMAND_IDS (line 4) | const MUTATION_COMMAND_IDS = new Set<FollowCommandId>([
  constant MUTATION_PREFIXES (line 24) | const MUTATION_PREFIXES = ["integration:custom:"]

FILE: apps/desktop/layer/renderer/src/modules/command/registry/command.ts
  function createCommand (line 11) | function createCommand<
  function createFollowCommand (line 36) | function createFollowCommand<T extends FollowCommand>(
  function defineFollowCommand (line 42) | function defineFollowCommand<T extends FollowCommandId>(

FILE: apps/desktop/layer/renderer/src/modules/command/registry/registry.ts
  method commands (line 12) | get commands() {
  method register (line 28) | register(options: CommandOptions) {
  method has (line 41) | has(id: string): boolean {
  method get (line 45) | get(id: string): Command | undefined {
  method getAll (line 53) | getAll(): Command[] {
  method run (line 57) | run(id: string, ...args: unknown[]) {
  function registerCommand (line 65) | function registerCommand(options: CommandOptions) {

FILE: apps/desktop/layer/renderer/src/modules/command/types.ts
  type ExtractCategory (line 5) | type ExtractCategory<T extends string> = T extends `category.${string}` ...
  type CommandCategory (line 6) | type CommandCategory = ExtractCategory<Parameters<typeof tShortcuts>[0]>
  type KeybindingOptions (line 8) | interface KeybindingOptions {
  type Command (line 16) | interface Command<
  type SimpleCommand (line 32) | type SimpleCommand<T extends string> = Command<{ id: T; fn: () => void }>
  type CommandOptions (line 34) | interface CommandOptions<
  type FollowCommandMap (line 55) | type FollowCommandMap = {
  type UnknownCommand (line 63) | type UnknownCommand = Command<{
  type FollowCommandId (line 68) | type FollowCommandId = FollowCommand["id"]
  type FollowCommand (line 69) | type FollowCommand = BasicCommand | UnknownCommand

FILE: apps/desktop/layer/renderer/src/modules/customize-toolbar/constant.ts
  type ToolbarActionOrder (line 5) | interface ToolbarActionOrder {
  constant ENTRY_ITEM_HIDE_IN_HEADER (line 10) | const ENTRY_ITEM_HIDE_IN_HEADER = new Set<UniqueIdentifier>([
  constant MAIN_ACTIONS (line 16) | const MAIN_ACTIONS = [
  constant MAIN_ACTIONS_SET (line 25) | const MAIN_ACTIONS_SET = new Set<UniqueIdentifier>(MAIN_ACTIONS)
  constant DEFAULT_ACTION_ORDER (line 27) | const DEFAULT_ACTION_ORDER: ToolbarActionOrder = {

FILE: apps/desktop/layer/renderer/src/modules/customize-toolbar/dnd.tsx
  function DroppableContainer (line 96) | function DroppableContainer({ children }: { children: ReactNode }) {

FILE: apps/desktop/layer/renderer/src/modules/debug/registry.ts
  class Registry (line 1) | class Registry {
    method add (line 4) | add(key: string, action: () => void) {
    method remove (line 12) | remove(key: string) {
    method getAll (line 16) | getAll() {
    method get (line 20) | get(key: string) {

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoverFeedCard.tsx
  function FeedCardActions (line 20) | function FeedCardActions<T extends TrendingFeedItem | DiscoveryItem>({
  type DiscoverFeedCardProps (line 84) | interface DiscoverFeedCardProps {

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoverFeedForm.tsx
  type RouteParams (line 79) | type RouteParams = Record<

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoverForm.tsx
  constant FEED_DISCOVERY_INFO (line 36) | const FEED_DISCOVERY_INFO = {
  function DiscoverForm (line 100) | function DiscoverForm({ type = "search" }: { type?: string }) {

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoverImport.tsx
  function DiscoverImport (line 36) | function DiscoverImport() {

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoverInboxList.tsx
  function DiscoverInboxList (line 10) | function DiscoverInboxList() {

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoverTransform.tsx
  function DiscoverTransform (line 48) | function DiscoverTransform() {

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoverUser.tsx
  function DiscoverUser (line 8) | function DiscoverUser() {

FILE: apps/desktop/layer/renderer/src/modules/discover/DiscoveryContent.tsx
  type Language (line 31) | type Language = "all" | "eng" | "cmn" | "fra"
  type DiscoveryView (line 32) | type DiscoveryView = "trending" | "categories"
  function DiscoveryContent (line 34) | function DiscoveryContent() {

FILE: apps/desktop/layer/renderer/src/modules/discover/FeedForm.tsx
  type FeedFormDataValuesType (line 59) | type FeedFormDataValuesType = z.infer<typeof formSchema>
  method onError (line 310) | onError(err) {
  function onSubmit (line 315) | function onSubmit(values: z.infer<typeof formSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/discover/FeedSummary.tsx
  type FeedSummaryProps (line 13) | interface FeedSummaryProps {

FILE: apps/desktop/layer/renderer/src/modules/discover/InboxForm.tsx
  function onSubmit (line 110) | function onSubmit(values: z.infer<typeof formSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/discover/ListForm.tsx
  type ListFormDataValuesType (line 50) | type ListFormDataValuesType = z.infer<typeof formSchema>
  method onError (line 241) | async onError(err) {
  function onSubmit (line 247) | function onSubmit(values: z.infer<typeof formSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/discover/OpmlAbstractGraphic.tsx
  constant RSS_READERS (line 8) | const RSS_READERS = [
  function OpmlAbstractGraphic (line 21) | function OpmlAbstractGraphic({ className }: { className?: string }) {

FILE: apps/desktop/layer/renderer/src/modules/discover/OpmlSelectionModal.tsx
  method onError (line 84) | async onError(err) {

FILE: apps/desktop/layer/renderer/src/modules/discover/UnifiedDiscoverForm.tsx
  function detectInputType (line 49) | function detectInputType(value: string): "rss" | "rsshub" | "search" {
  type SearchFormData (line 75) | type SearchFormData = z.infer<typeof searchSchema>
  type ToolLinkProps (line 78) | interface ToolLinkProps {
  function ToolLink (line 84) | function ToolLink({ icon, label, onClick }: ToolLinkProps) {
  function UnifiedDiscoverForm (line 101) | function UnifiedDiscoverForm() {

FILE: apps/desktop/layer/renderer/src/modules/discover/recommendations.tsx
  function Recommendations (line 6) | function Recommendations() {

FILE: apps/desktop/layer/renderer/src/modules/discover/types.ts
  type ParsedFeedItem (line 1) | type ParsedFeedItem = {

FILE: apps/desktop/layer/renderer/src/modules/download/index.tsx
  function DownloadPage (line 8) | function DownloadPage() {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/all-item.tsx
  function AllItem (line 70) | function AllItem({ entryId, translation, currentFeedTitle }: UniversalIt...
  function AllItemStateLess (line 210) | function AllItemStateLess({ entry, feed }: EntryItemStatelessProps) {
  function AudioIcon (line 247) | function AudioIcon({ entryId, src }: { entryId: string; src: string }) {
  function VideoIcon (line 289) | function VideoIcon({ src }: { src: string }) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/article-item.tsx
  function ArticleItem (line 13) | function ArticleItem({ entryId, translation }: UniversalItemProps) {
  function ArticleItemStateLess (line 19) | function ArticleItemStateLess({ entry, feed }: EntryItemStatelessProps) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/audio-item.tsx
  function AudioItem (line 8) | function AudioItem({ entryId, translation }: UniversalItemProps) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/notification-item.tsx
  function NotificationItem (line 11) | function NotificationItem({ entryId, translation }: UniversalItemProps) {
  function NotificationItemStateLess (line 17) | function NotificationItemStateLess({ entry, feed }: EntryItemStatelessPr...

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-item-stateless.tsx
  function PictureItemStateLess (line 14) | function PictureItemStateLess({ entry }: EntryItemStatelessProps) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-item.tsx
  function PictureItem (line 23) | function PictureItem({ entryId, translation }: UniversalItemProps) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/picture-masonry.tsx
  function scrollOutViewMarkRead (line 156) | function scrollOutViewMarkRead(entries: IntersectionObserverEntry[]) {
  function renderInViewMarkRead (line 187) | function renderInViewMarkRead(entries: IntersectionObserverEntry[]) {
  type MasonryProps (line 305) | interface MasonryProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/social-media-item.tsx
  function SocialMediaItemStateLess (line 159) | function SocialMediaItemStateLess({ entry, feed }: EntryItemStatelessPro...

FILE: apps/desktop/layer/renderer/src/modules/entry-column/Items/video-item.tsx
  function VideoItem (line 21) | function VideoItem({ entryId, translation }: UniversalItemProps) {
  function VideoItemStateLess (line 134) | function VideoItemStateLess({ entry, feed }: EntryItemStatelessProps) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/components/DateItem.tsx
  type DateItemInnerProps (line 15) | interface DateItemInnerProps {
  type DateItemProps (line 22) | type DateItemProps = Pick<DateItemInnerProps, "isSticky"> & {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/components/FooterMarkItem.tsx
  type FooterMarkItemProps (line 28) | interface FooterMarkItemProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/components/VirtualRowItem.tsx
  type VirtualRowItemProps (line 12) | interface VirtualRowItemProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/components/ai-timeline-loading/AITimelineLoadingOverlay.tsx
  type Props (line 7) | type Props = {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/components/entry-column-wrapper/types.tsx
  type EntryColumnWrapperProps (line 1) | interface EntryColumnWrapperProps extends ComponentType {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/components/mark-all-button.tsx
  type MarkAllButtonProps (line 24) | interface MarkAllButtonProps {
  method onAutoClose (line 68) | onAutoClose() {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/context/EntriesContext.tsx
  type EntriesStateContextValue (line 8) | type EntriesStateContextValue = {
  type EntriesActionsContextValue (line 21) | type EntriesActionsContextValue = {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/hooks/useAttachScrollBeyond.tsx
  constant DEFAULT_THRESHOLD (line 6) | const DEFAULT_THRESHOLD = 30

FILE: apps/desktop/layer/renderer/src/modules/entry-column/hooks/useEntriesByView.ts
  function getEntryIdsFromMultiplePlace (line 112) | function getEntryIdsFromMultiplePlace(...entryIds: Array<string[] | unde...

FILE: apps/desktop/layer/renderer/src/modules/entry-column/hooks/useEntryMarkReadHandler.tsx
  function batchMarkRead (line 47) | function batchMarkRead(ids: string[]) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/hooks/useEntryVirtualization.ts
  type UseEntryVirtualizationOptions (line 9) | interface UseEntryVirtualizationOptions {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/hooks/useLocalEntries.ts
  type UseLocalEntriesOptions (line 19) | interface UseLocalEntriesOptions {
  function getEntryIdsFromMultiplePlace (line 28) | function getEntryIdsFromMultiplePlace(...entryIds: Array<string[] | unde...

FILE: apps/desktop/layer/renderer/src/modules/entry-column/hooks/useMarkAll.ts
  type MarkAllFilter (line 7) | type MarkAllFilter =

FILE: apps/desktop/layer/renderer/src/modules/entry-column/hooks/useWheelGestureClose.ts
  type UseWheelGestureCloseOptions (line 6) | interface UseWheelGestureCloseOptions {
  type UseWheelGestureCloseReturn (line 13) | interface UseWheelGestureCloseReturn {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/index.tsx
  function EntryColumnContent (line 38) | function EntryColumnContent() {
  function EntryColumnImpl (line 206) | function EntryColumnImpl() {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/item.tsx
  type EntryItemProps (line 14) | interface EntryItemProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/list.tsx
  type EntryListProps (line 49) | type EntryListProps = {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/store/EntryColumnContext.ts
  type EntryRootStateContext (line 4) | type EntryRootStateContext = {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/templates/grid-item-template.tsx
  type GridItemProps (line 22) | interface GridItemProps extends UniversalItemProps {
  function GridItem (line 26) | function GridItem(props: GridItemProps) {

FILE: apps/desktop/layer/renderer/src/modules/entry-column/templates/list-item-template.tsx
  function ListItem (line 55) | function ListItem({
  function AudioCover (line 291) | function AudioCover({

FILE: apps/desktop/layer/renderer/src/modules/entry-column/types.ts
  type UniversalItemProps (line 6) | type UniversalItemProps = {
  type EntryListItemFC (line 12) | type EntryListItemFC<P extends object = object> = FC<P & UniversalItemPr...
  type EntryItemStatelessProps (line 16) | type EntryItemStatelessProps = {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/AISummary.tsx
  function AISummary (line 16) | function AISummary({ entryId }: { entryId: string }) {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/EntryAttachments.tsx
  constant SUPPORTED_MIME_TYPES (line 10) | const SUPPORTED_MIME_TYPES = new Set(["application/x-bittorrent"])
  function EntryAttachments (line 12) | function EntryAttachments({ entryId }: { entryId: string }) {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/EntryTitle.tsx
  type EntryLinkProps (line 23) | interface EntryLinkProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/WarnGoToExternalLink.tsx
  function open (line 85) | function open() {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/EntryContentFallback.tsx
  type EntryContentFallbackProps (line 8) | interface EntryContentFallbackProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/accessories/index.tsx
  type EntryContentAccessoriesRef (line 7) | type EntryContentAccessoriesRef = {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-content/types.tsx
  type EntryContentProps (line 2) | interface EntryContentProps {
  type EntryContentClassNames (line 8) | interface EntryContentClassNames {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/AIEntryHeader.tsx
  function EntryHeaderImpl (line 9) | function EntryHeaderImpl({ entryId, className, compact }: EntryHeaderPro...

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/EntryHeader.tsx
  function EntryHeaderImpl (line 9) | function EntryHeaderImpl({ entryId, className, compact }: EntryHeaderPro...

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderActionsContainer.tsx
  function EntryHeaderActionsContainerImpl (line 10) | function EntryHeaderActionsContainerImpl({ isSmallWidth }: { isSmallWidt...

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderBreadcrumb.tsx
  function ViewSubscriptionsDropdown (line 39) | function ViewSubscriptionsDropdown({
  function FeedEntriesDropdown (line 151) | function FeedEntriesDropdown({
  function EntryHeaderBreadcrumb (line 223) | function EntryHeaderBreadcrumb() {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderMeta.tsx
  function EntryHeaderMetaImpl (line 6) | function EntryHeaderMetaImpl() {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/EntryHeaderReadHistory.tsx
  function EntryHeaderReadHistoryImpl (line 12) | function EntryHeaderReadHistoryImpl({ className }: { className?: string ...

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/internal/context.tsx
  type EntryHeaderContextValue (line 12) | interface EntryHeaderContextValue {
  function useEntryHeaderContext (line 18) | function useEntryHeaderContext() {
  type EntryHeaderRootProps (line 24) | interface EntryHeaderRootProps extends EntryHeaderProps {
  function EntryHeaderRootImpl (line 29) | function EntryHeaderRootImpl({

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/entry-header/types.tsx
  type EntryHeaderProps (line 1) | interface EntryHeaderProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/factory.ts
  type EntryLayoutComponent (line 9) | type EntryLayoutComponent = FC<EntryLayoutProps>

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/AudioPlayer.tsx
  type AudioPlayerProps (line 11) | interface AudioPlayerProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/AuthorHeader.tsx
  type AuthorHeaderProps (line 11) | interface AuthorHeaderProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/ContentBody.tsx
  type ContentBodyProps (line 8) | interface ContentBodyProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/MediaTranscript.tsx
  constant MAX_PARAGRAPH_LENGTH (line 7) | const MAX_PARAGRAPH_LENGTH = 300
  type SubtitleItem (line 9) | interface SubtitleItem {
  type MediaTranscriptProps (line 18) | interface MediaTranscriptProps {
  function srtTimeToSeconds (line 32) | function srtTimeToSeconds(timeString: string): number {
  function formatTimeString (line 53) | function formatTimeString(seconds: number): string {
  function processEnglishContent (line 64) | function processEnglishContent(allText: string): string[] {
  function calculateEnglishTiming (line 119) | function calculateEnglishTiming(
  function parseSrt (line 243) | function parseSrt(srtText: string): SubtitleItem[] {
  function formatTime (line 326) | function formatTime(timeString: string): string {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/TranscriptToggle.tsx
  type TranscriptToggleProps (line 3) | interface TranscriptToggleProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/shared/VideoPlayer.tsx
  type VideoPlayerProps (line 24) | interface VideoPlayerProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/layouts/types.ts
  type EntryLayoutProps (line 4) | interface EntryLayoutProps {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/selection/GlassButton.tsx
  type GlassButtonProps (line 5) | interface GlassButtonProps extends HTMLMotionProps<"button"> {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/selection/SharePosterModal.tsx
  type SharePosterModalProps (line 16) | type SharePosterModalProps = {
  type Mode (line 21) | type Mode = "light" | "dark"
  function SharePosterModal (line 23) | function SharePosterModal({ selectedText, entryId }: SharePosterModalPro...
  function loadImage (line 475) | function loadImage(url: string): Promise<HTMLImageElement | null> {
  function wrapText (line 485) | function wrapText(ctx: CanvasRenderingContext2D, text: string, maxWidth:...

FILE: apps/desktop/layer/renderer/src/modules/entry-content/components/selection/TextSelectionToolbar.tsx
  type TextSelectionToolbarProps (line 29) | type TextSelectionToolbarProps = {
  constant DEFAULT_DIMENSIONS (line 36) | const DEFAULT_DIMENSIONS = {
  constant VIEWPORT_PADDING (line 41) | const VIEWPORT_PADDING = 12
  function TextSelectionToolbar (line 43) | function TextSelectionToolbar({
  type ToolbarButtonProps (line 181) | type ToolbarButtonProps = {
  function ToolbarButton (line 188) | function ToolbarButton({ iconClassName, label, onClick, active }: Toolba...
  function getViewport (line 209) | function getViewport() {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/constants/navigation-hints.ts
  constant NAVIGATION_HINTS_CONSTANTS (line 4) | const NAVIGATION_HINTS_CONSTANTS = {
  constant NAVIGATION_HINTS_TEXT (line 27) | const NAVIGATION_HINTS_TEXT = {
  constant NAVIGATION_HINTS_ICONS (line 35) | const NAVIGATION_HINTS_ICONS = {

FILE: apps/desktop/layer/renderer/src/modules/entry-content/hooks/useEntryNavigationHints.ts
  type UseEntryNavigationHintsOptions (line 8) | interface UseEntryNavigationHintsOptions {
  type UseEntryNavigationHintsReturn (line 17) | interface UseEntryNavigationHintsReturn {

FILE: apps/desktop/layer/renderer/src/modules/feed/feed-icon.tsx
  type GetIconPropsProps (line 24) | type GetIconPropsProps = {
  function getIconProps (line 34) | function getIconProps(props: GetIconPropsProps) {
  type IconTarget (line 171) | type IconTarget = {
  type FeedIconEntry (line 184) | type FeedIconEntry = { authorAvatar?: string | null; firstPhotoUrl?: str...
  function FeedIcon (line 191) | function FeedIcon({

FILE: apps/desktop/layer/renderer/src/modules/feed/feed-summary.tsx
  function FollowSummary (line 19) | function FollowSummary({

FILE: apps/desktop/layer/renderer/src/modules/integration/CustomIntegrationPreview.tsx
  type CustomIntegrationPreviewProps (line 9) | interface CustomIntegrationPreviewProps {

FILE: apps/desktop/layer/renderer/src/modules/integration/CustomIntegrationValidator.tsx
  type CustomIntegrationValidatorProps (line 7) | interface CustomIntegrationValidatorProps {

FILE: apps/desktop/layer/renderer/src/modules/integration/PlaceholderHelp.tsx
  type PlaceholderHelpProps (line 8) | interface PlaceholderHelpProps {

FILE: apps/desktop/layer/renderer/src/modules/integration/URLSchemePreview.tsx
  type URLSchemePreviewProps (line 9) | interface URLSchemePreviewProps {

FILE: apps/desktop/layer/renderer/src/modules/integration/custom-integration-manager.ts
  type PlaceholderContext (line 21) | interface PlaceholderContext {
  class CustomIntegrationManager (line 36) | class CustomIntegrationManager {
    method getEntryContentAsMarkdown (line 40) | private static async getEntryContentAsMarkdown(entry: EntryModel): Pro...
    method getDescription (line 61) | private static getDescription(entry: EntryModel): string {
    method buildPlaceholderContext (line 75) | static async buildPlaceholderContext(entry: EntryModel): Promise<Place...
    method getAvailablePlaceholders (line 93) | static getAvailablePlaceholders(): Array<{ key: string; description: s...
    method replacePlaceholders (line 113) | static replacePlaceholders(
    method processFetchTemplate (line 167) | static async processFetchTemplate(
    method executeIntegration (line 206) | static async executeIntegration(
    method executeWithToast (line 311) | static async executeWithToast(integration: CustomIntegration, entry: E...
    method validateFetchTemplate (line 328) | static validateFetchTemplate(fetchTemplate: FetchTemplate): { valid: b...
    method validateURLSchemeTemplate (line 363) | static validateURLSchemeTemplate(template: URLSchemeTemplate): {
    method validateCustomIntegration (line 396) | static validateCustomIntegration(integration: Partial<CustomIntegratio...
    method getTemplatePreview (line 436) | static async getTemplatePreview(
    method getURLSchemePreview (line 463) | static getURLSchemePreview(

FILE: apps/desktop/layer/renderer/src/modules/integration/fetch-adapter.ts
  type FetchRequestOptions (line 11) | interface FetchRequestOptions {
  type FetchResponse (line 21) | interface FetchResponse {
  class BrowserFetchAdapter (line 40) | class BrowserFetchAdapter extends BaseFetchAdapter {
    method fetch (line 41) | async fetch(url: string, options?: FetchRequestOptions): Promise<Fetch...
  class ElectronFetchAdapter (line 81) | class ElectronFetchAdapter extends BaseFetchAdapter {
    method fetch (line 82) | async fetch(url: string, options?: FetchRequestOptions): Promise<Fetch...
  class FetchAdapterManager (line 115) | class FetchAdapterManager {
    method constructor (line 120) | private constructor() {
    method getInstance (line 133) | static getInstance(): FetchAdapterManager {
    method preferElectronFetch (line 143) | preferElectronFetch() {
    method preferClientFetch (line 150) | preferClientFetch() {
    method createAdapter (line 158) | private createAdapter(): BaseFetchAdapter {
    method fetch (line 171) | async fetch(url: string, options?: FetchRequestOptions): Promise<Fetch...

FILE: apps/desktop/layer/renderer/src/modules/integration/url-scheme-handler.ts
  class URLSchemeHandler (line 6) | class URLSchemeHandler {
    method getInstance (line 9) | static getInstance(): URLSchemeHandler {
    method replacePlaceholders (line 19) | private replacePlaceholders(template: string, data: Record<string, str...
    method executeURLScheme (line 35) | async executeURLScheme(
    method openURLScheme (line 69) | private async openURLScheme(scheme: string): Promise<void> {
    method canExecuteURLScheme (line 86) | canExecuteURLScheme(): boolean {
    method getExamples (line 95) | static getExamples(): { name: string; scheme: string; description: str...

FILE: apps/desktop/layer/renderer/src/modules/new-user-guide/ai-chat-pane.tsx
  constant SUGGESTION_KEYS (line 45) | const SUGGESTION_KEYS = [
  constant SUGGESTION_SAMPLE_SIZE (line 62) | const SUGGESTION_SAMPLE_SIZE = 5
  type SuggestionKey (line 64) | type SuggestionKey = (typeof SUGGESTION_KEYS)[number]
  function pickSuggestionKeys (line 66) | function pickSuggestionKeys(previous?: readonly SuggestionKey[]): Sugges...
  function AIChatPane (line 99) | function AIChatPane() {
  function AIChatPaneImpl (line 107) | function AIChatPaneImpl() {
  type WelcomeProps (line 165) | interface WelcomeProps {
  function Welcome (line 169) | function Welcome({ onSuggestionClick }: WelcomeProps) {
  function FinishListener (line 232) | function FinishListener() {
  constant SCROLL_BOTTOM_THRESHOLD (line 247) | const SCROLL_BOTTOM_THRESHOLD = 100
  type AIChatInterfaceProps (line 249) | interface AIChatInterfaceProps {
  function AIChatInterface (line 253) | function AIChatInterface({ inputRef }: AIChatInterfaceProps) {
  constant GRADIENT_COLORS (line 535) | const GRADIENT_COLORS = [
  function gradientByIndex (line 558) | function gradientByIndex(index: number, isDark: boolean) {

FILE: apps/desktop/layer/renderer/src/modules/new-user-guide/discover-import-step.tsx
  function DiscoverImportStep (line 9) | function DiscoverImportStep() {

FILE: apps/desktop/layer/renderer/src/modules/new-user-guide/feeds-selection-list.tsx
  type FeedToSelect (line 25) | type FeedToSelect = Omit<FeedSelection, "selected">
  function FeedsSelectionList (line 43) | function FeedsSelectionList() {
  function FeedSelectionOperationScreen (line 59) | function FeedSelectionOperationScreen() {
  function FeedSelectionItem (line 133) | function FeedSelectionItem({ feedAtom }: { feedAtom: PrimitiveAtom<FeedS...
  function FeedSelectionFirstScreen (line 194) | function FeedSelectionFirstScreen() {

FILE: apps/desktop/layer/renderer/src/modules/new-user-guide/pre-finish.tsx
  constant WAIT_DURATION_MS (line 9) | const WAIT_DURATION_MS = 5000
  function PreFinish (line 13) | function PreFinish() {

FILE: apps/desktop/layer/renderer/src/modules/new-user-guide/store.ts
  type FeedSelection (line 10) | type FeedSelection = {

FILE: apps/desktop/layer/renderer/src/modules/panel/cmdk.tsx
  type SearchListType (line 231) | type SearchListType = {

FILE: apps/desktop/layer/renderer/src/modules/player/corner-player.tsx
  type ControlButtonProps (line 43) | interface ControlButtonProps {
  constant ONE_HOUR_IN_SECONDS (line 339) | const ONE_HOUR_IN_SECONDS = 60 * 60

FILE: apps/desktop/layer/renderer/src/modules/player/entry-tts.ts
  constant TTS_MIME_FALLBACK (line 5) | const TTS_MIME_FALLBACK = "audio/ogg; codecs=opus"
  constant STREAM_PLACEHOLDER_SRC (line 6) | const STREAM_PLACEHOLDER_SRC = "about:blank"
  type TtsAudioHandle (line 11) | type TtsAudioHandle = {
  type AudioContextConstructor (line 16) | type AudioContextConstructor =

FILE: apps/desktop/layer/renderer/src/modules/power/transaction-section/tx-table/components.tsx
  type TxTableProps (line 13) | interface TxTableProps {

FILE: apps/desktop/layer/renderer/src/modules/profile/account-management.tsx
  function AuthProviderButton (line 13) | function AuthProviderButton({ provider }: { provider: string }) {
  function AccountManagement (line 65) | function AccountManagement() {

FILE: apps/desktop/layer/renderer/src/modules/profile/email-management.tsx
  function EmailManagement (line 32) | function EmailManagement() {
  function EmailManagementForm (line 106) | function EmailManagementForm() {

FILE: apps/desktop/layer/renderer/src/modules/profile/hooks.ts
  type Variant (line 53) | type Variant = "drawer" | "dialog"
  type ResponseType (line 71) | type ResponseType = ReturnType<typeof useDataFetcher>["data"]
  function useTOTPModalWrapper (line 105) | function useTOTPModalWrapper<T extends { TOTPCode?: string }>(

FILE: apps/desktop/layer/renderer/src/modules/profile/profile-setting-form.tsx
  type ExtendedUser (line 58) | type ExtendedUser = ReturnType<typeof useWhoami> & {
  function onSubmit (line 126) | function onSubmit(values: z.infer<typeof formSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/profile/two-factor.tsx
  type PasswordFormValues (line 37) | type PasswordFormValues = z.infer<typeof passwordFormSchema>
  type TOTPFormValues (line 42) | type TOTPFormValues = z.infer<typeof totpFormSchema>
  type PasswordFormProps (line 44) | type PasswordFormProps<V> = {
  function TOTPForm (line 65) | function TOTPForm({
  function PasswordForm (line 143) | function PasswordForm({
  function TwoFactor (line 250) | function TwoFactor() {

FILE: apps/desktop/layer/renderer/src/modules/profile/update-password-form.tsx
  function onSubmit (line 72) | function onSubmit(values: z.infer<typeof updatePasswordFormSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/profile/user-profile-modal/UserProfileModalContent.tsx
  type ItemVariant (line 32) | type ItemVariant = "loose" | "compact"
  type PickedUser (line 219) | type PickedUser = ReturnType<typeof pickUserData>

FILE: apps/desktop/layer/renderer/src/modules/profile/user-profile-modal/shared.tsx
  type ItemVariant (line 28) | type ItemVariant = "loose" | "compact"
  type SubscriptionModalContentProps (line 30) | interface SubscriptionModalContentProps {

FILE: apps/desktop/layer/renderer/src/modules/renderer/components/TimeStamp.tsx
  type CircleProgressProps (line 57) | interface CircleProgressProps {

FILE: apps/desktop/layer/renderer/src/modules/renderer/context.tsx
  type EntryContentContext (line 5) | interface EntryContentContext {
  type EntryInfoContext (line 25) | interface EntryInfoContext {

FILE: apps/desktop/layer/renderer/src/modules/renderer/html.tsx
  function EntryContentHTMLRenderer (line 19) | function EntryContentHTMLRenderer<AS extends keyof JSX.IntrinsicElements...
  function isValidTimeString (line 103) | function isValidTimeString(time: string): boolean {

FILE: apps/desktop/layer/renderer/src/modules/renderer/markdown.tsx
  type MarkdownProps (line 18) | type MarkdownProps = Omit<ComponentProps<typeof Markdown>, "children">
  function EntryContentMarkdownRenderer (line 20) | function EntryContentMarkdownRenderer({
  function isValidTimeString (line 104) | function isValidTimeString(time: string): boolean {

FILE: apps/desktop/layer/renderer/src/modules/renderer/types.ts
  type EntryContentRendererProps (line 3) | type EntryContentRendererProps = {

FILE: apps/desktop/layer/renderer/src/modules/review-prompt/utils.ts
  constant REVIEW_PROMPT_QUIET_WINDOW_MS (line 12) | const REVIEW_PROMPT_QUIET_WINDOW_MS = 5000
  type DesktopReviewDistribution (line 14) | type DesktopReviewDistribution = "mas" | "microsoft_store" | "unsupported"
  type DesktopReviewRateTarget (line 15) | type DesktopReviewRateTarget = "mas" | "microsoft_store" | null
  constant APPLE_REVIEW_URL (line 17) | const APPLE_REVIEW_URL =
  constant MICROSOFT_PRODUCT_ID (line 19) | const MICROSOFT_PRODUCT_ID = "9NVFZPV0V0HT"
  constant MICROSOFT_REVIEW_URI (line 20) | const MICROSOFT_REVIEW_URI = `ms-windows-store://review/?ProductId=${MIC...
  constant MICROSOFT_REVIEW_URL (line 21) | const MICROSOFT_REVIEW_URL = "https://apps.microsoft.com/detail/9nvfzpv0...
  constant SUPPORT_EMAIL (line 22) | const SUPPORT_EMAIL = "support@folo.is"
  constant REVIEW_PROMPT_STORAGE_PREFIX (line 23) | const REVIEW_PROMPT_STORAGE_PREFIX = getStorageNS("review-prompt")

FILE: apps/desktop/layer/renderer/src/modules/rsshub/add-modal-content.tsx
  function AddModalContent (line 26) | function AddModalContent({

FILE: apps/desktop/layer/renderer/src/modules/rsshub/set-modal-content.tsx
  function SetModalContent (line 27) | function SetModalContent({

FILE: apps/desktop/layer/renderer/src/modules/settings/constants.ts
  constant SETTING_MODAL_ID (line 1) | const SETTING_MODAL_ID = "setting-modal"
  constant GUEST_ALLOWED_SETTING_TABS (line 3) | const GUEST_ALLOWED_SETTING_TABS = ["general", "appearance", "about", "s...
  constant GUEST_ALLOWED_SETTING_TABS_SET (line 5) | const GUEST_ALLOWED_SETTING_TABS_SET = new Set<string>(GUEST_ALLOWED_SET...

FILE: apps/desktop/layer/renderer/src/modules/settings/helper/setting-builder.tsx
  type SharedSettingItem (line 14) | type SharedSettingItem = {
  type SettingItem (line 18) | type SettingItem<T, K extends keyof T = keyof T> = {
  type SectionSettingItem (line 36) | type SectionSettingItem = {
  type ActionSettingItem (line 42) | type ActionSettingItem = {
  type CustomSettingItem (line 48) | type CustomSettingItem = ReactNode | FC

FILE: apps/desktop/layer/renderer/src/modules/settings/helper/sync-queue.ts
  type SettingMapping (line 22) | type SettingMapping = {
  type SettingSyncTab (line 84) | type SettingSyncTab = keyof SettingMapping
  type SettingSyncQueueItem (line 85) | interface SettingSyncQueueItem<T extends SettingSyncTab = SettingSyncTab> {
  type PersistedSettingSyncQueue (line 91) | interface PersistedSettingSyncQueue {
  class SettingSyncQueue (line 96) | class SettingSyncQueue {
    method getCurrentUserId (line 100) | private getCurrentUserId() {
    method bindQueueOwner (line 104) | private bindQueueOwner(currentUserId: string) {
    method reportSyncError (line 116) | private reportSyncError(stage: "flush" | "syncLocal", error: unknown) {
    method init (line 124) | async init() {
    method teardown (line 155) | teardown() {
    method persist (line 164) | private persist() {
    method load (line 177) | private load() {
    method enqueue (line 222) | async enqueue<T extends SettingSyncTab>(tab: T, payload: Partial<Setti...
    method flush (line 256) | private async flush() {
    method replaceRemote (line 324) | replaceRemote(tab?: SettingSyncTab) {
    method syncLocal (line 361) | async syncLocal() {

FILE: apps/desktop/layer/renderer/src/modules/settings/helper/withSettingEnable.tsx
  type WithSelect (line 5) | type WithSelect<T> = T & {

FILE: apps/desktop/layer/renderer/src/modules/settings/hooks/useWrapEnhancedSettingItem.ts
  type WrapEnhancedSettingTab (line 7) | enum WrapEnhancedSettingTab {

FILE: apps/desktop/layer/renderer/src/modules/settings/modal/hooks.ts
  type Ctx (line 5) | interface Ctx {

FILE: apps/desktop/layer/renderer/src/modules/settings/modal/layout.tsx
  function SettingModalLayout (line 34) | function SettingModalLayout(props: PropsWithChildren) {

FILE: apps/desktop/layer/renderer/src/modules/settings/modal/useSettingModal.ts
  type SettingModalOptions (line 8) | type SettingModalOptions =

FILE: apps/desktop/layer/renderer/src/modules/settings/sections/fonts.tsx
  constant FALLBACK_FONT (line 20) | const FALLBACK_FONT = "Default (UI Font)"
  constant DEFAULT_FONT (line 21) | const DEFAULT_FONT = "system-ui"
  constant CUSTOM_FONT (line 22) | const CUSTOM_FONT = "Custom"

FILE: apps/desktop/layer/renderer/src/modules/settings/settings-glob.ts
  function getSettings (line 6) | function getSettings() {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai.tsx
  constant AI_SETTING_SECTION_IDS (line 19) | const AI_SETTING_SECTION_IDS = {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/byok/ByokProviderItem.tsx
  type ByokProviderItemProps (line 6) | interface ByokProviderItemProps {
  constant PROVIDER_LABELS (line 12) | const PROVIDER_LABELS: Record<string, string> = {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/byok/ByokProviderModalContent.tsx
  type ByokProviderModalContentProps (line 17) | interface ByokProviderModalContentProps {
  constant EMPTY_CONFIGURED_PROVIDERS (line 24) | const EMPTY_CONFIGURED_PROVIDERS: ByokProviderName[] = []

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/byok/constants.ts
  constant PROVIDER_OPTIONS (line 3) | const PROVIDER_OPTIONS: { value: ByokProviderName; label: string }[] = [

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/mcp/MCPPresetCard.tsx
  type MCPPresetCardProps (line 5) | interface MCPPresetCardProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/mcp/MCPPresetSelectionModal.tsx
  type MCPPresetSelectionModalProps (line 7) | interface MCPPresetSelectionModalProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/mcp/MCPServiceItem.tsx
  type MCPServiceItemProps (line 7) | interface MCPServiceItemProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/mcp/MCPServiceModalContent.tsx
  type MCPServiceModalContentProps (line 17) | interface MCPServiceModalContentProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/mcp/MCPServicesSection.tsx
  type OptimisticMCPService (line 29) | type OptimisticMCPService = WithOptimistic<MCPService>

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/mcp/types.ts
  type MCPPreset (line 1) | interface MCPPreset {
  constant MCP_PRESETS (line 18) | const MCP_PRESETS: MCPPreset[] = [

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/shortcuts/ShortcutItem.tsx
  type ShortcutItemProps (line 9) | interface ShortcutItemProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/shortcuts/ShortcutModalContent.tsx
  type ShortcutModalContentProps (line 20) | interface ShortcutModalContentProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/DetailedUsageModal.tsx
  function Metric (line 154) | function Metric({ label, value, unit }: { label: string; value: string; ...

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/EfficiencyTab.tsx
  type EfficiencyTabProps (line 8) | interface EfficiencyTabProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/HistoryTab.tsx
  type HistoryTabProps (line 7) | interface HistoryTabProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/OverviewTab.tsx
  type OverviewTabProps (line 8) | interface OverviewTabProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/PatternsTab.tsx
  type PatternsTabProps (line 8) | interface PatternsTabProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/UsageProgressRing.tsx
  type UsageProgressRingProps (line 3) | interface UsageProgressRingProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/UsageWarningBanner.tsx
  type WarningLevel (line 4) | type WarningLevel = "safe" | "moderate" | "high" | "critical" | (string ...
  type UsageWarningBannerProps (line 6) | interface UsageWarningBannerProps {
  function formatEta (line 78) | function formatEta(ts: number) {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/charts/BarList.tsx
  type BarListProps (line 3) | interface BarListProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/charts/Sparkline.tsx
  type SparklineProps (line 1) | interface SparklineProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/components/charts/TinyBars.tsx
  type TinyBarsProps (line 5) | interface TinyBarsProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/ai/usage/types.ts
  type ChartDataPoint (line 2) | interface ChartDataPoint {
  type BarListItem (line 7) | interface BarListItem {
  type TokenCount (line 14) | interface TokenCount {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/appearance.tsx
  constant ACCENT_COLORS (line 571) | const ACCENT_COLORS: AccentColor[] = [

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/cli.tsx
  type CliInstallStatus (line 12) | interface CliInstallStatus {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/data-control.tsx
  function onSubmit (line 131) | function onSubmit(values: z.infer<typeof exportFeedFormSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/feeds.tsx
  type SortField (line 58) | type SortField = "name" | "view" | "date" | "subscriptionCount" | "updat...
  type SortDirection (line 59) | type SortDirection = "asc" | "desc"
  type FeedFilter (line 60) | type FeedFilter = "all" | "rsshub"
  constant GRID_COLS_CLASSNAME (line 72) | const GRID_COLS_CLASSNAME = "grid-cols-[30px_auto_100px_150px_60px_60px]"

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/general.tsx
  method onChange (line 87) | onChange(value) {
  method onChangeGuard (line 175) | onChangeGuard(value) {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/integration/CustomIntegrationModal.tsx
  type FormData (line 77) | type FormData = z.infer<ReturnType<typeof createFormSchema>>
  type CustomIntegrationModalProps (line 79) | interface CustomIntegrationModalProps {
  constant HTTP_METHODS (line 85) | const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"] as const
  constant INTEGRATION_TYPES (line 86) | const INTEGRATION_TYPES = [
  constant ICON_OPTIONS (line 91) | const ICON_OPTIONS = [

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/integration/CustomIntegrationSection.tsx
  type CustomIntegrationSectionProps (line 21) | interface CustomIntegrationSectionProps {
  type CustomIntegrationsSectionProps (line 179) | interface CustomIntegrationsSectionProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/lists/index.tsx
  method onError (line 43) | onError() {
  method onMutate (line 46) | onMutate() {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/lists/modals.tsx
  function onSubmit (line 98) | function onSubmit(values: z.infer<typeof formSchema>) {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/plan.tsx
  constant APPLE_SUBSCRIPTION_MANAGEMENT_URL (line 19) | const APPLE_SUBSCRIPTION_MANAGEMENT_URL = "https://apps.apple.com/accoun...
  type ActiveSubscription (line 21) | type ActiveSubscription = {
  constant AI_MODEL_SELECTION_VALUE_LABELS (line 31) | const AI_MODEL_SELECTION_VALUE_LABELS = {
  function SettingPlan (line 141) | function SettingPlan() {
  type PlanCardProps (line 213) | interface PlanCardProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/tabs/shortcut.tsx
  type ShortcutInputWrapperProps (line 128) | interface ShortcutInputWrapperProps {

FILE: apps/desktop/layer/renderer/src/modules/settings/utils.ts
  type SettingPageContext (line 4) | interface SettingPageContext {
  type DisableWhy (line 9) | enum DisableWhy {
  type SettingPageConfig (line 14) | interface SettingPageConfig {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/CategoryRemoveDialogContent.tsx
  function CategoryRemoveDialogContent (line 12) | function CategoryRemoveDialogContent({

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/CategoryUnsubscribeDialogContent.tsx
  function CategoryUnsubscribeDialogContent (line 14) | function CategoryUnsubscribeDialogContent({

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/FeedCategory.tsx
  type FeedId (line 44) | type FeedId = string
  type FeedCategoryProps (line 45) | interface FeedCategoryProps {
  function FeedCategoryImpl (line 51) | function FeedCategoryImpl({
  function FilterReadFeedCategory (line 394) | function FilterReadFeedCategory(props: FeedCategoryProps) {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/FeedItem.tsx
  type FeedItemProps (line 46) | interface FeedItemProps {
  method click (line 182) | click() {
  type ListItemProps (line 307) | interface ListItemProps {
  type InboxItemProps (line 431) | interface InboxItemProps {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/RenameCategoryForm.tsx
  method onMutate (line 36) | onMutate({ lastCategory, newCategory }) {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/SimpleDiscoverModal.tsx
  function SimpleDiscoverModal (line 55) | function SimpleDiscoverModal({ dismiss }: { dismiss: () => void }) {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/SortedFeedItems.tsx
  type SortListProps (line 13) | type SortListProps = {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/SubscriptionTabButton.tsx
  function SubscriptionTabButton (line 24) | function SubscriptionTabButton({

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/TimelineTabsSettingsModal.tsx
  function ContainerDroppable (line 31) | function ContainerDroppable({
  function getViewMeta (line 63) | function getViewMeta(timelineId: string) {
  function TabItem (line 70) | function TabItem({ id }: { id: UniqueIdentifier }) {
  function SortableTabItem (line 83) | function SortableTabItem({ id }: { id: UniqueIdentifier }) {
  function useResolvedTimelineTabs (line 113) | function useResolvedTimelineTabs() {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/atom.ts
  type FeedListSortBy (line 7) | type FeedListSortBy = "count" | "alphabetical"
  type FeedListSortOrder (line 8) | type FeedListSortOrder = "asc" | "desc"
  constant SELECT_NOTHING (line 38) | const SELECT_NOTHING = []

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/hook.ts
  function useShouldFreeUpSpace (line 5) | function useShouldFreeUpSpace() {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/index.tsx
  function SubscriptionColumn (line 44) | function SubscriptionColumn({

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/sort-by/types.tsx
  type FeedListProps (line 3) | type FeedListProps = {
  type SortBy (line 8) | type SortBy = "count" | "alphabetical"
  type ListListProps (line 10) | type ListListProps = {

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/subscription-list/SortButton.tsx
  constant SORT_LIST (line 14) | const SORT_LIST = [

FILE: apps/desktop/layer/renderer/src/modules/subscription-column/subscription-list/SubscriptionListGuard.tsx
  type SubscriptionProps (line 22) | type SubscriptionProps = ComponentType<

FILE: apps/desktop/layer/renderer/src/modules/trending/index.tsx
  type Language (line 34) | type Language = "all" | "eng" | "cmn" | "fra"
  type View (line 36) | type View = "all" | string
  function Trending (line 56) | function Trending({

FILE: apps/desktop/layer/renderer/src/modules/user/LoginButton.tsx
  type LoginProps (line 8) | interface LoginProps {

FILE: apps/desktop/layer/renderer/src/modules/user/ProfileButton.tsx
  type ProfileButtonProps (line 35) | type ProfileButtonProps = LoginProps & {

FILE: apps/desktop/layer/renderer/src/modules/user/UserGallery.tsx
  type UserGalleryProps (line 5) | interface UserGalleryProps {

FILE: apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/action/index.tsx
  function Component (line 7) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/discover/index.tsx
  type SectionProps (line 16) | interface SectionProps {
  function Section (line 21) | function Section({ children, className }: SectionProps) {
  function Component (line 29) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/power/index.tsx
  function Component (line 9) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/(main)/(layer)/(subview)/rsshub/index.tsx
  function Component (line 23) | function Component() {
  type InstanceItem (line 83) | type InstanceItem = RSSHubListItem | { id: string; isOfficial: true }
  function List (line 248) | function List({ data }: { data?: RSSHubListItem[] }) {
  function SelectInstanceButton (line 301) | function SelectInstanceButton({ instance }: { instance: RSSHubListItem }) {

FILE: apps/desktop/layer/renderer/src/pages/(main)/index.sync.tsx
  function Component (line 9) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/ai.tsx
  function Component (line 17) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/appearance.tsx
  function Component (line 14) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/cli.tsx
  function Component (line 17) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/data-control.tsx
  function Component (line 15) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/feeds.tsx
  function Component (line 14) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/general.tsx
  function Component (line 14) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/integration.tsx
  function Component (line 14) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/list.tsx
  function Component (line 15) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/notifications.tsx
  function Component (line 14) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/plan.tsx
  function Component (line 16) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/profile.tsx
  function Component (line 24) | function Component() {

FILE: apps/desktop/layer/renderer/src/pages/settings/(settings)/shortcuts.tsx
  function Component (line 16) | function Component() {

FILE: apps/desktop/layer/renderer/src/providers/extension-expose-provider.tsx
  type CustomRoute (line 28) | interface CustomRoute {
  method updateDownloaded (line 39) | updateDownloaded() {
  method distributionUpdateAvailable (line 45) | distributionUpdateAvailable(payload: DistributionUpdateNotice) {
  method getApiUrl (line 81) | getApiUrl() {
  method getWebUrl (line 84) | getWebUrl() {
  method clearIfLoginOtherAccount (line 88) | clearIfLoginOtherAccount(newUserId: string) {
  method applyOneTimeToken (line 91) | async applyOneTimeToken(token: string) {
  method readyToUpdate (line 96) | readyToUpdate() {
  method invalidateQuery (line 102) | invalidateQuery(queryKey: string | string[]) {
  method profile (line 124) | profile(id, variant) {
  method rsshubRoute (line 127) | rsshubRoute(route) {

FILE: apps/desktop/layer/renderer/src/providers/invalidate-query-provider.tsx
  class ElectronCloseEvent (line 10) | class ElectronCloseEvent extends Event {
    method constructor (line 12) | constructor() {
  class ElectronShowEvent (line 16) | class ElectronShowEvent extends Event {
    method constructor (line 18) | constructor() {
  method predicate (line 55) | predicate(query) {

FILE: apps/desktop/layer/renderer/src/providers/lazy/index.ts
  method onClose (line 35) | onClose() {

FILE: apps/desktop/layer/renderer/src/providers/wrapped-element-provider.tsx
  type WrappedElementProviderProps (line 46) | interface WrappedElementProviderProps {

FILE: apps/desktop/layer/renderer/src/push-notification.ts
  function registerWebPushNotifications (line 12) | async function registerWebPushNotifications() {
  type NavigateEntryMessage (line 69) | interface NavigateEntryMessage {
  type ServiceWorkerMessage (line 80) | type ServiceWorkerMessage = NavigateEntryMessage

FILE: apps/desktop/layer/renderer/src/queries/auth.ts
  method retry (line 44) | retry(failureCount, error) {

FILE: apps/desktop/layer/renderer/src/queries/feed.ts
  type FeedQueryParams (line 15) | type FeedQueryParams = { id?: string; url?: string }
  method onError (line 63) | async onError(err) {
  method onSuccess (line 66) | onSuccess() {
  method onError (line 77) | async onError(err) {

FILE: apps/desktop/layer/renderer/src/queries/types.d.ts
  type MutationBaseProps (line 1) | interface MutationBaseProps {

FILE: apps/desktop/layer/renderer/src/queries/users.ts
  type AuthProvider (line 6) | interface AuthProvider {

FILE: apps/desktop/layer/renderer/src/queries/wallet.tsx
  method onError (line 52) | async onError(err) {
  method onSuccess (line 55) | onSuccess() {

FILE: apps/desktop/layer/renderer/src/store/feed/hooks.ts
  type PreferredTitleTarget (line 18) | type PreferredTitleTarget = {

FILE: apps/desktop/layer/renderer/src/store/image/db.ts
  function createStore (line 6) | function createStore(dbName: string, storeName: string): UseStore {

FILE: apps/desktop/layer/renderer/src/store/image/index.ts
  type StoreImageType (line 6) | interface StoreImageType {
  type State (line 13) | interface State {
  class ImageActions (line 23) | class ImageActions {
    method getImage (line 24) | getImage(src: string) {
    method saveImages (line 28) | saveImages(images: StoreImageType[]) {
    method fetchDimensionsFromDb (line 38) | async fetchDimensionsFromDb(images: string[]) {
    method getImagesFromEntry (line 45) | getImagesFromEntry(entry: EntryModel) {

FILE: apps/desktop/layer/renderer/src/store/search/constants.ts
  type SearchType (line 12) | type SearchType = (typeof SearchType)[keyof typeof SearchType]

FILE: apps/desktop/layer/renderer/src/store/search/index.ts
  class SearchActions (line 35) | class SearchActions {
    method reset (line 36) | reset() {
    method createFuse (line 40) | private createFuse<T extends object>(data: T[], keys: (keyof T)[]) {
    method createLocalDbSearch (line 48) | async createLocalDbSearch() {
    method setSearchType (line 108) | setSearchType(type: SearchType) {
    method getCurrentKeyword (line 112) | getCurrentKeyword() {

FILE: apps/desktop/layer/renderer/src/store/search/types.ts
  type SearchResult (line 6) | interface SearchResult<T extends object, A extends object = object> exte...
  type SearchState (line 10) | interface SearchState {
  type SearchInstance (line 17) | interface SearchInstance {

FILE: apps/desktop/layer/renderer/src/store/utils/helper.ts
  method get (line 59) | get(_, prop) {
  type FunctionKeys (line 81) | type FunctionKeys<T> = {
  type FunctionProps (line 85) | type FunctionProps<T> = Pick<T, FunctionKeys<T>>
  function createImmerSetter (line 100) | function createImmerSetter<T>(useStore: UseBoundStore<StoreApi<T>>) {
  type MayBeDraft (line 109) | type MayBeDraft<T> = T
  type SyncOrAsync (line 113) | type SyncOrAsync<T> = T | Promise<T>
  type ExecutorFn (line 114) | type ExecutorFn<S, Ctx> = (snapshot: S, ctx: Ctx) => SyncOrAsync<void>
  class Transaction (line 116) | class Transaction<S, Ctx> {
    method constructor (line 124) | constructor(snapshot?: S, ctx?: Ctx) {
    method rollback (line 129) | rollback(fn: ExecutorFn<S, Ctx>): this {
    method execute (line 134) | execute(executor: ExecutorFn<S, Ctx>): this {
    method optimistic (line 139) | optimistic(executor: ExecutorFn<S, Ctx>): this {
    method persist (line 144) | persist(fn: ExecutorFn<S, Ctx>): this {
    method run (line 149) | async run(): Promise<void> {

FILE: apps/desktop/layer/renderer/src/workers/sw/pusher.ts
  type NewEntryMessage (line 2) | interface NewEntryMessage {
  type Message (line 11) | type Message = NewEntryMessage

FILE: apps/desktop/plugins/vite/ast.ts
  method transform (line 10) | transform(node) {

FILE: apps/desktop/plugins/vite/cleanup.ts
  function cleanupUnnecessaryFilesPlugin (line 6) | function cleanupUnnecessaryFilesPlugin(files: string[]): Plugin {

FILE: apps/desktop/plugins/vite/compress.ts
  function compressDirectory (line 12) | async function compressDirectory(sourceDir: string, outputFile: string) {
  function compressAndFingerprintPlugin (line 23) | function compressAndFingerprintPlugin(outDir: string): Plugin {

FILE: apps/desktop/plugins/vite/deps.ts
  function createDependencyChunksPlugin (line 3) | function createDependencyChunksPlugin(dependencies: string[][]): Plugin {

FILE: apps/desktop/plugins/vite/generate-main-hash.ts
  function calculateMainHash (line 8) | async function calculateMainHash(
  function main (line 36) | async function main() {

FILE: apps/desktop/plugins/vite/hmr.ts
  function isNodeWithinCircularImports (line 5) | function isNodeWithinCircularImports(
  method configureServer (line 45) | configureServer(server) {
  method handleHotUpdate (line 50) | handleHotUpdate({ file, server }: HmrContext) {

FILE: apps/desktop/plugins/vite/html-inject.ts
  function htmlInjectPlugin (line 4) | function htmlInjectPlugin(env: typeof EnvType): PluginOption {

FILE: apps/desktop/plugins/vite/i18n-hmr.ts
  function customI18nHmrPlugin (line 5) | function customI18nHmrPlugin(): Plugin {

FILE: apps/desktop/plugins/vite/locales-json.ts
  function localesJsonPlugin (line 10) | function localesJsonPlugin(): Plugin {

FILE: apps/desktop/plugins/vite/locales.ts
  function localesPlugin (line 8) | function localesPlugin(): Plugin {

FILE: apps/desktop/plugins/vite/manifest.ts
  function manifestPlugin (line 6) | function manifestPlugin(): Plugin {

FILE: apps/desktop/plugins/vite/specific-import.ts
  type Platform (line 3) | type Platform = "electron" | "web"
  function createPlatformSpecificImportPlugin (line 4) | function createPlatformSpecificImportPlugin(platform: Platform): Plugin {

FILE: apps/desktop/plugins/vite/utils/i18n-completeness.ts
  type LanguageCompletion (line 6) | type LanguageCompletion = Record<string, number>
  function getLanguageFiles (line 8) | function getLanguageFiles(dir: string): string[] {
  function getNamespaces (line 12) | function getNamespaces(localesDir: string): string[] {
  function countKeys (line 18) | function countKeys(obj: any): number {
  function calculateCompleteness (line 30) | function calculateCompleteness(localesDir: string): LanguageCompletion {

FILE: apps/desktop/scripts/generate-appx-manifest.ts
  type AppXManifestConfig (line 7) | interface AppXManifestConfig {
  function generateAppXManifest (line 20) | function generateAppXManifest(config: AppXManifestConfig, templatePath: ...
  function main (line 50) | async function main() {

FILE: apps/desktop/scripts/merge-yml.ts
  function findYmlFiles (line 8) | function findYmlFiles(dir: string): string[] {

FILE: apps/desktop/vite.config.ts
  constant ROOT (line 26) | const ROOT = resolve(__dirname, "./layer/renderer")
  method configureServer (line 30) | configureServer(server: ViteDevServer) {
  function checkBrowserSupport (line 309) | function checkBrowserSupport() {
  method configResolved (line 323) | configResolved(resolvedConfig) {
  method closeBundle (line 327) | closeBundle() {
  method transformIndexHtml (line 346) | transformIndexHtml(html) {

FILE: apps/landing/global.d.ts
  type NextErrorProps (line 6) | type NextErrorProps = {
  type NextPageParams (line 10) | type NextPageParams<P extends {}, Props = {}> = PropsWithChildren<
  type Component (line 16) | type Component<P = {}> = FC<ComponentType & P>
  type ComponentType (line 18) | type ComponentType<P = {}> = {
  type Document (line 24) | interface Document {
  type ViewTransition (line 28) | interface ViewTransition {
  type AriaAttributes (line 37) | interface AriaAttributes {

FILE: apps/landing/next.config.mjs
  method rewrites (line 34) | async rewrites() {

FILE: apps/landing/plugins/eslint-recursive-sort.mjs
  method create (line 27) | create(context) {

FILE: apps/landing/src/app/[locale]/download/page.tsx
  type LocaleParams (line 10) | type LocaleParams = { locale?: string }
  function generateMetadata (line 14) | async function generateMetadata({
  function DownloadPage (line 33) | async function DownloadPage() {

FILE: apps/landing/src/app/[locale]/layout.tsx
  type LocaleParams (line 21) | type LocaleParams = { locale?: string }
  type MaybeAsyncLocaleParams (line 23) | type MaybeAsyncLocaleParams = LocaleParams | Promise<LocaleParams> | und...
  function generateStaticParams (line 37) | function generateStaticParams() {
  function generateMetadata (line 41) | async function generateMetadata(props: {
  function generateViewport (line 106) | function generateViewport(): Viewport {
  function LocaleLayout (line 121) | async function LocaleLayout({

FILE: apps/landing/src/app/[locale]/page.tsx
  type LocaleParams (line 14) | type LocaleParams = { locale?: string }
  function Home (line 16) | async function Home({

FILE: apps/landing/src/app/[locale]/pricing/page.tsx
  type LocaleParams (line 8) | type LocaleParams = { locale?: string }
  function generateMetadata (line 12) | async function generateMetadata({
  function PricingPage (line 31) | async function PricingPage() {

FILE: apps/landing/src/app/[locale]/privacy-policy/page.tsx
  function PrivacyPolicyPage (line 32) | async function PrivacyPolicyPage() {

FILE: apps/landing/src/app/[locale]/terms-of-service/page.tsx
  function TermsOfServicePage (line 30) | async function TermsOfServicePage() {

FILE: apps/landing/src/app/apple-app-site-association/route.ts
  function GET (line 3) | function GET() {

FILE: apps/landing/src/app/discover-sources/route.ts
  constant CACHE_TTL_MS (line 8) | const CACHE_TTL_MS = 12 * 60 * 60 * 1000
  function GET (line 17) | async function GET() {

FILE: apps/landing/src/app/layout.tsx
  function RootLayout (line 3) | function RootLayout({

FILE: apps/landing/src/app/robots.ts
  function robots (line 3) | function robots(): MetadataRoute.Robots {

FILE: apps/landing/src/components/common/GithubTrending.tsx
  function GithubTrending (line 1) | function GithubTrending() {

FILE: apps/landing/src/components/common/Lazyload.tsx
  type LazyLoadProps (line 9) | type LazyLoadProps = {

FILE: apps/landing/src/components/common/LightRays.tsx
  type RaysOrigin (line 10) | type RaysOrigin =
  type LightRaysProps (line 20) | interface LightRaysProps {
  constant DEFAULT_COLOR (line 41) | const DEFAULT_COLOR = '#ffffff'

FILE: apps/landing/src/components/common/QueryHydrate.tsx
  function QueryHydrate (line 6) | function QueryHydrate(props: HydrationBoundaryProps) {

FILE: apps/landing/src/components/layout/footer/Footer.tsx
  type LinkItem (line 8) | type LinkItem = { label: string; href: string; external?: boolean }
  function LinkColumn (line 49) | function LinkColumn({ title, links }: { title: string; links: LinkItem[]...
  type FooterProps (line 75) | interface FooterProps {

FILE: apps/landing/src/components/ui/accordion/Accordion.tsx
  type CollapseContextValue (line 14) | interface CollapseContextValue {
  type CollapseGroupProps (line 29) | interface CollapseGroupProps {
  type CollapseProps (line 66) | interface CollapseProps {
  type CollapseContentProps (line 139) | interface CollapseContentProps {

FILE: apps/landing/src/components/ui/border-beam.tsx
  type BorderBeamProps (line 6) | interface BorderBeamProps {

FILE: apps/landing/src/components/ui/button/Button.tsx
  type ButtonProps (line 104) | interface ButtonProps

FILE: apps/landing/src/components/ui/checkbox/Checkbox.tsx
  type CheckboxProps (line 42) | type CheckboxProps = React.ComponentProps<typeof CheckboxPrimitive.Root> &
  function Checkbox (line 48) | function Checkbox({

FILE: apps/landing/src/components/ui/collapse/CollapseCss.tsx
  type CollapseContextValue (line 12) | interface CollapseContextValue {
  type CollapseGroupProps (line 27) | interface CollapseGroupProps {
  type CollapseProps (line 64) | interface CollapseProps {
  type CollapseCssRef (line 78) | interface CollapseCssRef {
  type CollapseContentProps (line 147) | interface CollapseContentProps {

FILE: apps/landing/src/components/ui/collapse/hooks.tsx
  type CollapseContextValue (line 4) | interface CollapseContextValue {

FILE: apps/landing/src/components/ui/dialog/Dialog.tsx
  type DialogContextType (line 11) | type DialogContextType = {
  type DialogProps (line 27) | type DialogProps = React.ComponentProps<typeof DialogPrimitive.Root>
  function Dialog (line 29) | function Dialog({ children, ...props }: DialogProps) {
  type DialogTriggerProps (line 59) | type DialogTriggerProps = React.ComponentProps<typeof DialogPrimitive.Tr...
  function DialogTrigger (line 61) | function DialogTrigger(props: DialogTriggerProps) {
  type DialogPortalProps (line 65) | type DialogPortalProps = React.ComponentProps<typeof DialogPrimitive.Por...
  function DialogPortal (line 67) | function DialogPortal(props: DialogPortalProps) {
  type DialogCloseProps (line 71) | type DialogCloseProps = React.ComponentProps<typeof DialogPrimitive.Close>
  function DialogClose (line 73) | function DialogClose(props: DialogCloseProps) {
  type DialogOverlayProps (line 83) | type DialogOverlayProps = React.ComponentProps<typeof DialogPrimitive.Ov...
  function DialogOverlay (line 85) | function DialogOverlay({ className, ...props }: DialogOverlayProps) {
  type DialogContentProps (line 98) | type DialogContentProps = React.ComponentProps<
  function DialogContent (line 114) | function DialogContent({
  type DialogHeaderProps (line 187) | type DialogHeaderProps = React.ComponentProps<'div'>
  function DialogHeader (line 189) | function DialogHeader({ className, ...props }: DialogHeaderProps) {
  type DialogFooterProps (line 202) | type DialogFooterProps = React.ComponentProps<'div'>
  function DialogFooter (line 204) | function DialogFooter({ className, ...props }: DialogFooterProps) {
  type DialogTitleProps (line 217) | type DialogTitleProps = React.ComponentProps<typeof DialogPrimitive.Title>
  function DialogTitle (line 219) | function DialogTitle({ className, ...props }: DialogTitleProps) {
  type DialogDescriptionProps (line 232) | type DialogDescriptionProps = React.ComponentProps<
  function DialogDescription (line 236) | function DialogDescription({ className, ...props }: DialogDescriptionPro...

FILE: apps/landing/src/components/ui/effects/GridGuides.tsx
  type GridGuidesProps (line 3) | type GridGuidesProps = React.PropsWithChildren<{
  function GridGuides (line 16) | function GridGuides({

FILE: apps/landing/src/components/ui/effects/ParticlesAura.tsx
  type ParticlesAuraProps (line 6) | type ParticlesAuraProps = {
  function ParticlesAura (line 16) | function ParticlesAura({

FILE: apps/landing/src/components/ui/effects/TiltCard.tsx
  type TiltCardProps (line 6) | type TiltCardProps = React.PropsWithChildren<{
  function TiltCard (line 16) | function TiltCard({

FILE: apps/landing/src/components/ui/glass/index.tsx
  type GlassSurfaceProps (line 8) | interface GlassSurfaceProps {

FILE: apps/landing/src/components/ui/highlighter.tsx
  type AnnotationAction (line 7) | type AnnotationAction =
  type HighlighterProps (line 16) | interface HighlighterProps {
  function Highlighter (line 28) | function Highlighter({

FILE: apps/landing/src/components/ui/hover-card/HoverCard.tsx
  type HoverCardProps (line 10) | type HoverCardProps = React.ComponentProps<typeof HoverCardPrimitive.Root>
  type HoverCardTriggerProps (line 12) | type HoverCardTriggerProps = React.ComponentProps<
  type HoverCardContentProps (line 16) | type HoverCardContentProps = React.ComponentProps<

FILE: apps/landing/src/components/ui/input/Input.tsx
  type InputProps (line 67) | interface InputProps

FILE: apps/landing/src/components/ui/input/TextArea.tsx
  type TextareaProps (line 9) | interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAre...

FILE: apps/landing/src/components/ui/json-highlighter/index.tsx
  type JsonHighlighterProps (line 5) | interface JsonHighlighterProps {
  type Token (line 89) | interface Token {
  function highlightJson (line 106) | function highlightJson(jsonString: string): string {
  function tokenizeJson (line 120) | function tokenizeJson(json: string): Token[] {
  function isFollowedByColon (line 346) | function isFollowedByColon(json: string, startIndex: number): boolean {
  function renderTokens (line 360) | function renderTokens(tokens: Token[], originalJson: string): string {

FILE: apps/landing/src/components/ui/label/Label.tsx
  type LabelProps (line 7) | interface LabelProps extends React.ComponentPropsWithoutRef<

FILE: apps/landing/src/components/ui/light-rays.tsx
  type LightRaysProps (line 8) | interface LightRaysProps extends React.HTMLAttributes<HTMLDivElement> {
  type LightRay (line 17) | type LightRay = {
  function LightRays (line 87) | function LightRays({

FILE: apps/landing/src/components/ui/loading/index.tsx
  type LoadingProps (line 5) | type LoadingProps = {

FILE: apps/landing/src/components/ui/magic-card.tsx
  type MagicCardProps (line 8) | interface MagicCardProps {
  function MagicCard (line 18) | function MagicCard({

FILE: apps/landing/src/components/ui/markdown/index.tsx
  type MarkdownContentProps (line 9) | interface MarkdownContentProps {
  function MarkdownContent (line 13) | function MarkdownContent({ content }: MarkdownContentProps) {
  function getMarkdownContent (line 30) | async function getMarkdownContent(filePath: string) {

FILE: apps/landing/src/components/ui/modal/ModalManager.ts
  method present (line 14) | present<P = unknown>(
  method dismiss (line 49) | dismiss(id: string): void {
  method __registerCloser (line 64) | __registerCloser(id: string, fn: () => void) {
  method __unregisterCloser (line 67) | __unregisterCloser(id: string) {

FILE: apps/landing/src/components/ui/modal/types.ts
  type ModalComponentProps (line 5) | type ModalComponentProps = {
  type ModalComponent (line 10) | type ModalComponent<P = unknown> = FC<ModalComponentProps & P> & {
  type ModalContentConfig (line 19) | type ModalContentConfig = Partial<DialogContentProps>
  type ModalItem (line 21) | type ModalItem = {

FILE: apps/landing/src/components/ui/prompts/BasePrompt.tsx
  type PromptVariant (line 19) | type PromptVariant = 'danger' | 'info'
  type PromptOptions (line 21) | type PromptOptions = {

FILE: apps/landing/src/components/ui/prompts/InputPrompt.tsx
  type InputPromptVariant (line 20) | type InputPromptVariant = 'danger' | 'info'
  type InputPromptOptions (line 22) | type InputPromptOptions = {

FILE: apps/landing/src/components/ui/prompts/Prompt.ts
  method prompt (line 8) | prompt(options: PromptOptions) {
  method input (line 11) | input(options: InputPromptOptions): Promise<string | null> {

FILE: apps/landing/src/components/ui/segment-tab/SegmentTab.tsx
  type SegmentTabItem (line 10) | interface SegmentTabItem<T = string> {
  type SegmentTabProps (line 16) | interface SegmentTabProps<T = string> {
  function SegmentTab (line 63) | function SegmentTab<T = string>({

FILE: apps/landing/src/components/ui/select/ComboboxSelect.tsx
  type ComboboxSelectProps (line 15) | interface ComboboxSelectProps {
  constant DEFAULT_OPTIONS (line 30) | const DEFAULT_OPTIONS: string[] = []

FILE: apps/landing/src/components/ui/select/MultiSelect.tsx
  type MultiSelectProps (line 13) | interface MultiSelectProps {
  constant DEFAULT_VALUE (line 26) | const DEFAULT_VALUE: string[] = []
  constant DEFAULT_OPTIONS (line 27) | const DEFAULT_OPTIONS: string[] = []

FILE: apps/landing/src/components/ui/sheet/Sheet.tsx
  type PresentSheetProps (line 7) | interface PresentSheetProps {

FILE: apps/landing/src/components/ui/switch/index.tsx
  type SwitchProps (line 10) | type SwitchProps = React.ComponentProps<typeof SwitchPrimitives.Root> &
  function Switch (line 17) | function Switch({

FILE: apps/landing/src/components/ui/transition/IconTransiton.tsx
  type IconTransitionProps (line 10) | interface IconTransitionProps {

FILE: apps/landing/src/components/ui/transition/factor.tsx
  type TransitionViewParams (line 18) | interface TransitionViewParams {

FILE: apps/landing/src/components/ui/transition/typings.ts
  type BaseTransitionProps (line 3) | interface BaseTransitionProps extends HTMLMotionProps<'div'> {

FILE: apps/landing/src/components/widgets/download/PlatformDownloads.tsx
  type PlatformDownloadsProps (line 18) | type PlatformDownloadsProps = {

FILE: apps/landing/src/components/widgets/landing/Features.tsx
  type FeaturesProps (line 13) | type FeaturesProps = {
  type FGProps (line 69) | type FGProps = {
  function FeatureGridItem (line 77) | function FeatureGridItem({
  function DiscoverWindow (line 98) | function DiscoverWindow({ sources }: { sources: DiscoverSource[] }) {

FILE: apps/landing/src/components/widgets/landing/Hero.tsx
  type LandingHeroProps (line 26) | type LandingHeroProps = {
  type AudienceKey (line 30) | type AudienceKey = 'human' | 'agent'

FILE: apps/landing/src/components/widgets/landing/RepoStats.tsx
  function RepoStats (line 9) | function RepoStats() {
  function StatBox (line 63) | function StatBox({

FILE: apps/landing/src/components/widgets/landing/TrustedBy.tsx
  type TrustedByProps (line 7) | type TrustedByProps = {

FILE: apps/landing/src/components/widgets/landing/ViewsShowcase.tsx
  type ViewKey (line 7) | type ViewKey = 'list' | 'social' | 'masonry' | 'grid'

FILE: apps/landing/src/components/widgets/landing/WindowChrome.tsx
  function WindowChrome (line 7) | function WindowChrome({

FILE: apps/landing/src/components/widgets/pricing/PricingPlans.tsx
  type BillingPeriod (line 12) | type BillingPeriod = 'monthly' | 'yearly'
  constant AI_MODEL_SELECTION_LABELS (line 14) | const AI_MODEL_SELECTION_LABELS = {

FILE: apps/landing/src/components/widgets/simulators/EntryChatPanel.tsx
  type EntryChatPanelProps (line 7) | interface EntryChatPanelProps {

FILE: apps/landing/src/components/widgets/simulators/ListDemo.tsx
  constant DEFAULT_ITEMS (line 53) | const DEFAULT_ITEMS: HeroTimelineItem[] = [

FILE: apps/landing/src/components/widgets/simulators/TimelineChatDemo.tsx
  constant INITIAL_HEADER_TITLE (line 5) | const INITIAL_HEADER_TITLE = 'Folo AI - Timeline Summary'

FILE: apps/landing/src/components/widgets/simulators/components/ai/AIChainOfThought.tsx
  type ChainReasoningPart (line 16) | type ChainReasoningPart = ReasoningUIPart | ToolUIPart
  type AIChainOfThoughtProps (line 17) | interface AIChainOfThoughtProps {

FILE: apps/landing/src/components/widgets/simulators/components/ai/AIReasoningPart.tsx
  type AIReasoningPartProps (line 4) | interface AIReasoningPartProps {

FILE: apps/landing/src/components/widgets/simulators/components/ai/ToolInvocationComponent.tsx
  type ToolInvocationComponentProps (line 7) | interface ToolInvocationComponentProps {

FILE: apps/landing/src/components/widgets/simulators/components/ai/animated/AnimatedMarkdown.tsx
  type MarkdownAnimateTextProps (line 13) | interface MarkdownAnimateTextProps {

FILE: apps/landing/src/components/widgets/simulators/components/ai/animated/TokenizedText.tsx
  type TokenWithSource (line 10) | interface TokenWithSource {
  type TokenType (line 23) | type TokenType = string | TokenWithSource | ReactElement

FILE: apps/landing/src/components/widgets/simulators/components/ai/animated/constants.ts
  constant DEFAULT_ANIMATION (line 1) | const DEFAULT_ANIMATION = 'mask-left-to-right 0.5s ease-in-out'
  constant ANIMATION_STYLE (line 2) | const ANIMATION_STYLE = {

FILE: apps/landing/src/components/widgets/simulators/components/ai/mocks.ts
  constant REASONING_GROUPS (line 3) | const REASONING_GROUPS = ReasoningGroups.groups as any

FILE: apps/landing/src/components/widgets/simulators/components/ai/shiny-text/ShinyText.tsx
  type AnimatedShinyTextProps (line 7) | interface AnimatedShinyTextProps extends ComponentPropsWithoutRef<'span'> {

FILE: apps/landing/src/components/widgets/simulators/components/chat/AiMockMessage.tsx
  type AiMockMessageProps (line 11) | interface AiMockMessageProps {

FILE: apps/landing/src/components/widgets/simulators/components/chat/ListChatPlayer.tsx
  type ListChatPlayerProps (line 52) | interface ListChatPlayerProps {

FILE: apps/landing/src/components/widgets/simulators/components/chat/stream.ts
  type StreamOptions (line 1) | interface StreamOptions {
  type StreamHandle (line 9) | interface StreamHandle {
  function streamText (line 18) | function streamText(text: string, options: StreamOptions): StreamHandle {

FILE: apps/landing/src/components/widgets/simulators/mocks.tsx
  type EntryDetail (line 7) | type EntryDetail = {
  constant ENTRY_DETAIL (line 22) | const ENTRY_DETAIL: EntryDetail = {
  type ToolInvocation (line 37) | type ToolInvocation = {
  constant AI_TOOL_INVOCATION (line 43) | const AI_TOOL_INVOCATION: ToolInvocation = {
  type UserContext (line 61) | type UserContext = {
  type UserPlainTextStep (line 67) | type UserPlainTextStep = {
  type UserComponentStep (line 72) | type UserComponentStep = {
  type UserStep (line 81) | type UserStep = UserPlainTextStep | UserComponentStep
  type AIReasoningPart (line 82) | type AIReason
Condensed preview — 2869 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (9,386K chars).
[
  {
    "path": ".agents/settings.local.json",
    "chars": 660,
    "preview": "{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(gh pr view:*)\",\n      \"Bash(pnpm bump:*)\",\n      \"Bash(git add:*)\",\n    "
  },
  {
    "path": ".agents/skills/desktop-release/SKILL.md",
    "chars": 6143,
    "preview": "---\nname: desktop-release\ndescription: Perform a regular desktop release from the dev branch. Gathers commits since last"
  },
  {
    "path": ".agents/skills/installing-mobile-preview-builds/SKILL.md",
    "chars": 2682,
    "preview": "---\nname: installing-mobile-preview-builds\ndescription: Builds and installs the iOS preview build for apps/mobile using "
  },
  {
    "path": ".agents/skills/mobile-e2e/SKILL.md",
    "chars": 4048,
    "preview": "---\nname: mobile-e2e\ndescription: Run apps/mobile Maestro end-to-end tests in this repo. Use when an agent needs to vali"
  },
  {
    "path": ".agents/skills/mobile-release/SKILL.md",
    "chars": 4726,
    "preview": "---\nname: mobile-release\ndescription: Perform a regular mobile release from the dev branch. Gathers commits since last r"
  },
  {
    "path": ".agents/skills/mobile-self-test/SKILL.md",
    "chars": 14398,
    "preview": "---\nname: mobile-self-test\ndescription: Self-test a mobile feature change or bug fix after implementation in `apps/mobil"
  },
  {
    "path": ".agents/skills/update-deps/SKILL.md",
    "chars": 6262,
    "preview": "---\nname: update-deps\ndescription: Update all dependencies across frontend and backend projects. Reads changelogs for br"
  },
  {
    "path": ".cursorignore",
    "chars": 82,
    "preview": "# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)\n"
  },
  {
    "path": ".editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".gitattributes",
    "chars": 68,
    "preview": "* text=auto eol=lf\n*.splinecode filter=lfs diff=lfs merge=lfs -text\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 2952,
    "preview": "name: 🐞 Bug report\ndescription: Report an issue\nlabels: [pending triage, bug]\ntype: Bug\nbody:\n  - type: markdown\n    att"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 369,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: 💬 Follow's Discord Server\n    url: https://discord.gg/tUDVZjEr\n    "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1559,
    "preview": "name: 🚀 New feature proposal\ndescription: Propose a new feature\nlabels: [enhancement]\ntype: Feature\nbody:\n  - type: mark"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/i18n.yml",
    "chars": 1974,
    "preview": "name: 🌐 Internationalization (i18n)\ndescription: Contribute to or report issues with translations\ntitle: \"[i18n]: \"\nlabe"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/typo.yml",
    "chars": 723,
    "preview": "name: 👀 Typo / Grammar fix\ndescription: You can just go ahead and send a PR! Thank you!\nlabels: []\nbody:\n  - type: markd"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1092,
    "preview": "<!-- DO NOT IGNORE THE TEMPLATE!\n\nThank you for contributing!\n\nBefore submitting the PR, please make sure you do the fol"
  },
  {
    "path": ".github/actions/setup-version/action.yml",
    "chars": 1145,
    "preview": "name: Setup Version\ndescription: \"Setup Version\"\ninputs:\n  type:\n    required: true\n    description: \"Type of the app, e"
  },
  {
    "path": ".github/actions/setup-xcode/action.yml",
    "chars": 4277,
    "preview": "name: \"Setup Xcode\"\ndescription: \"Setup specific Xcode version for iOS builds\"\ninputs:\n  xcode-version:\n    description:"
  },
  {
    "path": ".github/advanced-issue-labeler.yml",
    "chars": 338,
    "preview": "policy:\n  - section:\n      - id: [platform]\n        block-list: [\"None\", \"Other\"]\n        label:\n          - name: \"plat"
  },
  {
    "path": ".github/copilot-instructions.md",
    "chars": 73,
    "preview": "Before you start, you need to read and follow the rules in @../CLAUDE.md\n"
  },
  {
    "path": ".github/dependabot.yaml",
    "chars": 1667,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: npm\n    directory: /\n    schedule:\n      interval: weekly\n      day: friday\n "
  },
  {
    "path": ".github/prompts/similar_issues.prompt.yml",
    "chars": 1733,
    "preview": "messages:\n  - role: system\n    content: |-\n      You are a GitHub assistant with access to GitHub Model Context Protocol"
  },
  {
    "path": ".github/scripts/extract-release-info.mjs",
    "chars": 3485,
    "preview": "#!/usr/bin/env node\n\n/**\n * Extract release version and platform information from git commit messages\n * Used by GitHub "
  },
  {
    "path": ".github/workflows/build-android.yml",
    "chars": 4240,
    "preview": "name: 🤖 Build Android\n\non:\n  push:\n    branches:\n      - \"**\"\n    paths:\n      - \"apps/mobile/**\"\n      - \"pnpm-lock.yam"
  },
  {
    "path": ".github/workflows/build-desktop.yml",
    "chars": 13043,
    "preview": "name: 🖥️ Build Desktop\n\non:\n  push:\n    branches:\n      - \"**\"\n    paths:\n      - \"apps/desktop/**\"\n      - \"packages/**"
  },
  {
    "path": ".github/workflows/build-ios-development.yml",
    "chars": 4684,
    "preview": "name: 📱 Build iOS for development\n\non:\n  push:\n    branches:\n      - \"**\"\n    paths:\n      - \"apps/mobile/web-app/**\"\n  "
  },
  {
    "path": ".github/workflows/build-ios.yml",
    "chars": 4075,
    "preview": "name: 🍎 Build iOS\n\non:\n  push:\n    branches:\n      - \"**\"\n    paths:\n      - \"apps/mobile/**\"\n      - \"pnpm-lock.yaml\"\n "
  },
  {
    "path": ".github/workflows/build-web.yml",
    "chars": 1178,
    "preview": "on:\n  pull_request:\n  push:\n    branches: [main, dev]\n\nname: 🌐 CI Build web and SSR server\nconcurrency:\n  group: ${{ git"
  },
  {
    "path": ".github/workflows/deploy-cloudflare-desktop.yml",
    "chars": 2060,
    "preview": "name: \"\\u2601\\ufe0f Deploy Desktop to Cloudflare\"\n\non:\n  push:\n    branches: [main, dev]\n\nconcurrency:\n  group: ${{ gith"
  },
  {
    "path": ".github/workflows/deploy-cloudflare-landing.yml",
    "chars": 2664,
    "preview": "name: \"\\u2601\\ufe0f Deploy Landing to Cloudflare\"\n\non:\n  push:\n    branches: [main, dev]\n  workflow_dispatch:\n    inputs"
  },
  {
    "path": ".github/workflows/deploy-cloudflare-ssr.yml",
    "chars": 3560,
    "preview": "name: \"\\u2601\\ufe0f Deploy SSR to Cloudflare\"\n\non:\n  push:\n    branches: [main, dev]\n  workflow_dispatch:\n    inputs:\n  "
  },
  {
    "path": ".github/workflows/issue-labeler.yml",
    "chars": 786,
    "preview": "name: 🏷️ Issue labeler\non:\n  issues:\n    types: [opened]\n\npermissions:\n  contents: read\n\njobs:\n  label-component:\n    ru"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 1556,
    "preview": "on:\n  pull_request:\n  push:\n    branches: [main, dev]\n\n# Do not use secrets or variables to allow for public access\nenv:"
  },
  {
    "path": ".github/workflows/pr-title-check.yml",
    "chars": 431,
    "preview": "name: ✅ PR Conventional Commit Validation\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened, edited]\n\njobs:"
  },
  {
    "path": ".github/workflows/similar-issues.yml",
    "chars": 4109,
    "preview": "name: Similar Issues via AI MCP\n\non:\n  issues:\n    types: [opened]\n\njobs:\n  find-similar:\n    permissions:\n      content"
  },
  {
    "path": ".github/workflows/sync.yaml",
    "chars": 1045,
    "preview": "name: 🔄 Sync Release Branches To Dev\n\non:\n  push:\n    branches:\n      - main\n      - mobile-main\n\npermissions:\n  content"
  },
  {
    "path": ".github/workflows/tag.yml",
    "chars": 7481,
    "preview": "name: 🏷️ Release Orchestrator\n\non:\n  push:\n    branches:\n      - main\n      - mobile-main\n\npermissions:\n  contents: writ"
  },
  {
    "path": ".github/workflows/translator.yml",
    "chars": 584,
    "preview": "name: \"🌍 translator\"\non:\n  issues:\n    types: [opened, edited]\n  issue_comment:\n    types: [created, edited]\n  discussio"
  },
  {
    "path": ".gitignore",
    "chars": 697,
    "preview": "node_modules\ndist\nout\n.next\n.open-next\nnext-env.d.ts\n.DS_Store\n*.log*\n.env\n.eslintcache\n.env.*\n!.env.example\n\n# Sentry C"
  },
  {
    "path": ".npmrc",
    "chars": 58,
    "preview": "shamefully-hoist=true\nnode-linker=hoisted\nsave-exact=true\n"
  },
  {
    "path": ".nvmrc",
    "chars": 7,
    "preview": "stable\n"
  },
  {
    "path": ".prettierignore",
    "chars": 226,
    "preview": "pnpm-lock.yaml\n\nCHANGELOG.md\n.context\n\napps/external/postcss.config.cjs\n\napps/mobile/android\napps/mobile/ios\napps/mobile"
  },
  {
    "path": ".prettierrc.mjs",
    "chars": 853,
    "preview": "/** @type {import(\"prettier\").Config & import(\"prettier-plugin-tailwindcss\").PluginOptions} */\nexport default {\n  semi: "
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 159,
    "preview": "{\n  \"recommendations\": [\n    \"dbaeumer.vscode-eslint\",\n    \"johnsoncodehk.vscode-tsslint\",\n    \"esbenp.prettier-vscode\","
  },
  {
    "path": ".vscode/launch.json",
    "chars": 915,
    "preview": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Debug Main Process\",\n      \"type\": \"node\",\n      \"req"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 2764,
    "preview": "{\n  \"editor.formatOnSave\": true,\n  \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n  \"[javascript][javascriptreact]"
  },
  {
    "path": "AGENTS.md",
    "chars": 7681,
    "preview": "# AGENTS.md\n\nThis file provides concise, agent-focused guidance for working in this monorepo. It consolidates the reposi"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3349,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3585,
    "preview": "# Contributing to Folo\n\nThank you for considering contributing to Folo! We welcome contributions from the community to h"
  },
  {
    "path": "LICENSE",
    "chars": 34769,
    "preview": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C)"
  },
  {
    "path": "README.md",
    "chars": 12991,
    "preview": "<div align=\"center\">\n  <a href=\"https://github.com/RSSNext/Folo\">\n    <img src=\"https://github.com/RSSNext/Folo/raw/refs"
  },
  {
    "path": "SECURITY.md",
    "chars": 226,
    "preview": "# Security Policy\n\n## Supported Versions\n\nWe always recommend using the latest version of Follow to ensure you get all s"
  },
  {
    "path": "api/vercel_webhook.ts",
    "chars": 3206,
    "preview": "import crypto from \"node:crypto\"\n\nimport type { VercelRequest, VercelResponse } from \"@vercel/node\"\nimport getRawBody fr"
  },
  {
    "path": "apps/cli/package.json",
    "chars": 1062,
    "preview": "{\n  \"name\": \"folocli\",\n  \"type\": \"module\",\n  \"version\": \"0.0.1\",\n  \"description\": \"Folo CLI for terminal workflows and a"
  },
  {
    "path": "apps/cli/skill.md",
    "chars": 6357,
    "preview": "# Folo CLI Skill\n\n## Trigger Conditions\n\nUse this skill when a user asks to:\n\n- Manage RSS subscriptions\n- Browse timeli"
  },
  {
    "path": "apps/cli/src/args.test.ts",
    "chars": 1945,
    "preview": "import { describe, expect, it } from \"vitest\"\n\nimport { parseFormat, parseISODate, parseNonNegativeInt, parsePositiveInt"
  },
  {
    "path": "apps/cli/src/args.ts",
    "chars": 1753,
    "preview": "import type { OutputFormat } from \"./output\"\n\nconst viewMap: Readonly<Record<string, number>> = {\n  article: 0,\n  articl"
  },
  {
    "path": "apps/cli/src/browser-login.test.ts",
    "chars": 1998,
    "preview": "import { describe, expect, it } from \"vitest\"\n\nimport { DEFAULT_VALUES } from \"../../../packages/internal/shared/src/env"
  },
  {
    "path": "apps/cli/src/browser-login.ts",
    "chars": 6235,
    "preview": "import { spawnSync } from \"node:child_process\"\nimport { createServer } from \"node:http\"\nimport type { AddressInfo } from"
  },
  {
    "path": "apps/cli/src/cli.e2e.test.ts",
    "chars": 2812,
    "preview": "import type { ExecFileException } from \"node:child_process\"\nimport { execFile } from \"node:child_process\"\nimport { mkdte"
  },
  {
    "path": "apps/cli/src/client.ts",
    "chars": 2785,
    "preview": "import { FollowClient } from \"@follow-app/client-sdk\"\nimport type { Command } from \"commander\"\n\nimport type { FoloCLICon"
  },
  {
    "path": "apps/cli/src/command.ts",
    "chars": 995,
    "preview": "import type { Command } from \"commander\"\n\nimport type { CommandContext } from \"./client\"\nimport { createCommandContext, "
  },
  {
    "path": "apps/cli/src/commands/auth.ts",
    "chars": 3634,
    "preview": "import type { Command } from \"commander\"\n\nimport { parsePositiveInt } from \"../args\"\nimport { loginWithBrowser } from \"."
  },
  {
    "path": "apps/cli/src/commands/collection.ts",
    "chars": 2249,
    "preview": "import type { EntryListRequest } from \"@follow-app/client-sdk\"\nimport type { Command } from \"commander\"\n\nimport { parseI"
  },
  {
    "path": "apps/cli/src/commands/entry.ts",
    "chars": 2733,
    "preview": "import type { MarkAllAsReadRequest } from \"@follow-app/client-sdk\"\nimport type { Command } from \"commander\"\n\nimport { pa"
  },
  {
    "path": "apps/cli/src/commands/feed.ts",
    "chars": 1448,
    "preview": "import type { Command } from \"commander\"\n\nimport { runCommand } from \"../command\"\n\nconst isURL = (value: string) => valu"
  },
  {
    "path": "apps/cli/src/commands/list.ts",
    "chars": 4552,
    "preview": "import type { Command } from \"commander\"\n\nimport { parseNonNegativeInt, parseView, viewHelp } from \"../args\"\nimport { ru"
  },
  {
    "path": "apps/cli/src/commands/opml.ts",
    "chars": 2368,
    "preview": "import { mkdir, readFile, writeFile } from \"node:fs/promises\"\n\nimport type { Command } from \"commander\"\nimport { basenam"
  },
  {
    "path": "apps/cli/src/commands/search.ts",
    "chars": 3655,
    "preview": "import type { Command } from \"commander\"\n\nimport { parsePositiveInt, parseView, viewHelp } from \"../args\"\nimport { runCo"
  },
  {
    "path": "apps/cli/src/commands/subscription.ts",
    "chars": 6231,
    "preview": "import type {\n  SubscriptionCreateRequest,\n  SubscriptionDeleteRequest,\n  SubscriptionUpdateRequest,\n} from \"@follow-app"
  },
  {
    "path": "apps/cli/src/commands/timeline.ts",
    "chars": 2852,
    "preview": "import type { EntryListRequest } from \"@follow-app/client-sdk\"\nimport type { Command } from \"commander\"\n\nimport { parseI"
  },
  {
    "path": "apps/cli/src/commands/unread.ts",
    "chars": 3399,
    "preview": "import type {\n  InboxSubscriptionResponse,\n  ListSubscriptionResponse,\n  SubscriptionWithFeed,\n} from \"@follow-app/clien"
  },
  {
    "path": "apps/cli/src/config.ts",
    "chars": 1750,
    "preview": "import { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\n\nimport { join } from \"p"
  },
  {
    "path": "apps/cli/src/index.ts",
    "chars": 2155,
    "preview": "import { Command } from \"commander\"\n\nimport packageJSON from \"../package.json\"\nimport { parseFormat } from \"./args\"\nimpo"
  },
  {
    "path": "apps/cli/src/output.test.ts",
    "chars": 2073,
    "preview": "import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\"\n\nimport { CLIError, normalizeError, printFailur"
  },
  {
    "path": "apps/cli/src/output.ts",
    "chars": 4412,
    "preview": "import { inspect } from \"node:util\"\n\nimport { FollowAPIError, FollowAuthError } from \"@follow-app/client-sdk\"\n\nexport ty"
  },
  {
    "path": "apps/cli/tsconfig.json",
    "chars": 280,
    "preview": "{\n  \"extends\": \"@follow/configs/tsconfig.extend.json\",\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"ESN"
  },
  {
    "path": "apps/cli/tsup.config.ts",
    "chars": 287,
    "preview": "import { defineConfig } from \"tsup\"\n\nexport default defineConfig({\n  entry: [\"src/index.ts\"],\n  format: [\"esm\"],\n  targe"
  },
  {
    "path": "apps/cli/vitest.config.ts",
    "chars": 121,
    "preview": "import { defineProject } from \"vitest/config\"\n\nexport default defineProject({\n  test: {\n    environment: \"node\",\n  },\n})"
  },
  {
    "path": "apps/desktop/.env.example",
    "chars": 256,
    "preview": "VITE_WEB_URL=http://localhost:5173\nVITE_API_URL=http://localhost:3000\nVITE_IMGPROXY_URL=http://localhost:2873\nVITE_SENTR"
  },
  {
    "path": "apps/desktop/AGENTS.md",
    "chars": 9099,
    "preview": "# AGENTS.md\n\nThis file provides specific guidance for developing the Electron desktop application.\n\n## Architecture\n\n- *"
  },
  {
    "path": "apps/desktop/build/appxmanifest-template.xml",
    "chars": 1741,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Package\n   xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows1"
  },
  {
    "path": "apps/desktop/build/entitlements.mac.plist",
    "chars": 251,
    "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": "apps/desktop/build/entitlements.mas.child.plist",
    "chars": 304,
    "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": "apps/desktop/build/entitlements.mas.plist",
    "chars": 460,
    "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": "apps/desktop/bump.config.ts",
    "chars": 845,
    "preview": "/* eslint-disable no-template-curly-in-string */\nimport { defineConfig } from \"nbump\"\n\nexport default defineConfig({\n  l"
  },
  {
    "path": "apps/desktop/bump.hotfix.config.js",
    "chars": 585,
    "preview": "/* eslint-disable no-template-curly-in-string */\nimport { execSync } from \"node:child_process\"\n\nimport { defineConfig } "
  },
  {
    "path": "apps/desktop/changelog/0.1.2.md",
    "chars": 688,
    "preview": "# What's new in v0.1.2\n\n## Features\n\n- Custom CSS is now supported, so you can add any CSS style and apply it to the Ent"
  },
  {
    "path": "apps/desktop/changelog/0.2.0.md",
    "chars": 1114,
    "preview": "# What's new in v0.2.0\n\n## New Features\n\n- Feed owners can now reset their feeds.\n- Image Gallery: Clicking on the image"
  },
  {
    "path": "apps/desktop/changelog/0.2.1.md",
    "chars": 412,
    "preview": "# What's new in v0.2.1\n\n## New Features\n\n- Added zoom functionality for viewing pictures.\n- Set `Show Unread Only` as th"
  },
  {
    "path": "apps/desktop/changelog/0.2.2.md",
    "chars": 232,
    "preview": "# What's new in v0.2.2\n\n## New Features\n\n- And Or conditions for actions\n- Add achievement badge\n\n## Improvements\n\n- ele"
  },
  {
    "path": "apps/desktop/changelog/0.2.3.md",
    "chars": 287,
    "preview": "# What's new in v0.2.3\n\n## New Features\n\n- Hold shift to quickly select multiple Feeds\n- Collect entry from List\n\n## Imp"
  },
  {
    "path": "apps/desktop/changelog/0.2.4.md",
    "chars": 353,
    "preview": "# What's new in v0.2.4-web\n\n## New Features\n\n🎉 HUGE NEWS! Follow finally goes mobile!\n\nEver wished you could Follow your"
  },
  {
    "path": "apps/desktop/changelog/0.2.5.md",
    "chars": 279,
    "preview": "# What's new in v0.2.5\n\n## New Features\n\n- Customizable columns for masonry view\n- Manually trigger AI summary or transl"
  },
  {
    "path": "apps/desktop/changelog/0.2.6.md",
    "chars": 349,
    "preview": "# What's new in v0.2.6\n\nWe've made some ux optimizations:\n\n- [Mobile]: Now when returning to the list from the entry det"
  },
  {
    "path": "apps/desktop/changelog/0.2.7.md",
    "chars": 797,
    "preview": "# What's new in v0.2.7\n\n## New Features\n\n1. New `Move to Category` operation in feed subscription context menu\n\n![](http"
  },
  {
    "path": "apps/desktop/changelog/0.2.8.md",
    "chars": 116,
    "preview": "# What's new in v0.2.8\n\n## New Features\n\n- Register or Login with email and password\n\n## Improvements\n\n## Bug Fixes\n"
  },
  {
    "path": "apps/desktop/changelog/0.2.9.md",
    "chars": 71,
    "preview": "# What's new in v0.2.9\n\n## New Features\n\n## Improvements\n\n## Bug Fixes\n"
  },
  {
    "path": "apps/desktop/changelog/0.3.0.md",
    "chars": 876,
    "preview": "# What's new in v0.3.0\n\n## New Features\n\n- **Custom RSSHub Instances**: Use custom RSSHub instances to improve data acqu"
  },
  {
    "path": "apps/desktop/changelog/0.3.1.md",
    "chars": 766,
    "preview": "# What's new in v0.3.1\n\n## New Features\n\n- **Customize toolbar**: Customize the toolbar to display the items you most fr"
  },
  {
    "path": "apps/desktop/changelog/0.3.10.md",
    "chars": 72,
    "preview": "# What's new in v0.3.10\n\n## New Features\n\n## Improvements\n\n## Bug Fixes\n"
  },
  {
    "path": "apps/desktop/changelog/0.3.11.md",
    "chars": 72,
    "preview": "# What's new in v0.3.11\n\n## New Features\n\n## Improvements\n\n## Bug Fixes\n"
  },
  {
    "path": "apps/desktop/changelog/0.3.12.md",
    "chars": 72,
    "preview": "# What's new in v0.3.12\n\n## New Features\n\n## Improvements\n\n## Bug Fixes\n"
  },
  {
    "path": "apps/desktop/changelog/0.3.13.md",
    "chars": 193,
    "preview": "# What's new in v0.3.13\n\n## New Features\n\n- New Action Language setting\n- Export entry content to a PDF file\n\n## Improve"
  },
  {
    "path": "apps/desktop/changelog/0.3.2.md",
    "chars": 368,
    "preview": "# What's new in v0.3.2\n\n## New Features\n\n- Added support for Two-Factor Authentication (2FA) for login and large transac"
  },
  {
    "path": "apps/desktop/changelog/0.3.3.md",
    "chars": 284,
    "preview": "# What's new in v0.3.3\n\n## New Features\n\n## Improvements\n\n- Merge actions for toggling state\n- Action supports matching "
  },
  {
    "path": "apps/desktop/changelog/0.3.4.md",
    "chars": 630,
    "preview": "# What's new in v0.3.4\n\n## New Features\n\n- The audio and notification views have been merged into the article view, bein"
  },
  {
    "path": "apps/desktop/changelog/0.3.5.md",
    "chars": 111,
    "preview": "# What's new in v0.3.5\n\n## New Features\n\n- Restore audio and notification views\n\n## Improvements\n\n## Bug Fixes\n"
  },
  {
    "path": "apps/desktop/changelog/0.3.6.md",
    "chars": 285,
    "preview": "# What's new in v0.3.6\n\n## New Features\n\n- Added a quick selector to the timeline column.\n\n![](https://github.com/RSSNex"
  },
  {
    "path": "apps/desktop/changelog/0.3.7.md",
    "chars": 257,
    "preview": "# What's new in v0.3.7\n\n## New Features\n\n- Revert to the previous list display styles.\n  ![Timeline selector3](https://g"
  },
  {
    "path": "apps/desktop/changelog/0.3.8.md",
    "chars": 164,
    "preview": "# What's new in v0.3.8\n\n## New Features\n\n## Improvements\n\n## Bug Fixes\n\n- The app is stuck after closing the dialog\n- Ca"
  },
  {
    "path": "apps/desktop/changelog/0.3.9.md",
    "chars": 71,
    "preview": "# What's new in v0.3.9\n\n## New Features\n\n## Improvements\n\n## Bug Fixes\n"
  },
  {
    "path": "apps/desktop/changelog/0.4.0.md",
    "chars": 784,
    "preview": "# What's New in v0.4.0\n\n## New Features\n\n- New global settings for AI summary and translation (#3294)\n- Display estimate"
  },
  {
    "path": "apps/desktop/changelog/0.4.1.md",
    "chars": 987,
    "preview": "# What's New in v0.4.1\n\n## New Features\n\n- Added a \"Mark Above as Read\" button at the bottom of the entry list (88963a9)"
  },
  {
    "path": "apps/desktop/changelog/0.4.2.md",
    "chars": 1269,
    "preview": "# What's New in v0.4.2\n\n## Shiny new things\n\n- Added Cubox integration (#3385)\n- Included document links on the actions "
  },
  {
    "path": "apps/desktop/changelog/0.4.3.md",
    "chars": 1060,
    "preview": "# What's New in v0.4.3\n\n## Shiny new things\n\n- Added server‑side readability with AI‑powered summaries and translation s"
  },
  {
    "path": "apps/desktop/changelog/0.4.4.md",
    "chars": 806,
    "preview": "# What's new in v0.4.4\n\n## Shiny new things\n\n- Added status action that allows you to send notifications or trigger webh"
  },
  {
    "path": "apps/desktop/changelog/0.4.5.md",
    "chars": 1483,
    "preview": "# What's new in v0.4.5\n\n## Shiny new things\n\n- Translation view toggle – choose between “translation-only” or bilingual "
  },
  {
    "path": "apps/desktop/changelog/0.4.6.md",
    "chars": 1377,
    "preview": "# What's new in v0.4.6\n\n## Shiny new things\n\n- Hide read subscriptions. You can now hide subscriptions with no unread it"
  },
  {
    "path": "apps/desktop/changelog/0.4.8.md",
    "chars": 1453,
    "preview": "# What's new in v0.4.8\n\n## Shiny new things\n\n- Keyboard Shortcuts – a full-featured command system covering global actio"
  },
  {
    "path": "apps/desktop/changelog/0.5.0.md",
    "chars": 1495,
    "preview": "# What's New in v0.5.0\n\n## Shiny new things\n\n- Double-clicking the draggable edge on either side of the entry list reset"
  },
  {
    "path": "apps/desktop/changelog/0.6.0.md",
    "chars": 57,
    "preview": "# What's New in v0.6.0\n\nThis version has been withdrawn.\n"
  },
  {
    "path": "apps/desktop/changelog/0.6.1.md",
    "chars": 1260,
    "preview": "# What's New in v0.6.1\n\n## Shiny New Things\n\n- Import and export your Actions (394d00f)\n- Add a bio, website, and social"
  },
  {
    "path": "apps/desktop/changelog/0.6.2.md",
    "chars": 1521,
    "preview": "# What's new in v0.6.2\n\n## Shiny new things\n\n- New “Fade Read Items” option dims read entries in the timeline (eeeea47)\n"
  },
  {
    "path": "apps/desktop/changelog/0.6.3.md",
    "chars": 1576,
    "preview": "# What's new in v0.6.3\n\n## Shiny new things\n\n- Hide From Timeline option for subscriptions to make your timeline cleaner"
  },
  {
    "path": "apps/desktop/changelog/0.7.0.md",
    "chars": 350,
    "preview": "# What's new in v0.7.0\n\n## Shiny new things\n\n- Add custom integration configurations to adapt to more apps\n\n## Improveme"
  },
  {
    "path": "apps/desktop/changelog/0.8.0.md",
    "chars": 409,
    "preview": "# What's new in v0.8.0\n\n## Shiny new things\n\n- Smart onboarding that gets you started in seconds.\n- One place for all yo"
  },
  {
    "path": "apps/desktop/changelog/0.9.0.md",
    "chars": 433,
    "preview": "# What's new in v0.9.0\n\n## Improvements\n\n- Removed the limit on the maximum number of views (b69a935)\n- Allow hiding the"
  },
  {
    "path": "apps/desktop/changelog/1.0.0.md",
    "chars": 566,
    "preview": "# What's new in v1.0.0\n\n## Shiny new things\n\n- 🌟 **Folo is now the AI Reader** — a smarter way to follow everything.\n\n##"
  },
  {
    "path": "apps/desktop/changelog/1.1.0.md",
    "chars": 1109,
    "preview": "# What's new in v1.1.0\n\n## Shiny new things\n\n- Reintroduced the clean, efficient three-column layout for a smoother read"
  },
  {
    "path": "apps/desktop/changelog/1.2.2.md",
    "chars": 1435,
    "preview": "# What's new in v1.2\n\n## Shiny new things\n\n- **AI**\n  - Added **BYOK (Bring Your Own Key)** support — choose your own AI"
  },
  {
    "path": "apps/desktop/changelog/1.2.6.md",
    "chars": 421,
    "preview": "# What's new in v1.2.6\n\n## Shiny new things\n\n- Optimize Stripe subscription management UI with dynamic billing portal an"
  },
  {
    "path": "apps/desktop/changelog/1.3.0.md",
    "chars": 416,
    "preview": "# What's new in v1.3.0\n\n## Shiny new things\n\n- Markdown link supports open share feed link directly in the app\n\n## No lo"
  },
  {
    "path": "apps/desktop/changelog/1.3.1.md",
    "chars": 476,
    "preview": "# What's new in v1.3.1\n\n## Shiny new things\n\n- Supported French localization.\n\n## Improvements\n\n- Migrated error trackin"
  },
  {
    "path": "apps/desktop/changelog/1.4.0.md",
    "chars": 432,
    "preview": "# What's new in v1.4.0\n\n## Shiny new things\n\n- Added in-app review prompts\n\n## Improvements\n\n- Expanded desktop end-to-e"
  },
  {
    "path": "apps/desktop/changelog/next.md",
    "chars": 177,
    "preview": "# What's new in vNEXT_VERSION\n\n## Shiny new things\n\n## Improvements\n\n## No longer broken\n\n## Thanks\n\nSpecial thanks to v"
  },
  {
    "path": "apps/desktop/changelog/next.template.md",
    "chars": 177,
    "preview": "# What's new in vNEXT_VERSION\n\n## Shiny new things\n\n## Improvements\n\n## No longer broken\n\n## Thanks\n\nSpecial thanks to v"
  },
  {
    "path": "apps/desktop/configs/vite.electron-render.config.ts",
    "chars": 1648,
    "preview": "import { fileURLToPath } from \"node:url\"\n\nimport { dirname, resolve } from \"pathe\"\nimport type { UserConfig } from \"vite"
  },
  {
    "path": "apps/desktop/configs/vite.render.config.ts",
    "chars": 2862,
    "preview": "import { readFileSync } from \"node:fs\"\nimport { fileURLToPath } from \"node:url\"\n\nimport react from \"@vitejs/plugin-react"
  },
  {
    "path": "apps/desktop/dev-only/dev-app-update.yml",
    "chars": 33,
    "preview": "provider: custom\nchannel: latest\n"
  },
  {
    "path": "apps/desktop/e2e/playwright.config.ts",
    "chars": 1331,
    "preview": "import { defineConfig, devices } from \"@playwright/test\"\n\nimport { resolveDesktopE2EEnv } from \"./support/env\"\n\nconst en"
  },
  {
    "path": "apps/desktop/e2e/scripts/capture-ui-audit.ts",
    "chars": 4722,
    "preview": "import { mkdir } from \"node:fs/promises\"\n\nimport { chromium } from \"@playwright/test\"\nimport { join } from \"pathe\"\n\nimpo"
  },
  {
    "path": "apps/desktop/e2e/support/account.ts",
    "chars": 1119,
    "preview": "import type { Page } from \"@playwright/test\"\n\nimport type { DesktopE2EEnv } from \"./env\"\n\nexport interface TestAccount {"
  },
  {
    "path": "apps/desktop/e2e/support/app.ts",
    "chars": 20302,
    "preview": "import type { Locator, Page } from \"@playwright/test\"\nimport { expect } from \"@playwright/test\"\n\nimport type { TestAccou"
  },
  {
    "path": "apps/desktop/e2e/support/auth-bootstrap.ts",
    "chars": 5755,
    "preview": "import type { BrowserContext, Page } from \"@playwright/test\"\nimport { nanoid } from \"nanoid\"\n\nimport type { TestAccount "
  },
  {
    "path": "apps/desktop/e2e/support/electron.ts",
    "chars": 2556,
    "preview": "import { execSync } from \"node:child_process\"\nimport { mkdtemp, rm } from \"node:fs/promises\"\nimport { tmpdir } from \"nod"
  },
  {
    "path": "apps/desktop/e2e/support/env.ts",
    "chars": 2360,
    "preview": "import { fileURLToPath } from \"node:url\"\n\nimport { join } from \"pathe\"\n\nexport type DesktopE2EProfile = \"local\" | \"prod\""
  },
  {
    "path": "apps/desktop/e2e/tests/electron/core.spec.ts",
    "chars": 2091,
    "preview": "import { expect, test } from \"@playwright/test\"\n\nimport { createTestAccount, tryDeleteCurrentUser } from \"../../support/"
  },
  {
    "path": "apps/desktop/e2e/tests/web/core.spec.ts",
    "chars": 2583,
    "preview": "import { expect, test } from \"@playwright/test\"\n\nimport { createTestAccount, tryDeleteCurrentUser } from \"../../support/"
  },
  {
    "path": "apps/desktop/e2e/tests/web/settings-sync.spec.ts",
    "chars": 2790,
    "preview": "import type { BrowserContext } from \"@playwright/test\"\nimport { expect, test } from \"@playwright/test\"\n\nimport { createT"
  },
  {
    "path": "apps/desktop/electron.vite.config.ts",
    "chars": 1061,
    "preview": "import { defineConfig } from \"electron-vite\"\nimport { resolve } from \"pathe\"\n\nimport { getGitHash } from \"../../scripts/"
  },
  {
    "path": "apps/desktop/forge.config.cts",
    "chars": 10199,
    "preview": "import crypto from \"node:crypto\"\nimport fs, { readdirSync } from \"node:fs\"\nimport { cp, readdir } from \"node:fs/promises"
  },
  {
    "path": "apps/desktop/layer/main/export.ts",
    "chars": 81,
    "preview": "// Export types for renderer to use\nexport type { IpcServices } from \"./src/ipc\"\n"
  },
  {
    "path": "apps/desktop/layer/main/global.d.ts",
    "chars": 88,
    "preview": "import \"../../types/vite\"\n\ndeclare global {\n  const GIT_COMMIT_HASH: string\n}\nexport {}\n"
  },
  {
    "path": "apps/desktop/layer/main/package.json",
    "chars": 1714,
    "preview": "{\n  \"name\": \"@follow/electron-main\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"author\": \"Folo Team\",\n  \"license\": \"GPL-3"
  },
  {
    "path": "apps/desktop/layer/main/preload/index.d.ts",
    "chars": 200,
    "preview": "import type { ElectronAPI } from \"@electron-toolkit/preload\"\n\ndeclare global {\n  interface Window {\n    electron?: Elect"
  },
  {
    "path": "apps/desktop/layer/main/preload/index.ts",
    "chars": 1744,
    "preview": "import os from \"node:os\"\nimport { platform } from \"node:process\"\n\nimport { electronAPI } from \"@electron-toolkit/preload"
  },
  {
    "path": "apps/desktop/layer/main/src/@types/constants.ts",
    "chars": 267,
    "preview": "const langs = [\"en\", \"zh-CN\", \"zh-TW\", \"ja\"] as const\nexport const currentSupportedLanguages = [...langs].sort() as stri"
  },
  {
    "path": "apps/desktop/layer/main/src/@types/i18next.d.ts",
    "chars": 462,
    "preview": "import type { defaultNS, ns } from \"./constants\"\nimport type { resources } from \"./resources\"\n\ndeclare module \"i18next\" "
  },
  {
    "path": "apps/desktop/layer/main/src/@types/resources.ts",
    "chars": 492,
    "preview": "import en from \"@locales/native/en.json\"\nimport ja from \"@locales/native/ja.json\"\nimport zhCn from \"@locales/native/zh-C"
  },
  {
    "path": "apps/desktop/layer/main/src/before-bootstrap.ts",
    "chars": 490,
    "preview": "import { app, protocol } from \"electron\"\nimport path from \"pathe\"\n\nconst e2eUserDataDir = process.env.FOLO_E2E_USER_DATA"
  },
  {
    "path": "apps/desktop/layer/main/src/bootstrap.ts",
    "chars": 300,
    "preview": "import { app } from \"electron\"\nimport squirrelStartup from \"electron-squirrel-startup\"\n\nimport { DEVICE_ID } from \"./con"
  },
  {
    "path": "apps/desktop/layer/main/src/constants/app.ts",
    "chars": 530,
    "preview": "import { app } from \"electron\"\nimport path from \"pathe\"\n\nexport const UNREAD_BACKGROUND_POLLING_INTERVAL = 1000 * 60 * 5"
  },
  {
    "path": "apps/desktop/layer/main/src/constants/system.ts",
    "chars": 90,
    "preview": "import { machineIdSync } from \"node-machine-id\"\n\nexport const DEVICE_ID = machineIdSync()\n"
  },
  {
    "path": "apps/desktop/layer/main/src/env.ts",
    "chars": 850,
    "preview": "import os from \"node:os\"\n\nimport { DEV } from \"@follow/shared/constants\"\n\nexport const channel: \"development\" | \"beta\" |"
  },
  {
    "path": "apps/desktop/layer/main/src/helper.ts",
    "chars": 1107,
    "preview": "import { fileURLToPath, pathToFileURL } from \"node:url\"\n\nimport { MODE, ModeEnum } from \"@follow/shared/constants\"\nimpor"
  },
  {
    "path": "apps/desktop/layer/main/src/index.ts",
    "chars": 49,
    "preview": "import \"./before-bootstrap\"\nimport \"./bootstrap\"\n"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/index.ts",
    "chars": 1115,
    "preview": "import type { MergeIpcService } from \"electron-ipc-decorator\"\nimport { createServices } from \"electron-ipc-decorator\"\n\ni"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/app.ts",
    "chars": 7019,
    "preview": "import fsp from \"node:fs/promises\"\nimport { fileURLToPath } from \"node:url\"\n\nimport { callWindowExpose } from \"@follow/s"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/auth.ts",
    "chars": 5689,
    "preview": "import { env } from \"@follow/shared/env.desktop\"\nimport { createDesktopAPIHeaders } from \"@follow/utils/headers\"\nimport "
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/cli.ts",
    "chars": 2218,
    "preview": "import type { IpcContext } from \"electron-ipc-decorator\"\nimport { IpcMethod, IpcService } from \"electron-ipc-decorator\"\n"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/debug.ts",
    "chars": 422,
    "preview": "import type { IpcContext } from \"electron-ipc-decorator\"\nimport { IpcMethod, IpcService } from \"electron-ipc-decorator\"\n"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/dock.ts",
    "chars": 2367,
    "preview": "import type { IpcContext } from \"electron-ipc-decorator\"\nimport { IpcMethod, IpcService } from \"electron-ipc-decorator\"\n"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/integration.ts",
    "chars": 11582,
    "preview": "import { existsSync } from \"node:fs\"\nimport fsp from \"node:fs/promises\"\n\nimport { shell } from \"electron\"\nimport type { "
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/menu.ts",
    "chars": 2141,
    "preview": "import type { MenuItemConstructorOptions, MessageBoxOptions } from \"electron\"\nimport { dialog, Menu, ShareMenu } from \"e"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/reader.ts",
    "chars": 3367,
    "preview": "import fs from \"node:fs\"\n\nimport { callWindowExpose } from \"@follow/shared/bridge\"\nimport { readability } from \"@follow-"
  },
  {
    "path": "apps/desktop/layer/main/src/ipc/services/setting.ts",
    "chars": 2239,
    "preview": "import { createRequire } from \"node:module\"\n\nimport { app, nativeTheme } from \"electron\"\nimport type { IpcContext } from"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/api-client.ts",
    "chars": 2628,
    "preview": "import { env } from \"@follow/shared/env.desktop\"\nimport { createDesktopAPIHeaders } from \"@follow/utils/headers\"\nimport "
  },
  {
    "path": "apps/desktop/layer/main/src/lib/auth-cookie-migration.ts",
    "chars": 2454,
    "preview": "import type { Cookie, CookiesSetDetails, Session } from \"electron\"\n\nimport { BETTER_AUTH_COOKIE_NAME_SESSION_TOKEN } fro"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/cleaner.ts",
    "chars": 4874,
    "preview": "import { statSync } from \"node:fs\"\nimport fsp from \"node:fs/promises\"\n\nimport { callWindowExpose } from \"@follow/shared/"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/cli-session-sync.ts",
    "chars": 3435,
    "preview": "import { execFile } from \"node:child_process\"\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { hom"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/dock.ts",
    "chars": 488,
    "preview": "import { app } from \"electron\"\n\nimport { isWindows } from \"../env\"\n\nexport const setDockCount = (input: number) => {\n  /"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/download.ts",
    "chars": 3751,
    "preview": "import { createHash } from \"node:crypto\"\nimport { createWriteStream } from \"node:fs\"\nimport { mkdir } from \"node:fs/prom"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/i18n.ts",
    "chars": 320,
    "preview": "import i18next from \"i18next\"\n\nimport { resources } from \"../@types/resources\"\n\nexport const defaultNS = \"native\"\n\nexpor"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/proxy.test.ts",
    "chars": 2936,
    "preview": "import { session } from \"electron\"\nimport type { Mock } from \"vitest\"\nimport { beforeEach, describe, expect, it, vi } fr"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/proxy.ts",
    "chars": 3265,
    "preview": "import { session } from \"electron\"\nimport { ProxyAgent, setGlobalDispatcher } from \"undici\"\n\nimport { logger } from \"../"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/router.ts",
    "chars": 3496,
    "preview": "import { callWindowExpose } from \"@follow/shared/bridge\"\nimport { extractElectronWindowOptions } from \"@follow/shared/el"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/store.ts",
    "chars": 655,
    "preview": "import type { Credentials } from \"@eneris/push-receiver/dist/types\"\nimport Store from \"electron-store\"\n\n// @keep-sorted\n"
  },
  {
    "path": "apps/desktop/layer/main/src/lib/tray.ts",
    "chars": 3110,
    "preview": "import { name } from \"@pkg\"\nimport { app, Menu, nativeImage, Tray } from \"electron\"\n\nimport { isMacOS, isMAS, isWindows "
  },
  {
    "path": "apps/desktop/layer/main/src/lib/user.ts",
    "chars": 1092,
    "preview": "import type { Credentials } from \"@eneris/push-receiver/dist/types\"\n\nimport { isLinux, isMacOS, isWindows } from \"~/env\""
  },
  {
    "path": "apps/desktop/layer/main/src/lib/utils.ts",
    "chars": 653,
    "preview": "import type { BrowserWindow } from \"electron\"\n\nexport const sleep = (ms: number) => new Promise((resolve) => setTimeout("
  },
  {
    "path": "apps/desktop/layer/main/src/logger.ts",
    "chars": 497,
    "preview": "import { app, shell } from \"electron\"\nimport log from \"electron-log\"\n\nexport const logger = log.scope(\"main\")\nlog.initia"
  },
  {
    "path": "apps/desktop/layer/main/src/manager/app.ts",
    "chars": 7523,
    "preview": "import { PushReceiver } from \"@eneris/push-receiver\"\nimport { callWindowExpose } from \"@follow/shared/bridge\"\nimport { A"
  },
  {
    "path": "apps/desktop/layer/main/src/manager/bootstrap.ts",
    "chars": 7116,
    "preview": "import { rmSync } from \"node:fs\"\n\nimport { electronApp, optimizer } from \"@electron-toolkit/utils\"\nimport { callWindowEx"
  },
  {
    "path": "apps/desktop/layer/main/src/manager/lifecycle.ts",
    "chars": 1049,
    "preview": "import { app } from \"electron\"\n\nimport { WindowManager } from \"~/manager/window\"\n\nclass LifecycleManagerStatic {\n  priva"
  },
  {
    "path": "apps/desktop/layer/main/src/manager/window.ts",
    "chars": 14884,
    "preview": "import { fileURLToPath } from \"node:url\"\n\nimport { is } from \"@electron-toolkit/utils\"\nimport { LEGACY_APP_PROTOCOL } fr"
  },
  {
    "path": "apps/desktop/layer/main/src/menu.ts",
    "chars": 6512,
    "preview": "import { callWindowExpose } from \"@follow/shared/bridge\"\nimport { DEV } from \"@follow/shared/constants\"\nimport { dispatc"
  },
  {
    "path": "apps/desktop/layer/main/src/modules/language-detection/index.ts",
    "chars": 3784,
    "preview": "// @see https://github.dev/microsoft/vscode/blob/main/src/vs/workbench/services/languageDetection/browser/languageDetect"
  },
  {
    "path": "apps/desktop/layer/main/src/shims/utf-8-validate.cjs",
    "chars": 294,
    "preview": "\"use strict\"\n\nconst { isUtf8 } = require(\"node:buffer\")\n\nmodule.exports = function isValidUTF8(buffer) {\n  if (typeof is"
  },
  {
    "path": "apps/desktop/layer/main/src/updater/api.ts",
    "chars": 816,
    "preview": "import type {\n  DistributionStatusPayload,\n  GetLatestReleaseQuery,\n  LatestReleasePayload,\n} from \"@follow-app/client-s"
  }
]

// ... and 2669 more files (download for full content)

About this extraction

This page contains the full source code of the RSSNext/Folo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 2869 files (8.2 MB), approximately 2.3M tokens, and a symbol index with 3942 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!