diff --git a/.github/actions/prepare-tofu/action.yml b/.github/actions/prepare-tofu/action.yml index c64332e3c..dcb95842c 100644 --- a/.github/actions/prepare-tofu/action.yml +++ b/.github/actions/prepare-tofu/action.yml @@ -28,7 +28,7 @@ runs: SENTRY_PROJECT: ${{ env.SENTRY_PROJECT }} - name: Setup OpenTofu - uses: opentofu/setup-opentofu@9d84900f3238fab8cd84ce47d658d25dd008be2f # v1.0.8 + uses: opentofu/setup-opentofu@fc711fa910b93cba0f3fbecaafc9f42fd0c411cb # v2.0.0 with: tofu_version: ${{ inputs.opentofu-version }} diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 9b4c9acbb..f7d1e4180 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -19,7 +19,7 @@ runs: using: composite steps: - name: Setup pnpm - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 diff --git a/.github/workflows/cloudflare-web-preview.yml b/.github/workflows/cloudflare-web-preview.yml index 8b93a4bb9..1c768a453 100644 --- a/.github/workflows/cloudflare-web-preview.yml +++ b/.github/workflows/cloudflare-web-preview.yml @@ -8,7 +8,9 @@ on: - 'package.json' - 'package-lock.json' - 'vite.config.ts' + - 'tsconfig.web.json' - 'tsconfig.json' + - 'tsconfig.node.json' - '.github/workflows/cloudflare-web-preview.yml' - '.github/actions/setup/**' push: @@ -20,7 +22,9 @@ on: - 'package.json' - 'package-lock.json' - 'vite.config.ts' + - 'tsconfig.web.json' - 'tsconfig.json' + - 'tsconfig.node.json' - '.github/workflows/cloudflare-web-preview.yml' - '.github/actions/setup/**' diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 03a63ef99..540ded829 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -39,7 +39,7 @@ jobs: - name: Log in to GitHub Container Registry if: github.event_name != 'pull_request' - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -64,7 +64,7 @@ jobs: - name: Extract metadata id: meta - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} flavor: | @@ -94,14 +94,14 @@ jobs: NODE_OPTIONS=--max_old_space_size=4096 pnpm run build - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build and push Docker image id: push - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: . push: ${{ github.event_name != 'pull_request' }} diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index a9ac43708..b1d8f99e1 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -97,6 +97,112 @@ jobs: - name: Run tests run: pnpm run test:run + coverage: + name: Coverage thresholds + runs-on: ubuntu-latest + if: github.head_ref != 'release' + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup app + uses: ./.github/actions/setup + + - name: Run tests with coverage + run: pnpm run test:coverage + + missing-tests: + name: Check for missing tests + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' && github.head_ref != 'release' + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Check changed logic files for missing tests + id: check + run: | + BASE=${{ github.event.pull_request.base.sha }} + HEAD=${{ github.event.pull_request.head.sha }} + changed=$(git diff --name-only "$BASE" "$HEAD" -- 'src/**/*.ts' 'src/**/*.tsx' \ + | grep -v '\.test\.' | grep -v '\.spec\.' | grep -v '\.d\.ts' \ + | grep -v 'src/index\.tsx' | grep -v 'src/sw' | grep -v 'src/instrument' \ + | grep -v 'src/test/' || true) + missing="" + for f in $changed; do + base="${f%.*}" + if ! ls "${base}.test."* "${base}.spec."* 2>/dev/null | grep -q .; then + missing="$missing\n- $f" + fi + done + if [ -n "$missing" ]; then + echo "missing=true" >> $GITHUB_OUTPUT + printf 'files<> $GITHUB_OUTPUT + fi + + - name: Update PR comment + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + continue-on-error: true + with: + script: | + const marker = ''; + const missing = '${{ steps.check.outputs.missing }}' === 'true'; + const files = `${{ steps.check.outputs.files }}`; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const existing = comments.find(c => c.body && c.body.includes(marker)); + + if (!missing) { + if (existing) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + }); + } + return; + } + + const body = [ + marker, + '## ⚠️ Logic changes without tests', + '', + 'The following changed files have no corresponding `.test.` file.', + 'Consider adding tests, or note in your PR description why tests are not needed for these changes.', + '', + files, + ].join('\n'); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } + build: name: Build runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d3314e27b..6037b7363 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: - uses: knope-dev/action@19617851f9f13ab2f27a05989c55efb18aca3675 # v2.1.2 with: - version: 0.22.3 + version: 0.22.4 - name: Create Release run: knope release --verbose diff --git a/.github/workflows/sentry-preview-issues.yml b/.github/workflows/sentry-preview-issues.yml index c81787e74..eb5077992 100644 --- a/.github/workflows/sentry-preview-issues.yml +++ b/.github/workflows/sentry-preview-issues.yml @@ -8,7 +8,9 @@ on: - 'index.html' - 'package.json' - 'vite.config.ts' + - 'tsconfig.web.json' - 'tsconfig.json' + - 'tsconfig.node.json' workflow_dispatch: inputs: pr_number: diff --git a/.gitignore b/.gitignore index d6c83cfb1..fce1c2439 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ dist coverage node_modules devAssets +.tsbuildinfo +*.tsbuildinfo .DS_Store .idea diff --git a/.vscode/settings.json b/.vscode/settings.json index 29e56e92a..87d72b7ed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,15 @@ { "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", - "typescript.tsdk": "node_modules/typescript/lib", + "js/ts.tsdk.path": "node_modules/typescript/lib", "[jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "editor.formatOnSaveMode": "file", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" } } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 29f53db0b..82eb8b9e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,6 +47,8 @@ Also, we use [ESLint](https://eslint.org/) for clean and stylistically consisten If your change touches logic with testable behaviour, please include tests. See [docs/TESTING.md](./docs/TESTING.md) for a guide on how to write them. +For conventions on code style, naming, and project patterns, see [docs/CODE_QUALITY.md](./docs/CODE_QUALITY.md). + ## Restrictions on Generative AI Usage We expect and appreciate authentic engagement in our community. diff --git a/docs/CODE_QUALITY.md b/docs/CODE_QUALITY.md new file mode 100644 index 000000000..4f4c9cf6e --- /dev/null +++ b/docs/CODE_QUALITY.md @@ -0,0 +1,161 @@ +# Code Quality Guide + +This document describes the coding conventions and standards used throughout Sable. Most rules are enforced automatically in CI or by local linting. Read this to understand the why behind them and to get the conventions right the first time. + +## Enforcement layers + +| Layer | When it runs | What it checks | +| ----------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------- | +| **CI — format / lint / typecheck / knip** | On every PR and push to `dev` | Prettier format, ESLint, TypeScript, Knip dead-code analysis | +| **CI — tests** | On every PR and push to `dev` | Runs the full Vitest suite; fails if any test fails | +| **CI — coverage thresholds** | On every PR and push to `dev` | `pnpm test:coverage`; fails if overall coverage drops below the locked baseline | +| **CI — missing tests warning** | On PRs only | Comments listing changed logic files that have no `.test.` counterpart | +| **Editor** | As you type | ESLint + Prettier via VS Code extensions | + +PRs are not merged unless all CI quality checks are green. + +To fix all violations in the repo at once: + +```sh +pnpm run lint:fix +pnpm run fmt +``` + +## TypeScript + +### Prefer `type` over `interface` + +Use `type` for all type declarations. `interface` is not used in this codebase. + +```ts +type RoomAvatarProps = { + roomId: string; + src?: string; +}; +``` + +Rule: `@typescript-eslint/consistent-type-definitions: ['error', 'type']` + +### Use `import type` for type-only imports + +When an import is used only as a type and not at runtime, annotate it with `type`. + +```ts +import { type MatrixClient, MatrixError } from '$types/matrix-sdk'; +``` + +Rule: `@typescript-eslint/consistent-type-imports` with `inline-type-imports` style. + +### Strict null checks + +The project uses `strict: true`. Do not paper over nullability with unsafe casts. Handle the null or undefined case explicitly or use optional chaining. + +### Enums + +Use string enums for sets of related constants when they improve readability and debug output. + +## Imports + +### Ordering + +Group imports in this order, with a blank line between groups: + +1. External packages. +2. Internal path aliases. +3. Relative imports. + +```ts +import { useState } from 'react'; +import { Box, Text } from 'folds'; + +import { type MatrixClient } from '$types/matrix-sdk'; +import { useMatrixClient } from '$hooks/useMatrixClient'; + +import * as css from './RoomAvatar.css'; +``` + +### Path aliases + +Prefer the `$`-prefixed aliases over deep relative traversal for anything outside the current directory. + +## Naming conventions + +| Thing | Convention | Example | +| ---------------------- | ---------------------------------------- | ------------------------------ | +| React component | `PascalCase` function | `RoomAvatar` | +| Component props type | `[ComponentName]Props` | `RoomAvatarProps` | +| Custom hook | `use` prefix, `camelCase` | `useMatrixClient` | +| Jotai atom | `camelCase` + `Atom` suffix | `settingsAtom` | +| Jotai atom family | `camelCase` + `AtomFamily` suffix | `roomIdToOpenThreadAtomFamily` | +| Utility function | `camelCase` | `getMemberDisplayName` | +| Enum | `PascalCase` type + `PascalCase` members | `AsyncStatus.Loading` | +| CSS module file | `[ComponentName].css.ts` | `RoomAvatar.css.ts` | +| File (component) | `PascalCase.tsx` | `RoomAvatar.tsx` | +| File (hook/util/state) | `camelCase.ts` | `useMatrixClient.ts` | + +## React components + +### Named exports, not default exports + +Use named exports for components, hooks, and utilities. Default exports make refactors worse. + +### Keep components focused + +- One component per file is the default. +- Derive values from props and state instead of duplicating derived state. +- Move non-trivial logic into hooks or state helpers instead of burying it in JSX. + +### localStorage access + +Direct `localStorage` access is banned in `src/app/components/**` and `src/app/features/**`. + +Use one of these patterns instead: + +- Reactive state read by JSX: use `atomWithLocalStorage` in a state file. +- Values needed before React mounts or applied directly to DOM refs: use plain helper functions in `src/app/state/`. + +Examples: + +- [src/app/state/sentryStorage.ts](../src/app/state/sentryStorage.ts) +- [src/app/state/mediaVolume.ts](../src/app/state/mediaVolume.ts) + +## Styling + +Styles live in co-located `*.css.ts` files alongside the component. + +- Avoid inline `style={{}}` for static styling that should be a class. +- Do not import global CSS from component files. + +## Testing + +See [TESTING.md](./TESTING.md) for the full guide. + +- Put tests adjacent to the code they cover, or under `src/test/` for shared fixtures. +- Use `*.test.ts` / `*.test.tsx`. +- Use `@testing-library/react` for component tests. +- If your change touches logic with clear input/output, add or update tests. + +### Coverage thresholds + +Coverage thresholds are locked in `vitest.config.ts` and enforced by CI. They should only go up, never down. + +### Missing-tests advisory + +PRs get an advisory comment when changed logic files have no corresponding test file. That job is informational only. + +## What the linter enforces automatically + +| Rule | Enforced as | +| ----------------------------------------------------------------------- | ------------------------- | +| `@typescript-eslint/consistent-type-definitions` | error | +| `@typescript-eslint/consistent-type-imports` | error | +| `@typescript-eslint/no-unused-vars` | error | +| `@typescript-eslint/no-shadow` | error | +| `react-hooks/rules-of-hooks` | error | +| `react-hooks/exhaustive-deps` | error | +| `react/no-unstable-nested-components` | error | +| No direct `localStorage` in `components/` or `features/` | error | +| Prettier formatting | error | +| Knip dead exports and unused files | error | +| Coverage thresholds (statements/functions/lines >= 1.5%, branches >= 1) | CI error | +| Logic files without a `.test.` counterpart | CI advisory comment on PR | diff --git a/eslint.config.js b/eslint.config.js index 9fa0c87bb..f7cf561c8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,9 +1,11 @@ import path from 'node:path'; +import e18ePlugin from '@e18e/eslint-plugin'; import { includeIgnoreFile } from '@eslint/compat'; import js from '@eslint/js'; import { defineConfig } from 'eslint/config'; import { configs, helpers, plugins } from 'eslint-config-airbnb-extended'; +import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'; import { rules as prettierConfigRules } from 'eslint-config-prettier'; import prettierPlugin from 'eslint-plugin-prettier'; import reactPlugin from 'eslint-plugin-react'; @@ -11,6 +13,14 @@ import globals from 'globals'; const gitignorePath = path.resolve('.', '.gitignore'); const { jsFiles, tsFiles } = helpers.extensions; +const recommendedConfig = e18ePlugin.configs?.recommended; +const e18eRecommendedRules = + recommendedConfig && + !Array.isArray(recommendedConfig) && + 'rules' in recommendedConfig && + recommendedConfig.rules + ? recommendedConfig.rules + : {}; const jsConfig = defineConfig([ // ESLint recommended config @@ -66,10 +76,52 @@ const prettierConfig = defineConfig([ }, ]); +const e18eConfig = defineConfig([ + { + name: 'e18e/recommended', + files: ['src/**/*.{js,jsx,ts,tsx}', 'scripts/**/*.js'], + ignores: ['src/**/*.{test,spec}.{js,jsx,ts,tsx}', 'src/**/*.d.ts'], + plugins: { + e18e: e18ePlugin, + }, + rules: { + ...e18eRecommendedRules, + 'e18e/prefer-static-regex': 'off', + }, + }, +]); + +const scriptOverrides = defineConfig([ + { + name: 'project/script-overrides', + files: ['scripts/**/*.js'], + languageOptions: { + globals: { + ...globals.node, + }, + }, + rules: { + 'no-await-in-loop': 'off', + 'no-bitwise': 'off', + 'no-continue': 'off', + 'no-restricted-syntax': 'off', + 'prefer-destructuring': 'off', + }, + }, +]); + const projectOverrides = defineConfig([ { name: 'project/rule-overrides', files: [...jsFiles, ...tsFiles], + settings: { + 'import-x/resolver-next': [ + createTypeScriptImportResolver({ + alwaysTryTypes: true, + project: ['tsconfig.web.json', 'tsconfig.node.json'], + }), + ], + }, languageOptions: { globals: { JSX: 'readonly', @@ -110,9 +162,19 @@ const projectOverrides = defineConfig([ { name: 'project/typescript-rule-overrides', files: tsFiles, + languageOptions: { + parserOptions: { + projectService: false, + project: ['tsconfig.web.json', 'tsconfig.node.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, rules: { - // disabled for now to get eslint to pass - '@typescript-eslint/consistent-type-definitions': 'off', + '@typescript-eslint/consistent-type-definitions': ['error', 'type'], + '@typescript-eslint/consistent-type-imports': [ + 'error', + { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, + ], '@typescript-eslint/no-unsafe-enum-comparison': 'off', '@typescript-eslint/only-throw-error': 'off', '@typescript-eslint/array-type': 'off', @@ -131,6 +193,34 @@ const projectOverrides = defineConfig([ 'no-undef': 'off', }, }, + { + name: 'project/no-direct-localstorage-in-ui', + files: ['src/app/components/**/*.{ts,tsx}', 'src/app/features/**/*.{ts,tsx}'], + ignores: ['src/app/components/**/*.test.{ts,tsx}', 'src/app/features/**/*.test.{ts,tsx}'], + rules: { + 'no-restricted-properties': [ + 'error', + { + object: 'localStorage', + message: + 'Direct localStorage access is not allowed in components or features. Use an atom (atomWithLocalStorage) or a storage utility from src/app/state/ instead.', + }, + { + object: 'window', + property: 'localStorage', + message: + 'Direct localStorage access is not allowed in components or features. Use an atom (atomWithLocalStorage) or a storage utility from src/app/state/ instead.', + }, + ], + }, + }, + { + name: 'project/typescript-definition-files', + files: ['**/*.d.ts'], + rules: { + '@typescript-eslint/consistent-type-definitions': 'off', + }, + }, ]); export default defineConfig([ @@ -138,6 +228,8 @@ export default defineConfig([ ...jsConfig, ...reactConfig, ...typescriptConfig, + ...e18eConfig, + ...scriptOverrides, ...prettierConfig, ...projectOverrides, ]); diff --git a/knip.json b/knip.json index c6cca1d75..f3b110164 100644 --- a/knip.json +++ b/knip.json @@ -1,6 +1,12 @@ { "$schema": "https://unpkg.com/knip@5/schema.json", - "entry": ["src/sw.ts", "scripts/normalize-imports.js"], + "entry": ["src/sw.ts"], + "ignore": [ + "src/ext.d.ts", + "src/types/matrix-sdk-events.d.ts", + "src/client/secretStorageKeys.d.ts", + "src/app/utils/bgColorImg.d.ts" + ], "ignoreExportsUsedInFile": { "interface": true, "type": true diff --git a/package.json b/package.json index cab220bb1..dbb0ec746 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,16 @@ "lint:fix": "eslint . --fix", "fmt": "prettier --write .", "fmt:check": "prettier --check .", - "typecheck": "tsc", + "typecheck": "tsc -b", "test": "vitest", "test:ui": "vitest --ui", "test:run": "vitest run", "test:coverage": "vitest run --coverage", + "imports:normalize": "node scripts/normalize-imports.js", + "imports:normalize:write": "node scripts/normalize-imports.js --write", + "imports:repair": "pnpm run imports:normalize:write", + "imports:migrate:matrix-sdk": "node scripts/migrate-matrix-sdk-imports.js", + "imports:migrate:matrix-sdk:write": "node scripts/migrate-matrix-sdk-imports.js --write", "knip": "knip", "tunnel": "cloudflared tunnel --url http://localhost:8080", "knope": "knope", @@ -32,27 +37,27 @@ "license": "AGPL-3.0-only", "dependencies": { "@arborium/arborium": "^2.16.0", - "@atlaskit/pragmatic-drag-and-drop": "^1.7.7", + "@atlaskit/pragmatic-drag-and-drop": "^1.7.9", "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.4.0", "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0", "@fontsource-variable/nunito": "5.2.7", "@fontsource/space-mono": "5.2.9", "@phosphor-icons/react": "^2.1.10", - "@sentry/react": "^10.43.0", - "@tanstack/react-query": "^5.90.21", - "@tanstack/react-query-devtools": "^5.91.3", - "@tanstack/react-virtual": "^3.13.19", + "@sentry/react": "^10.46.0", + "@tanstack/react-query": "^5.95.2", + "@tanstack/react-query-devtools": "^5.95.2", + "@tanstack/react-virtual": "^3.13.23", "@use-gesture/react": "10.3.1", - "@vanilla-extract/css": "^1.18.0", + "@vanilla-extract/css": "^1.20.1", "@vanilla-extract/recipes": "^0.5.7", - "@vanilla-extract/vite-plugin": "^5.1.4", + "@vanilla-extract/vite-plugin": "^5.2.2", "await-to-js": "^3.0.0", "badwords-list": "^2.0.1-4", "blurhash": "^2.0.5", "browser-encrypt-attachment": "^0.3.0", "chroma-js": "^3.2.0", "classnames": "^2.5.1", - "dayjs": "^1.11.19", + "dayjs": "^1.11.20", "domhandler": "^5.0.3", "dompurify": "^3.3.3", "emojibase": "^15.3.1", @@ -65,24 +70,24 @@ "framer-motion": "12.34.3", "html-dom-parser": "^5.1.8", "html-react-parser": "^4.2.10", - "i18next": "^25.8.13", + "i18next": "^25.10.10", "i18next-browser-languagedetector": "^8.2.1", "i18next-http-backend": "^2.7.3", "immer": "^9.0.21", "is-hotkey": "^0.2.0", - "jotai": "^2.18.0", + "jotai": "^2.19.0", "linkify-react": "^4.3.2", "linkifyjs": "^4.3.2", "matrix-js-sdk": "^38.4.0", "matrix-widget-api": "^1.16.1", - "pdfjs-dist": "^5.4.624", + "pdfjs-dist": "^5.5.207", "react": "^18.3.1", - "react-aria": "^3.46.0", + "react-aria": "^3.47.0", "react-blurhash": "^0.3.0", "react-colorful": "^5.6.1", "react-dom": "^18.3.1", "react-google-recaptcha": "^2.1.0", - "react-i18next": "^16.5.4", + "react-i18next": "^16.6.6", "react-range": "^1.10.0", "react-router-dom": "^6.30.3", "slate": "^0.123.0", @@ -94,7 +99,8 @@ "workbox-precaching": "^7.4.0" }, "devDependencies": { - "@cloudflare/vite-plugin": "^1.26.0", + "@cloudflare/vite-plugin": "^1.30.2", + "@e18e/eslint-plugin": "^0.3.0", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@eslint/compat": "2.0.2", "@eslint/js": "9.39.3", @@ -113,27 +119,28 @@ "@types/react-dom": "^18.3.7", "@types/react-google-recaptcha": "^2.1.9", "@types/ua-parser-js": "^0.7.39", - "@vitejs/plugin-react": "^5.1.4", - "@vitest/coverage-v8": "^4.1.0", - "@vitest/ui": "^4.1.0", + "@vitejs/plugin-react": "^6.0.1", + "@vitest/coverage-v8": "^4.1.2", + "@vitest/ui": "^4.1.2", "buffer": "^6.0.3", "cloudflared": "^0.7.1", "eslint": "9.39.3", "eslint-config-airbnb-extended": "3.0.1", "eslint-config-prettier": "10.1.8", + "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-prettier": "5.5.5", "globals": "17.3.0", - "jsdom": "^29.0.0", + "jsdom": "^29.0.1", "knip": "5.85.0", "prettier": "3.8.1", "typescript": "^5.9.3", - "vite": "^7.3.1", - "vite-plugin-compression2": "2.5.0", + "vite": "^8.0.3", + "vite-plugin-compression2": "2.5.3", "vite-plugin-pwa": "^1.2.0", - "vite-plugin-static-copy": "^3.2.0", + "vite-plugin-static-copy": "^4.0.0", "vite-plugin-svgr": "4.5.0", "vite-plugin-top-level-await": "^1.6.0", - "vitest": "^4.1.0", - "wrangler": "^4.70.0" + "vitest": "^4.1.2", + "wrangler": "^4.78.0" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a5589d58..9381d9149 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,14 +5,10 @@ settings: excludeLinksFromLockfile: false overrides: - brace-expansion: '>=1.1.12' - esbuild: '>=0.25.0' - lodash: '>=4.17.23' - minimatch: '>=10.2.3' - rollup: '>=4.59.0' - serialize-javascript: '>=7.0.3' - flatted: '>=3.4.2' - undici: '>=7.24.0' + serialize-javascript: '>=7.0.5' + picomatch: '>=4.0.4' + smol-toml: '>=1.6.1' + yaml: '>=2.8.3' importers: @@ -22,7 +18,7 @@ importers: specifier: ^2.16.0 version: 2.16.0 '@atlaskit/pragmatic-drag-and-drop': - specifier: ^1.7.7 + specifier: ^1.7.9 version: 1.7.9 '@atlaskit/pragmatic-drag-and-drop-auto-scroll': specifier: ^1.4.0 @@ -40,29 +36,29 @@ importers: specifier: ^2.1.10 version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/react': - specifier: ^10.43.0 - version: 10.43.0(react@18.3.1) + specifier: ^10.46.0 + version: 10.46.0(react@18.3.1) '@tanstack/react-query': - specifier: ^5.90.21 - version: 5.90.21(react@18.3.1) + specifier: ^5.95.2 + version: 5.95.2(react@18.3.1) '@tanstack/react-query-devtools': - specifier: ^5.91.3 - version: 5.91.3(@tanstack/react-query@5.90.21(react@18.3.1))(react@18.3.1) + specifier: ^5.95.2 + version: 5.95.2(@tanstack/react-query@5.95.2(react@18.3.1))(react@18.3.1) '@tanstack/react-virtual': - specifier: ^3.13.19 - version: 3.13.21(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^3.13.23 + version: 3.13.23(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@use-gesture/react': specifier: 10.3.1 version: 10.3.1(react@18.3.1) '@vanilla-extract/css': - specifier: ^1.18.0 - version: 1.18.0 + specifier: ^1.20.1 + version: 1.20.1 '@vanilla-extract/recipes': specifier: ^0.5.7 - version: 0.5.7(@vanilla-extract/css@1.18.0) + version: 0.5.7(@vanilla-extract/css@1.20.1) '@vanilla-extract/vite-plugin': - specifier: ^5.1.4 - version: 5.1.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(yaml@2.8.2) + specifier: ^5.2.2 + version: 5.2.2(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) await-to-js: specifier: ^3.0.0 version: 3.0.0 @@ -82,8 +78,8 @@ importers: specifier: ^2.5.1 version: 2.5.1 dayjs: - specifier: ^1.11.19 - version: 1.11.19 + specifier: ^1.11.20 + version: 1.11.20 domhandler: specifier: ^5.0.3 version: 5.0.3 @@ -110,7 +106,7 @@ importers: version: 10.3.1(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) folds: specifier: ^2.6.2 - version: 2.6.2(@vanilla-extract/css@1.18.0)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.18.0))(classnames@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.6.2(@vanilla-extract/css@1.20.1)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.20.1))(classnames@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) framer-motion: specifier: 12.34.3 version: 12.34.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -121,8 +117,8 @@ importers: specifier: ^4.2.10 version: 4.2.10(react@18.3.1) i18next: - specifier: ^25.8.13 - version: 25.8.17(typescript@5.9.3) + specifier: ^25.10.10 + version: 25.10.10(typescript@5.9.3) i18next-browser-languagedetector: specifier: ^8.2.1 version: 8.2.1 @@ -136,8 +132,8 @@ importers: specifier: ^0.2.0 version: 0.2.0 jotai: - specifier: ^2.18.0 - version: 2.18.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1) + specifier: ^2.19.0 + version: 2.19.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1) linkify-react: specifier: ^4.3.2 version: 4.3.2(linkifyjs@4.3.2)(react@18.3.1) @@ -151,13 +147,13 @@ importers: specifier: ^1.16.1 version: 1.17.0 pdfjs-dist: - specifier: ^5.4.624 + specifier: ^5.5.207 version: 5.5.207 react: specifier: ^18.3.1 version: 18.3.1 react-aria: - specifier: ^3.46.0 + specifier: ^3.47.0 version: 3.47.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-blurhash: specifier: ^0.3.0 @@ -172,8 +168,8 @@ importers: specifier: ^2.1.0 version: 2.1.0(react@18.3.1) react-i18next: - specifier: ^16.5.4 - version: 16.5.7(i18next@25.8.17(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + specifier: ^16.6.6 + version: 16.6.6(i18next@25.10.10(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3) react-range: specifier: ^1.10.0 version: 1.10.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -203,11 +199,14 @@ importers: version: 7.4.0 devDependencies: '@cloudflare/vite-plugin': - specifier: ^1.26.0 - version: 1.27.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workerd@1.20260310.1)(wrangler@4.72.0) + specifier: ^1.30.2 + version: 1.30.2(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1))(workerd@1.20260317.1)(wrangler@4.78.0) + '@e18e/eslint-plugin': + specifier: ^0.3.0 + version: 0.3.0(eslint@9.39.3(jiti@2.6.1)) '@esbuild-plugins/node-globals-polyfill': specifier: ^0.2.3 - version: 0.2.3(esbuild@0.27.3) + version: 0.2.3(esbuild@0.27.4) '@eslint/compat': specifier: 2.0.2 version: 2.0.2(eslint@9.39.3(jiti@2.6.1)) @@ -216,16 +215,16 @@ importers: version: 9.39.3 '@rollup/plugin-inject': specifier: ^5.0.5 - version: 5.0.5(rollup@4.59.0) + version: 5.0.5(rollup@4.60.0) '@rollup/plugin-wasm': specifier: ^6.2.2 - version: 6.2.2(rollup@4.59.0) + version: 6.2.2(rollup@4.60.0) '@sableclient/sable-call-embedded': specifier: v1.1.4 version: 1.1.4 '@sentry/vite-plugin': specifier: ^5.1.1 - version: 5.1.1(rollup@4.59.0) + version: 5.1.1(rollup@4.60.0) '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 @@ -260,14 +259,14 @@ importers: specifier: ^0.7.39 version: 0.7.39 '@vitejs/plugin-react': - specifier: ^5.1.4 - version: 5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) '@vitest/coverage-v8': - specifier: ^4.1.0 - version: 4.1.0(vitest@4.1.0) + specifier: ^4.1.2 + version: 4.1.2(vitest@4.1.2) '@vitest/ui': - specifier: ^4.1.0 - version: 4.1.0(vitest@4.1.0) + specifier: ^4.1.2 + version: 4.1.2(vitest@4.1.2) buffer: specifier: ^6.0.3 version: 6.0.3 @@ -283,6 +282,9 @@ importers: eslint-config-prettier: specifier: 10.1.8 version: 10.1.8(eslint@9.39.3(jiti@2.6.1)) + eslint-import-resolver-typescript: + specifier: ^4.4.4 + version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.57.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.3(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1)) eslint-plugin-prettier: specifier: 5.5.5 version: 5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))(prettier@3.8.1) @@ -290,8 +292,8 @@ importers: specifier: 17.3.0 version: 17.3.0 jsdom: - specifier: ^29.0.0 - version: 29.0.0 + specifier: ^29.0.1 + version: 29.0.1 knip: specifier: 5.85.0 version: 5.85.0(@types/node@24.10.13)(typescript@5.9.3) @@ -302,37 +304,37 @@ importers: specifier: ^5.9.3 version: 5.9.3 vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + specifier: ^8.0.3 + version: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) vite-plugin-compression2: - specifier: 2.5.0 - version: 2.5.0(rollup@4.59.0) + specifier: 2.5.3 + version: 2.5.3(rollup@4.60.0) vite-plugin-pwa: specifier: ^1.2.0 - version: 1.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) + version: 1.2.0(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) vite-plugin-static-copy: - specifier: ^3.2.0 - version: 3.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + specifier: ^4.0.0 + version: 4.0.0(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) vite-plugin-svgr: specifier: 4.5.0 - version: 4.5.0(rollup@4.59.0)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + version: 4.5.0(rollup@4.60.0)(typescript@5.9.3)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) vite-plugin-top-level-await: specifier: ^1.6.0 - version: 1.6.0(@swc/helpers@0.5.19)(rollup@4.59.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + version: 1.6.0(@swc/helpers@0.5.19)(rollup@4.60.0)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) vitest: - specifier: ^4.1.0 - version: 4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + specifier: ^4.1.2 + version: 4.1.2(@types/node@24.10.13)(@vitest/ui@4.1.2)(jsdom@29.0.1)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) wrangler: - specifier: ^4.70.0 - version: 4.72.0 + specifier: ^4.78.0 + version: 4.78.0 packages: '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - '@apideck/better-ajv-errors@0.3.6': - resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + '@apideck/better-ajv-errors@0.3.7': + resolution: {integrity: sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==} engines: {node: '>=10'} peerDependencies: ajv: '>=8' @@ -468,6 +470,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -762,18 +769,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.29.0': resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} engines: {node: '>=6.9.0'} @@ -889,8 +884,8 @@ packages: resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} engines: {node: '>=18.0.0'} - '@cloudflare/unenv-preset@2.15.0': - resolution: {integrity: sha512-EGYmJaGZKWl+X8tXxcnx4v2bOZSjQeNI5dWFeXivgX9+YCT69AkzHHwlNbVpqtEUTbew8eQurpyOpeN8fg00nw==} + '@cloudflare/unenv-preset@2.16.0': + resolution: {integrity: sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==} peerDependencies: unenv: 2.0.0-rc.24 workerd: 1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0 @@ -898,38 +893,38 @@ packages: workerd: optional: true - '@cloudflare/vite-plugin@1.27.0': - resolution: {integrity: sha512-+s8APrIadCf2h0zYZ3WIPVeQ2CYgbXgFiR3BIrKvbpoo5klLgTeYpTq8ggxBmtjwt88PNruI4eCGZ6XNaDHMaA==} + '@cloudflare/vite-plugin@1.30.2': + resolution: {integrity: sha512-1TG/GyYxMAVhRtbKs9dPCsJY+c4qaPk+NKiLywKLV/BuvgMTBGhrmIvkC9NQSaw9sa5fPOYmv9me68AIs5kXJQ==} peerDependencies: - vite: ^6.1.0 || ^7.0.0 - wrangler: ^4.72.0 + vite: ^6.1.0 || ^7.0.0 || ^8.0.0 + wrangler: ^4.78.0 - '@cloudflare/workerd-darwin-64@1.20260310.1': - resolution: {integrity: sha512-hF2VpoWaMb1fiGCQJqCY6M8I+2QQqjkyY4LiDYdTL5D/w6C1l5v1zhc0/jrjdD1DXfpJtpcSMSmEPjHse4p9Ig==} + '@cloudflare/workerd-darwin-64@1.20260317.1': + resolution: {integrity: sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260310.1': - resolution: {integrity: sha512-h/Vl3XrYYPI6yFDE27XO1QPq/1G1lKIM8tzZGIWYpntK3IN5XtH3Ee/sLaegpJ49aIJoqhF2mVAZ6Yw+Vk2gJw==} + '@cloudflare/workerd-darwin-arm64@1.20260317.1': + resolution: {integrity: sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260310.1': - resolution: {integrity: sha512-XzQ0GZ8G5P4d74bQYOIP2Su4CLdNPpYidrInaSOuSxMw+HamsHaFrjVsrV2mPy/yk2hi6SY2yMbgKFK9YjA7vw==} + '@cloudflare/workerd-linux-64@1.20260317.1': + resolution: {integrity: sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260310.1': - resolution: {integrity: sha512-sxv4CxnN4ZR0uQGTFVGa0V4KTqwdej/czpIc5tYS86G8FQQoGIBiAIs2VvU7b8EROPcandxYHDBPTb+D9HIMPw==} + '@cloudflare/workerd-linux-arm64@1.20260317.1': + resolution: {integrity: sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260310.1': - resolution: {integrity: sha512-+1ZTViWKJypLfgH/luAHCqkent0DEBjAjvO40iAhOMHRLYP/SPphLvr4Jpi6lb+sIocS8Q1QZL4uM5Etg1Wskg==} + '@cloudflare/workerd-windows-64@1.20260317.1': + resolution: {integrity: sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -974,6 +969,17 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@e18e/eslint-plugin@0.3.0': + resolution: {integrity: sha512-hHgfpxsrZ2UYHcicA+tGZnmk19uJTaye9VH79O+XS8R4ona2Hx3xjhXghclNW58uXMk3xXlbYEOMr8thsoBmWg==} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + oxlint: ^1.55.0 + peerDependenciesMeta: + eslint: + optional: true + oxlint: + optional: true + '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -989,7 +995,7 @@ packages: '@esbuild-plugins/node-globals-polyfill@0.2.3': resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} peerDependencies: - esbuild: '>=0.25.0' + esbuild: '*' '@esbuild/aix-ppc64@0.27.3': resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} @@ -997,156 +1003,312 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.27.3': resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.27.3': resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.27.3': resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.27.3': resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.27.3': resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.27.3': resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.3': resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.27.3': resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.27.3': resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.27.3': resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.27.3': resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.27.3': resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.27.3': resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.27.3': resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.27.3': resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.27.3': resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.27.3': resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.3': resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.27.3': resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.3': resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.27.3': resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.27.3': resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.27.3': resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.27.3': resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.27.3': resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1538,6 +1700,9 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oxc-project/types@0.122.0': + resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==} + '@oxc-resolver/binding-android-arm-eabi@11.19.1': resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} cpu: [arm] @@ -2209,8 +2374,106 @@ packages: resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} engines: {node: '>=14.0.0'} - '@rolldown/pluginutils@1.0.0-rc.3': - resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + '@rolldown/binding-android-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.12': + resolution: {integrity: sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': + resolution: {integrity: sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': + resolution: {integrity: sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': + resolution: {integrity: sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': + resolution: {integrity: sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': + resolution: {integrity: sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': + resolution: {integrity: sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': + resolution: {integrity: sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': + resolution: {integrity: sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': + resolution: {integrity: sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.12': + resolution: {integrity: sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==} + + '@rolldown/pluginutils@1.0.0-rc.7': + resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} '@rollup/plugin-babel@5.3.1': resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} @@ -2218,7 +2481,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0 '@types/babel__core': ^7.1.9 - rollup: '>=4.59.0' + rollup: ^1.20.0||^2.0.0 peerDependenciesMeta: '@types/babel__core': optional: true @@ -2227,7 +2490,7 @@ packages: resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: '>=4.59.0' + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true @@ -2236,7 +2499,7 @@ packages: resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: '>=4.59.0' + rollup: ^2.78.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true @@ -2244,13 +2507,13 @@ packages: '@rollup/plugin-replace@2.4.2': resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} peerDependencies: - rollup: '>=4.59.0' + rollup: ^1.20.0 || ^2.0.0 '@rollup/plugin-terser@0.4.4': resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: '>=4.59.0' + rollup: ^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true @@ -2259,7 +2522,7 @@ packages: resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: '>=4.59.0' + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true @@ -2268,7 +2531,7 @@ packages: resolution: {integrity: sha512-gpC4R1G9Ni92ZIRTexqbhX7U+9estZrbhP+9SRb0DW9xpB9g7j34r+J2hqrcW/lRI7dJaU84MxZM0Rt82tqYPQ==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: '>=4.59.0' + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true @@ -2277,152 +2540,152 @@ packages: resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} peerDependencies: - rollup: '>=4.59.0' + rollup: ^1.20.0||^2.0.0 '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: '>=4.59.0' + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@rollup/rollup-android-arm-eabi@4.60.0': + resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@rollup/rollup-android-arm64@4.60.0': + resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@rollup/rollup-darwin-arm64@4.60.0': + resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@rollup/rollup-darwin-x64@4.60.0': + resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + '@rollup/rollup-freebsd-arm64@4.60.0': + resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@rollup/rollup-freebsd-x64@4.60.0': + resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@rollup/rollup-linux-arm-musleabihf@4.60.0': + resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@rollup/rollup-linux-arm64-gnu@4.60.0': + resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@rollup/rollup-linux-arm64-musl@4.60.0': + resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + '@rollup/rollup-linux-loong64-gnu@4.60.0': + resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + '@rollup/rollup-linux-loong64-musl@4.60.0': + resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@rollup/rollup-linux-ppc64-gnu@4.60.0': + resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + '@rollup/rollup-linux-ppc64-musl@4.60.0': + resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + '@rollup/rollup-linux-riscv64-gnu@4.60.0': + resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + '@rollup/rollup-linux-riscv64-musl@4.60.0': + resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@rollup/rollup-linux-s390x-gnu@4.60.0': + resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@rollup/rollup-linux-x64-gnu@4.60.0': + resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@rollup/rollup-linux-x64-musl@4.60.0': + resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + '@rollup/rollup-openbsd-x64@4.60.0': + resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@rollup/rollup-openharmony-arm64@4.60.0': + resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@rollup/rollup-win32-arm64-msvc@4.60.0': + resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + '@rollup/rollup-win32-ia32-msvc@4.60.0': + resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + '@rollup/rollup-win32-x64-gnu@4.60.0': + resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + '@rollup/rollup-win32-x64-msvc@4.60.0': + resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} cpu: [x64] os: [win32] @@ -2432,28 +2695,28 @@ packages: '@sableclient/sable-call-embedded@1.1.4': resolution: {integrity: sha512-XLRcbUPcn7i3QKZAPjIfUkUEXP0E4DOr0dyRoVCWMjHWj28kq+T7jeB2fRr5lB77olBwNHMjIuoTwrv02xiepQ==} - '@sentry-internal/browser-utils@10.43.0': - resolution: {integrity: sha512-8zYTnzhAPvNkVH1Irs62wl0J/c+0QcJ62TonKnzpSFUUD3V5qz8YDZbjIDGfxy+1EB9fO0sxtddKCzwTHF/MbQ==} + '@sentry-internal/browser-utils@10.46.0': + resolution: {integrity: sha512-WB1gBT9G13V02ekZ6NpUhoI1aGHV2eNfjEPthkU2bGBvFpQKnstwzjg7waIRGR7cu+YSW2Q6UI6aQLgBeOPD1g==} engines: {node: '>=18'} - '@sentry-internal/feedback@10.43.0': - resolution: {integrity: sha512-YoXuwluP6eOcQxTeTtaWb090++MrLyWOVsUTejzUQQ6LFL13Jwt+bDPF1kvBugMq4a7OHw/UNKQfd6//rZMn2g==} + '@sentry-internal/feedback@10.46.0': + resolution: {integrity: sha512-c4pI/z9nZCQXe9GYEw/hE/YTY9AxGBp8/wgKI+T8zylrN35SGHaXv63szzE1WbI8lacBY8lBF7rstq9bQVCaHw==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@10.43.0': - resolution: {integrity: sha512-ZIw1UNKOFXo1LbPCJPMAx9xv7D8TMZQusLDUgb6BsPQJj0igAuwd7KRGTkjjgnrwBp2O/sxcQFRhQhknWk7QPg==} + '@sentry-internal/replay-canvas@10.46.0': + resolution: {integrity: sha512-ub314MWUsekVCuoH0/HJbbimlI24SkV745UW2pj9xRbxOAEf1wjkmIzxKrMDbTgJGuEunug02XZVdJFJUzOcDw==} engines: {node: '>=18'} - '@sentry-internal/replay@10.43.0': - resolution: {integrity: sha512-khCXlGrlH1IU7P5zCEAJFestMeH97zDVCekj8OsNNDtN/1BmCJ46k6Xi0EqAUzdJgrOLJeLdoYdgtiIjovZ8Sg==} + '@sentry-internal/replay@10.46.0': + resolution: {integrity: sha512-JBsWeXG6bRbxBFK8GzWymWGOB9QE7Kl57BeF3jzgdHTuHSWZ2mRnAmb1K05T4LU+gVygk6yW0KmdC8Py9Qzg9A==} engines: {node: '>=18'} '@sentry/babel-plugin-component-annotate@5.1.1': resolution: {integrity: sha512-x2wEpBHwsTyTF2rWsLKJlzrRF1TTIGOfX+ngdE+Yd5DBkoS58HwQv824QOviPGQRla4/ypISqAXzjdDPL/zalg==} engines: {node: '>= 18'} - '@sentry/browser@10.43.0': - resolution: {integrity: sha512-2V3I3sXi3SMeiZpKixd9ztokSgK27cmvsD9J5oyOyjhGLTW/6QKCwHbKnluMgQMXq20nixQk5zN4wRjRUma3sg==} + '@sentry/browser@10.46.0': + resolution: {integrity: sha512-80DmGlTk5Z2/OxVOzLNxwolMyouuAYKqG8KUcoyintZqHbF6kO1RulI610HmyUt3OagKeBCqt9S7w0VIfCRL+Q==} engines: {node: '>=18'} '@sentry/bundler-plugin-core@5.1.1': @@ -2512,12 +2775,12 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/core@10.43.0': - resolution: {integrity: sha512-l0SszQAPiQGWl/ferw8GP3ALyHXiGiRKJaOvNmhGO+PrTQyZTZ6OYyPnGijAFRg58dE1V3RCH/zw5d2xSUIiNg==} + '@sentry/core@10.46.0': + resolution: {integrity: sha512-N3fj4zqBQOhXliS1Ne9euqIKuciHCGOJfPGQLwBoW9DNz03jF+NB8+dUKtrJ79YLoftjVgf8nbgwtADK7NR+2Q==} engines: {node: '>=18'} - '@sentry/react@10.43.0': - resolution: {integrity: sha512-shvErEpJ41i0Q3lIZl0CDWYQ7m8yHLi7ECG0gFvN8zf8pEdl5grQIOoe3t/GIUzcpCcor16F148ATmKJJypc/Q==} + '@sentry/react@10.46.0': + resolution: {integrity: sha512-Rb1S+9OuUPVwsz7GWnQ6Kgf3azbsseUymIegg3JZHNcW/fM1nPpaljzTBnuineia113DH0pgMBcdrrZDLaosFQ==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x @@ -2526,7 +2789,7 @@ packages: resolution: {integrity: sha512-1d5NkdRR6aKWBP7czkY8sFFWiKnfmfRpQOj+m9bJTsyTjbMiEQJst6315w5pCVlRItPhBqpAraqAhutZFgvyVg==} engines: {node: '>= 18'} peerDependencies: - rollup: '>=4.59.0' + rollup: '>=3.2.0' '@sentry/vite-plugin@5.1.1': resolution: {integrity: sha512-i6NWUDi2SDikfSUeMJvJTRdwEKYSfTd+mvBO2Ja51S1YK+hnickBuDfD+RvPerIXLuyRu3GamgNPbNqgCGUg/Q==} @@ -2704,31 +2967,31 @@ packages: '@swc/wasm@1.15.18': resolution: {integrity: sha512-zeSORFArxqUwfVMTRHu8AN9k9LlfSn0CKDSzLhJDITpgLoS0xpnocxsgMjQjUcVYDgO47r9zLP49HEjH/iGsFg==} - '@tanstack/query-core@5.90.20': - resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} + '@tanstack/query-core@5.95.2': + resolution: {integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==} - '@tanstack/query-devtools@5.93.0': - resolution: {integrity: sha512-+kpsx1NQnOFTZsw6HAFCW3HkKg0+2cepGtAWXjiiSOJJ1CtQpt72EE2nyZb+AjAbLRPoeRmPJ8MtQd8r8gsPdg==} + '@tanstack/query-devtools@5.95.2': + resolution: {integrity: sha512-QfaoqBn9uAZ+ICkA8brd1EHj+qBF6glCFgt94U8XP5BT6ppSsDBI8IJ00BU+cAGjQzp6wcKJL2EmRYvxy0TWIg==} - '@tanstack/react-query-devtools@5.91.3': - resolution: {integrity: sha512-nlahjMtd/J1h7IzOOfqeyDh5LNfG0eULwlltPEonYy0QL+nqrBB+nyzJfULV+moL7sZyxc2sHdNJki+vLA9BSA==} + '@tanstack/react-query-devtools@5.95.2': + resolution: {integrity: sha512-AFQFmbznVkbtfpx8VJ2DylW17wWagQel/qLstVLkYmNRo2CmJt3SNej5hvl6EnEeljJIdC3BTB+W7HZtpsH+3g==} peerDependencies: - '@tanstack/react-query': ^5.90.20 + '@tanstack/react-query': ^5.95.2 react: ^18 || ^19 - '@tanstack/react-query@5.90.21': - resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==} + '@tanstack/react-query@5.95.2': + resolution: {integrity: sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==} peerDependencies: react: ^18 || ^19 - '@tanstack/react-virtual@3.13.21': - resolution: {integrity: sha512-SYXFrmrbPgXBvf+HsOsKhFgqSe4M6B29VHOsX9Jih9TlNkNkDWx0hWMiMLUghMEzyUz772ndzdEeCEBx+3GIZw==} + '@tanstack/react-virtual@3.13.23': + resolution: {integrity: sha512-XnMRnHQ23piOVj2bzJqHrRrLg4r+F86fuBcwteKfbIjJrtGxb4z7tIvPVAe4B+4UVwo9G4Giuz5fmapcrnZ0OQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/virtual-core@3.13.21': - resolution: {integrity: sha512-ww+fmLHyCbPSf7JNbWZP3g7wl6SdNo3ah5Aiw+0e9FDErkVHLKprYUrwTm7dF646FtEkN/KkAKPYezxpmvOjxw==} + '@tanstack/virtual-core@3.13.23': + resolution: {integrity: sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg==} '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} @@ -3006,14 +3269,14 @@ packages: '@vanilla-extract/babel-plugin-debug-ids@1.2.2': resolution: {integrity: sha512-MeDWGICAF9zA/OZLOKwhoRlsUW+fiMwnfuOAqFVohL31Agj7Q/RBWAYweqjHLgFBCsdnr6XIfwjJnmb2znEWxw==} - '@vanilla-extract/compiler@0.3.4': - resolution: {integrity: sha512-W9HXf9EAccpE1vEIATvSoBVj/bQnmHfYHfDJjUN8dcOHW6oMcnoGTqweDM9I66BHqlNH4d0IsaeZKSViOv7K4w==} + '@vanilla-extract/compiler@0.7.0': + resolution: {integrity: sha512-rZQ40HVmsxfGLjoflwwsaUBLfpbpKDoZC19oiDA0FHq4LdrYtyVbFkc0MfqkNo/qBCvaZfsRezCqk0QQxCqZ8w==} - '@vanilla-extract/css@1.18.0': - resolution: {integrity: sha512-/p0dwOjr0o8gE5BRQ5O9P0u/2DjUd6Zfga2JGmE4KaY7ZITWMszTzk4x4CPlM5cKkRr2ZGzbE6XkuPNfp9shSQ==} + '@vanilla-extract/css@1.20.1': + resolution: {integrity: sha512-5I9RNo5uZW9tsBnqrWzJqELegOqTHBrZyDFnES0gR9gJJHBB9dom1N0bwITM9tKwBcfKrTX4a6DHVeQdJ2ubQA==} - '@vanilla-extract/integration@8.0.7': - resolution: {integrity: sha512-ILob4F9cEHXpbWAVt3Y2iaQJpqYq/c/5TJC8Fz58C2XmX3QW2Y589krvViiyJhQfydCGK3EbwPQhVFjQaBeKfg==} + '@vanilla-extract/integration@8.0.9': + resolution: {integrity: sha512-NP+CSo5IYHDmkMMy5vAxY4R9i2+CAg4sxgvVaxuHiuY9q30i6dNUTujNNKZGW2urEkd4HVVI6NggeIyYjbGPwA==} '@vanilla-extract/private@1.0.9': resolution: {integrity: sha512-gT2jbfZuaaCLrAxwXbRgIhGhcXbRZCG3v4TTUnjw0EJ7ArdBRxkq4msNJkbuRkCgfIK5ATmprB5t9ljvLeFDEA==} @@ -3023,59 +3286,66 @@ packages: peerDependencies: '@vanilla-extract/css': ^1.0.0 - '@vanilla-extract/vite-plugin@5.1.4': - resolution: {integrity: sha512-fTYNKUK3n4ApkUf2FEcO7mpqNKEHf9kDGg8DXlkqHtPxgwPhjuaajmDfQCSBsNgnA2SLI+CB5EO6kLQuKsw2Rw==} + '@vanilla-extract/vite-plugin@5.2.2': + resolution: {integrity: sha512-AUyB4fDR2b/Mo0lcXhhlf6RxnDPYwFMyKKopalJ4BwQNKYzZSoTwHJ1PLPO9SKhpz7lzXc0Z18GHQZOewzl3YA==} peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitejs/plugin-react@5.1.4': - resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==} + '@vitejs/plugin-react@6.0.1': + resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true - '@vitest/coverage-v8@4.1.0': - resolution: {integrity: sha512-nDWulKeik2bL2Va/Wl4x7DLuTKAXa906iRFooIRPR+huHkcvp9QDkPQ2RJdmjOFrqOqvNfoSQLF68deE3xC3CQ==} + '@vitest/coverage-v8@4.1.2': + resolution: {integrity: sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==} peerDependencies: - '@vitest/browser': 4.1.0 - vitest: 4.1.0 + '@vitest/browser': 4.1.2 + vitest: 4.1.2 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.0': - resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + '@vitest/expect@4.1.2': + resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} - '@vitest/mocker@4.1.0': - resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + '@vitest/mocker@4.1.2': + resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.1.0': - resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + '@vitest/pretty-format@4.1.2': + resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} - '@vitest/runner@4.1.0': - resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + '@vitest/runner@4.1.2': + resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} - '@vitest/snapshot@4.1.0': - resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + '@vitest/snapshot@4.1.2': + resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} - '@vitest/spy@4.1.0': - resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + '@vitest/spy@4.1.2': + resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} - '@vitest/ui@4.1.0': - resolution: {integrity: sha512-sTSDtVM1GOevRGsCNhp1mBUHKo9Qlc55+HCreFT4fe99AHxl1QQNXSL3uj4Pkjh5yEuWZIx8E2tVC94nnBZECQ==} + '@vitest/ui@4.1.2': + resolution: {integrity: sha512-/irhyeAcKS2u6Zokagf9tqZJ0t8S6kMZq4ZG9BHZv7I+fkRrYfQX4w7geYeC2r6obThz39PDxvXQzZX+qXqGeg==} peerDependencies: - vitest: 4.1.0 + vitest: 4.1.2 - '@vitest/utils@4.1.0': - resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + '@vitest/utils@4.1.2': + resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -3213,6 +3483,9 @@ packages: badwords-list@2.0.1-4: resolution: {integrity: sha512-FxfZUp7B9yCnesNtFQS9v6PvZdxTYa14Q60JR6vhjdQdWI4naTjJIyx22JzoER8ooeT8SAAKoHLjKfCV7XgYUQ==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -3244,8 +3517,14 @@ packages: blurhash@2.0.5: resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==} - brace-expansion@5.0.4: - resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + + brace-expansion@2.0.3: + resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -3343,6 +3622,9 @@ packages: compute-scroll-into-view@3.1.1: resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -3394,11 +3676,6 @@ packages: css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -3421,8 +3698,8 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} @@ -3538,6 +3815,10 @@ packages: emojibase@15.3.1: resolution: {integrity: sha512-GNsjHnG2J3Ktg684Fs/vZR/6XpOSkZPMAv85EHrr6br2RN2cJNwdS4am/3YSK3y+/gOv2kmoK3GGdahXdMxg2g==} + empathic@2.0.0: + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} + enhanced-resolve@5.20.0: resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} engines: {node: '>=10.13.0'} @@ -3603,6 +3884,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -3675,6 +3961,11 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-plugin-depend@1.5.0: + resolution: {integrity: sha512-i3UeLYmclf1Icp35+6W7CR4Bp2PIpDgBuf/mpmXK5UeLkZlvYJ21VuQKKHHAIBKRTPivPGX/gZl5JGno1o9Y0A==} + peerDependencies: + eslint: '>=8.40.0' + eslint-plugin-es-x@7.8.0: resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3845,7 +4136,7 @@ packages: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: - picomatch: ^3 || ^4 + picomatch: '>=4.0.4' peerDependenciesMeta: picomatch: optional: true @@ -4086,10 +4377,10 @@ packages: i18next-http-backend@2.7.3: resolution: {integrity: sha512-FgZxrXdRA5u44xfYsJlEBL4/KH3f2IluBpgV/7riW0YW2VEyM8FzVt2XHAOi6id0Ppj7vZvCZVpp5LrGXnc8Ig==} - i18next@25.8.17: - resolution: {integrity: sha512-vWtCttyn5bpOK4hWbRAe1ZXkA+Yzcn2OcACT+WJavtfGMcxzkfvXTLMeOU8MUhRmAySKjU4VVuKlo0sSGeBokA==} + i18next@25.10.10: + resolution: {integrity: sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==} peerDependencies: - typescript: ^5 + typescript: ^5 || ^6 peerDependenciesMeta: typescript: optional: true @@ -4310,8 +4601,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jotai@2.18.1: - resolution: {integrity: sha512-e0NOzK+yRFwHo7DOp0DS0Ycq74KMEAObDWFGmfEL28PD9nLqBTt3/Ug7jf9ca72x0gC9LQZG9zH+0ISICmy3iA==} + jotai@2.19.0: + resolution: {integrity: sha512-r2wwxEXP1F2JteDLZEOPoIpAHhV89paKsN5GWVYndPNMMP/uVZDcC+fNj0A8NjKgaPWzdyO8Vp8YcYKe0uCEqQ==} engines: {node: '>=12.20.0'} peerDependencies: '@babel/core': '>=7.0.0' @@ -4338,8 +4629,8 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdom@29.0.0: - resolution: {integrity: sha512-9FshNB6OepopZ08unmmGpsF7/qCjxGPbo3NbgfJAnPeHXnsODE9WWffXZtRFRFe0ntzaAOcSKNJFz8wiyvF1jQ==} + jsdom@29.0.1: + resolution: {integrity: sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==} engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -4364,9 +4655,6 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -4424,6 +4712,80 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4526,8 +4888,8 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - miniflare@4.20260310.0: - resolution: {integrity: sha512-uC5vNPenFpDSj5aUU3wGSABG6UUqMr+Xs1m4AkCrTHo37F4Z6xcQw5BXqViTfPDVT/zcYH1UgTVoXhr1l6ZMXw==} + miniflare@4.20260317.3: + resolution: {integrity: sha512-tK78D3X4q30/SXqVwMhWrUfH+ffRou9dJLC+jkhNy5zh1I7i7T4JH6xihOvYxdCSBavJ5fQXaaxDJz6orh09BA==} engines: {node: '>=18.0.0'} hasBin: true @@ -4535,6 +4897,13 @@ packages: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -4548,6 +4917,9 @@ packages: modern-ahocorasick@1.1.0: resolution: {integrity: sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ==} + module-replacements@2.11.0: + resolution: {integrity: sha512-j5sNQm3VCpQQ7nTqGeOZtoJtV3uKERgCBm9QRhmGRiXiqkf7iRFOkfxdJRZWLkqYY8PNf4cDQF/WfXUYLENrRA==} + motion-dom@12.35.2: resolution: {integrity: sha512-pWXFMTwvGDbx1Fe9YL5HZebv2NhvGBzRtiNUv58aoK7+XrsuaydQ0JGRKK2r+bTKlwgSWwWxHbP5249Qr/BNpg==} @@ -4712,16 +5084,8 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pkg-types@1.3.1: @@ -4813,14 +5177,14 @@ packages: peerDependencies: react: '>=16.4.1' - react-i18next@16.5.7: - resolution: {integrity: sha512-t/si6ng+hMPvgRGNgGvHAkMuVRBsIBx5mN+exm/yiBPSFL7VooQ37YYfISxSE0LjvQjG+MTe+0htKdOJY0S/vw==} + react-i18next@16.6.6: + resolution: {integrity: sha512-ZgL2HUoW34UKUkOV7uSQFE1CDnRPD+tCR3ywSuWH7u2iapnz86U8Bi3Vrs620qNDzCf1F47NxglCEkchCTDOHw==} peerDependencies: - i18next: '>= 25.6.2' + i18next: '>= 25.10.9' react: '>= 16.8.0' react-dom: '*' react-native: '*' - typescript: ^5 + typescript: ^5 || ^6 peerDependenciesMeta: react-dom: optional: true @@ -4844,10 +5208,6 @@ packages: react: '*' react-dom: '*' - react-refresh@0.18.0: - resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} - engines: {node: '>=0.10.0'} - react-router-dom@6.30.3: resolution: {integrity: sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==} engines: {node: '>=14.0.0'} @@ -4927,8 +5287,18 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + rolldown@1.0.0-rc.12: + resolution: {integrity: sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup@2.80.0: + resolution: {integrity: sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + + rollup@4.60.0: + resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5050,8 +5420,8 @@ packages: resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==} engines: {node: '>=20.0.0'} - smol-toml@1.6.0: - resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} snake-case@3.0.4: @@ -5303,8 +5673,12 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici@7.24.3: - resolution: {integrity: sha512-eJdUmK/Wrx2d+mnWWmwwLRyA7OQCkLap60sk3dOK4ViZR7DKwwptwuIvFBg2HaiP9ESaEdhtpSymQPvytpmkCA==} + undici@7.24.4: + resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} + engines: {node: '>=20.18.1'} + + undici@7.24.6: + resolution: {integrity: sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: @@ -5391,8 +5765,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite-plugin-compression2@2.5.0: - resolution: {integrity: sha512-bHxtBibPxxSn5eZSe0IAzvYucP/hg8Bz8ppjbH7lndU5kIHT+92qTkB4z9xWYfnyV0YHuir1SjOuyO0fzU4Vgg==} + vite-plugin-compression2@2.5.3: + resolution: {integrity: sha512-ItPgqQWkcnBbVw7is9OKwiZ8v6+ju9rYROl5Lp6QfQDEx/d55AwJQb/KLpsQqsU9HoigYBsZ8tK6I02UwJNvEw==} vite-plugin-pwa@1.2.0: resolution: {integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==} @@ -5406,11 +5780,11 @@ packages: '@vite-pwa/assets-generator': optional: true - vite-plugin-static-copy@3.2.0: - resolution: {integrity: sha512-g2k9z8B/1Bx7D4wnFjPLx9dyYGrqWMLTpwTtPHhcU+ElNZP2O4+4OsyaficiDClus0dzVhdGvoGFYMJxoXZ12Q==} - engines: {node: ^18.0.0 || >=20.0.0} + vite-plugin-static-copy@4.0.0: + resolution: {integrity: sha512-TTf6cVTV4M2pH2Wfr3zhevdRsIQezfm2ltDkSfkjqvvdryJHYQyNoPISvuytX3r9jFZV0yVeMYyGTsAvAH2XLw==} + engines: {node: ^22.0.0 || >=24.0.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 vite-plugin-svgr@4.5.0: resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==} @@ -5437,7 +5811,7 @@ packages: sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 - yaml: ^2.4.2 + yaml: '>=2.8.3' peerDependenciesMeta: '@types/node': optional: true @@ -5462,21 +5836,64 @@ packages: yaml: optional: true - vitest@4.1.0: - resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + vite@8.0.3: + resolution: {integrity: sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.0 + esbuild: ^0.27.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: '>=2.8.3' + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.2: + resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0 - '@vitest/browser-preview': 4.1.0 - '@vitest/browser-webdriverio': 4.1.0 - '@vitest/ui': 4.1.0 + '@vitest/browser-playwright': 4.1.2 + '@vitest/browser-preview': 4.1.2 + '@vitest/browser-webdriverio': 4.1.2 + '@vitest/ui': 4.1.2 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -5612,17 +6029,17 @@ packages: workbox-window@7.4.0: resolution: {integrity: sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==} - workerd@1.20260310.1: - resolution: {integrity: sha512-yawXhypXXHtArikJj15HOMknNGikpBbSg2ZDe6lddUbqZnJXuCVSkgc/0ArUeVMG1jbbGvpst+REFtKwILvRTQ==} + workerd@1.20260317.1: + resolution: {integrity: sha512-ZuEq1OdrJBS+NV+L5HMYPCzVn49a2O60slQiiLpG44jqtlOo+S167fWC76kEXteXLLLydeuRrluRel7WdOUa4g==} engines: {node: '>=16'} hasBin: true - wrangler@4.72.0: - resolution: {integrity: sha512-bKkb8150JGzJZJWiNB2nu/33smVfawmfYiecA6rW4XH7xS23/jqMbgpdelM34W/7a1IhR66qeQGVqTRXROtAZg==} - engines: {node: '>=20.0.0'} + wrangler@4.78.0: + resolution: {integrity: sha512-He/vUhk4ih0D0eFmtNnlbT6Od8j+BEokaSR+oYjbVsH0SWIrIch+eHqfLRSBjBQaOoh6HCNxcafcIkBm2u0Hag==} + engines: {node: '>=20.3.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260310.1 + '@cloudflare/workers-types': ^4.20260317.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -5649,11 +6066,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} - engines: {node: '>= 14.6'} - hasBin: true - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -5677,10 +6089,9 @@ snapshots: '@adobe/css-tools@4.4.4': {} - '@apideck/better-ajv-errors@0.3.6(ajv@8.18.0)': + '@apideck/better-ajv-errors@0.3.7(ajv@8.18.0)': dependencies: ajv: 8.18.0 - json-schema: 0.4.0 jsonpointer: 5.0.1 leven: 3.1.0 @@ -5716,7 +6127,7 @@ snapshots: '@atlaskit/pragmatic-drag-and-drop@1.7.9': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 bind-event-listener: 3.0.0 raf-schd: 4.0.3 @@ -5878,6 +6289,11 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + optional: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -6204,16 +6620,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -6399,38 +6805,38 @@ snapshots: '@cloudflare/kv-asset-handler@0.4.2': {} - '@cloudflare/unenv-preset@2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260310.1)': + '@cloudflare/unenv-preset@2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260317.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260310.1 + workerd: 1.20260317.1 - '@cloudflare/vite-plugin@1.27.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workerd@1.20260310.1)(wrangler@4.72.0)': + '@cloudflare/vite-plugin@1.30.2(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1))(workerd@1.20260317.1)(wrangler@4.78.0)': dependencies: - '@cloudflare/unenv-preset': 2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260310.1) - miniflare: 4.20260310.0 + '@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260317.1) + miniflare: 4.20260317.3 unenv: 2.0.0-rc.24 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - wrangler: 4.72.0 + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) + wrangler: 4.78.0 ws: 8.18.0 transitivePeerDependencies: - bufferutil - utf-8-validate - workerd - '@cloudflare/workerd-darwin-64@1.20260310.1': + '@cloudflare/workerd-darwin-64@1.20260317.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260310.1': + '@cloudflare/workerd-darwin-arm64@1.20260317.1': optional: true - '@cloudflare/workerd-linux-64@1.20260310.1': + '@cloudflare/workerd-linux-64@1.20260317.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260310.1': + '@cloudflare/workerd-linux-arm64@1.20260317.1': optional: true - '@cloudflare/workerd-windows-64@1.20260310.1': + '@cloudflare/workerd-windows-64@1.20260317.1': optional: true '@cspotcode/source-map-support@0.8.1': @@ -6461,6 +6867,12 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} + '@e18e/eslint-plugin@0.3.0(eslint@9.39.3(jiti@2.6.1))': + dependencies: + eslint-plugin-depend: 1.5.0(eslint@9.39.3(jiti@2.6.1)) + optionalDependencies: + eslint: 9.39.3(jiti@2.6.1) + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -6479,88 +6891,166 @@ snapshots: '@emotion/hash@0.9.2': {} - '@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.27.3)': + '@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.27.4)': dependencies: - esbuild: 0.27.3 + esbuild: 0.27.4 '@esbuild/aix-ppc64@0.27.3': optional: true + '@esbuild/aix-ppc64@0.27.4': + optional: true + '@esbuild/android-arm64@0.27.3': optional: true + '@esbuild/android-arm64@0.27.4': + optional: true + '@esbuild/android-arm@0.27.3': optional: true + '@esbuild/android-arm@0.27.4': + optional: true + '@esbuild/android-x64@0.27.3': optional: true + '@esbuild/android-x64@0.27.4': + optional: true + '@esbuild/darwin-arm64@0.27.3': optional: true + '@esbuild/darwin-arm64@0.27.4': + optional: true + '@esbuild/darwin-x64@0.27.3': optional: true + '@esbuild/darwin-x64@0.27.4': + optional: true + '@esbuild/freebsd-arm64@0.27.3': optional: true + '@esbuild/freebsd-arm64@0.27.4': + optional: true + '@esbuild/freebsd-x64@0.27.3': optional: true + '@esbuild/freebsd-x64@0.27.4': + optional: true + '@esbuild/linux-arm64@0.27.3': optional: true + '@esbuild/linux-arm64@0.27.4': + optional: true + '@esbuild/linux-arm@0.27.3': optional: true + '@esbuild/linux-arm@0.27.4': + optional: true + '@esbuild/linux-ia32@0.27.3': optional: true + '@esbuild/linux-ia32@0.27.4': + optional: true + '@esbuild/linux-loong64@0.27.3': optional: true + '@esbuild/linux-loong64@0.27.4': + optional: true + '@esbuild/linux-mips64el@0.27.3': optional: true + '@esbuild/linux-mips64el@0.27.4': + optional: true + '@esbuild/linux-ppc64@0.27.3': optional: true + '@esbuild/linux-ppc64@0.27.4': + optional: true + '@esbuild/linux-riscv64@0.27.3': optional: true + '@esbuild/linux-riscv64@0.27.4': + optional: true + '@esbuild/linux-s390x@0.27.3': optional: true + '@esbuild/linux-s390x@0.27.4': + optional: true + '@esbuild/linux-x64@0.27.3': optional: true + '@esbuild/linux-x64@0.27.4': + optional: true + '@esbuild/netbsd-arm64@0.27.3': optional: true + '@esbuild/netbsd-arm64@0.27.4': + optional: true + '@esbuild/netbsd-x64@0.27.3': optional: true + '@esbuild/netbsd-x64@0.27.4': + optional: true + '@esbuild/openbsd-arm64@0.27.3': optional: true + '@esbuild/openbsd-arm64@0.27.4': + optional: true + '@esbuild/openbsd-x64@0.27.3': optional: true + '@esbuild/openbsd-x64@0.27.4': + optional: true + '@esbuild/openharmony-arm64@0.27.3': optional: true + '@esbuild/openharmony-arm64@0.27.4': + optional: true + '@esbuild/sunos-x64@0.27.3': optional: true + '@esbuild/sunos-x64@0.27.4': + optional: true + '@esbuild/win32-arm64@0.27.3': optional: true + '@esbuild/win32-arm64@0.27.4': + optional: true + '@esbuild/win32-ia32@0.27.3': optional: true + '@esbuild/win32-ia32@0.27.4': + optional: true + '@esbuild/win32-x64@0.27.3': optional: true + '@esbuild/win32-x64@0.27.4': + optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3(jiti@2.6.1))': dependencies: eslint: 9.39.3(jiti@2.6.1) @@ -6578,7 +7068,7 @@ snapshots: dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 - minimatch: 10.2.4 + minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -6603,7 +7093,7 @@ snapshots: ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.1 - minimatch: 10.2.4 + minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color @@ -6886,6 +7376,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oxc-project/types@0.122.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true @@ -7932,182 +8424,239 @@ snapshots: '@remix-run/router@1.23.2': {} - '@rolldown/pluginutils@1.0.0-rc.3': {} + '@rolldown/binding-android-arm64@1.0.0-rc.12': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.12': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.12': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.12': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.12': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.12': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.12': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.12': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.12': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.12': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.12': {} + + '@rolldown/pluginutils@1.0.0-rc.7': {} - '@rollup/plugin-babel@5.3.1(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@4.59.0)': + '@rollup/plugin-babel@5.3.1(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@2.80.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.28.6 - '@rollup/pluginutils': 3.1.0(rollup@4.59.0) - rollup: 4.59.0 + '@rollup/pluginutils': 3.1.0(rollup@2.80.0) + rollup: 2.80.0 optionalDependencies: '@types/babel__core': 7.20.5 transitivePeerDependencies: - supports-color - '@rollup/plugin-inject@5.0.5(rollup@4.59.0)': + '@rollup/plugin-inject@5.0.5(rollup@4.60.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.60.0) estree-walker: 2.0.2 magic-string: 0.30.21 optionalDependencies: - rollup: 4.59.0 + rollup: 4.60.0 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.59.0)': + '@rollup/plugin-node-resolve@15.3.1(rollup@2.80.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@2.80.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.11 optionalDependencies: - rollup: 4.59.0 + rollup: 2.80.0 - '@rollup/plugin-replace@2.4.2(rollup@4.59.0)': + '@rollup/plugin-replace@2.4.2(rollup@2.80.0)': dependencies: - '@rollup/pluginutils': 3.1.0(rollup@4.59.0) + '@rollup/pluginutils': 3.1.0(rollup@2.80.0) magic-string: 0.25.9 - rollup: 4.59.0 + rollup: 2.80.0 - '@rollup/plugin-terser@0.4.4(rollup@4.59.0)': + '@rollup/plugin-terser@0.4.4(rollup@2.80.0)': dependencies: serialize-javascript: 7.0.5 smob: 1.6.1 terser: 5.46.1 optionalDependencies: - rollup: 4.59.0 + rollup: 2.80.0 - '@rollup/plugin-virtual@3.0.2(rollup@4.59.0)': + '@rollup/plugin-virtual@3.0.2(rollup@4.60.0)': optionalDependencies: - rollup: 4.59.0 + rollup: 4.60.0 - '@rollup/plugin-wasm@6.2.2(rollup@4.59.0)': + '@rollup/plugin-wasm@6.2.2(rollup@4.60.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.60.0) optionalDependencies: - rollup: 4.59.0 + rollup: 4.60.0 - '@rollup/pluginutils@3.1.0(rollup@4.59.0)': + '@rollup/pluginutils@3.1.0(rollup@2.80.0)': dependencies: '@types/estree': 0.0.39 estree-walker: 1.0.1 - picomatch: 2.3.2 - rollup: 4.59.0 + picomatch: 4.0.4 + rollup: 2.80.0 + + '@rollup/pluginutils@5.3.0(rollup@2.80.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 2.80.0 - '@rollup/pluginutils@5.3.0(rollup@4.59.0)': + '@rollup/pluginutils@5.3.0(rollup@4.60.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.3 + picomatch: 4.0.4 optionalDependencies: - rollup: 4.59.0 + rollup: 4.60.0 - '@rollup/rollup-android-arm-eabi@4.59.0': + '@rollup/rollup-android-arm-eabi@4.60.0': optional: true - '@rollup/rollup-android-arm64@4.59.0': + '@rollup/rollup-android-arm64@4.60.0': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + '@rollup/rollup-darwin-arm64@4.60.0': optional: true - '@rollup/rollup-darwin-x64@4.59.0': + '@rollup/rollup-darwin-x64@4.60.0': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + '@rollup/rollup-freebsd-arm64@4.60.0': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + '@rollup/rollup-freebsd-x64@4.60.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + '@rollup/rollup-linux-arm-musleabihf@4.60.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + '@rollup/rollup-linux-arm64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + '@rollup/rollup-linux-arm64-musl@4.60.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@rollup/rollup-linux-loong64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@rollup/rollup-linux-loong64-musl@4.60.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@rollup/rollup-linux-ppc64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@rollup/rollup-linux-ppc64-musl@4.60.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@rollup/rollup-linux-riscv64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@rollup/rollup-linux-riscv64-musl@4.60.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@rollup/rollup-linux-s390x-gnu@4.60.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@rollup/rollup-linux-x64-gnu@4.60.0': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@rollup/rollup-linux-x64-musl@4.60.0': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@rollup/rollup-openbsd-x64@4.60.0': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@rollup/rollup-openharmony-arm64@4.60.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@rollup/rollup-win32-arm64-msvc@4.60.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@rollup/rollup-win32-ia32-msvc@4.60.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@rollup/rollup-win32-x64-gnu@4.60.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@rollup/rollup-win32-x64-msvc@4.60.0': optional: true '@rtsao/scc@1.1.0': {} '@sableclient/sable-call-embedded@1.1.4': {} - '@sentry-internal/browser-utils@10.43.0': + '@sentry-internal/browser-utils@10.46.0': dependencies: - '@sentry/core': 10.43.0 + '@sentry/core': 10.46.0 - '@sentry-internal/feedback@10.43.0': + '@sentry-internal/feedback@10.46.0': dependencies: - '@sentry/core': 10.43.0 + '@sentry/core': 10.46.0 - '@sentry-internal/replay-canvas@10.43.0': + '@sentry-internal/replay-canvas@10.46.0': dependencies: - '@sentry-internal/replay': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry-internal/replay': 10.46.0 + '@sentry/core': 10.46.0 - '@sentry-internal/replay@10.43.0': + '@sentry-internal/replay@10.46.0': dependencies: - '@sentry-internal/browser-utils': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry-internal/browser-utils': 10.46.0 + '@sentry/core': 10.46.0 '@sentry/babel-plugin-component-annotate@5.1.1': {} - '@sentry/browser@10.43.0': + '@sentry/browser@10.46.0': dependencies: - '@sentry-internal/browser-utils': 10.43.0 - '@sentry-internal/feedback': 10.43.0 - '@sentry-internal/replay': 10.43.0 - '@sentry-internal/replay-canvas': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry-internal/browser-utils': 10.46.0 + '@sentry-internal/feedback': 10.46.0 + '@sentry-internal/replay': 10.46.0 + '@sentry-internal/replay-canvas': 10.46.0 + '@sentry/core': 10.46.0 '@sentry/bundler-plugin-core@5.1.1': dependencies: @@ -8166,27 +8715,27 @@ snapshots: - encoding - supports-color - '@sentry/core@10.43.0': {} + '@sentry/core@10.46.0': {} - '@sentry/react@10.43.0(react@18.3.1)': + '@sentry/react@10.46.0(react@18.3.1)': dependencies: - '@sentry/browser': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry/browser': 10.46.0 + '@sentry/core': 10.46.0 react: 18.3.1 - '@sentry/rollup-plugin@5.1.1(rollup@4.59.0)': + '@sentry/rollup-plugin@5.1.1(rollup@4.60.0)': dependencies: '@sentry/bundler-plugin-core': 5.1.1 magic-string: 0.30.21 - rollup: 4.59.0 + rollup: 4.60.0 transitivePeerDependencies: - encoding - supports-color - '@sentry/vite-plugin@5.1.1(rollup@4.59.0)': + '@sentry/vite-plugin@5.1.1(rollup@4.60.0)': dependencies: '@sentry/bundler-plugin-core': 5.1.1 - '@sentry/rollup-plugin': 5.1.1(rollup@4.59.0) + '@sentry/rollup-plugin': 5.1.1(rollup@4.60.0) transitivePeerDependencies: - encoding - rollup @@ -8206,7 +8755,7 @@ snapshots: eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 - picomatch: 4.0.3 + picomatch: 4.0.4 '@surma/rollup-plugin-off-main-thread@2.2.3': dependencies: @@ -8344,28 +8893,28 @@ snapshots: '@swc/wasm@1.15.18': {} - '@tanstack/query-core@5.90.20': {} + '@tanstack/query-core@5.95.2': {} - '@tanstack/query-devtools@5.93.0': {} + '@tanstack/query-devtools@5.95.2': {} - '@tanstack/react-query-devtools@5.91.3(@tanstack/react-query@5.90.21(react@18.3.1))(react@18.3.1)': + '@tanstack/react-query-devtools@5.95.2(@tanstack/react-query@5.95.2(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/query-devtools': 5.93.0 - '@tanstack/react-query': 5.90.21(react@18.3.1) + '@tanstack/query-devtools': 5.95.2 + '@tanstack/react-query': 5.95.2(react@18.3.1) react: 18.3.1 - '@tanstack/react-query@5.90.21(react@18.3.1)': + '@tanstack/react-query@5.95.2(react@18.3.1)': dependencies: - '@tanstack/query-core': 5.90.20 + '@tanstack/query-core': 5.95.2 react: 18.3.1 - '@tanstack/react-virtual@3.13.21(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-virtual@3.13.23(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/virtual-core': 3.13.21 + '@tanstack/virtual-core': 3.13.23 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@tanstack/virtual-core@3.13.21': {} + '@tanstack/virtual-core@3.13.23': {} '@testing-library/dom@10.4.1': dependencies: @@ -8410,24 +8959,28 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 + optional: true '@types/babel__generator@7.27.0': dependencies: '@babel/types': 7.29.0 + optional: true '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 + optional: true '@types/babel__traverse@7.28.0': dependencies: '@babel/types': 7.29.0 + optional: true '@types/chai@5.2.3': dependencies: @@ -8640,15 +9193,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@vanilla-extract/compiler@0.3.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)': + '@vanilla-extract/compiler@0.7.0(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)': dependencies: - '@vanilla-extract/css': 1.18.0 - '@vanilla-extract/integration': 8.0.7 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + '@vanilla-extract/css': 1.20.1 + '@vanilla-extract/integration': 8.0.9 + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) + vite-node: 3.2.4(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1) transitivePeerDependencies: - '@types/node' + - '@vitejs/devtools' - babel-plugin-macros + - esbuild - jiti - less - lightningcss @@ -8661,12 +9216,11 @@ snapshots: - tsx - yaml - '@vanilla-extract/css@1.18.0': + '@vanilla-extract/css@1.20.1': dependencies: '@emotion/hash': 0.9.2 '@vanilla-extract/private': 1.0.9 css-what: 6.2.2 - cssesc: 3.0.0 csstype: 3.2.3 dedent: 1.7.2 deep-object-diff: 1.1.9 @@ -8678,14 +9232,14 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - '@vanilla-extract/integration@8.0.7': + '@vanilla-extract/integration@8.0.9': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) '@vanilla-extract/babel-plugin-debug-ids': 1.2.2 - '@vanilla-extract/css': 1.18.0 + '@vanilla-extract/css': 1.20.1 dedent: 1.7.2 - esbuild: 0.27.3 + esbuild: 0.27.4 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0 @@ -8696,18 +9250,20 @@ snapshots: '@vanilla-extract/private@1.0.9': {} - '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.18.0)': + '@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.20.1)': dependencies: - '@vanilla-extract/css': 1.18.0 + '@vanilla-extract/css': 1.20.1 - '@vanilla-extract/vite-plugin@5.1.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(yaml@2.8.2)': + '@vanilla-extract/vite-plugin@5.2.2(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1))': dependencies: - '@vanilla-extract/compiler': 0.3.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - '@vanilla-extract/integration': 8.0.7 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + '@vanilla-extract/compiler': 0.7.0(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1) + '@vanilla-extract/integration': 8.0.9 + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) transitivePeerDependencies: - '@types/node' + - '@vitejs/devtools' - babel-plugin-macros + - esbuild - jiti - less - lightningcss @@ -8720,22 +9276,15 @@ snapshots: - tsx - yaml - '@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))': + '@vitejs/plugin-react@6.0.1(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@rolldown/pluginutils': 1.0.0-rc.3 - '@types/babel__core': 7.20.5 - react-refresh: 0.18.0 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color + '@rolldown/pluginutils': 1.0.0-rc.7 + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) - '@vitest/coverage-v8@4.1.0(vitest@4.1.0)': + '@vitest/coverage-v8@4.1.2(vitest@4.1.2)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.0 + '@vitest/utils': 4.1.2 ast-v8-to-istanbul: 1.0.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 @@ -8744,57 +9293,57 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + vitest: 4.1.2(@types/node@24.10.13)(@vitest/ui@4.1.2)(jsdom@29.0.1)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) - '@vitest/expect@4.1.0': + '@vitest/expect@4.1.2': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))': + '@vitest/mocker@4.1.2(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1))': dependencies: - '@vitest/spy': 4.1.0 + '@vitest/spy': 4.1.2 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) - '@vitest/pretty-format@4.1.0': + '@vitest/pretty-format@4.1.2': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.0': + '@vitest/runner@4.1.2': dependencies: - '@vitest/utils': 4.1.0 + '@vitest/utils': 4.1.2 pathe: 2.0.3 - '@vitest/snapshot@4.1.0': + '@vitest/snapshot@4.1.2': dependencies: - '@vitest/pretty-format': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/pretty-format': 4.1.2 + '@vitest/utils': 4.1.2 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.0': {} + '@vitest/spy@4.1.2': {} - '@vitest/ui@4.1.0(vitest@4.1.0)': + '@vitest/ui@4.1.2(vitest@4.1.2)': dependencies: - '@vitest/utils': 4.1.0 + '@vitest/utils': 4.1.2 fflate: 0.8.2 flatted: 3.4.2 pathe: 2.0.3 sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vitest: 4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) + vitest: 4.1.2(@types/node@24.10.13)(@vitest/ui@4.1.2)(jsdom@29.0.1)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) - '@vitest/utils@4.1.0': + '@vitest/utils@4.1.2': dependencies: - '@vitest/pretty-format': 4.1.0 + '@vitest/pretty-format': 4.1.2 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -8837,7 +9386,7 @@ snapshots: anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.1 + picomatch: 4.0.4 argparse@2.0.1: {} @@ -8966,6 +9515,8 @@ snapshots: badwords-list@2.0.1-4: {} + balanced-match@1.0.2: {} + balanced-match@4.0.4: {} base-x@5.0.1: {} @@ -8986,7 +9537,16 @@ snapshots: blurhash@2.0.5: {} - brace-expansion@5.0.4: + brace-expansion@1.1.13: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.3: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 @@ -9081,6 +9641,8 @@ snapshots: compute-scroll-into-view@3.1.1: {} + concat-map@0.0.1: {} + confbox@0.1.8: {} confusing-browser-globals@1.0.11: {} @@ -9127,8 +9689,6 @@ snapshots: css.escape@1.5.1: {} - cssesc@3.0.0: {} - csstype@3.2.3: {} damerau-levenshtein@1.0.8: {} @@ -9158,7 +9718,7 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - dayjs@1.11.19: {} + dayjs@1.11.20: {} debug@3.2.7: dependencies: @@ -9253,6 +9813,8 @@ snapshots: emojibase@15.3.1: {} + empathic@2.0.0: {} + enhanced-resolve@5.20.0: dependencies: graceful-fs: 4.2.11 @@ -9404,6 +9966,35 @@ snapshots: '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 + esbuild@0.27.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -9482,6 +10073,13 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-plugin-depend@1.5.0(eslint@9.39.3(jiti@2.6.1)): + dependencies: + empathic: 2.0.0 + eslint: 9.39.3(jiti@2.6.1) + module-replacements: 2.11.0 + semver: 7.7.4 + eslint-plugin-es-x@7.8.0(eslint@9.39.3(jiti@2.6.1)): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1)) @@ -9522,7 +10120,7 @@ snapshots: hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 - minimatch: 10.2.4 + minimatch: 3.1.5 object.fromentries: 2.0.8 object.groupby: 1.0.3 object.values: 1.2.1 @@ -9550,7 +10148,7 @@ snapshots: hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 - minimatch: 10.2.4 + minimatch: 3.1.5 object.fromentries: 2.0.8 safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 @@ -9602,7 +10200,7 @@ snapshots: estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 - minimatch: 10.2.4 + minimatch: 3.1.5 object.entries: 1.1.9 object.fromentries: 2.0.8 object.values: 1.2.1 @@ -9656,7 +10254,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 10.2.4 + minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -9735,9 +10333,9 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 fflate@0.8.2: {} @@ -9749,7 +10347,7 @@ snapshots: filelist@1.0.6: dependencies: - minimatch: 10.2.4 + minimatch: 5.1.9 fill-range@7.1.1: dependencies: @@ -9779,10 +10377,10 @@ snapshots: dependencies: tabbable: 6.4.0 - folds@2.6.2(@vanilla-extract/css@1.18.0)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.18.0))(classnames@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + folds@2.6.2(@vanilla-extract/css@1.20.1)(@vanilla-extract/recipes@0.5.7(@vanilla-extract/css@1.20.1))(classnames@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@vanilla-extract/css': 1.18.0 - '@vanilla-extract/recipes': 0.5.7(@vanilla-extract/css@1.18.0) + '@vanilla-extract/css': 1.20.1 + '@vanilla-extract/recipes': 0.5.7(@vanilla-extract/css@1.20.1) classnames: 2.5.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -9999,9 +10597,9 @@ snapshots: transitivePeerDependencies: - encoding - i18next@25.8.17(typescript@5.9.3): + i18next@25.10.10(typescript@5.9.3): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 optionalDependencies: typescript: 5.9.3 @@ -10213,7 +10811,7 @@ snapshots: jiti@2.6.1: {} - jotai@2.18.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1): + jotai@2.19.0(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@18.3.28)(react@18.3.1): optionalDependencies: '@babel/core': 7.29.0 '@babel/template': 7.28.6 @@ -10228,7 +10826,7 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@29.0.0: + jsdom@29.0.1: dependencies: '@asamuzakjp/css-color': 5.0.1 '@asamuzakjp/dom-selector': 7.0.3 @@ -10245,7 +10843,7 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.1 - undici: 7.24.3 + undici: 7.24.6 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -10264,8 +10862,6 @@ snapshots: json-schema-traverse@1.0.0: {} - json-schema@0.4.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: @@ -10308,8 +10904,8 @@ snapshots: minimist: 1.2.8 oxc-resolver: 11.19.1 picocolors: 1.1.1 - picomatch: 4.0.3 - smol-toml: 1.6.0 + picomatch: 4.0.4 + smol-toml: 1.6.1 strip-json-comments: 5.0.3 typescript: 5.9.3 zod: 4.3.6 @@ -10327,6 +10923,55 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lines-and-columns@1.2.4: {} linkify-react@4.3.2(linkifyjs@4.3.2)(react@18.3.1): @@ -10418,23 +11063,23 @@ snapshots: media-query-parser@2.0.2: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 merge2@1.4.1: {} micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 4.0.4 min-indent@1.0.1: {} - miniflare@4.20260310.0: + miniflare@4.20260317.3: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 - undici: 7.24.3 - workerd: 1.20260310.1 + undici: 7.24.4 + workerd: 1.20260317.1 ws: 8.18.0 youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -10443,7 +11088,15 @@ snapshots: minimatch@10.2.4: dependencies: - brace-expansion: 5.0.4 + brace-expansion: 5.0.5 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.13 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.0.3 minimist@1.2.8: {} @@ -10458,6 +11111,8 @@ snapshots: modern-ahocorasick@1.1.0: {} + module-replacements@2.11.0: {} + motion-dom@12.35.2: dependencies: motion-utils: 12.29.2 @@ -10638,11 +11293,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} - - picomatch@2.3.2: {} - - picomatch@4.0.3: {} + picomatch@4.0.4: {} pkg-types@1.3.1: dependencies: @@ -10767,11 +11418,11 @@ snapshots: react: 18.3.1 react-async-script: 1.2.0(react@18.3.1) - react-i18next@16.5.7(i18next@25.8.17(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3): + react-i18next@16.6.6(i18next@25.10.10(typescript@5.9.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.9.3): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 html-parse-stringify: 3.0.1 - i18next: 25.8.17(typescript@5.9.3) + i18next: 25.10.10(typescript@5.9.3) react: 18.3.1 use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: @@ -10789,8 +11440,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-refresh@0.18.0: {} - react-router-dom@6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@remix-run/router': 1.23.2 @@ -10809,7 +11458,7 @@ snapshots: readdirp@3.6.0: dependencies: - picomatch: 2.3.1 + picomatch: 4.0.4 redent@3.0.0: dependencies: @@ -10882,35 +11531,60 @@ snapshots: reusify@1.1.0: {} - rollup@4.59.0: + rolldown@1.0.0-rc.12: + dependencies: + '@oxc-project/types': 0.122.0 + '@rolldown/pluginutils': 1.0.0-rc.12 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.12 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.12 + '@rolldown/binding-darwin-x64': 1.0.0-rc.12 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.12 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.12 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.12 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.12 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.12 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.12 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.12 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.12 + + rollup@2.80.0: + optionalDependencies: + fsevents: 2.3.3 + + rollup@4.60.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 + '@rollup/rollup-android-arm-eabi': 4.60.0 + '@rollup/rollup-android-arm64': 4.60.0 + '@rollup/rollup-darwin-arm64': 4.60.0 + '@rollup/rollup-darwin-x64': 4.60.0 + '@rollup/rollup-freebsd-arm64': 4.60.0 + '@rollup/rollup-freebsd-x64': 4.60.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 + '@rollup/rollup-linux-arm-musleabihf': 4.60.0 + '@rollup/rollup-linux-arm64-gnu': 4.60.0 + '@rollup/rollup-linux-arm64-musl': 4.60.0 + '@rollup/rollup-linux-loong64-gnu': 4.60.0 + '@rollup/rollup-linux-loong64-musl': 4.60.0 + '@rollup/rollup-linux-ppc64-gnu': 4.60.0 + '@rollup/rollup-linux-ppc64-musl': 4.60.0 + '@rollup/rollup-linux-riscv64-gnu': 4.60.0 + '@rollup/rollup-linux-riscv64-musl': 4.60.0 + '@rollup/rollup-linux-s390x-gnu': 4.60.0 + '@rollup/rollup-linux-x64-gnu': 4.60.0 + '@rollup/rollup-linux-x64-musl': 4.60.0 + '@rollup/rollup-openbsd-x64': 4.60.0 + '@rollup/rollup-openharmony-arm64': 4.60.0 + '@rollup/rollup-win32-arm64-msvc': 4.60.0 + '@rollup/rollup-win32-ia32-msvc': 4.60.0 + '@rollup/rollup-win32-x64-gnu': 4.60.0 + '@rollup/rollup-win32-x64-msvc': 4.60.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -11086,7 +11760,7 @@ snapshots: smob@1.6.1: {} - smol-toml@1.6.0: {} + smol-toml@1.6.1: {} snake-case@3.0.4: dependencies: @@ -11241,8 +11915,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinyrainbow@3.1.0: {} @@ -11278,7 +11952,7 @@ snapshots: ts-declaration-location@1.0.7(typescript@5.9.3): dependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 typescript: 5.9.3 tsconfig-paths@3.15.0: @@ -11355,7 +12029,9 @@ snapshots: undici-types@7.16.0: {} - undici@7.24.3: {} + undici@7.24.4: {} + + undici@7.24.6: {} unenv@2.0.0-rc.24: dependencies: @@ -11429,13 +12105,13 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - vite-node@3.2.4(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2): + vite-node@3.2.4(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1) transitivePeerDependencies: - '@types/node' - jiti @@ -11450,95 +12126,109 @@ snapshots: - tsx - yaml - vite-plugin-compression2@2.5.0(rollup@4.59.0): + vite-plugin-compression2@2.5.3(rollup@4.60.0): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.60.0) tar-mini: 0.2.0 transitivePeerDependencies: - rollup - vite-plugin-pwa@1.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): + vite-plugin-pwa@1.2.0(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): dependencies: debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) workbox-build: 7.4.0(@types/babel__core@7.20.5) workbox-window: 7.4.0 transitivePeerDependencies: - supports-color - vite-plugin-static-copy@3.2.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-static-copy@4.0.0(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)): dependencies: chokidar: 3.6.0 p-map: 7.0.4 picocolors: 1.1.1 tinyglobby: 0.2.15 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) - vite-plugin-svgr@4.5.0(rollup@4.59.0)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-svgr@4.5.0(rollup@4.60.0)(typescript@5.9.3)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@rollup/pluginutils': 5.3.0(rollup@4.60.0) '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) transitivePeerDependencies: - rollup - supports-color - typescript - vite-plugin-top-level-await@1.6.0(@swc/helpers@0.5.19)(rollup@4.59.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): + vite-plugin-top-level-await@1.6.0(@swc/helpers@0.5.19)(rollup@4.60.0)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)): dependencies: - '@rollup/plugin-virtual': 3.0.2(rollup@4.59.0) + '@rollup/plugin-virtual': 3.0.2(rollup@4.60.0) '@swc/core': 1.15.18(@swc/helpers@0.5.19) '@swc/wasm': 1.15.18 uuid: 10.0.0 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) transitivePeerDependencies: - '@swc/helpers' - rollup - vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2): + vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1): dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + esbuild: 0.27.4 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.8 + rollup: 4.60.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.13 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 + terser: 5.46.1 + + vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 postcss: 8.5.8 - rollup: 4.59.0 + rolldown: 1.0.0-rc.12 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.10.13 + esbuild: 0.27.4 fsevents: 2.3.3 jiti: 2.6.1 terser: 5.46.1 - yaml: 2.8.2 - vitest@4.1.0(@types/node@24.10.13)(@vitest/ui@4.1.0)(jsdom@29.0.0)(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)): + vitest@4.1.2(@types/node@24.10.13)(@vitest/ui@4.1.2)(jsdom@29.0.1)(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)): dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(vite@8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(terser@5.46.1)(yaml@2.8.2) + vite: 8.0.3(@types/node@24.10.13)(esbuild@0.27.4)(jiti@2.6.1)(terser@5.46.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.13 - '@vitest/ui': 4.1.0(vitest@4.1.0) - jsdom: 29.0.0 + '@vitest/ui': 4.1.2(vitest@4.1.2) + jsdom: 29.0.1 transitivePeerDependencies: - msw @@ -11640,14 +12330,14 @@ snapshots: workbox-build@7.4.0(@types/babel__core@7.20.5): dependencies: - '@apideck/better-ajv-errors': 0.3.6(ajv@8.18.0) + '@apideck/better-ajv-errors': 0.3.7(ajv@8.18.0) '@babel/core': 7.29.0 '@babel/preset-env': 7.29.2(@babel/core@7.29.0) '@babel/runtime': 7.29.2 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@4.59.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.59.0) - '@rollup/plugin-replace': 2.4.2(rollup@4.59.0) - '@rollup/plugin-terser': 0.4.4(rollup@4.59.0) + '@rollup/plugin-babel': 5.3.1(@babel/core@7.29.0)(@types/babel__core@7.20.5)(rollup@2.80.0) + '@rollup/plugin-node-resolve': 15.3.1(rollup@2.80.0) + '@rollup/plugin-replace': 2.4.2(rollup@2.80.0) + '@rollup/plugin-terser': 0.4.4(rollup@2.80.0) '@surma/rollup-plugin-off-main-thread': 2.2.3 ajv: 8.18.0 common-tags: 1.8.2 @@ -11656,7 +12346,7 @@ snapshots: glob: 11.1.0 lodash: 4.17.23 pretty-bytes: 5.6.0 - rollup: 4.59.0 + rollup: 2.80.0 source-map: 0.8.0-beta.0 stringify-object: 3.3.0 strip-comments: 2.0.1 @@ -11742,24 +12432,24 @@ snapshots: '@types/trusted-types': 2.0.7 workbox-core: 7.4.0 - workerd@1.20260310.1: + workerd@1.20260317.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260310.1 - '@cloudflare/workerd-darwin-arm64': 1.20260310.1 - '@cloudflare/workerd-linux-64': 1.20260310.1 - '@cloudflare/workerd-linux-arm64': 1.20260310.1 - '@cloudflare/workerd-windows-64': 1.20260310.1 + '@cloudflare/workerd-darwin-64': 1.20260317.1 + '@cloudflare/workerd-darwin-arm64': 1.20260317.1 + '@cloudflare/workerd-linux-64': 1.20260317.1 + '@cloudflare/workerd-linux-arm64': 1.20260317.1 + '@cloudflare/workerd-windows-64': 1.20260317.1 - wrangler@4.72.0: + wrangler@4.78.0: dependencies: '@cloudflare/kv-asset-handler': 0.4.2 - '@cloudflare/unenv-preset': 2.15.0(unenv@2.0.0-rc.24)(workerd@1.20260310.1) + '@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260317.1) blake3-wasm: 2.1.5 esbuild: 0.27.3 - miniflare: 4.20260310.0 + miniflare: 4.20260317.3 path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260310.1 + workerd: 1.20260317.1 optionalDependencies: fsevents: 2.3.3 transitivePeerDependencies: @@ -11774,9 +12464,6 @@ snapshots: yallist@3.1.1: {} - yaml@2.8.2: - optional: true - yocto-queue@0.1.0: {} youch-core@0.3.3: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fbcf9139d..ebb82f27f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,19 +12,16 @@ minimumReleaseAgeExclude: - '@sableclient/sable-call-embedded' overrides: - brace-expansion: '>=1.1.12' - esbuild: '>=0.25.0' - flatted: '>=3.4.2' - lodash: '>=4.17.23' - minimatch: '>=10.2.3' - rollup: '>=4.59.0' - serialize-javascript: '>=7.0.3' - undici: '>=7.24.0' + serialize-javascript: '>=7.0.5' + picomatch: '>=4.0.4' + smol-toml: '>=1.6.1' + yaml: '>=2.8.3' peerDependencyRules: allowedVersions: - 'folds>@vanilla-extract/css': '1.18.0' - 'folds>@vanilla-extract/recipes': '0.5.7' - 'folds>classnames': '2.5.1' - 'folds>react': '18.3.1' - 'folds>react-dom': '18.3.1' + 'folds>@vanilla-extract/css': '^1.20.1' + 'folds>@vanilla-extract/recipes': '^0.5.7' + 'folds>classnames': '^2.5.1' + 'folds>react': '^18.3.1' + 'folds>react-dom': '^18.3.1' + 'vite-plugin-pwa>vite': '^8.0.3' diff --git a/scripts/import-rewrites.test.js b/scripts/import-rewrites.test.js new file mode 100644 index 000000000..65348b81f --- /dev/null +++ b/scripts/import-rewrites.test.js @@ -0,0 +1,74 @@ +import fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; + +import { afterEach, describe, expect, it } from 'vitest'; + +import { + getMatrixModuleSpecifierFromDeclarationFile, + loadAliasMapFromTsconfig, + rewriteSourceImports, +} from './utils/import-rewrites.js'; + +/** @type {string[]} */ +const tempDirs = []; + +async function makeTempProject() { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'sable-import-rewrites-')); + tempDirs.push(dir); + return dir; +} + +afterEach(async () => { + await Promise.all(tempDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true }))); +}); + +describe('loadAliasMapFromTsconfig + rewriteSourceImports', () => { + it('rewrites relative imports using tsconfig path aliases', async () => { + const projectRoot = await makeTempProject(); + await fs.writeFile( + path.join(projectRoot, 'tsconfig.web.json'), + JSON.stringify( + { + compilerOptions: { + baseUrl: '.', + paths: { + '$components/*': ['src/app/components/*'], + '$types/*': ['src/types/*'], + }, + }, + }, + null, + 2 + ) + ); + + const aliases = await loadAliasMapFromTsconfig( + path.join(projectRoot, 'tsconfig.web.json'), + projectRoot + ); + + const filePath = path.join(projectRoot, 'src/app/pages/Home.tsx'); + const sourceCode = [ + "import { Header } from '../components/Header';", + "import { MatrixClient } from 'matrix-js-sdk/lib/client';", + '', + ].join('\n'); + + const result = rewriteSourceImports(filePath, sourceCode, aliases, projectRoot); + + expect(result.changed).toBe(true); + expect(result.updatedCode).toContain("from '$components/Header'"); + expect(result.updatedCode).toContain("from '$types/matrix-sdk'"); + }); +}); + +describe('getMatrixModuleSpecifierFromDeclarationFile', () => { + it('normalizes matrix-js-sdk declaration paths to bare module specifiers', () => { + const declarationFile = String.raw`C:\repo\node_modules\matrix-js-sdk\lib\models\room.d.ts`; + + expect(getMatrixModuleSpecifierFromDeclarationFile(declarationFile)).toBe( + 'matrix-js-sdk/lib/models/room' + ); + }); +}); diff --git a/scripts/install-knope.js b/scripts/install-knope.js index cbda39e57..27e7bb609 100644 --- a/scripts/install-knope.js +++ b/scripts/install-knope.js @@ -1,5 +1,6 @@ #!/usr/bin/env node import { spawnSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; import process from 'node:process'; import { chmodSync, existsSync, mkdirSync, realpathSync, writeFileSync } from 'node:fs'; import { join, dirname, resolve, sep } from 'node:path'; @@ -10,20 +11,52 @@ import { PrefixedLogger, createTextHelpers } from './utils/console-style.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); -const VERSION = '0.22.3'; +const VERSION = '0.22.4'; +/** + * @typedef {'linux-x64' | 'linux-arm64' | 'darwin-x64' | 'darwin-arm64' | 'win32-x64'} SupportedTargetKey + */ + +/** + * Pinned to the published release asset digests for knope. + * Source: GitHub release asset metadata at publish time. + * @type {Record} + */ const TARGETS = { - 'linux-x64': 'x86_64-unknown-linux-musl', - 'linux-arm64': 'aarch64-unknown-linux-musl', - 'darwin-x64': 'x86_64-apple-darwin', - 'darwin-arm64': 'aarch64-apple-darwin', - 'win32-x64': 'x86_64-pc-windows-msvc', + 'linux-x64': { + target: 'x86_64-unknown-linux-musl', + digest: 'sha256:45a74925ae9f4c9c2c33b51992ae50241ec4fa836bf8d2977c0b8e8172dd69cf', + }, + 'linux-arm64': { + target: 'aarch64-unknown-linux-musl', + digest: 'sha256:95e882afdb4154c5baaba91f7bbd1fb1d41cec6898363a2b30e7abad4057b83b', + }, + 'darwin-x64': { + target: 'x86_64-apple-darwin', + digest: 'sha256:010dc197bf159bbd9d60e897252248ba2b0e204beae7250ce54a9deae1ec4876', + }, + 'darwin-arm64': { + target: 'aarch64-apple-darwin', + digest: 'sha256:02131f284315c8ece8a4ef69a0aff5f658309d4df73b95cfdfbe0fbd9e9ce259', + }, + 'win32-x64': { + target: 'x86_64-pc-windows-msvc', + digest: 'sha256:09f735b2da42cd594189042d1379c0a3a350a8c0ccb741015a84c6ff334543b1', + }, }; +/** + * @param {string | null | undefined} output + * @returns {string | null} + */ function parseKnopeVersion(output) { const version = output?.trim().replace(/^knope\s+/, ''); return version || null; } +/** + * @param {string} command + * @returns {string | null} + */ function getKnopeVersion(command) { const result = spawnSync(command, ['--version'], { encoding: 'utf8' }); if (result.status !== 0) { @@ -32,17 +65,30 @@ function getKnopeVersion(command) { return parseKnopeVersion(result.stdout); } +/** + * @param {Buffer} buffer + * @returns {string} + */ function readNullTerminatedString(buffer) { const nulIndex = buffer.indexOf(0); const end = nulIndex === -1 ? buffer.length : nulIndex; return buffer.toString('utf8', 0, end); } +/** + * @param {string} entryName + * @returns {string} + */ function getTarBasename(entryName) { const segments = entryName.split('/').filter(Boolean); return segments.at(-1) ?? ''; } +/** + * @param {Buffer} tarBuffer + * @param {string} expectedBasename + * @returns {Buffer} + */ function extractRegularFileFromTar(tarBuffer, expectedBasename) { let offset = 0; const regularEntries = []; @@ -87,6 +133,9 @@ function extractRegularFileFromTar(tarBuffer, expectedBasename) { ); } +/** + * @returns {string | null} + */ function getSystemKnopePath() { const which = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['knope'], { encoding: 'utf8', @@ -102,7 +151,16 @@ function getSystemKnopePath() { ); } +/** + * @param {string} candidatePath + * @param {string} rootPath + * @returns {boolean} + */ function isPathWithin(candidatePath, rootPath) { + /** + * @param {string} value + * @returns {string} + */ const toComparablePath = (value) => { const resolved = resolve(value); return process.platform === 'win32' ? resolved.toLowerCase() : resolved; @@ -112,6 +170,14 @@ function isPathWithin(candidatePath, rootPath) { return candidate === root || candidate.startsWith(`${root}${sep}`); } +/** + * @param {Buffer} buffer + * @returns {string} + */ +function getSha256Digest(buffer) { + return `sha256:${createHash('sha256').update(buffer).digest('hex')}`; +} + const logger = new PrefixedLogger('[postinstall:knope]'); const { dim, red, green } = createTextHelpers({ useColor: logger.useColor }); @@ -120,14 +186,18 @@ if (process.env.GITHUB_ACTIONS && process.env.CI) { process.exit(0); } -const target = TARGETS[`${process.platform}-${process.arch}`]; -if (!target) { +const targetKey = `${process.platform}-${process.arch}`; +const targetConfig = Object.hasOwn(TARGETS, targetKey) + ? TARGETS[/** @type {SupportedTargetKey} */ (targetKey)] + : undefined; +if (!targetConfig) { const supported = Object.keys(TARGETS).join(', '); logger.error( `${dim('Unsupported platform: ')}${red(`${process.platform}-${process.arch}`)}${dim('. Supported targets: ')}${supported}` ); process.exit(1); } +const { target, digest: expectedDigest } = targetConfig; const bin = join( __dirname, @@ -173,7 +243,8 @@ if (systemKnopePath) { } } -const url = `https://github.com/knope-dev/knope/releases/download/knope%2Fv${VERSION}/knope-${target}.tgz`; +const assetName = `knope-${target}.tgz`; +const url = `https://github.com/knope-dev/knope/releases/download/knope%2Fv${VERSION}/${assetName}`; logger.info( `${dim('Downloading knope ')}${green(`v${VERSION}`)}${dim(' for ')}${target}${dim('...')}` ); @@ -182,6 +253,12 @@ if (!response.ok) { throw new Error(`Failed to download knope: ${response.status} ${response.statusText}`); } const gzipBytes = Buffer.from(await response.arrayBuffer()); +const actualDigest = getSha256Digest(gzipBytes); +if (actualDigest !== expectedDigest) { + throw new Error( + `Downloaded ${assetName} digest mismatch: expected ${expectedDigest}, got ${actualDigest}` + ); +} const tarBytes = gunzipSync(gzipBytes); const expectedBinaryName = process.platform === 'win32' ? 'knope.exe' : 'knope'; const knopeBinary = extractRegularFileFromTar(tarBytes, expectedBinaryName); diff --git a/scripts/migrate-matrix-sdk-imports.js b/scripts/migrate-matrix-sdk-imports.js new file mode 100644 index 000000000..af862aeeb --- /dev/null +++ b/scripts/migrate-matrix-sdk-imports.js @@ -0,0 +1,302 @@ +#!/usr/bin/env node +/* eslint-disable no-console */ + +import fs from 'node:fs/promises'; +import path from 'node:path'; +import process from 'node:process'; + +import ts from 'typescript'; + +import { createTextHelpers } from './utils/console-style.js'; +import { + DEFAULT_ROOTS, + applyTextReplacements, + collectSourceFiles, + getMatrixModuleSpecifierFromDeclarationFile, + renderMatrixImportGroups, + toPosix, +} from './utils/import-rewrites.js'; + +const MATRIX_BOUNDARY_SPECIFIER = '$types/matrix-sdk'; + +/** + * @typedef {{ + * write: boolean; + * roots: string[]; + * }} CliArgs + */ + +/** + * @typedef {{ + * importedName: string; + * localName: string; + * }} ImportEntry + */ + +/** + * @typedef {{ + * values: ImportEntry[]; + * types: ImportEntry[]; + * }} MatrixImportGroup + */ + +/** + * @typedef {{ + * start: number; + * end: number; + * original: string; + * value: string; + * }} Replacement + */ + +/** + * @param {string[]} argv + * @returns {CliArgs} + */ +function parseArgs(argv) { + let write = false; + const roots = []; + let index = 0; + + while (index < argv.length) { + const arg = argv[index]; + if (arg === '--write') { + write = true; + } else if (arg === '--root' && argv[index + 1]) { + roots.push(argv[index + 1]); + index += 1; + } else if (arg.startsWith('--root=')) { + roots.push(arg.slice('--root='.length)); + } else if (arg === '--help' || arg === '-h') { + console.log( + [ + 'Usage: node scripts/migrate-matrix-sdk-imports.js [--write] [--root ]', + '', + 'Default mode is dry-run.', + '--write Apply changes to files.', + '--root Root directory to scan (repeatable). Default: src', + ].join('\n') + ); + process.exit(0); + } + + index += 1; + } + + return { + write, + roots: roots.length > 0 ? roots : DEFAULT_ROOTS, + }; +} + +/** + * @param {string} projectRoot + * @returns {import('typescript').Program} + */ +function loadProgram(projectRoot) { + const tsconfigPath = path.join(projectRoot, 'tsconfig.web.json'); + const configResult = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + + if (configResult.error) { + const message = ts.flattenDiagnosticMessageText(configResult.error.messageText, '\n'); + throw new Error(`Failed to read tsconfig.web.json: ${message}`); + } + + const parsedConfig = ts.parseJsonConfigFileContent( + configResult.config, + ts.sys, + projectRoot, + undefined, + tsconfigPath + ); + + if (parsedConfig.errors.length > 0) { + const message = parsedConfig.errors + .map((error) => ts.flattenDiagnosticMessageText(error.messageText, '\n')) + .join('\n'); + throw new Error(`Failed to parse tsconfig.web.json:\n${message}`); + } + + return ts.createProgram({ + rootNames: parsedConfig.fileNames, + options: parsedConfig.options, + }); +} + +/** + * @param {string} filePath + * @param {string[]} rootPaths + * @returns {boolean} + */ +function isWithinRoots(filePath, rootPaths) { + return rootPaths.some((rootPath) => { + const relativePath = path.relative(rootPath, filePath); + return ( + relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) + ); + }); +} + +/** + * @param {import('typescript').TypeChecker} checker + * @param {import('typescript').ImportSpecifier} specifier + * @returns {string | null} + */ +function getDeclarationModuleSpecifier(checker, specifier) { + const importedSymbol = checker.getSymbolAtLocation(specifier.name); + if (!importedSymbol) return null; + + const resolvedSymbol = + importedSymbol.flags & ts.SymbolFlags.Alias + ? checker.getAliasedSymbol(importedSymbol) + : importedSymbol; + const declaration = resolvedSymbol.declarations?.[0]; + if (!declaration) return null; + + return getMatrixModuleSpecifierFromDeclarationFile(declaration.getSourceFile().fileName); +} + +/** + * @param {import('typescript').ImportSpecifier} specifier + * @returns {ImportEntry} + */ +function getImportEntry(specifier) { + return { + importedName: specifier.propertyName?.text ?? specifier.name.text, + localName: specifier.name.text, + }; +} + +/** + * @param {import('typescript').TypeChecker} checker + * @param {import('typescript').ImportDeclaration} importDeclaration + * @returns {string | null} + */ +function buildReplacementText(checker, importDeclaration) { + const importClause = importDeclaration.importClause; + if ( + !importClause || + !importClause.namedBindings || + !ts.isNamedImports(importClause.namedBindings) + ) { + return null; + } + + /** @type {Map} */ + const groups = new Map(); + + for (const specifier of importClause.namedBindings.elements) { + const moduleSpecifier = getDeclarationModuleSpecifier(checker, specifier); + if (!moduleSpecifier) { + return null; + } + + const group = groups.get(moduleSpecifier) ?? { values: [], types: [] }; + const bucket = importClause.isTypeOnly || specifier.isTypeOnly ? group.types : group.values; + bucket.push(getImportEntry(specifier)); + groups.set(moduleSpecifier, group); + } + + return renderMatrixImportGroups(groups).join('\n'); +} + +/** + * @param {import('typescript').SourceFile} sourceFile + * @param {import('typescript').TypeChecker} checker + * @returns {Replacement[]} + */ +function collectReplacements(sourceFile, checker) { + /** @type {Replacement[]} */ + const replacements = []; + + /** + * @param {import('typescript').Node} node + */ + function visit(node) { + if ( + ts.isImportDeclaration(node) && + ts.isStringLiteral(node.moduleSpecifier) && + node.moduleSpecifier.text === MATRIX_BOUNDARY_SPECIFIER + ) { + const replacementText = buildReplacementText(checker, node); + if (replacementText) { + replacements.push({ + start: node.getStart(sourceFile), + end: node.getEnd(), + original: '', + value: replacementText, + }); + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + return replacements.sort((left, right) => right.start - left.start); +} + +async function main() { + const projectRoot = process.cwd(); + const { write, roots } = parseArgs(process.argv.slice(2)); + const targetRoots = roots.map((root) => path.resolve(projectRoot, root)); + const sourceFiles = ( + await Promise.all( + targetRoots.map(async (root) => { + try { + const stat = await fs.stat(root); + if (!stat.isDirectory()) return []; + return collectSourceFiles(root); + } catch { + return []; + } + }) + ) + ).flat(); + + const sourceFileSet = new Set(sourceFiles.map((filePath) => path.normalize(filePath))); + const program = loadProgram(projectRoot); + const checker = program.getTypeChecker(); + const { dim, green } = createTextHelpers(); + + const changes = []; + + for (const sourceFile of program.getSourceFiles()) { + const filePath = path.normalize(sourceFile.fileName); + if (!sourceFileSet.has(filePath) || !isWithinRoots(filePath, targetRoots)) continue; + + const replacements = collectReplacements(sourceFile, checker); + if (replacements.length === 0) continue; + + const originalCode = sourceFile.getFullText(); + const updatedCode = applyTextReplacements(originalCode, replacements); + + if (write) { + await fs.writeFile(filePath, updatedCode, 'utf8'); + } + + changes.push({ + file: toPosix(path.relative(projectRoot, filePath)), + replacements: replacements.length, + }); + } + + changes + .sort((left, right) => left.file.localeCompare(right.file)) + .forEach((change) => { + console.log( + `${dim(change.file)}: ${green(`${change.replacements} matrix import rewrite(s)`)}` + ); + }); + + const mode = write ? 'Applied' : 'Dry run'; + console.log(`${mode}: ${changes.length} files.`); + if (!write) { + console.log('Re-run with --write to apply changes.'); + } +} + +main().catch((error) => { + console.error(error instanceof Error ? error.message : String(error)); + process.exit(1); +}); diff --git a/scripts/normalize-imports.js b/scripts/normalize-imports.js index 74f9c4a91..2fb08797b 100644 --- a/scripts/normalize-imports.js +++ b/scripts/normalize-imports.js @@ -4,21 +4,34 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import process from 'node:process'; -import ts from 'typescript'; import { createTextHelpers } from './utils/console-style.js'; - -const DEFAULT_ROOTS = ['src']; -const SKIP_DIRS = new Set(['.git', '.hg', '.svn', 'node_modules', 'dist', 'coverage']); -const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs']); -const MATRIX_IMPORT_BOUNDARY_FILES = new Set([ - path.normalize('src/types/matrix-sdk.ts'), - path.normalize('src/types/matrix-sdk-events.d.ts'), -]); - -function toPosix(inputPath) { - return inputPath.split(path.sep).join('/'); -} - +import { + DEFAULT_ROOTS, + collectSourceFiles, + loadAliasMapFromTsconfig, + rewriteSourceImports, + toPosix, +} from './utils/import-rewrites.js'; + +/** + * @typedef {{ + * write: boolean; + * roots: string[]; + * }} CliArgs + */ + +/** + * @typedef {{ + * file: string; + * replacements: number; + * edits: { from: string; to: string }[]; + * }} FileResult + */ + +/** + * @param {string[]} argv + * @returns {CliArgs} + */ function parseArgs(argv) { let write = false; const roots = []; @@ -55,190 +68,17 @@ function parseArgs(argv) { }; } -async function loadAliasMap(viteConfigPath, projectRoot) { - const viteConfig = await fs.readFile(viteConfigPath, 'utf8'); - const regex = /(\$[A-Za-z0-9_]+)\s*:\s*path\.resolve\(__dirname,\s*'([^']+)'\s*\)/g; - const aliasMap = []; - let match = regex.exec(viteConfig); - - while (match) { - const alias = match[1]; - const relativePath = match[2]; - aliasMap.push({ - alias, - absolutePath: path.resolve(projectRoot, relativePath), - }); - match = regex.exec(viteConfig); - } - - aliasMap.sort((a, b) => b.absolutePath.length - a.absolutePath.length); - return aliasMap; -} - -async function collectSourceFiles(rootDir) { - const files = []; - - async function walk(currentDir) { - const entries = await fs.readdir(currentDir, { withFileTypes: true }); - await Promise.all( - entries.map(async (entry) => { - if (entry.name.startsWith('.') && entry.name !== '.eslintrc') return; - if (entry.isDirectory()) { - if (SKIP_DIRS.has(entry.name)) return; - await walk(path.join(currentDir, entry.name)); - return; - } - - if (!entry.isFile()) return; - const filePath = path.join(currentDir, entry.name); - const ext = path.extname(entry.name); - if (!SOURCE_EXTENSIONS.has(ext)) return; - files.push(filePath); - }) - ); - } - - await walk(rootDir); - return files; -} - -function splitSpecifier(specifier) { - const match = specifier.match(/^([^?#]+)([?#].*)?$/); - if (!match) { - return { bare: specifier, suffix: '' }; - } - return { - bare: match[1], - suffix: match[2] ?? '', - }; -} - -function findMatchingAlias(absoluteTargetPath, aliases) { - return aliases.find(({ absolutePath }) => { - const rel = path.relative(absolutePath, absoluteTargetPath); - return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel)); - }); -} - -function getRewrittenSpecifier(filePath, specifier, aliases, projectRoot) { - const normalizedFilePath = path.normalize(path.relative(projectRoot, filePath)); - const { bare, suffix } = splitSpecifier(specifier); - - if ( - !MATRIX_IMPORT_BOUNDARY_FILES.has(normalizedFilePath) && - (bare === 'matrix-js-sdk' || bare.startsWith('matrix-js-sdk/')) - ) { - return `$types/matrix-sdk${suffix}`; - } - - if (!/^\.\.(?:\/|$)/.test(bare)) { - return null; - } - - const absoluteTargetPath = path.resolve(path.dirname(filePath), bare); - const matchedAlias = findMatchingAlias(absoluteTargetPath, aliases); - if (!matchedAlias) return null; - - const aliasRelativePath = toPosix(path.relative(matchedAlias.absolutePath, absoluteTargetPath)); - const aliasImport = aliasRelativePath - ? `${matchedAlias.alias}/${aliasRelativePath}` - : matchedAlias.alias; - return `${aliasImport}${suffix}`; -} - -function queueReplacement(sourceFile, literalNode, replacements, aliases, filePath, projectRoot) { - const specifier = literalNode.text; - const rewrittenSpecifier = getRewrittenSpecifier(filePath, specifier, aliases, projectRoot); - if (!rewrittenSpecifier || rewrittenSpecifier === specifier) return; - - replacements.push({ - start: literalNode.getStart(sourceFile) + 1, - end: literalNode.getEnd() - 1, - original: specifier, - value: rewrittenSpecifier, - }); -} - -function rewriteFileImports(filePath, sourceCode, aliases, projectRoot) { - const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true); - const replacements = []; - - function visit(node) { - if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) { - queueReplacement( - sourceFile, - node.moduleSpecifier, - replacements, - aliases, - filePath, - projectRoot - ); - } else if ( - ts.isExportDeclaration(node) && - node.moduleSpecifier && - ts.isStringLiteral(node.moduleSpecifier) - ) { - queueReplacement( - sourceFile, - node.moduleSpecifier, - replacements, - aliases, - filePath, - projectRoot - ); - } else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) { - const { literal } = node.argument; - if (ts.isStringLiteral(literal)) { - queueReplacement(sourceFile, literal, replacements, aliases, filePath, projectRoot); - } - } else if (ts.isCallExpression(node) && node.arguments.length > 0) { - const firstArg = node.arguments[0]; - if (ts.isStringLiteral(firstArg)) { - const isDynamicImport = node.expression.kind === ts.SyntaxKind.ImportKeyword; - const isRequire = ts.isIdentifier(node.expression) && node.expression.text === 'require'; - if (isDynamicImport || isRequire) { - queueReplacement(sourceFile, firstArg, replacements, aliases, filePath, projectRoot); - } - } - } - - ts.forEachChild(node, visit); - } - - visit(sourceFile); - if (replacements.length === 0) { - return { changed: false, updatedCode: sourceCode, replacements: 0 }; - } - - const uniqueReplacements = Array.from( - new Map(replacements.map((r) => [`${r.start}:${r.end}`, r])).values() - ).sort((a, b) => b.start - a.start); - - const updatedCode = uniqueReplacements.reduce( - (code, replacement) => - code.slice(0, replacement.start) + replacement.value + code.slice(replacement.end), - sourceCode - ); - - return { - changed: updatedCode !== sourceCode, - updatedCode, - replacements: uniqueReplacements.length, - edits: uniqueReplacements.map((replacement) => ({ - from: replacement.original, - to: replacement.value, - })), - }; -} - async function main() { const projectRoot = process.cwd(); const { write, roots } = parseArgs(process.argv.slice(2)); - const aliases = await loadAliasMap(path.join(projectRoot, 'vite.config.ts'), projectRoot); + const aliases = await loadAliasMapFromTsconfig( + path.join(projectRoot, 'tsconfig.web.json'), + projectRoot + ); const { dim, red, green } = createTextHelpers(); if (aliases.length === 0) { - throw new Error('No aliases found in vite.config.ts'); + throw new Error('No aliases found in tsconfig.web.json'); } const targetRoots = roots.map((root) => path.resolve(projectRoot, root)); @@ -259,7 +99,7 @@ async function main() { const fileResults = await Promise.all( sourceFiles.map(async (filePath) => { const sourceCode = await fs.readFile(filePath, 'utf8'); - const { changed, updatedCode, replacements, edits } = rewriteFileImports( + const { changed, updatedCode, replacements, edits } = rewriteSourceImports( filePath, sourceCode, aliases, @@ -280,6 +120,7 @@ async function main() { }) ); + /** @type {FileResult[]} */ const changedFiles = fileResults.filter((result) => result !== null); const filesChanged = changedFiles.length; const importRewrites = changedFiles.reduce((total, result) => total + result.replacements, 0); diff --git a/scripts/utils/console-style.js b/scripts/utils/console-style.js index 96ed5bf3e..7819168e7 100644 --- a/scripts/utils/console-style.js +++ b/scripts/utils/console-style.js @@ -1,6 +1,17 @@ /* eslint-disable no-console */ import process from 'node:process'; +/** + * @typedef {object} TextHelperOptions + * @property {boolean} [useColor] + */ + +/** + * @typedef {TextHelperOptions & { + * prefixColor?: string; + * }} LoggerOptions + */ + export const ANSI = { reset: '\x1b[0m', red: '\x1b[31m', @@ -14,16 +25,33 @@ export function shouldUseColor() { return Boolean(process.stdout.isTTY); } +/** + * @param {string} text + * @param {string} color + * @param {boolean} enabled + * @returns {string} + */ export function styleText(text, color, enabled) { if (!enabled) return text; return `${color}${text}${ANSI.reset}`; } +/** + * @param {TextHelperOptions} [options] + */ export function createTextHelpers(options = {}) { const useColor = options.useColor ?? shouldUseColor(); + /** + * @param {string} text + * @param {string} color + * @returns {string} + */ const style = (text, color) => styleText(text, color, useColor); + /** @param {string} text */ const dim = (text) => styleText(text, ANSI.dim, useColor); + /** @param {string} text */ const red = (text) => styleText(text, ANSI.red, useColor); + /** @param {string} text */ const green = (text) => styleText(text, ANSI.green, useColor); return { useColor, @@ -35,20 +63,30 @@ export function createTextHelpers(options = {}) { } export class PrefixedLogger { + /** + * @param {string} prefix + * @param {LoggerOptions} [options] + */ constructor(prefix, options = {}) { this.prefix = prefix; this.useColor = options.useColor ?? shouldUseColor(); this.prefixColor = options.prefixColor ?? ANSI.dim; } + /** + * @param {string} message + * @returns {string} + */ withPrefix(message) { return `${styleText(this.prefix, this.prefixColor, this.useColor)} ${message}`; } + /** @param {string} message */ info(message) { console.log(this.withPrefix(message)); } + /** @param {string} message */ error(message) { console.error(this.withPrefix(message)); } diff --git a/scripts/utils/import-rewrites.js b/scripts/utils/import-rewrites.js new file mode 100644 index 000000000..376a3cfb0 --- /dev/null +++ b/scripts/utils/import-rewrites.js @@ -0,0 +1,394 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import ts from 'typescript'; + +/** + * @typedef {{ + * alias: string; + * absolutePath: string; + * }} AliasEntry + */ + +/** + * @typedef {{ + * start: number; + * end: number; + * original: string; + * value: string; + * }} TextReplacement + */ + +/** + * @typedef {{ + * importedName: string; + * localName: string; + * }} MatrixImportSpecifier + */ + +/** + * @typedef {{ + * values: MatrixImportSpecifier[]; + * types: MatrixImportSpecifier[]; + * }} MatrixImportGroup + */ + +export const DEFAULT_ROOTS = ['src']; +export const SKIP_DIRS = new Set(['.git', '.hg', '.svn', 'node_modules', 'dist', 'coverage']); +export const SOURCE_EXTENSIONS = new Set([ + '.ts', + '.tsx', + '.js', + '.jsx', + '.mts', + '.cts', + '.mjs', + '.cjs', +]); +export const MATRIX_IMPORT_BOUNDARY_FILES = new Set([ + path.normalize('src/types/matrix-sdk.ts'), + path.normalize('src/types/matrix-sdk-events.d.ts'), +]); + +/** + * @param {string} inputPath + * @returns {string} + */ +export function toPosix(inputPath) { + return inputPath.replace(/\\/g, '/'); +} + +/** + * @param {string} pattern + * @returns {string} + */ +function normalizeAliasPattern(pattern) { + return pattern.replace(/\/\*$/, ''); +} + +/** + * @param {import('typescript').Diagnostic} error + * @returns {string} + */ +function getConfigErrorMessage(error) { + return ts.flattenDiagnosticMessageText(error.messageText, '\n'); +} + +/** + * @param {string} tsconfigPath + * @param {string} projectRoot + * @returns {Promise} + */ +export async function loadAliasMapFromTsconfig(tsconfigPath, projectRoot) { + const configResult = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + if (configResult.error) { + throw new Error( + `Failed to read ${path.basename(tsconfigPath)}: ${getConfigErrorMessage(configResult.error)}` + ); + } + + const compilerOptions = configResult.config.compilerOptions ?? {}; + const baseUrl = compilerOptions.baseUrl ?? '.'; + const paths = compilerOptions.paths ?? {}; + + /** @type {Map} */ + const aliasEntries = new Map(); + for (const [aliasPattern, targets] of Object.entries(paths)) { + if (!Array.isArray(targets) || targets.length === 0) continue; + + const alias = normalizeAliasPattern(aliasPattern); + const targetPattern = normalizeAliasPattern(targets[0]); + const absolutePath = path.resolve(projectRoot, baseUrl, targetPattern); + aliasEntries.set(`${alias}:${absolutePath}`, { alias, absolutePath }); + } + + const aliasMap = [...aliasEntries.values()]; + + aliasMap.sort((a, b) => b.absolutePath.length - a.absolutePath.length); + return aliasMap; +} + +/** + * @param {string} rootDir + * @returns {Promise} + */ +export async function collectSourceFiles(rootDir) { + /** @type {string[]} */ + const files = []; + + /** + * @param {string} currentDir + * @returns {Promise} + */ + async function walk(currentDir) { + const entries = await fs.readdir(currentDir, { withFileTypes: true }); + await Promise.all( + entries.map(async (entry) => { + if (entry.name.startsWith('.') && entry.name !== '.eslintrc') return; + if (entry.isDirectory()) { + if (SKIP_DIRS.has(entry.name)) return; + await walk(path.join(currentDir, entry.name)); + return; + } + + if (!entry.isFile()) return; + const filePath = path.join(currentDir, entry.name); + if (!SOURCE_EXTENSIONS.has(path.extname(entry.name))) return; + files.push(filePath); + }) + ); + } + + await walk(rootDir); + return files; +} + +/** + * @param {string} specifier + * @returns {{ bare: string; suffix: string }} + */ +function splitSpecifier(specifier) { + const match = specifier.match(/^([^?#]+)([?#].*)?$/); + if (!match) { + return { bare: specifier, suffix: '' }; + } + + return { + bare: match[1], + suffix: match[2] ?? '', + }; +} + +/** + * @param {string} absoluteTargetPath + * @param {AliasEntry[]} aliases + * @returns {AliasEntry | undefined} + */ +function findMatchingAlias(absoluteTargetPath, aliases) { + return aliases.find(({ absolutePath }) => { + const rel = path.relative(absolutePath, absoluteTargetPath); + return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel)); + }); +} + +/** + * @param {string} filePath + * @param {string} specifier + * @param {AliasEntry[]} aliases + * @param {string} projectRoot + * @returns {string | null} + */ +function getRewrittenSpecifier(filePath, specifier, aliases, projectRoot) { + const normalizedFilePath = path.normalize(path.relative(projectRoot, filePath)); + const { bare, suffix } = splitSpecifier(specifier); + + if ( + !MATRIX_IMPORT_BOUNDARY_FILES.has(normalizedFilePath) && + (bare === 'matrix-js-sdk' || bare.startsWith('matrix-js-sdk/')) + ) { + return `$types/matrix-sdk${suffix}`; + } + + if (!/^\.\.(?:\/|$)/.test(bare)) { + return null; + } + + const absoluteTargetPath = path.resolve(path.dirname(filePath), bare); + const matchedAlias = findMatchingAlias(absoluteTargetPath, aliases); + if (!matchedAlias) return null; + + const aliasRelativePath = toPosix(path.relative(matchedAlias.absolutePath, absoluteTargetPath)); + const aliasImport = aliasRelativePath + ? `${matchedAlias.alias}/${aliasRelativePath}` + : matchedAlias.alias; + return `${aliasImport}${suffix}`; +} + +/** + * @param {import('typescript').SourceFile} sourceFile + * @param {import('typescript').StringLiteral} literalNode + * @param {TextReplacement[]} replacements + * @param {AliasEntry[]} aliases + * @param {string} filePath + * @param {string} projectRoot + */ +function queueReplacement(sourceFile, literalNode, replacements, aliases, filePath, projectRoot) { + const specifier = literalNode.text; + const rewrittenSpecifier = getRewrittenSpecifier(filePath, specifier, aliases, projectRoot); + if (!rewrittenSpecifier || rewrittenSpecifier === specifier) return; + + replacements.push({ + start: literalNode.getStart(sourceFile) + 1, + end: literalNode.getEnd() - 1, + original: specifier, + value: rewrittenSpecifier, + }); +} + +/** + * @param {string} sourceCode + * @param {TextReplacement[]} replacements + * @returns {string} + */ +export function applyTextReplacements(sourceCode, replacements) { + return replacements.reduce( + (code, replacement) => + code.slice(0, replacement.start) + replacement.value + code.slice(replacement.end), + sourceCode + ); +} + +/** + * @param {string} filePath + * @param {string} sourceCode + * @param {AliasEntry[]} aliases + * @param {string} projectRoot + */ +export function rewriteSourceImports(filePath, sourceCode, aliases, projectRoot) { + const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true); + /** @type {TextReplacement[]} */ + const replacements = []; + + /** + * @param {import('typescript').Node} node + */ + function visit(node) { + if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) { + queueReplacement( + sourceFile, + node.moduleSpecifier, + replacements, + aliases, + filePath, + projectRoot + ); + } else if ( + ts.isExportDeclaration(node) && + node.moduleSpecifier && + ts.isStringLiteral(node.moduleSpecifier) + ) { + queueReplacement( + sourceFile, + node.moduleSpecifier, + replacements, + aliases, + filePath, + projectRoot + ); + } else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) { + const { literal } = node.argument; + if (ts.isStringLiteral(literal)) { + queueReplacement(sourceFile, literal, replacements, aliases, filePath, projectRoot); + } + } else if (ts.isCallExpression(node) && node.arguments.length > 0) { + const firstArg = node.arguments[0]; + if (ts.isStringLiteral(firstArg)) { + const isDynamicImport = node.expression.kind === ts.SyntaxKind.ImportKeyword; + const isRequire = ts.isIdentifier(node.expression) && node.expression.text === 'require'; + if (isDynamicImport || isRequire) { + queueReplacement(sourceFile, firstArg, replacements, aliases, filePath, projectRoot); + } + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + if (replacements.length === 0) { + return { changed: false, updatedCode: sourceCode, replacements: 0, edits: [] }; + } + + /** @type {TextReplacement[]} */ + const uniqueReplacements = [ + ...new Map( + replacements.map((replacement) => [`${replacement.start}:${replacement.end}`, replacement]) + ).values(), + ].toSorted((a, b) => b.start - a.start); + + const updatedCode = applyTextReplacements(sourceCode, uniqueReplacements); + + return { + changed: updatedCode !== sourceCode, + updatedCode, + replacements: uniqueReplacements.length, + edits: uniqueReplacements.map((replacement) => ({ + from: replacement.original, + to: replacement.value, + })), + }; +} + +/** + * @param {string} relativePath + * @returns {string} + */ +function stripDeclarationExtension(relativePath) { + return relativePath + .replace(/\.d\.[cm]?ts$/i, '') + .replace(/\.[cm]?tsx?$/i, '') + .replace(/\.[cm]?js$/i, ''); +} + +/** + * @param {string} declarationFile + * @returns {string | null} + */ +export function getMatrixModuleSpecifierFromDeclarationFile(declarationFile) { + const normalizedFile = toPosix(declarationFile); + const marker = '/node_modules/matrix-js-sdk/'; + const markerIndex = normalizedFile.lastIndexOf(marker); + + if (markerIndex === -1) return null; + + const relativePath = normalizedFile.slice(markerIndex + marker.length); + return `matrix-js-sdk/${stripDeclarationExtension(relativePath)}`; +} + +/** + * @param {MatrixImportSpecifier[]} specifiers + * @returns {MatrixImportSpecifier[]} + */ +function sortSpecifiers(specifiers) { + return [...specifiers].toSorted((left, right) => + left.importedName.localeCompare(right.importedName) + ); +} + +/** + * @param {MatrixImportSpecifier} specifier + * @returns {string} + */ +function formatSpecifier({ importedName, localName }) { + return importedName === localName ? importedName : `${importedName} as ${localName}`; +} + +/** + * @param {Map} groups + * @returns {string[]} + */ +export function renderMatrixImportGroups(groups) { + /** @type {string[]} */ + const lines = []; + + [...groups.entries()] + .toSorted(([left], [right]) => left.localeCompare(right)) + .forEach(([moduleSpecifier, group]) => { + const valueSpecifiers = sortSpecifiers(group.values); + const typeSpecifiers = sortSpecifiers(group.types); + + if (valueSpecifiers.length > 0) { + lines.push( + `import { ${valueSpecifiers.map(formatSpecifier).join(', ')} } from '${moduleSpecifier}';` + ); + } + + if (typeSpecifiers.length > 0) { + lines.push( + `import type { ${typeSpecifiers.map(formatSpecifier).join(', ')} } from '${moduleSpecifier}';` + ); + } + }); + + return lines; +} diff --git a/src/app/components/AccountDataEditor.tsx b/src/app/components/AccountDataEditor.tsx index f8fd8445f..4639bcb73 100644 --- a/src/app/components/AccountDataEditor.tsx +++ b/src/app/components/AccountDataEditor.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { type FormEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Text, @@ -14,7 +14,7 @@ import { Scroll, config, } from 'folds'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { Cursor } from '$plugins/text-area'; import { syntaxErrorPosition } from '$utils/dom'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; diff --git a/src/app/components/ActionUIA.tsx b/src/app/components/ActionUIA.tsx index caffb0329..16d30d73b 100644 --- a/src/app/components/ActionUIA.tsx +++ b/src/app/components/ActionUIA.tsx @@ -1,5 +1,5 @@ -import { ReactNode } from 'react'; -import { AuthDict, AuthType, IAuthData, UIAFlow } from '$types/matrix-sdk'; +import { type ReactNode } from 'react'; +import { type AuthDict, AuthType, type IAuthData, type UIAFlow } from '$types/matrix-sdk'; import { getUIAFlowForStages } from '$utils/matrix-uia'; import { useSupportedUIAFlows, useUIACompleted, useUIAFlow } from '$hooks/useUIAFlows'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/AuthFlowsLoader.tsx b/src/app/components/AuthFlowsLoader.tsx index 4ea21b92c..a4af0dd9b 100644 --- a/src/app/components/AuthFlowsLoader.tsx +++ b/src/app/components/AuthFlowsLoader.tsx @@ -1,12 +1,12 @@ -import { ReactNode, useCallback, useEffect, useMemo } from 'react'; -import { MatrixError, createClient } from '$types/matrix-sdk'; +import { type ReactNode, useCallback, useEffect, useMemo } from 'react'; +import { type MatrixError, createClient } from '$types/matrix-sdk'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useAutoDiscoveryInfo } from '$hooks/useAutoDiscoveryInfo'; import { promiseFulfilledResult, promiseRejectedResult } from '$utils/common'; import { - AuthFlows, + type AuthFlows, RegisterFlowStatus, - RegisterFlowsResponse, + type RegisterFlowsResponse, parseRegisterErrResp, } from '$hooks/useAuthFlows'; diff --git a/src/app/components/BackRouteHandler.tsx b/src/app/components/BackRouteHandler.tsx index a6e91bcd1..6ce06352f 100644 --- a/src/app/components/BackRouteHandler.tsx +++ b/src/app/components/BackRouteHandler.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useCallback } from 'react'; +import { type ReactNode, useCallback } from 'react'; import { useSetAtom } from 'jotai'; import { matchPath, useLocation, useNavigate } from 'react-router-dom'; import { diff --git a/src/app/components/BackupRestore.tsx b/src/app/components/BackupRestore.tsx index 78d65dff4..f19a8f298 100644 --- a/src/app/components/BackupRestore.tsx +++ b/src/app/components/BackupRestore.tsx @@ -1,6 +1,6 @@ -import { MouseEventHandler, useCallback, useState } from 'react'; +import { type MouseEventHandler, useCallback, useState } from 'react'; import { useAtom } from 'jotai'; -import { CryptoApi, KeyBackupInfo } from '$types/matrix-sdk'; +import { type CryptoApi, type KeyBackupInfo } from '$types/matrix-sdk'; import { Badge, Box, @@ -14,7 +14,7 @@ import { percent, PopOut, ProgressBar, - RectCords, + type RectCords, Spinner, Text, } from 'folds'; diff --git a/src/app/components/CallEmbedProvider.tsx b/src/app/components/CallEmbedProvider.tsx index fe2f84468..4e88b0217 100644 --- a/src/app/components/CallEmbedProvider.tsx +++ b/src/app/components/CallEmbedProvider.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useCallback, useRef } from 'react'; +import { type ReactNode, useCallback, useRef } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; import { useAutoJoinCall } from '$hooks/useAutoJoinCall'; import { @@ -9,7 +9,7 @@ import { useCallThemeSync, useCallMemberSoundSync, } from '$hooks/useCallEmbed'; -import { CallEmbed, useClientWidgetApiEvent, ElementWidgetActions } from '$plugins/call'; +import { type CallEmbed, useClientWidgetApiEvent, ElementWidgetActions } from '$plugins/call'; import { callChatAtom, callEmbedAtom } from '$state/callEmbed'; import { useSelectedRoom } from '$hooks/router/useSelectedRoom'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; diff --git a/src/app/components/ClientConfigLoader.tsx b/src/app/components/ClientConfigLoader.tsx index 92af0c149..4c04f1560 100644 --- a/src/app/components/ClientConfigLoader.tsx +++ b/src/app/components/ClientConfigLoader.tsx @@ -1,6 +1,6 @@ -import { ReactNode, useCallback, useEffect, useState } from 'react'; +import { type ReactNode, useCallback, useEffect, useState } from 'react'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; -import { ClientConfig } from '$hooks/useClientConfig'; +import { type ClientConfig } from '$hooks/useClientConfig'; import { trimTrailingSlash } from '$utils/common'; const getClientConfig = async (): Promise => { diff --git a/src/app/components/ConfirmPasswordMatch.tsx b/src/app/components/ConfirmPasswordMatch.tsx index dea228924..a1ea3a5a6 100644 --- a/src/app/components/ConfirmPasswordMatch.tsx +++ b/src/app/components/ConfirmPasswordMatch.tsx @@ -1,4 +1,4 @@ -import { ReactNode, RefObject, useCallback, useRef, useState } from 'react'; +import { type ReactNode, type RefObject, useCallback, useRef, useState } from 'react'; import { useDebounce } from '$hooks/useDebounce'; type ConfirmPasswordMatchProps = { diff --git a/src/app/components/DeviceVerification.tsx b/src/app/components/DeviceVerification.tsx index fa29ae11e..4c718d66e 100644 --- a/src/app/components/DeviceVerification.tsx +++ b/src/app/components/DeviceVerification.tsx @@ -1,11 +1,11 @@ import { - ShowSasCallbacks, + type ShowSasCallbacks, VerificationPhase, - VerificationRequest, - Verifier, + type VerificationRequest, + type Verifier, VerificationMethod, } from '$types/matrix-sdk'; -import { CSSProperties, useCallback, useEffect, useState } from 'react'; +import { type CSSProperties, useCallback, useEffect, useState } from 'react'; import { Box, Button, diff --git a/src/app/components/DeviceVerificationSetup.tsx b/src/app/components/DeviceVerificationSetup.tsx index 78fe8ded2..c020eea50 100644 --- a/src/app/components/DeviceVerificationSetup.tsx +++ b/src/app/components/DeviceVerificationSetup.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, forwardRef, useCallback, useState } from 'react'; +import { type FormEventHandler, forwardRef, useCallback, useState } from 'react'; import { Dialog, Header, @@ -15,7 +15,7 @@ import { } from 'folds'; import FileSaver from 'file-saver'; import to from 'await-to-js'; -import { AuthDict, IAuthData, MatrixError, UIAuthCallback } from '$types/matrix-sdk'; +import { type AuthDict, type IAuthData, MatrixError, type UIAuthCallback } from '$types/matrix-sdk'; import { clearSecretStorageKeys } from '$client/secretStorageKeys'; import { ContainerColor } from '$styles/ContainerColor.css'; import { copyToClipboard } from '$utils/dom'; diff --git a/src/app/components/DeviceVerificationStatus.ts b/src/app/components/DeviceVerificationStatus.ts index f69c64ac9..6a5868125 100644 --- a/src/app/components/DeviceVerificationStatus.ts +++ b/src/app/components/DeviceVerificationStatus.ts @@ -1,8 +1,8 @@ -import { ReactNode } from 'react'; -import { CryptoApi } from '$types/matrix-sdk'; +import { type ReactNode } from 'react'; +import { type CryptoApi } from '$types/matrix-sdk'; import { useDeviceVerificationStatus, - VerificationStatus, + type VerificationStatus, } from '$hooks/useDeviceVerificationStatus'; type DeviceVerificationStatusProps = { diff --git a/src/app/components/GlobalKeyboardShortcuts.tsx b/src/app/components/GlobalKeyboardShortcuts.tsx index 0571e54a4..f2de4fd03 100644 --- a/src/app/components/GlobalKeyboardShortcuts.tsx +++ b/src/app/components/GlobalKeyboardShortcuts.tsx @@ -21,7 +21,7 @@ import { HOME_ROOM_PATH, DIRECT_ROOM_PATH, SPACE_ROOM_PATH } from '$pages/paths' import { getCanonicalAliasOrRoomId } from '$utils/matrix'; import { announce } from '$utils/announce'; import { roomIdToReplyDraftAtomFamily } from '$state/room/roomInputDrafts'; -import { Room } from 'matrix-js-sdk'; +import { type Room } from 'matrix-js-sdk'; export function GlobalKeyboardShortcuts() { const navigate = useNavigate(); @@ -62,7 +62,7 @@ export function GlobalKeyboardShortcuts() { } else { const parents = roomToParents.get(roomId); if (parents && parents.size > 0) { - const spaceId = Array.from(parents)[0]; + const spaceId = [...parents][0]; const spaceIdOrAlias = getCanonicalAliasOrRoomId(mx, spaceId); navigate(getSpaceRoomPath(spaceIdOrAlias, roomIdOrAliasToNav)); } else { @@ -80,7 +80,7 @@ export function GlobalKeyboardShortcuts() { const handleNextUnreadKeyDown = useCallback( (evt: KeyboardEvent) => { if (!isKeyHotkey('alt+n', evt)) return; - const unreadEntries = Array.from(roomToUnread.entries()) + const unreadEntries = [...roomToUnread.entries()] .filter(([id, u]) => u.total > 0 && id !== currentRoom?.roomId) .sort((a, b) => b[1].highlight - a[1].highlight || b[1].total - a[1].total); if (unreadEntries.length === 0) return; @@ -98,7 +98,7 @@ export function GlobalKeyboardShortcuts() { const isDown = isKeyHotkey('alt+shift+down', evt); const isUp = isKeyHotkey('alt+shift+up', evt); if (!isDown && !isUp) return; - const unreadEntries = Array.from(roomToUnread.entries()) + const unreadEntries = [...roomToUnread.entries()] .filter(([, u]) => u.total > 0) .sort((a, b) => b[1].highlight - a[1].highlight || b[1].total - a[1].total); if (unreadEntries.length === 0) return; diff --git a/src/app/components/HexColorPickerPopOut.tsx b/src/app/components/HexColorPickerPopOut.tsx index 25c5d661b..c111d90dd 100644 --- a/src/app/components/HexColorPickerPopOut.tsx +++ b/src/app/components/HexColorPickerPopOut.tsx @@ -1,6 +1,6 @@ import FocusTrap from 'focus-trap-react'; -import { Box, Button, config, Menu, PopOut, RectCords, Text } from 'folds'; -import { MouseEventHandler, ReactNode, useState } from 'react'; +import { Box, Button, config, Menu, PopOut, type RectCords, Text } from 'folds'; +import { type MouseEventHandler, type ReactNode, useState } from 'react'; import { stopPropagation } from '$utils/keyboard'; type HexColorPickerPopOutProps = { diff --git a/src/app/components/JoinRulesSwitcher.tsx b/src/app/components/JoinRulesSwitcher.tsx index 5b33b397d..e1c1eb05f 100644 --- a/src/app/components/JoinRulesSwitcher.tsx +++ b/src/app/components/JoinRulesSwitcher.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useMemo, useState } from 'react'; +import { type MouseEventHandler, useCallback, useMemo, useState } from 'react'; import { config, Box, @@ -6,8 +6,8 @@ import { Text, Icon, Icons, - IconSrc, - RectCords, + type IconSrc, + type RectCords, PopOut, Menu, Button, diff --git a/src/app/components/LogoutDialog.tsx b/src/app/components/LogoutDialog.tsx index c1cf9d1ce..96fcd5990 100644 --- a/src/app/components/LogoutDialog.tsx +++ b/src/app/components/LogoutDialog.tsx @@ -16,7 +16,7 @@ type LogoutDialogProps = { export const LogoutDialog = forwardRef( ({ handleClose }, ref) => { const mx = useMatrixClient(); - const hasEncryptedRoom = !!mx.getRooms().find((room) => room.hasEncryptionStateEvent()); + const hasEncryptedRoom = mx.getRooms().some((room) => room.hasEncryptionStateEvent()); const crossSigningActive = useCrossSigningActive(); const verificationStatus = useDeviceVerificationStatus( mx.getCrypto(), diff --git a/src/app/components/ManualVerification.tsx b/src/app/components/ManualVerification.tsx index 31903f957..cd087a3f6 100644 --- a/src/app/components/ManualVerification.tsx +++ b/src/app/components/ManualVerification.tsx @@ -1,11 +1,11 @@ -import { MouseEventHandler, ReactNode, useCallback, useState } from 'react'; +import { type MouseEventHandler, type ReactNode, useCallback, useState } from 'react'; import { Box, Text, Chip, Icon, Icons, - RectCords, + type RectCords, PopOut, Menu, config, @@ -13,7 +13,7 @@ import { color, } from 'folds'; import FocusTrap from 'focus-trap-react'; -import { SecretStorageKeyContent } from '$types/matrix/accountData'; +import { type SecretStorageKeyContent } from '$types/matrix/accountData'; import { storePrivateKey } from '$client/secretStorageKeys'; import { stopPropagation } from '$utils/keyboard'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/Modal500.tsx b/src/app/components/Modal500.tsx index fc75b8a13..a1236e637 100644 --- a/src/app/components/Modal500.tsx +++ b/src/app/components/Modal500.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useRef } from 'react'; +import { type ReactNode, useRef } from 'react'; import FocusTrap from 'focus-trap-react'; import { Modal, Overlay, OverlayBackdrop, OverlayCenter } from 'folds'; import { stopPropagation } from '$utils/keyboard'; diff --git a/src/app/components/Pdf-viewer/PdfViewer.tsx b/src/app/components/Pdf-viewer/PdfViewer.tsx index 1bd90c8c2..20229d7e8 100644 --- a/src/app/components/Pdf-viewer/PdfViewer.tsx +++ b/src/app/components/Pdf-viewer/PdfViewer.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-param-reassign */ -import { FormEventHandler, MouseEventHandler, useEffect, useRef, useState } from 'react'; +import { type FormEventHandler, type MouseEventHandler, useEffect, useRef, useState } from 'react'; import classNames from 'classnames'; import { Box, @@ -13,7 +13,7 @@ import { Input, Menu, PopOut, - RectCords, + type RectCords, Scroll, Spinner, Text, diff --git a/src/app/components/RenderMessageContent.tsx b/src/app/components/RenderMessageContent.tsx index cc07d5082..defa9c402 100644 --- a/src/app/components/RenderMessageContent.tsx +++ b/src/app/components/RenderMessageContent.tsx @@ -5,8 +5,8 @@ import { useSettingsLinkBaseUrl } from '$features/settings/useSettingsLinkBaseUr import { testMatrixTo } from '$plugins/matrix-to'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom, CaptionPosition } from '$state/settings'; -import { HTMLReactParserOptions } from 'html-react-parser'; -import { Opts } from 'linkifyjs'; +import { type HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts } from 'linkifyjs'; import { Box, config } from 'folds'; import { AudioContent, @@ -55,8 +55,8 @@ type RenderMessageContentProps = { const getMediaType = (url: string) => { const cleanUrl = url.toLowerCase(); - if (cleanUrl.match(/\.(mp4|webm|ogg)$/i)) return 'video'; - if (cleanUrl.match(/\.(png|jpg|jpeg|gif|webp)$/i) || cleanUrl.match(/@(jpeg|webp|png|jpg)$/i)) + if (/\.(mp4|webm|ogg)$/i.test(cleanUrl)) return 'video'; + if (/\.(png|jpg|jpeg|gif|webp)$/i.test(cleanUrl) || /@(jpeg|webp|png|jpg)$/i.test(cleanUrl)) return 'image'; return null; }; diff --git a/src/app/components/RoomNotificationSwitcher.test.tsx b/src/app/components/RoomNotificationSwitcher.test.tsx index ef4938210..78c530856 100644 --- a/src/app/components/RoomNotificationSwitcher.test.tsx +++ b/src/app/components/RoomNotificationSwitcher.test.tsx @@ -1,5 +1,6 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; +import type * as RoomNotificationPreferencesModule from '$hooks/useRoomsNotificationPreferences'; import { RoomNotificationMode } from '$hooks/useRoomsNotificationPreferences'; @@ -11,7 +12,7 @@ const { mockSetMode, modeStateStatus } = vi.hoisted(() => ({ })); vi.mock('$hooks/useRoomsNotificationPreferences', async () => { - const actual = await vi.importActual( + const actual = await vi.importActual( '$hooks/useRoomsNotificationPreferences' ); diff --git a/src/app/components/RoomNotificationSwitcher.tsx b/src/app/components/RoomNotificationSwitcher.tsx index 2bab6de16..d6fcdea15 100644 --- a/src/app/components/RoomNotificationSwitcher.tsx +++ b/src/app/components/RoomNotificationSwitcher.tsx @@ -1,5 +1,5 @@ import { Box, Icon, Text } from 'folds'; -import { type MouseEventHandler, ReactNode } from 'react'; +import { type MouseEventHandler, type ReactNode } from 'react'; import { SettingMenuSelector, type SettingMenuOption } from '$components/setting-menu-selector'; import { getRoomNotificationModeIcon, diff --git a/src/app/components/RoomSummaryLoader.tsx b/src/app/components/RoomSummaryLoader.tsx index 93baa5721..0a8fb135e 100644 --- a/src/app/components/RoomSummaryLoader.tsx +++ b/src/app/components/RoomSummaryLoader.tsx @@ -1,9 +1,9 @@ -import { ReactNode, useCallback, useState } from 'react'; -import { MatrixClient, Room, IHierarchyRoom } from '$types/matrix-sdk'; +import { type ReactNode, useCallback, useState } from 'react'; +import { type MatrixClient, type Room, type IHierarchyRoom } from '$types/matrix-sdk'; import { useQuery } from '@tanstack/react-query'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { LocalRoomSummary, useLocalRoomSummary } from '$hooks/useLocalRoomSummary'; -import { AsyncState, AsyncStatus } from '$hooks/useAsyncCallback'; +import { type LocalRoomSummary, useLocalRoomSummary } from '$hooks/useLocalRoomSummary'; +import { type AsyncState, AsyncStatus } from '$hooks/useAsyncCallback'; export type IRoomSummary = Awaited>; diff --git a/src/app/components/RoomUnreadProvider.tsx b/src/app/components/RoomUnreadProvider.tsx index 5a1fc13a6..cfa4d8f66 100644 --- a/src/app/components/RoomUnreadProvider.tsx +++ b/src/app/components/RoomUnreadProvider.tsx @@ -1,5 +1,5 @@ -import { ReactElement } from 'react'; -import { Unread } from '$types/matrix/room'; +import { type ReactElement } from 'react'; +import { type Unread } from '$types/matrix/room'; import { useRoomUnread, useRoomsUnread } from '$state/hooks/unread'; import { roomToUnreadAtom } from '$state/room/roomToUnread'; diff --git a/src/app/components/SecretStorage.tsx b/src/app/components/SecretStorage.tsx index 07b2e15d5..3f8849247 100644 --- a/src/app/components/SecretStorage.tsx +++ b/src/app/components/SecretStorage.tsx @@ -1,7 +1,10 @@ -import { FormEventHandler, useCallback } from 'react'; +import { type FormEventHandler, useCallback } from 'react'; import { Box, Text, Button, Spinner, color } from 'folds'; import { decodeRecoveryKey, deriveRecoveryKeyFromPassphrase } from '$types/matrix-sdk'; -import { SecretStorageKeyContent, SecretStoragePassphraseContent } from '$types/matrix/accountData'; +import { + type SecretStorageKeyContent, + type SecretStoragePassphraseContent, +} from '$types/matrix/accountData'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useAlive } from '$hooks/useAlive'; diff --git a/src/app/components/ServerConfigsLoader.tsx b/src/app/components/ServerConfigsLoader.tsx index 3f7b395e5..7c212d1fd 100644 --- a/src/app/components/ServerConfigsLoader.tsx +++ b/src/app/components/ServerConfigsLoader.tsx @@ -1,8 +1,12 @@ -import { ReactNode, useCallback, useMemo } from 'react'; -import { Capabilities, validateAuthMetadata, ValidatedAuthMetadata } from '$types/matrix-sdk'; +import { type ReactNode, useCallback, useMemo } from 'react'; +import { + type Capabilities, + validateAuthMetadata, + type ValidatedAuthMetadata, +} from '$types/matrix-sdk'; import { AsyncStatus, useAsyncCallbackValue } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { MediaConfig } from '$hooks/useMediaConfig'; +import { type MediaConfig } from '$hooks/useMediaConfig'; import { promiseFulfilledResult } from '$utils/common'; import { createLogger } from '$utils/debug'; diff --git a/src/app/components/SpecVersionsLoader.tsx b/src/app/components/SpecVersionsLoader.tsx index 2bbbff272..2ac87f5d3 100644 --- a/src/app/components/SpecVersionsLoader.tsx +++ b/src/app/components/SpecVersionsLoader.tsx @@ -1,6 +1,6 @@ -import { ReactNode, useCallback, useEffect, useState } from 'react'; +import { type ReactNode, useCallback, useEffect, useState } from 'react'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; -import { SpecVersions, specVersions } from '../cs-api'; +import { type SpecVersions, specVersions } from '../cs-api'; type SpecVersionsLoaderProps = { baseUrl: string; diff --git a/src/app/components/SupportedUIAFlowsLoader.tsx b/src/app/components/SupportedUIAFlowsLoader.tsx index cd3f81499..4c839600f 100644 --- a/src/app/components/SupportedUIAFlowsLoader.tsx +++ b/src/app/components/SupportedUIAFlowsLoader.tsx @@ -1,5 +1,5 @@ -import { ReactNode } from 'react'; -import { UIAFlow } from '$types/matrix-sdk'; +import { type ReactNode } from 'react'; +import { type UIAFlow } from '$types/matrix-sdk'; import { useSupportedUIAFlows } from '$hooks/useUIAFlows'; export function SupportedUIAFlowsLoader({ diff --git a/src/app/components/SwipeableChatWrapper.tsx b/src/app/components/SwipeableChatWrapper.tsx index 2eba0d011..0811c54be 100644 --- a/src/app/components/SwipeableChatWrapper.tsx +++ b/src/app/components/SwipeableChatWrapper.tsx @@ -1,16 +1,16 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { motion, useMotionValue, useSpring } from 'framer-motion'; import { useDrag } from '@use-gesture/react'; import { useAtomValue } from 'jotai'; import { settingsAtom, RightSwipeAction } from '$state/settings'; import { mobileOrTablet } from '$utils/user-agent'; -interface SwipeableChatWrapperProps { +type SwipeableChatWrapperProps = { children: ReactNode; onOpenSidebar?: () => void; onOpenMembers?: () => void; onReply?: () => void; -} +}; export function SwipeableChatWrapper({ children, diff --git a/src/app/components/SwipeableMessageWrapper.tsx b/src/app/components/SwipeableMessageWrapper.tsx index 4b219355c..e697fc24b 100644 --- a/src/app/components/SwipeableMessageWrapper.tsx +++ b/src/app/components/SwipeableMessageWrapper.tsx @@ -1,6 +1,6 @@ import { useMotionValue, useSpring, useTransform, motion } from 'framer-motion'; import { useDrag } from '@use-gesture/react'; -import { ReactNode, useMemo, useState } from 'react'; +import { type ReactNode, useMemo, useState } from 'react'; import { useAtomValue } from 'jotai'; import { config, Icon, Icons } from 'folds'; import { mobileOrTablet } from '$utils/user-agent'; diff --git a/src/app/components/SwipeableOverlayWrapper.tsx b/src/app/components/SwipeableOverlayWrapper.tsx index 15521bb12..6cc488204 100644 --- a/src/app/components/SwipeableOverlayWrapper.tsx +++ b/src/app/components/SwipeableOverlayWrapper.tsx @@ -1,15 +1,15 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { motion, useMotionValue, useSpring } from 'framer-motion'; import { useDrag } from '@use-gesture/react'; import { useAtomValue } from 'jotai'; import { settingsAtom } from '$state/settings'; import { mobileOrTablet } from '$utils/user-agent'; -interface SwipeableOverlayWrapperProps { +type SwipeableOverlayWrapperProps = { children: ReactNode; onClose: () => void; direction: 'left' | 'right'; -} +}; export function SwipeableOverlayWrapper({ children, diff --git a/src/app/components/UIAFlowOverlay.tsx b/src/app/components/UIAFlowOverlay.tsx index 85211a0f0..57ea24be9 100644 --- a/src/app/components/UIAFlowOverlay.tsx +++ b/src/app/components/UIAFlowOverlay.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Overlay, OverlayBackdrop, diff --git a/src/app/components/UseStateProvider.tsx b/src/app/components/UseStateProvider.tsx index 21e5b3cad..cb62b7903 100644 --- a/src/app/components/UseStateProvider.tsx +++ b/src/app/components/UseStateProvider.tsx @@ -1,4 +1,4 @@ -import { Dispatch, ReactElement, SetStateAction, useState } from 'react'; +import { type Dispatch, type ReactElement, type SetStateAction, useState } from 'react'; type UseStateProviderProps = { initial: T | (() => T); diff --git a/src/app/components/UserRoomProfileRenderer.tsx b/src/app/components/UserRoomProfileRenderer.tsx index 97489d277..f2e0780ae 100644 --- a/src/app/components/UserRoomProfileRenderer.tsx +++ b/src/app/components/UserRoomProfileRenderer.tsx @@ -1,7 +1,7 @@ import { Menu, PopOut, toRem } from 'folds'; import FocusTrap from 'focus-trap-react'; import { useCloseUserRoomProfile, useUserRoomProfileState } from '$state/hooks/userRoomProfile'; -import { UserRoomProfileState } from '$state/userRoomProfile'; +import { type UserRoomProfileState } from '$state/userRoomProfile'; import { useAllJoinedRoomsSet, useGetRoom } from '$hooks/useGetRoom'; import { stopPropagation } from '$utils/keyboard'; import { SpaceProvider } from '$hooks/useSpace'; diff --git a/src/app/components/create-room/AdditionalCreatorInput.tsx b/src/app/components/create-room/AdditionalCreatorInput.tsx index f362122a0..3ee2b331c 100644 --- a/src/app/components/create-room/AdditionalCreatorInput.tsx +++ b/src/app/components/create-room/AdditionalCreatorInput.tsx @@ -10,7 +10,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Scroll, Text, toRem, @@ -18,9 +18,9 @@ import { import { isKeyHotkey } from 'is-hotkey'; import FocusTrap from 'focus-trap-react'; import { - ChangeEventHandler, - KeyboardEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type KeyboardEventHandler, + type MouseEventHandler, useMemo, useState, } from 'react'; @@ -28,7 +28,7 @@ import { getMxIdLocalPart, getMxIdServer, isUserId } from '$utils/matrix'; import { useDirectUsers } from '$hooks/useDirectUsers'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { stopPropagation } from '$utils/keyboard'; -import { useAsyncSearch, UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; +import { useAsyncSearch, type UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; import { highlightText, makeHighlightRegex } from '$plugins/react-custom-html-parser'; import { SettingTile } from '$components/setting-tile'; @@ -44,7 +44,7 @@ export const useAdditionalCreators = (defaultCreators?: string[]) => { setAdditionalCreators((creators) => { const creatorsSet = new Set(creators); creatorsSet.add(userId); - return Array.from(creatorsSet); + return [...creatorsSet]; }); }; @@ -52,7 +52,7 @@ export const useAdditionalCreators = (defaultCreators?: string[]) => { setAdditionalCreators((creators) => { const creatorsSet = new Set(creators); creatorsSet.delete(userId); - return Array.from(creatorsSet); + return [...creatorsSet]; }); }; diff --git a/src/app/components/create-room/CreateRoomAccessSelector.tsx b/src/app/components/create-room/CreateRoomAccessSelector.tsx index 6dd049835..f20c03955 100644 --- a/src/app/components/create-room/CreateRoomAccessSelector.tsx +++ b/src/app/components/create-room/CreateRoomAccessSelector.tsx @@ -1,4 +1,4 @@ -import { Box, Text, Icon, Icons, config, IconSrc } from 'folds'; +import { Box, Text, Icon, Icons, config, type IconSrc } from 'folds'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { CreateRoomAccess } from './types'; diff --git a/src/app/components/create-room/CreateRoomAliasInput.tsx b/src/app/components/create-room/CreateRoomAliasInput.tsx index 0460717a5..570edb0bb 100644 --- a/src/app/components/create-room/CreateRoomAliasInput.tsx +++ b/src/app/components/create-room/CreateRoomAliasInput.tsx @@ -1,6 +1,6 @@ import { - FormEventHandler, - KeyboardEventHandler, + type FormEventHandler, + type KeyboardEventHandler, useCallback, useEffect, useRef, @@ -12,7 +12,7 @@ import { isKeyHotkey } from 'is-hotkey'; import { getMxIdServer } from '$utils/matrix'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { replaceSpaceWithDash } from '$utils/common'; -import { AsyncState, AsyncStatus, useAsync } from '$hooks/useAsyncCallback'; +import { type AsyncState, AsyncStatus, useAsync } from '$hooks/useAsyncCallback'; import { useDebounce } from '$hooks/useDebounce'; export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) { diff --git a/src/app/components/create-room/CreateRoomTypeSelector.tsx b/src/app/components/create-room/CreateRoomTypeSelector.tsx index 42468a03d..c47bb4f0c 100644 --- a/src/app/components/create-room/CreateRoomTypeSelector.tsx +++ b/src/app/components/create-room/CreateRoomTypeSelector.tsx @@ -1,4 +1,4 @@ -import { Box, Text, Icon, Icons, config, IconSrc } from 'folds'; +import { Box, Text, Icon, Icons, config, type IconSrc } from 'folds'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { BetaNoticeBadge } from '$components/BetaNoticeBadge'; diff --git a/src/app/components/create-room/RoomVersionSelector.tsx b/src/app/components/create-room/RoomVersionSelector.tsx index a86b1d2df..c3d7fe3af 100644 --- a/src/app/components/create-room/RoomVersionSelector.tsx +++ b/src/app/components/create-room/RoomVersionSelector.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useState } from 'react'; +import { type MouseEventHandler, useState } from 'react'; import { Box, Button, @@ -8,7 +8,7 @@ import { Icons, Menu, PopOut, - RectCords, + type RectCords, Text, toRem, } from 'folds'; diff --git a/src/app/components/create-room/utils.ts b/src/app/components/create-room/utils.ts index d03226823..2a47a1b13 100644 --- a/src/app/components/create-room/utils.ts +++ b/src/app/components/create-room/utils.ts @@ -1,11 +1,11 @@ import { - ICreateRoomOpts, - ICreateRoomStateEvent, + type ICreateRoomOpts, + type ICreateRoomStateEvent, JoinRule, - MatrixClient, + type MatrixClient, RestrictedAllowType, - Room, - RoomJoinRulesEventContent, + type Room, + type RoomJoinRulesEventContent, } from '$types/matrix-sdk'; import { RoomType, StateEvent } from '$types/matrix/room'; import { getViaServers } from '$plugins/via-servers'; diff --git a/src/app/components/cutout-card/CutoutCard.tsx b/src/app/components/cutout-card/CutoutCard.tsx index 5a6c2ae3d..24860264f 100644 --- a/src/app/components/cutout-card/CutoutCard.tsx +++ b/src/app/components/cutout-card/CutoutCard.tsx @@ -1,4 +1,4 @@ -import { as, ContainerColor as TContainerColor } from 'folds'; +import { as, type ContainerColor as TContainerColor } from 'folds'; import classNames from 'classnames'; import { ContainerColor } from '$styles/ContainerColor.css'; import * as css from './CutoutCard.css'; diff --git a/src/app/components/editor/Editor.tsx b/src/app/components/editor/Editor.tsx index 7595fd46d..32d332c4e 100644 --- a/src/app/components/editor/Editor.tsx +++ b/src/app/components/editor/Editor.tsx @@ -1,8 +1,8 @@ /* eslint-disable no-param-reassign */ import { - ClipboardEventHandler, - KeyboardEventHandler, - ReactNode, + type ClipboardEventHandler, + type KeyboardEventHandler, + type ReactNode, forwardRef, useCallback, useEffect, @@ -11,21 +11,21 @@ import { useState, } from 'react'; import { Box, Scroll, Text } from 'folds'; -import { Descendant, Editor, Node, createEditor } from 'slate'; +import { type Descendant, type Editor, Node, createEditor } from 'slate'; import { Slate, Editable, withReact, - RenderLeafProps, - RenderElementProps, - RenderPlaceholderProps, + type RenderLeafProps, + type RenderElementProps, + type RenderPlaceholderProps, ReactEditor, } from 'slate-react'; import { withHistory } from 'slate-history'; import { mobileOrTablet } from '$utils/user-agent'; import { BlockType } from './types'; import { RenderElement, RenderLeaf } from './Elements'; -import { CustomElement } from './slate'; +import type { CustomElement } from './slate'; import * as css from './Editor.css'; import { toggleKeyboardShortcut } from './keyboard'; diff --git a/src/app/components/editor/Elements.tsx b/src/app/components/editor/Elements.tsx index 7e3500810..8663f019b 100644 --- a/src/app/components/editor/Elements.tsx +++ b/src/app/components/editor/Elements.tsx @@ -1,7 +1,7 @@ import { Scroll, Text } from 'folds'; import { - RenderElementProps, - RenderLeafProps, + type RenderElementProps, + type RenderLeafProps, useFocused, useSelected, useSlate, @@ -15,7 +15,7 @@ import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { nicknamesAtom } from '$state/nicknames'; import { BlockType } from './types'; import { getBeginCommand } from './utils'; -import { CommandElement, EmoticonElement, LinkElement, MentionElement } from './slate'; +import type { CommandElement, EmoticonElement, LinkElement, MentionElement } from './slate'; // Put this at the start and end of an inline component to work around this Chromium bug: // https://bugs.chromium.org/p/chromium/issues/detail?id=1249405 diff --git a/src/app/components/editor/Toolbar.tsx b/src/app/components/editor/Toolbar.tsx index 62cd9356a..90441431f 100644 --- a/src/app/components/editor/Toolbar.tsx +++ b/src/app/components/editor/Toolbar.tsx @@ -6,18 +6,18 @@ import { Icon, IconButton, Icons, - IconSrc, + type IconSrc, Line, Menu, PopOut, - RectCords, + type RectCords, Scroll, Text, Tooltip, TooltipProvider, toRem, } from 'folds'; -import { MouseEventHandler, ReactNode, useState } from 'react'; +import { type MouseEventHandler, type ReactNode, useState } from 'react'; import { ReactEditor, useSlate } from 'slate-react'; import { isMacOS } from '$utils/user-agent'; import { KeySymbol } from '$utils/key-symbol'; @@ -25,7 +25,7 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { stopPropagation } from '$utils/keyboard'; import { floatingToolbar } from '$styles/overrides/Composer.css'; -import { HeadingLevel } from './slate'; +import type { HeadingLevel } from './slate'; import { BlockType, MarkType } from './types'; import * as css from './Editor.css'; import { diff --git a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx index fdb4b570d..63091cb29 100644 --- a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx +++ b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx @@ -1,11 +1,11 @@ -import { ReactNode, useEffect, useRef, useState } from 'react'; +import { type ReactNode, useEffect, useRef, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; import { Header, Menu, Scroll, config } from 'folds'; import { preventScrollWithArrowKey, stopPropagation } from '$utils/keyboard'; import { useAlive } from '$hooks/useAlive'; -import { Editor } from 'slate'; +import { type Editor } from 'slate'; import { ReactEditor } from 'slate-react'; import * as css from './AutocompleteMenu.css'; import { BaseAutocompleteMenu } from './BaseAutocompleteMenu'; diff --git a/src/app/components/editor/autocomplete/AutocompleteNotice.tsx b/src/app/components/editor/autocomplete/AutocompleteNotice.tsx index a9b21e8cf..859d8048f 100644 --- a/src/app/components/editor/autocomplete/AutocompleteNotice.tsx +++ b/src/app/components/editor/autocomplete/AutocompleteNotice.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Header, Menu } from 'folds'; import { BaseAutocompleteMenu } from './BaseAutocompleteMenu'; import * as css from './AutocompleteMenu.css'; diff --git a/src/app/components/editor/autocomplete/BaseAutocompleteMenu.tsx b/src/app/components/editor/autocomplete/BaseAutocompleteMenu.tsx index 9f1bf316f..9d6de8d03 100644 --- a/src/app/components/editor/autocomplete/BaseAutocompleteMenu.tsx +++ b/src/app/components/editor/autocomplete/BaseAutocompleteMenu.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import * as css from './AutocompleteMenu.css'; type BaseAutocompleteMenuProps = { diff --git a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx index b4c8e8f46..e6317e040 100644 --- a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx @@ -1,25 +1,25 @@ -import { KeyboardEvent as ReactKeyboardEvent, useEffect, useMemo } from 'react'; -import { Editor } from 'slate'; +import { type KeyboardEvent as ReactKeyboardEvent, useEffect, useMemo } from 'react'; +import { type Editor } from 'slate'; import { ReactEditor } from 'slate-react'; import { Box, MenuItem, Text, toRem } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; +import { type UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; import { onTabPress } from '$utils/keyboard'; import { useRecentEmoji } from '$hooks/useRecentEmoji'; import { useRelevantImagePacks } from '$hooks/useImagePacks'; -import { IEmoji, emojis } from '$plugins/emoji'; +import { type IEmoji, emojis } from '$plugins/emoji'; import { useKeyDown } from '$hooks/useKeyDown'; import { mxcUrlToHttp } from '$utils/matrix'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; -import { ImageUsage, PackImageReader } from '$plugins/custom-emoji'; +import { ImageUsage, type PackImageReader } from '$plugins/custom-emoji'; import { getEmoticonSearchStr } from '$plugins/utils'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { createEmoticonElement, moveCursor, replaceWithElement } from '$components/editor/utils'; import { AutocompleteMenu } from './AutocompleteMenu'; -import { AutocompleteQuery } from './autocompleteQuery'; +import { type AutocompleteQuery } from './autocompleteQuery'; type EmoticonCompleteHandler = (key: string, shortcode: string) => void; diff --git a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx index 9ad67aad4..d7799d0b9 100644 --- a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx @@ -1,14 +1,14 @@ -import { KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect } from 'react'; -import { Editor } from 'slate'; +import { type KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect } from 'react'; +import { type Editor } from 'slate'; import { ReactEditor } from 'slate-react'; import { Avatar, Icon, Icons, MenuItem, Text } from 'folds'; -import { JoinRule, MatrixClient } from '$types/matrix-sdk'; +import { JoinRule, type MatrixClient } from '$types/matrix-sdk'; import { useAtomValue } from 'jotai'; import { getDirectRoomAvatarUrl } from '$utils/room'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { getMxIdServer, isRoomAlias } from '$utils/matrix'; -import { UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; +import { type UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; import { onTabPress } from '$utils/keyboard'; import { useKeyDown } from '$hooks/useKeyDown'; import { mDirectAtom } from '$state/mDirectList'; @@ -18,7 +18,7 @@ import { RoomAvatar, RoomIcon } from '$components/room-avatar'; import { getViaServers } from '$plugins/via-servers'; import { createMentionElement, moveCursor, replaceWithElement } from '$components/editor/utils'; import { AutocompleteMenu } from './AutocompleteMenu'; -import { AutocompleteQuery } from './autocompleteQuery'; +import { type AutocompleteQuery } from './autocompleteQuery'; type MentionAutoCompleteHandler = (roomAliasOrId: string, name: string) => void; diff --git a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx index e38838070..afd1c909d 100644 --- a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx @@ -1,12 +1,16 @@ -import { useEffect, KeyboardEvent as ReactKeyboardEvent } from 'react'; -import { Editor } from 'slate'; +import { useEffect, type KeyboardEvent as ReactKeyboardEvent } from 'react'; +import { type Editor } from 'slate'; import { ReactEditor } from 'slate-react'; import { Avatar, Icon, Icons, MenuItem, Text } from 'folds'; -import { MatrixClient, Room, RoomMember } from '$types/matrix-sdk'; +import { type MatrixClient, type Room, type RoomMember } from '$types/matrix-sdk'; import { useRoomMembers } from '$hooks/useRoomMembers'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { SearchItemStrGetter, UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; +import { + type SearchItemStrGetter, + type UseAsyncSearchOptions, + useAsyncSearch, +} from '$hooks/useAsyncSearch'; import { onTabPress } from '$utils/keyboard'; import { useKeyDown } from '$hooks/useKeyDown'; import { getMxIdLocalPart, getMxIdServer, isUserId } from '$utils/matrix'; @@ -18,7 +22,7 @@ import { useAtomValue } from 'jotai'; import { nicknamesAtom } from '$state/nicknames'; import { createMentionElement, moveCursor, replaceWithElement } from '$components/editor/utils'; import { AutocompleteMenu } from './AutocompleteMenu'; -import { AutocompleteQuery } from './autocompleteQuery'; +import { type AutocompleteQuery } from './autocompleteQuery'; type MentionAutoCompleteHandler = (userId: string, name: string) => void; diff --git a/src/app/components/editor/autocomplete/autocompleteQuery.ts b/src/app/components/editor/autocomplete/autocompleteQuery.ts index 5b8294afb..b1c86df9c 100644 --- a/src/app/components/editor/autocomplete/autocompleteQuery.ts +++ b/src/app/components/editor/autocomplete/autocompleteQuery.ts @@ -1,4 +1,4 @@ -import { BaseRange, Editor } from 'slate'; +import { type BaseRange, Editor } from 'slate'; export enum AutocompletePrefix { RoomMention = '#', diff --git a/src/app/components/editor/input.ts b/src/app/components/editor/input.ts index 27a6b68fc..7d3d00167 100644 --- a/src/app/components/editor/input.ts +++ b/src/app/components/editor/input.ts @@ -1,6 +1,6 @@ -import { Descendant, Text } from 'slate'; +import { type Descendant, Text } from 'slate'; import parse from 'html-dom-parser'; -import { ChildNode, Element, isText, isTag } from 'domhandler'; +import { type ChildNode, type Element, isText, isTag } from 'domhandler'; import { sanitizeCustomHtml } from '$utils/sanitize'; import { @@ -11,7 +11,7 @@ import { } from '$plugins/matrix-to'; import { escapeMarkdownInlineSequences, escapeMarkdownBlockSequences } from '$plugins/markdown'; import { BlockType, MarkType } from './types'; -import { +import type { BlockQuoteElement, CodeBlockElement, CodeLineElement, @@ -450,7 +450,7 @@ export const domToEditorInput = ( return; } - if (node.name.match(/^h[123456]$/)) { + if (/^h[123456]$/.test(node.name)) { appendLine(); children.push(parseHeadingNode(node, processText)); return; diff --git a/src/app/components/editor/keyboard.ts b/src/app/components/editor/keyboard.ts index a920a9050..ff9c796ed 100644 --- a/src/app/components/editor/keyboard.ts +++ b/src/app/components/editor/keyboard.ts @@ -1,5 +1,5 @@ import { isKeyHotkey } from 'is-hotkey'; -import { KeyboardEvent } from 'react'; +import { type KeyboardEvent } from 'react'; import { Editor, Element as SlateElement, Range, Transforms } from 'slate'; import { isAnyMarkActive, isBlockActive, removeAllMark, toggleBlock, toggleMark } from './utils'; import { BlockType, MarkType } from './types'; diff --git a/src/app/components/editor/output.ts b/src/app/components/editor/output.ts index 383678e1a..1d48c40a0 100644 --- a/src/app/components/editor/output.ts +++ b/src/app/components/editor/output.ts @@ -1,5 +1,5 @@ -import { Descendant, Editor, Text } from 'slate'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type Descendant, type Editor, Text } from 'slate'; +import { type MatrixClient } from '$types/matrix-sdk'; import { sanitizeText } from '$utils/sanitize'; import { parseBlockMD, @@ -10,7 +10,7 @@ import { import { findAndReplace } from '$utils/findAndReplace'; import { sanitizeForRegex } from '$utils/regex'; import { isUserId } from '$utils/matrix'; -import { CustomElement } from './slate'; +import type { CustomElement } from './slate'; import { BlockType } from './types'; export type OutputOptions = { @@ -130,8 +130,9 @@ export const toMatrixCustomHTML = ( // strip nicknames if needed if (opts.stripNickname && opts.nickNameReplacement) { - opts.nickNameReplacement?.keys().forEach((key) => { - const replacement = opts.nickNameReplacement!.get(key) ?? ''; + const { nickNameReplacement } = opts; + [...nickNameReplacement.keys()].forEach((key) => { + const replacement = nickNameReplacement.get(key) ?? ''; line = line.replaceAll(key, replacement); }); } @@ -215,7 +216,7 @@ export const toPlainText = ( if (Text.isText(node)) { if (stripNickname && nickNameReplacement) { let { text } = node; - nickNameReplacement?.keys().forEach((key) => { + [...nickNameReplacement.keys()].forEach((key) => { const replacement = nickNameReplacement.get(key) ?? ''; text = text.replaceAll(key, replacement); }); diff --git a/src/app/components/editor/slate.d.ts b/src/app/components/editor/slate.d.ts index 81d690fbc..255c459ae 100644 --- a/src/app/components/editor/slate.d.ts +++ b/src/app/components/editor/slate.d.ts @@ -1,7 +1,7 @@ -import { BaseEditor } from 'slate'; -import { ReactEditor } from 'slate-react'; -import { HistoryEditor } from 'slate-history'; -import { BlockType } from './types'; +import { type BaseEditor } from 'slate'; +import { type ReactEditor } from 'slate-react'; +import { type HistoryEditor } from 'slate-history'; +import { type BlockType } from './types'; export type HeadingLevel = 1 | 2 | 3; diff --git a/src/app/components/editor/utils.ts b/src/app/components/editor/utils.ts index 85ff437a1..29df43ade 100644 --- a/src/app/components/editor/utils.ts +++ b/src/app/components/editor/utils.ts @@ -1,6 +1,15 @@ -import { BasePoint, BaseRange, Editor, Element, Point, Range, Text, Transforms } from 'slate'; -import { BlockType, MarkType } from './types'; import { + type BasePoint, + type BaseRange, + Editor, + Element, + Point, + Range, + Text, + Transforms, +} from 'slate'; +import { BlockType, MarkType } from './types'; +import type { CommandElement, EmoticonElement, FormattedText, @@ -25,7 +34,7 @@ export const isMarkActive = (editor: Editor, format: MarkType) => { export const isAnyMarkActive = (editor: Editor) => { const marks = Editor.marks(editor); - return marks && !!ALL_MARK_TYPE.find((type) => marks[type] === true); + return marks && ALL_MARK_TYPE.some((type) => marks[type] === true); }; export const toggleMark = (editor: Editor, format: MarkType) => { @@ -220,10 +229,10 @@ export const moveCursor = (editor: Editor, withSpace?: boolean) => { Transforms.collapse(editor, { edge: 'end' }); }; -interface PointUntilCharOptions { +type PointUntilCharOptions = { match: (char: string) => boolean; reverse?: boolean; -} +}; export const getPointUntilChar = ( editor: Editor, cursorPoint: BasePoint, diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx index 193caf71d..214ebbc49 100644 --- a/src/app/components/emoji-board/EmojiBoard.tsx +++ b/src/app/components/emoji-board/EmojiBoard.tsx @@ -1,9 +1,9 @@ import { - ChangeEventHandler, - FocusEventHandler, - MouseEventHandler, - ReactNode, - RefObject, + type ChangeEventHandler, + type FocusEventHandler, + type MouseEventHandler, + type ReactNode, + type RefObject, useCallback, useEffect, useMemo, @@ -12,22 +12,22 @@ import { import { Box, config, Icons, Scroll } from 'folds'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; -import { Room } from '$types/matrix-sdk'; -import { atom, PrimitiveAtom, useAtom, useSetAtom } from 'jotai'; +import { type Room } from '$types/matrix-sdk'; +import { atom, type PrimitiveAtom, useAtom, useSetAtom } from 'jotai'; import { useVirtualizer } from '@tanstack/react-virtual'; -import { IEmoji, emojiGroups, emojis } from '$plugins/emoji'; +import { type IEmoji, emojiGroups, emojis } from '$plugins/emoji'; import { preventScrollWithArrowKey, stopPropagation } from '$utils/keyboard'; import { useRelevantImagePacks } from '$hooks/useImagePacks'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useRecentEmoji } from '$hooks/useRecentEmoji'; import { isUserId, mxcUrlToHttp } from '$utils/matrix'; import { editableActiveElement, targetFromEvent } from '$utils/dom'; -import { useAsyncSearch, UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; +import { useAsyncSearch, type UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; import { useDebounce } from '$hooks/useDebounce'; import { useThrottle } from '$hooks/useThrottle'; import { addRecentEmoji } from '$plugins/recent-emoji'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; -import { ImagePack, ImageUsage, PackImageReader } from '$plugins/custom-emoji'; +import { type ImagePack, ImageUsage, type PackImageReader } from '$plugins/custom-emoji'; import { getEmoticonSearchStr } from '$plugins/utils'; import { VirtualTile } from '$components/virtualizer'; import { useSetting } from '$state/hooks/settings'; @@ -43,7 +43,7 @@ import { NoStickerPacks, createPreviewDataAtom, Preview, - PreviewData, + type PreviewData, EmojiItem, StickerItem, CustomEmojiItem, @@ -417,8 +417,8 @@ export function EmojiBoard({ const searchList = useMemo(() => { let list: Array = []; - list = list.concat(imagePacks.flatMap((pack) => pack.getImages(usage))); - if (emojiTab) list = list.concat(emojis); + list = [...list, ...imagePacks.flatMap((pack) => pack.getImages(usage))]; + if (emojiTab) list = [...list, ...emojis]; return list; }, [emojiTab, usage, imagePacks]); diff --git a/src/app/components/emoji-board/components/Group.tsx b/src/app/components/emoji-board/components/Group.tsx index 293ac6145..97836e9d7 100644 --- a/src/app/components/emoji-board/components/Group.tsx +++ b/src/app/components/emoji-board/components/Group.tsx @@ -1,5 +1,5 @@ import { as, Box, Text } from 'folds'; -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import classNames from 'classnames'; import * as css from './styles.css'; diff --git a/src/app/components/emoji-board/components/Item.tsx b/src/app/components/emoji-board/components/Item.tsx index c593b5db0..895316d18 100644 --- a/src/app/components/emoji-board/components/Item.tsx +++ b/src/app/components/emoji-board/components/Item.tsx @@ -1,9 +1,9 @@ import { Box } from 'folds'; -import { MatrixClient } from '$types/matrix-sdk'; -import { PackImageReader } from '$plugins/custom-emoji'; -import { IEmoji } from '$plugins/emoji'; +import { type MatrixClient } from '$types/matrix-sdk'; +import { type PackImageReader } from '$plugins/custom-emoji'; +import { type IEmoji } from '$plugins/emoji'; import { mxcUrlToHttp } from '$utils/matrix'; -import { EmojiItemInfo, EmojiType } from '$components/emoji-board/types'; +import { type EmojiItemInfo, EmojiType } from '$components/emoji-board/types'; import * as css from './styles.css'; const ANIMATED_MIME_TYPES = new Set(['image/gif', 'image/apng']); diff --git a/src/app/components/emoji-board/components/Layout.tsx b/src/app/components/emoji-board/components/Layout.tsx index 286006896..8453f494b 100644 --- a/src/app/components/emoji-board/components/Layout.tsx +++ b/src/app/components/emoji-board/components/Layout.tsx @@ -1,5 +1,5 @@ import { as, Box, Line } from 'folds'; -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import classNames from 'classnames'; import * as css from './styles.css'; diff --git a/src/app/components/emoji-board/components/Preview.tsx b/src/app/components/emoji-board/components/Preview.tsx index 1f7cef289..65444a1cf 100644 --- a/src/app/components/emoji-board/components/Preview.tsx +++ b/src/app/components/emoji-board/components/Preview.tsx @@ -1,5 +1,5 @@ import { Box, Text } from 'folds'; -import { Atom, atom, useAtomValue } from 'jotai'; +import { type Atom, atom, useAtomValue } from 'jotai'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { mxcUrlToHttp } from '$utils/matrix'; diff --git a/src/app/components/emoji-board/components/SearchInput.tsx b/src/app/components/emoji-board/components/SearchInput.tsx index c3c80e939..8896d8ad9 100644 --- a/src/app/components/emoji-board/components/SearchInput.tsx +++ b/src/app/components/emoji-board/components/SearchInput.tsx @@ -1,4 +1,4 @@ -import { ChangeEventHandler, useRef } from 'react'; +import { type ChangeEventHandler, useRef } from 'react'; import { Input, Chip, Icon, Icons, Text } from 'folds'; import { mobileOrTablet } from '$utils/user-agent'; diff --git a/src/app/components/emoji-board/components/Sidebar.tsx b/src/app/components/emoji-board/components/Sidebar.tsx index 70bf87902..d03c9a4a4 100644 --- a/src/app/components/emoji-board/components/Sidebar.tsx +++ b/src/app/components/emoji-board/components/Sidebar.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Scroll, @@ -9,7 +9,7 @@ import { Text, IconButton, Icon, - IconSrc, + type IconSrc, Icons, } from 'folds'; import classNames from 'classnames'; diff --git a/src/app/components/emoji-board/components/Tabs.tsx b/src/app/components/emoji-board/components/Tabs.tsx index bef95c145..63546d651 100644 --- a/src/app/components/emoji-board/components/Tabs.tsx +++ b/src/app/components/emoji-board/components/Tabs.tsx @@ -1,4 +1,4 @@ -import { CSSProperties } from 'react'; +import { type CSSProperties } from 'react'; import { Badge, Box, Text } from 'folds'; import { EmojiBoardTab } from '$components/emoji-board/types'; diff --git a/src/app/components/emoji-board/useEmojiGroupIcons.ts b/src/app/components/emoji-board/useEmojiGroupIcons.ts index 0b59fbcd9..623885149 100644 --- a/src/app/components/emoji-board/useEmojiGroupIcons.ts +++ b/src/app/components/emoji-board/useEmojiGroupIcons.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { IconSrc, Icons } from 'folds'; +import { type IconSrc, Icons } from 'folds'; import { EmojiGroupId } from '$plugins/emoji'; diff --git a/src/app/components/event-history/EventHistory.tsx b/src/app/components/event-history/EventHistory.tsx index f08fd6c0e..acb8f1a48 100644 --- a/src/app/components/event-history/EventHistory.tsx +++ b/src/app/components/event-history/EventHistory.tsx @@ -14,7 +14,7 @@ import { color, config, } from 'folds'; -import { IContent, MatrixEvent, Room } from '$types/matrix-sdk'; +import { type IContent, type MatrixEvent, type Room } from '$types/matrix-sdk'; import { getMemberDisplayName } from '$utils/room'; import { getMxIdLocalPart } from '$utils/matrix'; import { useMatrixClient } from '$hooks/useMatrixClient'; @@ -30,8 +30,8 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { useCallback, useMemo, useState } from 'react'; import { getReactCustomHtmlParser, LINKIFY_OPTS } from '$plugins/react-custom-html-parser'; -import { Opts as LinkifyOpts } from 'linkifyjs'; -import { HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; +import { type HTMLReactParserOptions } from 'html-react-parser'; import { useSpoilerClickHandler } from '$hooks/useSpoilerClickHandler'; import { modalAtom, ModalType } from '$state/modal'; import { roomIdToReplyDraftAtomFamily } from '$state/room/roomInputDrafts'; diff --git a/src/app/components/event-readers/EventReaders.tsx b/src/app/components/event-readers/EventReaders.tsx index 6bb1737b4..b94efbb3e 100644 --- a/src/app/components/event-readers/EventReaders.tsx +++ b/src/app/components/event-readers/EventReaders.tsx @@ -12,7 +12,7 @@ import { as, config, } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useRoomEventReaders } from '$hooks/useRoomEventReaders'; import { getMemberDisplayName } from '$utils/room'; import { getMxIdLocalPart } from '$utils/matrix'; diff --git a/src/app/components/image-pack-view/ImagePackContent.tsx b/src/app/components/image-pack-view/ImagePackContent.tsx index 6f56d70f3..41132718d 100644 --- a/src/app/components/image-pack-view/ImagePackContent.tsx +++ b/src/app/components/image-pack-view/ImagePackContent.tsx @@ -1,18 +1,18 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { as, Box, Text, config, Button, Menu, Spinner } from 'folds'; import { - ImagePack, - ImageUsage, - PackContent, - PackImage, + type ImagePack, + type ImageUsage, + type PackContent, + type PackImage, PackImageReader, packMetaEqual, PackMetaReader, } from '$plugins/custom-emoji'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { useFilePicker } from '$hooks/useFilePicker'; -import { UploadSuccess } from '$state/upload'; -import { getImageInfo, TUploadContent } from '$utils/matrix'; +import { type UploadSuccess } from '$state/upload'; +import { getImageInfo, type TUploadContent } from '$utils/matrix'; import { getImageFileUrl, loadImageElement, renameFile } from '$utils/dom'; import { replaceSpaceWithDash, suffixRename } from '$utils/common'; import { getFileNameWithoutExt } from '$utils/mimeTypes'; @@ -39,7 +39,7 @@ export const ImagePackContent = as<'div', ImagePackContentProps>( const [savedMeta, setSavedMeta] = useState(); const currentMeta = savedMeta ?? imagePack.meta; - const images = useMemo(() => Array.from(imagePack.images.collection.values()), [imagePack]); + const images = useMemo(() => [...imagePack.images.collection.values()], [imagePack]); const [files, setFiles] = useState([]); const [uploadedImages, setUploadedImages] = useState([]); const [imagesEditing, setImagesEditing] = useState>(new Set()); @@ -50,11 +50,9 @@ export const ImagePackContent = as<'div', ImagePackContentProps>( (shortcode: string): boolean => { const hasInPack = imagePack.images.collection.has(shortcode); if (hasInPack) return true; - const hasInUploaded = - uploadedImages.find((img) => img.shortcode === shortcode) !== undefined; + const hasInUploaded = uploadedImages.some((img) => img.shortcode === shortcode); if (hasInUploaded) return true; - const hasInSaved = - Array.from(savedImages).find(([, img]) => img.shortcode === shortcode) !== undefined; + const hasInSaved = [...savedImages].some(([, img]) => img.shortcode === shortcode); return hasInSaved; }, [imagePack, savedImages, uploadedImages] diff --git a/src/app/components/image-pack-view/ImagePackView.tsx b/src/app/components/image-pack-view/ImagePackView.tsx index 304275e54..b79fd7b3b 100644 --- a/src/app/components/image-pack-view/ImagePackView.tsx +++ b/src/app/components/image-pack-view/ImagePackView.tsx @@ -1,5 +1,5 @@ import { Box, IconButton, Text, Icon, Icons, Scroll, Chip } from 'folds'; -import { PackAddress } from '$plugins/custom-emoji'; +import { type PackAddress } from '$plugins/custom-emoji'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { Page, PageHeader, PageContent } from '$components/page'; import { RoomImagePack } from './RoomImagePack'; diff --git a/src/app/components/image-pack-view/ImageTile.tsx b/src/app/components/image-pack-view/ImageTile.tsx index 2070f00e4..ee7e74fbe 100644 --- a/src/app/components/image-pack-view/ImageTile.tsx +++ b/src/app/components/image-pack-view/ImageTile.tsx @@ -1,10 +1,10 @@ -import { FormEventHandler, ReactNode, useMemo, useState } from 'react'; +import { type FormEventHandler, type ReactNode, useMemo, useState } from 'react'; import { Badge, Box, Button, Chip, Icon, Icons, Input, Text } from 'folds'; import { mxcUrlToHttp } from '$utils/matrix'; -import { ImageUsage, imageUsageEqual, PackImageReader } from '$plugins/custom-emoji'; +import { type ImageUsage, imageUsageEqual, PackImageReader } from '$plugins/custom-emoji'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useObjectURL } from '$hooks/useObjectURL'; -import { createUploadAtom, TUploadAtom } from '$state/upload'; +import { createUploadAtom, type TUploadAtom } from '$state/upload'; import { replaceSpaceWithDash } from '$utils/common'; import { SettingTile } from '$components/setting-tile'; import * as css from './style.css'; diff --git a/src/app/components/image-pack-view/PackMeta.tsx b/src/app/components/image-pack-view/PackMeta.tsx index 77e8a42f5..a45f75b49 100644 --- a/src/app/components/image-pack-view/PackMeta.tsx +++ b/src/app/components/image-pack-view/PackMeta.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useMemo, useState } from 'react'; +import { type FormEventHandler, useCallback, useMemo, useState } from 'react'; import { Box, Text, @@ -21,7 +21,7 @@ import { LINKIFY_OPTS } from '$plugins/react-custom-html-parser'; import { ContainerColor } from '$styles/ContainerColor.css'; import { useFilePicker } from '$hooks/useFilePicker'; import { useObjectURL } from '$hooks/useObjectURL'; -import { createUploadAtom, UploadSuccess } from '$state/upload'; +import { createUploadAtom, type UploadSuccess } from '$state/upload'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { PackMetaReader } from '$plugins/custom-emoji'; import { CompactUploadCardRenderer } from '$components/upload-card'; diff --git a/src/app/components/image-pack-view/RoomImagePack.tsx b/src/app/components/image-pack-view/RoomImagePack.tsx index 01f0f7dd7..1feb08e62 100644 --- a/src/app/components/image-pack-view/RoomImagePack.tsx +++ b/src/app/components/image-pack-view/RoomImagePack.tsx @@ -1,8 +1,8 @@ import { useCallback, useMemo } from 'react'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { usePowerLevels } from '$hooks/usePowerLevels'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { ImagePack, PackContent } from '$plugins/custom-emoji'; +import { ImagePack, type PackContent } from '$plugins/custom-emoji'; import { StateEvent } from '$types/matrix/room'; import { useRoomImagePack } from '$hooks/useImagePacks'; import { randomStr } from '$utils/common'; diff --git a/src/app/components/image-pack-view/UsageSwitcher.tsx b/src/app/components/image-pack-view/UsageSwitcher.tsx index 201a40aaa..4b022dc69 100644 --- a/src/app/components/image-pack-view/UsageSwitcher.tsx +++ b/src/app/components/image-pack-view/UsageSwitcher.tsx @@ -1,5 +1,16 @@ -import { MouseEventHandler, useMemo, useState } from 'react'; -import { Box, Button, config, Icon, Icons, Menu, MenuItem, PopOut, RectCords, Text } from 'folds'; +import { type MouseEventHandler, useMemo, useState } from 'react'; +import { + Box, + Button, + config, + Icon, + Icons, + Menu, + MenuItem, + PopOut, + type RectCords, + Text, +} from 'folds'; import FocusTrap from 'focus-trap-react'; import { ImageUsage } from '$plugins/custom-emoji'; import { stopPropagation } from '$utils/keyboard'; diff --git a/src/app/components/image-pack-view/UserImagePack.tsx b/src/app/components/image-pack-view/UserImagePack.tsx index a723b83ab..f16b34d1d 100644 --- a/src/app/components/image-pack-view/UserImagePack.tsx +++ b/src/app/components/image-pack-view/UserImagePack.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo } from 'react'; -import { ImagePack, PackContent } from '$plugins/custom-emoji'; +import { ImagePack, type PackContent } from '$plugins/custom-emoji'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { AccountDataEvent } from '$types/matrix/accountData'; import { useUserImagePack } from '$hooks/useImagePacks'; diff --git a/src/app/components/info-card/InfoCard.tsx b/src/app/components/info-card/InfoCard.tsx index 6070479ca..44761abba 100644 --- a/src/app/components/info-card/InfoCard.tsx +++ b/src/app/components/info-card/InfoCard.tsx @@ -1,5 +1,5 @@ -import { Box, ContainerColor, Text } from 'folds'; -import { ReactNode } from 'react'; +import { Box, type ContainerColor, Text } from 'folds'; +import { type ReactNode } from 'react'; import classNames from 'classnames'; import { BreakWord } from '$styles/Text.css'; import { ContainerColor as ContainerClr } from '$styles/ContainerColor.css'; diff --git a/src/app/components/invite-user-prompt/InviteUserPrompt.tsx b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx index c8fbabef5..ef7bf65e5 100644 --- a/src/app/components/invite-user-prompt/InviteUserPrompt.tsx +++ b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx @@ -1,7 +1,7 @@ import { - ChangeEventHandler, - FormEventHandler, - KeyboardEventHandler, + type ChangeEventHandler, + type FormEventHandler, + type KeyboardEventHandler, useCallback, useMemo, useRef, @@ -29,14 +29,14 @@ import { Scroll, MenuItem, } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { isKeyHotkey } from 'is-hotkey'; import FocusTrap from 'focus-trap-react'; import { stopPropagation } from '$utils/keyboard'; import { useDirectUsers } from '$hooks/useDirectUsers'; import { getMxIdLocalPart, getMxIdServer, isUserId } from '$utils/matrix'; import { Membership } from '$types/matrix/room'; -import { useAsyncSearch, UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; +import { useAsyncSearch, type UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; import { highlightText, makeHighlightRegex } from '$plugins/react-custom-html-parser'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/join-address-prompt/JoinAddressPrompt.tsx b/src/app/components/join-address-prompt/JoinAddressPrompt.tsx index d6a6478d2..956bf669b 100644 --- a/src/app/components/join-address-prompt/JoinAddressPrompt.tsx +++ b/src/app/components/join-address-prompt/JoinAddressPrompt.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useState } from 'react'; +import { type FormEventHandler, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { Dialog, diff --git a/src/app/components/knock-room-prompt/KnockRoomPrompt.tsx b/src/app/components/knock-room-prompt/KnockRoomPrompt.tsx index a7948e9ab..1d0e8b4cf 100644 --- a/src/app/components/knock-room-prompt/KnockRoomPrompt.tsx +++ b/src/app/components/knock-room-prompt/KnockRoomPrompt.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, FormEventHandler } from 'react'; +import { useCallback, useEffect, type FormEventHandler } from 'react'; import FocusTrap from 'focus-trap-react'; import { Dialog, @@ -17,7 +17,7 @@ import { Button, Spinner, } from 'folds'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; diff --git a/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx b/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx index 36cdd89de..de80ab323 100644 --- a/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx +++ b/src/app/components/leave-room-prompt/LeaveRoomPrompt.tsx @@ -16,7 +16,7 @@ import { Button, Spinner, } from 'folds'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { stopPropagation } from '$utils/keyboard'; diff --git a/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx b/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx index 5bee7af26..96345bc0c 100644 --- a/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx +++ b/src/app/components/leave-space-prompt/LeaveSpacePrompt.tsx @@ -16,7 +16,7 @@ import { Button, Spinner, } from 'folds'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { stopPropagation } from '$utils/keyboard'; diff --git a/src/app/components/media/Image.tsx b/src/app/components/media/Image.tsx index 29990e99e..0fc4e5f33 100644 --- a/src/app/components/media/Image.tsx +++ b/src/app/components/media/Image.tsx @@ -1,4 +1,4 @@ -import { ImgHTMLAttributes, forwardRef } from 'react'; +import { type ImgHTMLAttributes, forwardRef } from 'react'; import classNames from 'classnames'; import * as css from './media.css'; diff --git a/src/app/components/media/MediaControls.tsx b/src/app/components/media/MediaControls.tsx index 2360b9f96..ceb7d6c04 100644 --- a/src/app/components/media/MediaControls.tsx +++ b/src/app/components/media/MediaControls.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, as } from 'folds'; export type MediaControlProps = { diff --git a/src/app/components/media/Video.tsx b/src/app/components/media/Video.tsx index c28efcaa3..2d65fb2f3 100644 --- a/src/app/components/media/Video.tsx +++ b/src/app/components/media/Video.tsx @@ -1,5 +1,6 @@ -import { VideoHTMLAttributes, forwardRef, useEffect, useRef } from 'react'; +import { type VideoHTMLAttributes, forwardRef, useEffect, useRef } from 'react'; import classNames from 'classnames'; +import { getMediaVolume, setMediaVolume } from '$state/mediaVolume'; import * as css from './media.css'; export const Video = forwardRef>( @@ -9,8 +10,6 @@ export const Video = forwardRef(null); useEffect(() => { - const stored = localStorage.getItem(MEDIA_VOLUME_KEY); - if (innerRef.current && stored !== null) { - const parsed = parseFloat(stored); - if (!Number.isNaN(parsed)) innerRef.current.volume = parsed; - } + const volume = getMediaVolume(); + if (innerRef.current && volume !== undefined) innerRef.current.volume = volume; }, []); return ( @@ -30,7 +26,7 @@ export function PersistedVolumeVideo({ {...props} ref={innerRef} onVolumeChange={(e) => { - localStorage.setItem(MEDIA_VOLUME_KEY, String((e.target as HTMLVideoElement).volume)); + setMediaVolume((e.target as HTMLVideoElement).volume); onVolumeChange?.(e); }} /> diff --git a/src/app/components/member-tile/MemberTile.tsx b/src/app/components/member-tile/MemberTile.tsx index 9af782eef..a983ab9f7 100644 --- a/src/app/components/member-tile/MemberTile.tsx +++ b/src/app/components/member-tile/MemberTile.tsx @@ -1,6 +1,6 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { as, Avatar, Box, Icon, Icons, Text } from 'folds'; -import { MatrixClient, Room, RoomMember } from '$types/matrix-sdk'; +import { type MatrixClient, type Room, type RoomMember } from '$types/matrix-sdk'; import { getMemberDisplayName } from '$utils/room'; import { getMxIdLocalPart } from '$utils/matrix'; import { useSableCosmetics } from '$hooks/useSableCosmetics'; diff --git a/src/app/components/message/FileHeader.tsx b/src/app/components/message/FileHeader.tsx index 36d8adeb1..c800c4a08 100644 --- a/src/app/components/message/FileHeader.tsx +++ b/src/app/components/message/FileHeader.tsx @@ -1,6 +1,6 @@ import { Badge, Box, Icon, IconButton, Icons, Spinner, Text, as, toRem } from 'folds'; -import { ReactNode, useCallback } from 'react'; -import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; +import { type ReactNode, useCallback } from 'react'; +import { type EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; import FileSaver from 'file-saver'; import { mimeTypeToExt } from '$utils/mimeTypes'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index 6d1a63cce..6d85a4bb7 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -1,19 +1,19 @@ -import { CSSProperties, ReactNode, useMemo } from 'react'; +import { type CSSProperties, type ReactNode, useMemo } from 'react'; import { Box, Chip, Icon, Icons, Text, toRem } from 'folds'; -import { IContent } from '$types/matrix-sdk'; +import { type IContent } from '$types/matrix-sdk'; import { JUMBO_EMOJI_REG, URL_REG } from '$utils/regex'; import { trimReplyFromBody } from '$utils/room'; import { - IAudioContent, - IAudioInfo, - IEncryptedFile, - IFileContent, - IFileInfo, - IImageContent, - IImageInfo, - IThumbnailContent, - IVideoContent, - IVideoInfo, + type IAudioContent, + type IAudioInfo, + type IEncryptedFile, + type IFileContent, + type IFileInfo, + type IImageContent, + type IImageInfo, + type IThumbnailContent, + type IVideoContent, + type IVideoInfo, MATRIX_SPOILER_PROPERTY_NAME, MATRIX_SPOILER_REASON_PROPERTY_NAME, } from '$types/matrix/common'; @@ -21,7 +21,7 @@ import { FALLBACK_MIMETYPE, getBlobSafeMimeType } from '$utils/mimeTypes'; import { parseGeoUri, scaleYDimension } from '$utils/common'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; -import { PerMessageProfileBeeperFormat } from '$hooks/usePerMessageProfile'; +import { type PerMessageProfileBeeperFormat } from '$hooks/usePerMessageProfile'; import { Attachment, AttachmentBox, AttachmentContent, AttachmentHeader } from './attachment'; import { FileHeader, FileDownloadButton } from './FileHeader'; import { diff --git a/src/app/components/message/Reaction.tsx b/src/app/components/message/Reaction.tsx index 155de11f4..f411adb24 100644 --- a/src/app/components/message/Reaction.tsx +++ b/src/app/components/message/Reaction.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { Box, Icon, Icons, Text, as } from 'folds'; import classNames from 'classnames'; -import { MatrixClient, MatrixEvent, Room } from '$types/matrix-sdk'; +import { type MatrixClient, type MatrixEvent, type Room } from '$types/matrix-sdk'; import { getHexcodeForEmoji, getShortcodeFor } from '$plugins/emoji'; import { getMemberDisplayName } from '$utils/room'; import { eventWithShortcode, getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix'; diff --git a/src/app/components/message/RenderBody.tsx b/src/app/components/message/RenderBody.tsx index ad027db22..cfa3c031d 100644 --- a/src/app/components/message/RenderBody.tsx +++ b/src/app/components/message/RenderBody.tsx @@ -1,8 +1,8 @@ -import { MouseEventHandler, useEffect, useState } from 'react'; -import parse, { HTMLReactParserOptions } from 'html-react-parser'; +import { type MouseEventHandler, useEffect, useState } from 'react'; +import parse, { type HTMLReactParserOptions } from 'html-react-parser'; import Linkify from 'linkify-react'; -import { Opts } from 'linkifyjs'; -import { PopOut, RectCords, Text, Tooltip, TooltipProvider, toRem } from 'folds'; +import { type Opts } from 'linkifyjs'; +import { PopOut, type RectCords, Text, Tooltip, TooltipProvider, toRem } from 'folds'; import { sanitizeCustomHtml } from '$utils/sanitize'; import { highlightText, scaleSystemEmoji } from '$plugins/react-custom-html-parser'; import { useRoomAbbreviationsContext } from '$hooks/useRoomAbbreviations'; diff --git a/src/app/components/message/Reply.test.tsx b/src/app/components/message/Reply.test.tsx index 1d57a4645..a72441dfa 100644 --- a/src/app/components/message/Reply.test.tsx +++ b/src/app/components/message/Reply.test.tsx @@ -1,5 +1,7 @@ import { render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; +import type * as JotaiModule from 'jotai'; +import type * as RoomModule from '$utils/room'; import { Reply } from './Reply'; const { mockUseRoomEvent, mockInvalidateQueries } = vi.hoisted(() => ({ @@ -14,7 +16,7 @@ vi.mock('@tanstack/react-query', () => ({ })); vi.mock('jotai', async (importActual) => { - const actual = await importActual(); + const actual = await importActual(); return { ...actual, useAtomValue: () => ({}), @@ -65,7 +67,7 @@ vi.mock('$features/settings/useSettingsLinkBaseUrl', () => ({ })); vi.mock('$utils/room', async (importActual) => { - const actual = await importActual(); + const actual = await importActual(); return { ...actual, getMemberDisplayName: () => 'Alice', diff --git a/src/app/components/message/Reply.tsx b/src/app/components/message/Reply.tsx index 331ef043a..d3614ac4e 100644 --- a/src/app/components/message/Reply.tsx +++ b/src/app/components/message/Reply.tsx @@ -1,6 +1,11 @@ -import { Box, Chip, Icon, IconSrc, Icons, Text, as, color, toRem } from 'folds'; -import { EventTimelineSet, IMentions, Room, SessionMembershipData } from '$types/matrix-sdk'; -import { MouseEventHandler, ReactNode, useCallback, useMemo } from 'react'; +import { Box, Chip, Icon, type IconSrc, Icons, Text, as, color, toRem } from 'folds'; +import { + type EventTimelineSet, + type IMentions, + type Room, + type SessionMembershipData, +} from '$types/matrix-sdk'; +import { type MouseEventHandler, type ReactNode, useCallback, useMemo } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import classNames from 'classnames'; import parse from 'html-react-parser'; diff --git a/src/app/components/message/Time.tsx b/src/app/components/message/Time.tsx index dc190d93d..844a2f1b7 100644 --- a/src/app/components/message/Time.tsx +++ b/src/app/components/message/Time.tsx @@ -1,4 +1,4 @@ -import { ComponentProps } from 'react'; +import { type ComponentProps } from 'react'; import { Text, as, Tooltip, TooltipProvider } from 'folds'; import { timeDayMonYear, diff --git a/src/app/components/message/attachment/Attachment.css.ts b/src/app/components/message/attachment/Attachment.css.ts index dad95f616..213ac1066 100644 --- a/src/app/components/message/attachment/Attachment.css.ts +++ b/src/app/components/message/attachment/Attachment.css.ts @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css'; -import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; +import { type RecipeVariants, recipe } from '@vanilla-extract/recipes'; import { DefaultReset, color, config, toRem } from 'folds'; export const Attachment = recipe({ diff --git a/src/app/components/message/content/AudioContent.tsx b/src/app/components/message/content/AudioContent.tsx index fb0f2bef9..871918fd9 100644 --- a/src/app/components/message/content/AudioContent.tsx +++ b/src/app/components/message/content/AudioContent.tsx @@ -1,13 +1,13 @@ /* eslint-disable jsx-a11y/media-has-caption */ -import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; +import { type ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import { Badge, Chip, Icon, IconButton, Icons, ProgressBar, Spinner, Text, toRem } from 'folds'; -import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; +import { type EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; import { Range } from 'react-range'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; -import { IAudioInfo } from '$types/matrix/common'; +import { type IAudioInfo } from '$types/matrix/common'; import { - PlayTimeCallback, + type PlayTimeCallback, useMediaLoading, useMediaPlay, useMediaPlayTimeCallback, @@ -18,7 +18,7 @@ import { useThrottle } from '$hooks/useThrottle'; import { secondsToMinutesAndSeconds } from '$utils/common'; import { decryptFile, downloadEncryptedMedia, downloadMedia, mxcUrlToHttp } from '$utils/matrix'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; -import { MEDIA_VOLUME_KEY } from '$components/media'; +import { getMediaVolume, setMediaVolume } from '$state/mediaVolume'; const PLAY_TIME_THROTTLE_OPS = { wait: 500, @@ -62,11 +62,8 @@ export function AudioContent({ const audioRef = useRef(null); useEffect(() => { - const stored = localStorage.getItem(MEDIA_VOLUME_KEY); - if (audioRef.current && stored !== null) { - const parsed = parseFloat(stored); - if (!Number.isNaN(parsed)) audioRef.current.volume = parsed; - } + const volume = getMediaVolume(); + if (audioRef.current && volume !== undefined) audioRef.current.volume = volume; }, []); const [currentTime, setCurrentTime] = useState(0); @@ -225,7 +222,7 @@ export function AudioContent({ autoPlay ref={audioRef} onVolumeChange={(e) => { - localStorage.setItem(MEDIA_VOLUME_KEY, String((e.target as HTMLAudioElement).volume)); + setMediaVolume((e.target as HTMLAudioElement).volume); }} > {srcState.status === AsyncStatus.Success && } diff --git a/src/app/components/message/content/EventContent.tsx b/src/app/components/message/content/EventContent.tsx index 35449e1ba..bcdd8cb63 100644 --- a/src/app/components/message/content/EventContent.tsx +++ b/src/app/components/message/content/EventContent.tsx @@ -1,5 +1,5 @@ -import { Box, Icon, IconSrc } from 'folds'; -import { ReactNode } from 'react'; +import { Box, Icon, type IconSrc } from 'folds'; +import { type ReactNode } from 'react'; import { MessageLayout } from '$state/settings'; import { BubbleLayout, CompactLayout, ModernLayout } from '$components/message/layout'; diff --git a/src/app/components/message/content/FileContent.tsx b/src/app/components/message/content/FileContent.tsx index af802d46b..89c50ec52 100644 --- a/src/app/components/message/content/FileContent.tsx +++ b/src/app/components/message/content/FileContent.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useCallback, useState } from 'react'; +import { type ReactNode, useCallback, useState } from 'react'; import { Box, Button, @@ -15,9 +15,9 @@ import { as, } from 'folds'; import FileSaver from 'file-saver'; -import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; +import { type EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; import FocusTrap from 'focus-trap-react'; -import { IFileInfo } from '$types/matrix/common'; +import { type IFileInfo } from '$types/matrix/common'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { bytesToSize } from '$utils/common'; diff --git a/src/app/components/message/content/ImageContent.tsx b/src/app/components/message/content/ImageContent.tsx index ad51d2a4f..ce53650c9 100644 --- a/src/app/components/message/content/ImageContent.tsx +++ b/src/app/components/message/content/ImageContent.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useCallback, useEffect, useState } from 'react'; +import { type ReactNode, useCallback, useEffect, useState } from 'react'; import { Badge, Box, @@ -22,8 +22,8 @@ import { import classNames from 'classnames'; import { BlurhashCanvas } from 'react-blurhash'; import FocusTrap from 'focus-trap-react'; -import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; -import { IImageInfo, MATRIX_BLUR_HASH_PROPERTY_NAME } from '$types/matrix/common'; +import { type EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; +import { type IImageInfo, MATRIX_BLUR_HASH_PROPERTY_NAME } from '$types/matrix/common'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { bytesToSize } from '$utils/common'; diff --git a/src/app/components/message/content/ThumbnailContent.tsx b/src/app/components/message/content/ThumbnailContent.tsx index 2d0510bed..15ec34020 100644 --- a/src/app/components/message/content/ThumbnailContent.tsx +++ b/src/app/components/message/content/ThumbnailContent.tsx @@ -1,5 +1,5 @@ -import { ReactNode, useCallback, useEffect } from 'react'; -import { IThumbnailContent } from '$types/matrix/common'; +import { type ReactNode, useCallback, useEffect } from 'react'; +import { type IThumbnailContent } from '$types/matrix/common'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { decryptFile, downloadEncryptedMedia, mxcUrlToHttp } from '$utils/matrix'; diff --git a/src/app/components/message/content/VideoContent.tsx b/src/app/components/message/content/VideoContent.tsx index 71613c598..6fbb42a04 100644 --- a/src/app/components/message/content/VideoContent.tsx +++ b/src/app/components/message/content/VideoContent.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useCallback, useEffect, useState } from 'react'; +import { type ReactNode, useCallback, useEffect, useState } from 'react'; import { Badge, Box, @@ -17,10 +17,10 @@ import { } from 'folds'; import classNames from 'classnames'; import { BlurhashCanvas } from 'react-blurhash'; -import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; +import { type EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; import { - IThumbnailContent, - IVideoInfo, + type IThumbnailContent, + type IVideoInfo, MATRIX_BLUR_HASH_PROPERTY_NAME, } from '$types/matrix/common'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/message/layout/Bubble.tsx b/src/app/components/message/layout/Bubble.tsx index 21120da47..9ffeabe2d 100644 --- a/src/app/components/message/layout/Bubble.tsx +++ b/src/app/components/message/layout/Bubble.tsx @@ -1,6 +1,6 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import classNames from 'classnames'; -import { Box, ContainerColor, as, color } from 'folds'; +import { Box, type ContainerColor, as, color } from 'folds'; import * as css from './layout.css'; type BubbleArrowProps = { diff --git a/src/app/components/message/layout/Compact.tsx b/src/app/components/message/layout/Compact.tsx index e9015ab96..5e4b7fece 100644 --- a/src/app/components/message/layout/Compact.tsx +++ b/src/app/components/message/layout/Compact.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, as } from 'folds'; import * as css from './layout.css'; diff --git a/src/app/components/message/layout/Modern.tsx b/src/app/components/message/layout/Modern.tsx index e73e93013..a2c6273a6 100644 --- a/src/app/components/message/layout/Modern.tsx +++ b/src/app/components/message/layout/Modern.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, as } from 'folds'; import * as css from './layout.css'; diff --git a/src/app/components/message/layout/layout.css.ts b/src/app/components/message/layout/layout.css.ts index ee423ca2c..3dca06a0d 100644 --- a/src/app/components/message/layout/layout.css.ts +++ b/src/app/components/message/layout/layout.css.ts @@ -1,5 +1,5 @@ import { createVar, keyframes, style, styleVariants } from '@vanilla-extract/css'; -import { recipe, RecipeVariants } from '@vanilla-extract/recipes'; +import { recipe, type RecipeVariants } from '@vanilla-extract/recipes'; import { DefaultReset, color, config, toRem } from 'folds'; export const StickySection = style({ diff --git a/src/app/components/message/modals/MessageDelete.tsx b/src/app/components/message/modals/MessageDelete.tsx index 666895102..12c3b3546 100644 --- a/src/app/components/message/modals/MessageDelete.tsx +++ b/src/app/components/message/modals/MessageDelete.tsx @@ -1,5 +1,5 @@ -import { FormEventHandler, MouseEvent, useCallback, useEffect } from 'react'; -import { Room, MatrixEvent } from '$types/matrix-sdk'; +import { type FormEventHandler, type MouseEvent, useCallback, useEffect } from 'react'; +import { type Room, type MatrixEvent } from '$types/matrix-sdk'; import { useSetAtom } from 'jotai'; import { Box, diff --git a/src/app/components/message/modals/MessageEditHistory.tsx b/src/app/components/message/modals/MessageEditHistory.tsx index 9581aa419..5057c82a2 100644 --- a/src/app/components/message/modals/MessageEditHistory.tsx +++ b/src/app/components/message/modals/MessageEditHistory.tsx @@ -1,5 +1,5 @@ -import { MouseEvent } from 'react'; -import { Room, MatrixEvent } from '$types/matrix-sdk'; +import { type MouseEvent } from 'react'; +import { type Room, type MatrixEvent } from '$types/matrix-sdk'; import { useSetAtom } from 'jotai'; import { MenuItem, Icon, Icons, Text } from 'folds'; import { getEventEdits } from '$utils/room'; diff --git a/src/app/components/message/modals/MessageForward.tsx b/src/app/components/message/modals/MessageForward.tsx index 133dc940e..91ca02909 100644 --- a/src/app/components/message/modals/MessageForward.tsx +++ b/src/app/components/message/modals/MessageForward.tsx @@ -17,7 +17,7 @@ import { as, } from 'folds'; import { useAtomValue, useSetAtom } from 'jotai'; -import { JoinRule, MatrixEvent, Room } from '$types/matrix-sdk'; +import { JoinRule, type MatrixEvent, type Room } from '$types/matrix-sdk'; import { useEffect, useMemo, useState } from 'react'; import { allRoomsAtom } from '$state/room-list/roomList'; import { useAllJoinedRoomsSet, useGetRoom } from '$hooks/useGetRoom'; diff --git a/src/app/components/message/modals/MessageReactions.tsx b/src/app/components/message/modals/MessageReactions.tsx index f2fa7a5bd..04a32b511 100644 --- a/src/app/components/message/modals/MessageReactions.tsx +++ b/src/app/components/message/modals/MessageReactions.tsx @@ -1,5 +1,5 @@ -import { MouseEvent } from 'react'; -import { Room, Relations } from '$types/matrix-sdk'; +import { type MouseEvent } from 'react'; +import { type Room, type Relations } from '$types/matrix-sdk'; import { useSetAtom } from 'jotai'; import { Icon, Icons, Text, MenuItem } from 'folds'; import { modalAtom, ModalType } from '$state/modal'; diff --git a/src/app/components/message/modals/MessageReadRecipts.tsx b/src/app/components/message/modals/MessageReadRecipts.tsx index 6642627c0..7252e696d 100644 --- a/src/app/components/message/modals/MessageReadRecipts.tsx +++ b/src/app/components/message/modals/MessageReadRecipts.tsx @@ -1,5 +1,5 @@ -import { MouseEvent } from 'react'; -import { Room } from '$types/matrix-sdk'; +import { type MouseEvent } from 'react'; +import { type Room } from '$types/matrix-sdk'; import { useSetAtom } from 'jotai'; import { MenuItem, Icon, Icons, Text } from 'folds'; import { modalAtom, ModalType } from '$state/modal'; diff --git a/src/app/components/message/modals/MessageReport.tsx b/src/app/components/message/modals/MessageReport.tsx index 71cf03cc5..beae33e1e 100644 --- a/src/app/components/message/modals/MessageReport.tsx +++ b/src/app/components/message/modals/MessageReport.tsx @@ -1,5 +1,5 @@ -import { FormEventHandler, MouseEvent, useCallback, useEffect } from 'react'; -import { Room, MatrixEvent } from '$types/matrix-sdk'; +import { type FormEventHandler, type MouseEvent, useCallback, useEffect } from 'react'; +import { type Room, type MatrixEvent } from '$types/matrix-sdk'; import { useSetAtom } from 'jotai'; import { Box, diff --git a/src/app/components/message/modals/MessageSource.tsx b/src/app/components/message/modals/MessageSource.tsx index eff8e1e58..1deb28ebf 100644 --- a/src/app/components/message/modals/MessageSource.tsx +++ b/src/app/components/message/modals/MessageSource.tsx @@ -1,5 +1,5 @@ -import { MouseEvent } from 'react'; -import { Room, MatrixEvent } from '$types/matrix-sdk'; +import { type MouseEvent } from 'react'; +import { type Room, type MatrixEvent } from '$types/matrix-sdk'; import { useSetAtom } from 'jotai'; import { MenuItem, Icon, Icons, Text } from 'folds'; import { TextViewer } from '$components/text-viewer'; diff --git a/src/app/components/message/placeholder/CompactPlaceholder.tsx b/src/app/components/message/placeholder/CompactPlaceholder.tsx index c6e6dda00..961cf165e 100644 --- a/src/app/components/message/placeholder/CompactPlaceholder.tsx +++ b/src/app/components/message/placeholder/CompactPlaceholder.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { as, ContainerColor, toRem } from 'folds'; +import { as, type ContainerColor, toRem } from 'folds'; import { randomNumberBetween } from '$utils/common'; import { CompactLayout } from '$components/message/layout'; import { LinePlaceholder } from './LinePlaceholder'; diff --git a/src/app/components/message/placeholder/DefaultPlaceholder.tsx b/src/app/components/message/placeholder/DefaultPlaceholder.tsx index 444990ac6..d0a73733f 100644 --- a/src/app/components/message/placeholder/DefaultPlaceholder.tsx +++ b/src/app/components/message/placeholder/DefaultPlaceholder.tsx @@ -1,5 +1,5 @@ -import { CSSProperties, useMemo } from 'react'; -import { Avatar, Box, ContainerColor, as, color, toRem } from 'folds'; +import { type CSSProperties, useMemo } from 'react'; +import { Avatar, Box, type ContainerColor, as, color, toRem } from 'folds'; import { randomNumberBetween } from '$utils/common'; import { ModernLayout } from '$components/message/layout'; import { LinePlaceholder } from './LinePlaceholder'; diff --git a/src/app/components/message/placeholder/LinePlaceholder.css.ts b/src/app/components/message/placeholder/LinePlaceholder.css.ts index 34ad76a3e..bcf69b7e5 100644 --- a/src/app/components/message/placeholder/LinePlaceholder.css.ts +++ b/src/app/components/message/placeholder/LinePlaceholder.css.ts @@ -1,6 +1,6 @@ -import { ComplexStyleRule } from '@vanilla-extract/css'; -import { recipe, RecipeVariants } from '@vanilla-extract/recipes'; -import { ContainerColor, DefaultReset, color, config, toRem } from 'folds'; +import { type ComplexStyleRule } from '@vanilla-extract/css'; +import { recipe, type RecipeVariants } from '@vanilla-extract/recipes'; +import { type ContainerColor, DefaultReset, color, config, toRem } from 'folds'; const getVariant = (variant: ContainerColor): ComplexStyleRule => ({ backgroundColor: color[variant].Container, diff --git a/src/app/components/nav/NavCategory.tsx b/src/app/components/nav/NavCategory.tsx index 7285330f0..b03c0916a 100644 --- a/src/app/components/nav/NavCategory.tsx +++ b/src/app/components/nav/NavCategory.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { as } from 'folds'; import classNames from 'classnames'; import * as css from './styles.css'; diff --git a/src/app/components/nav/NavCategoryHeader.tsx b/src/app/components/nav/NavCategoryHeader.tsx index 1d4152fc9..1cbea5b10 100644 --- a/src/app/components/nav/NavCategoryHeader.tsx +++ b/src/app/components/nav/NavCategoryHeader.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import classNames from 'classnames'; import { Header, as } from 'folds'; import * as css from './styles.css'; diff --git a/src/app/components/nav/NavEmptyLayout.tsx b/src/app/components/nav/NavEmptyLayout.tsx index 25b69380b..cf21d6699 100644 --- a/src/app/components/nav/NavEmptyLayout.tsx +++ b/src/app/components/nav/NavEmptyLayout.tsx @@ -1,5 +1,5 @@ import { Box, config } from 'folds'; -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; export function NavEmptyCenter({ children }: { children: ReactNode }) { return ( diff --git a/src/app/components/nav/NavItem.tsx b/src/app/components/nav/NavItem.tsx index 68a967f6f..f3eefa4f5 100644 --- a/src/app/components/nav/NavItem.tsx +++ b/src/app/components/nav/NavItem.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import { ComponentProps, forwardRef } from 'react'; +import { type ComponentProps, forwardRef } from 'react'; import { Link } from 'react-router-dom'; import { as } from 'folds'; import * as css from './styles.css'; diff --git a/src/app/components/nav/NavItemContent.tsx b/src/app/components/nav/NavItemContent.tsx index eaf8f113e..4ca35cc0c 100644 --- a/src/app/components/nav/NavItemContent.tsx +++ b/src/app/components/nav/NavItemContent.tsx @@ -1,4 +1,4 @@ -import { ComponentProps } from 'react'; +import { type ComponentProps } from 'react'; import { Text, as } from 'folds'; import classNames from 'classnames'; import * as css from './styles.css'; diff --git a/src/app/components/nav/NavItemOptions.tsx b/src/app/components/nav/NavItemOptions.tsx index a735781e5..d43fc7ab0 100644 --- a/src/app/components/nav/NavItemOptions.tsx +++ b/src/app/components/nav/NavItemOptions.tsx @@ -1,4 +1,4 @@ -import { ComponentProps } from 'react'; +import { type ComponentProps } from 'react'; import { Box, as } from 'folds'; import classNames from 'classnames'; import * as css from './styles.css'; diff --git a/src/app/components/nav/styles.css.ts b/src/app/components/nav/styles.css.ts index 48b2a4d34..cd02a25f4 100644 --- a/src/app/components/nav/styles.css.ts +++ b/src/app/components/nav/styles.css.ts @@ -1,6 +1,14 @@ -import { ComplexStyleRule, createVar, style } from '@vanilla-extract/css'; -import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; -import { ContainerColor, DefaultReset, Disabled, RadiiVariant, color, config, toRem } from 'folds'; +import { type ComplexStyleRule, createVar, style } from '@vanilla-extract/css'; +import { type RecipeVariants, recipe } from '@vanilla-extract/recipes'; +import { + type ContainerColor, + DefaultReset, + Disabled, + RadiiVariant, + color, + config, + toRem, +} from 'folds'; export const NavCategory = style([ DefaultReset, diff --git a/src/app/components/notification-banner/NotificationBanner.tsx b/src/app/components/notification-banner/NotificationBanner.tsx index b574e3585..553080642 100644 --- a/src/app/components/notification-banner/NotificationBanner.tsx +++ b/src/app/components/notification-banner/NotificationBanner.tsx @@ -1,8 +1,8 @@ import { useAtom } from 'jotai'; -import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; +import { type ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import { Box, Icon, IconButton, Icons, Text } from 'folds'; import { createLogger } from '$utils/debug'; -import { inAppBannerAtom, InAppBannerNotification } from '$state/sessions'; +import { inAppBannerAtom, type InAppBannerNotification } from '$state/sessions'; import * as css from './NotificationBanner.css'; const log = createLogger('NotificationBanner'); @@ -65,7 +65,7 @@ function BannerItem({ notification, onDismiss }: BannerItemProps) { if (dismissedRef.current) return; dismissedRef.current = true; setDismissing(true); - dismissAnimTimerRef.current = setTimeout(() => onDismiss(notification.id), 200); + dismissAnimTimerRef.current = setTimeout(onDismiss, 200, notification.id); }, [notification.id, onDismiss]); // Auto-dismiss timer — only runs when not paused. diff --git a/src/app/components/page/Page.tsx b/src/app/components/page/Page.tsx index b481df637..87534b479 100644 --- a/src/app/components/page/Page.tsx +++ b/src/app/components/page/Page.tsx @@ -1,4 +1,4 @@ -import { ComponentProps, MutableRefObject, ReactNode } from 'react'; +import { type ComponentProps, type MutableRefObject, type ReactNode } from 'react'; import { Box, Header, Line, Scroll, Text, as } from 'folds'; import classNames from 'classnames'; import { ContainerColor } from '$styles/ContainerColor.css'; diff --git a/src/app/components/page/style.css.ts b/src/app/components/page/style.css.ts index bd14cd583..5241c11eb 100644 --- a/src/app/components/page/style.css.ts +++ b/src/app/components/page/style.css.ts @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css'; -import { recipe, RecipeVariants } from '@vanilla-extract/recipes'; +import { recipe, type RecipeVariants } from '@vanilla-extract/recipes'; import { DefaultReset, color, config, toRem } from 'folds'; export const PageNav = recipe({ diff --git a/src/app/components/password-input/PasswordInput.tsx b/src/app/components/password-input/PasswordInput.tsx index d1fdea246..0fce3feda 100644 --- a/src/app/components/password-input/PasswordInput.tsx +++ b/src/app/components/password-input/PasswordInput.tsx @@ -1,4 +1,4 @@ -import { ComponentProps, forwardRef } from 'react'; +import { type ComponentProps, forwardRef } from 'react'; import { Icon, IconButton, Input, config, Icons } from 'folds'; import { UseStateProvider } from '$components/UseStateProvider'; diff --git a/src/app/components/power/PowerIcon.tsx b/src/app/components/power/PowerIcon.tsx index f86331035..69bb3e90c 100644 --- a/src/app/components/power/PowerIcon.tsx +++ b/src/app/components/power/PowerIcon.tsx @@ -5,10 +5,25 @@ type PowerIconProps = css.PowerIconVariants & { iconSrc: string; name?: string; }; + +const ALLOWED_ICON_PROTOCOLS = new Set(['http:', 'https:']); + +function getSafeIconUrl(iconSrc: string): string | undefined { + try { + const parsed = new URL(iconSrc); + return ALLOWED_ICON_PROTOCOLS.has(parsed.protocol) ? parsed.href : undefined; + } catch { + return undefined; + } +} + export function PowerIcon({ size, iconSrc, name }: PowerIconProps) { - return JUMBO_EMOJI_REG.test(iconSrc) ? ( - {iconSrc} - ) : ( - {name} - ); + if (JUMBO_EMOJI_REG.test(iconSrc)) { + return {iconSrc}; + } + + const safeIconUrl = getSafeIconUrl(iconSrc); + if (!safeIconUrl) return null; + + return {name}; } diff --git a/src/app/components/power/PowerSelector.tsx b/src/app/components/power/PowerSelector.tsx index 23188aeae..5d6aa9d51 100644 --- a/src/app/components/power/PowerSelector.tsx +++ b/src/app/components/power/PowerSelector.tsx @@ -1,7 +1,7 @@ -import { forwardRef, MouseEventHandler, ReactNode, useState } from 'react'; +import { forwardRef, type MouseEventHandler, type ReactNode, useState } from 'react'; import FocusTrap from 'focus-trap-react'; -import { Box, config, Menu, MenuItem, PopOut, Scroll, Text, toRem, RectCords } from 'folds'; -import { getPowers, PowerLevelTags } from '$hooks/usePowerLevelTags'; +import { Box, config, Menu, MenuItem, PopOut, Scroll, Text, toRem, type RectCords } from 'folds'; +import { getPowers, type PowerLevelTags } from '$hooks/usePowerLevelTags'; import { stopPropagation } from '$utils/keyboard'; import { PowerColorBadge } from './PowerColorBadge'; diff --git a/src/app/components/power/style.css.ts b/src/app/components/power/style.css.ts index 60737f8cb..7a076df3a 100644 --- a/src/app/components/power/style.css.ts +++ b/src/app/components/power/style.css.ts @@ -1,5 +1,5 @@ import { createVar, style } from '@vanilla-extract/css'; -import { recipe, RecipeVariants } from '@vanilla-extract/recipes'; +import { recipe, type RecipeVariants } from '@vanilla-extract/recipes'; import { color, config, DefaultReset, toRem } from 'folds'; export const PowerColorBadge = style({ diff --git a/src/app/components/presence/Presence.tsx b/src/app/components/presence/Presence.tsx index e6ac463bb..8e3d3c7d1 100644 --- a/src/app/components/presence/Presence.tsx +++ b/src/app/components/presence/Presence.tsx @@ -3,14 +3,14 @@ import { Badge, Box, color, - ContainerColor, - MainColor, + type ContainerColor, + type MainColor, Text, Tooltip, TooltipProvider, toRem, } from 'folds'; -import { ReactNode, useId } from 'react'; +import { type ReactNode, useId } from 'react'; import { Presence, usePresenceLabel } from '$hooks/useUserPresence'; import * as css from './styles.css'; diff --git a/src/app/components/room-avatar/AvatarImage.tsx b/src/app/components/room-avatar/AvatarImage.tsx index 1e4cb0d0a..97c06bb48 100644 --- a/src/app/components/room-avatar/AvatarImage.tsx +++ b/src/app/components/room-avatar/AvatarImage.tsx @@ -1,5 +1,5 @@ import { AvatarImage as FoldsAvatarImage } from 'folds'; -import { ReactEventHandler, useState, useEffect } from 'react'; +import { type ReactEventHandler, useState, useEffect } from 'react'; import bgColorImg from '$utils/bgColorImg'; import { settingsAtom } from '$state/settings'; import { useSetting } from '$state/hooks/settings'; diff --git a/src/app/components/room-avatar/RoomAvatar.tsx b/src/app/components/room-avatar/RoomAvatar.tsx index 33f9f6881..f938822da 100644 --- a/src/app/components/room-avatar/RoomAvatar.tsx +++ b/src/app/components/room-avatar/RoomAvatar.tsx @@ -1,6 +1,6 @@ -import { JoinRule } from '$types/matrix-sdk'; +import { type JoinRule } from '$types/matrix-sdk'; import { AvatarFallback, Icon, Icons, color } from 'folds'; -import { ComponentProps, ReactNode, forwardRef, useEffect, useState } from 'react'; +import { type ComponentProps, type ReactNode, forwardRef, useEffect, useState } from 'react'; import { getRoomIconSrc } from '$utils/room'; import colorMXID from '$utils/colorMXID'; import * as css from './RoomAvatar.css'; diff --git a/src/app/components/room-card/RoomCard.tsx b/src/app/components/room-card/RoomCard.tsx index 7f9046d77..a507b684c 100644 --- a/src/app/components/room-card/RoomCard.tsx +++ b/src/app/components/room-card/RoomCard.tsx @@ -1,5 +1,5 @@ -import { ReactNode, useCallback, useRef, useState } from 'react'; -import { JoinRule, MatrixError, Room } from '$types/matrix-sdk'; +import { type ReactNode, useCallback, useRef, useState } from 'react'; +import { JoinRule, type MatrixError, type Room } from '$types/matrix-sdk'; import { Avatar, Badge, diff --git a/src/app/components/room-intro/RoomIntro.tsx b/src/app/components/room-intro/RoomIntro.tsx index 3ad39546f..aaf0698f6 100644 --- a/src/app/components/room-intro/RoomIntro.tsx +++ b/src/app/components/room-intro/RoomIntro.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useState } from 'react'; import { Avatar, Box, Button, Icon, Icons, Spinner, Text, as } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useAtomValue } from 'jotai'; -import { IRoomCreateContent, Membership, StateEvent } from '$types/matrix/room'; +import { type IRoomCreateContent, Membership, StateEvent } from '$types/matrix/room'; import { getMemberDisplayName, getStateEvent } from '$utils/room'; import { nicknamesAtom } from '$state/nicknames'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/scroll-top-container/ScrollTopContainer.tsx b/src/app/components/scroll-top-container/ScrollTopContainer.tsx index 4681e7baf..ecb204017 100644 --- a/src/app/components/scroll-top-container/ScrollTopContainer.tsx +++ b/src/app/components/scroll-top-container/ScrollTopContainer.tsx @@ -1,4 +1,4 @@ -import { RefObject, useCallback, useState } from 'react'; +import { type RefObject, useCallback, useState } from 'react'; import { Box, as } from 'folds'; import classNames from 'classnames'; import { diff --git a/src/app/components/sequence-card/SequenceCard.tsx b/src/app/components/sequence-card/SequenceCard.tsx index 810afd3da..41b528c9d 100644 --- a/src/app/components/sequence-card/SequenceCard.tsx +++ b/src/app/components/sequence-card/SequenceCard.tsx @@ -1,7 +1,7 @@ -import { ComponentProps } from 'react'; +import { type ComponentProps } from 'react'; import { Box, as } from 'folds'; import classNames from 'classnames'; -import { ContainerColor, ContainerColorVariants } from '$styles/ContainerColor.css'; +import { ContainerColor, type ContainerColorVariants } from '$styles/ContainerColor.css'; import * as css from './style.css'; export const SequenceCard = as< diff --git a/src/app/components/sequence-card/style.css.ts b/src/app/components/sequence-card/style.css.ts index 2665b46e7..17f02dcc3 100644 --- a/src/app/components/sequence-card/style.css.ts +++ b/src/app/components/sequence-card/style.css.ts @@ -1,5 +1,5 @@ import { createVar } from '@vanilla-extract/css'; -import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; +import { type RecipeVariants, recipe } from '@vanilla-extract/recipes'; import { config } from 'folds'; const outlinedWidth = createVar('0'); diff --git a/src/app/components/setting-menu-selector/SettingMenuSelector.tsx b/src/app/components/setting-menu-selector/SettingMenuSelector.tsx index 3e39dd243..e1a5834bb 100644 --- a/src/app/components/setting-menu-selector/SettingMenuSelector.tsx +++ b/src/app/components/setting-menu-selector/SettingMenuSelector.tsx @@ -8,7 +8,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Spinner, Text, } from 'folds'; diff --git a/src/app/components/setting-tile/SettingTile.tsx b/src/app/components/setting-tile/SettingTile.tsx index 9ee2696dc..4bd17151f 100644 --- a/src/app/components/setting-tile/SettingTile.tsx +++ b/src/app/components/setting-tile/SettingTile.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Icon, IconButton, Icons, Text } from 'folds'; import { BreakWord } from '$styles/Text.css'; import { buildSettingsLink } from '$features/settings/settingsLink'; diff --git a/src/app/components/sidebar/Sidebar.css.ts b/src/app/components/sidebar/Sidebar.css.ts index 2952a0f7d..063b83c4d 100644 --- a/src/app/components/sidebar/Sidebar.css.ts +++ b/src/app/components/sidebar/Sidebar.css.ts @@ -1,5 +1,5 @@ import { createVar, style } from '@vanilla-extract/css'; -import { recipe, RecipeVariants } from '@vanilla-extract/recipes'; +import { recipe, type RecipeVariants } from '@vanilla-extract/recipes'; import { color, config, DefaultReset, Disabled, FocusOutline, toRem } from 'folds'; import { ContainerColor } from '$styles/ContainerColor.css'; diff --git a/src/app/components/sidebar/SidebarContent.tsx b/src/app/components/sidebar/SidebarContent.tsx index a4546b196..6e7f62fc2 100644 --- a/src/app/components/sidebar/SidebarContent.tsx +++ b/src/app/components/sidebar/SidebarContent.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box } from 'folds'; type SidebarContentProps = { diff --git a/src/app/components/sidebar/SidebarItem.tsx b/src/app/components/sidebar/SidebarItem.tsx index b624bf0c9..b0af1ec13 100644 --- a/src/app/components/sidebar/SidebarItem.tsx +++ b/src/app/components/sidebar/SidebarItem.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import { as, Avatar, Text, Tooltip, TooltipProvider, toRem } from 'folds'; -import { ComponentProps, ReactNode, RefCallback } from 'react'; +import { type ComponentProps, type ReactNode, type RefCallback } from 'react'; import * as css from './Sidebar.css'; export const SidebarItem = as<'div', css.SidebarItemVariants>( diff --git a/src/app/components/sidebar/SidebarUnreadBadge.tsx b/src/app/components/sidebar/SidebarUnreadBadge.tsx index df1f5b7ee..4ed6ef639 100644 --- a/src/app/components/sidebar/SidebarUnreadBadge.tsx +++ b/src/app/components/sidebar/SidebarUnreadBadge.tsx @@ -1,4 +1,8 @@ -import { UnreadBadge, UnreadBadgeMode, resolveUnreadBadgeMode } from '$components/unread-badge'; +import { + UnreadBadge, + type UnreadBadgeMode, + resolveUnreadBadgeMode, +} from '$components/unread-badge'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { SidebarItemBadge } from './SidebarItem'; diff --git a/src/app/components/splash-screen/SplashScreen.tsx b/src/app/components/splash-screen/SplashScreen.tsx index 463fd66d6..71cada585 100644 --- a/src/app/components/splash-screen/SplashScreen.tsx +++ b/src/app/components/splash-screen/SplashScreen.tsx @@ -1,5 +1,5 @@ import { Box, Text } from 'folds'; -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import classNames from 'classnames'; import * as patternsCSS from '$styles/Patterns.css'; import * as css from './SplashScreen.css'; diff --git a/src/app/components/stacked-avatar/styles.css.ts b/src/app/components/stacked-avatar/styles.css.ts index 3bbf7271a..b7f279e2e 100644 --- a/src/app/components/stacked-avatar/styles.css.ts +++ b/src/app/components/stacked-avatar/styles.css.ts @@ -1,6 +1,6 @@ -import { ComplexStyleRule } from '@vanilla-extract/css'; -import { recipe, RecipeVariants } from '@vanilla-extract/recipes'; -import { color, config, ContainerColor, toRem } from 'folds'; +import { type ComplexStyleRule } from '@vanilla-extract/css'; +import { recipe, type RecipeVariants } from '@vanilla-extract/recipes'; +import { color, config, type ContainerColor, toRem } from 'folds'; const getVariant = (variant: ContainerColor): ComplexStyleRule => ({ outlineColor: color[variant].Container, diff --git a/src/app/components/telemetry-consent/TelemetryConsentBanner.tsx b/src/app/components/telemetry-consent/TelemetryConsentBanner.tsx index 5c1e90a08..6335cf7af 100644 --- a/src/app/components/telemetry-consent/TelemetryConsentBanner.tsx +++ b/src/app/components/telemetry-consent/TelemetryConsentBanner.tsx @@ -1,14 +1,11 @@ import { useEffect, useRef, useState } from 'react'; import { Box, Button, Icon, Icons, Text } from 'folds'; +import { isSentryDecided, setSentryEnabled } from '$state/sentryStorage'; import * as css from './TelemetryConsentBanner.css'; -const SENTRY_KEY = 'sable_sentry_enabled'; - export function TelemetryConsentBanner() { const isSentryConfigured = Boolean(import.meta.env.VITE_SENTRY_DSN); - const [visible, setVisible] = useState( - isSentryConfigured && localStorage.getItem(SENTRY_KEY) === null - ); + const [visible, setVisible] = useState(isSentryConfigured && !isSentryDecided()); const [dismissing, setDismissing] = useState(false); const dismissTimerRef = useRef | null>(null); @@ -22,14 +19,14 @@ export function TelemetryConsentBanner() { if (!visible) return null; const handleEnable = () => { - localStorage.setItem(SENTRY_KEY, 'true'); + setSentryEnabled(true); window.location.reload(); }; const handleDecline = () => { - localStorage.setItem(SENTRY_KEY, 'false'); + setSentryEnabled(false); setDismissing(true); - dismissTimerRef.current = setTimeout(() => setVisible(false), 220); + dismissTimerRef.current = setTimeout(setVisible, 220, false); }; return ( diff --git a/src/app/components/text-viewer/TextViewer.tsx b/src/app/components/text-viewer/TextViewer.tsx index c2d06937c..d8310cb9d 100644 --- a/src/app/components/text-viewer/TextViewer.tsx +++ b/src/app/components/text-viewer/TextViewer.tsx @@ -1,4 +1,4 @@ -import { ComponentProps, HTMLAttributes, forwardRef } from 'react'; +import { type ComponentProps, type HTMLAttributes, forwardRef } from 'react'; import classNames from 'classnames'; import { Box, Chip, Header, Icon, IconButton, Icons, Scroll, Text, as } from 'folds'; import { CodeHighlightRenderer } from '$components/code-highlight'; diff --git a/src/app/components/time-date/DatePicker.tsx b/src/app/components/time-date/DatePicker.tsx index 078d91d8e..1d92ccff7 100644 --- a/src/app/components/time-date/DatePicker.tsx +++ b/src/app/components/time-date/DatePicker.tsx @@ -60,67 +60,64 @@ export const DatePicker = forwardRef( - {Array.from(new Array(daysInMonth(selectedMonth, selectedYear)).keys()) - .map((i) => i + 1) - .map((day) => ( - handleDay(day)} - disabled={ - (selectedYear === minYear && selectedMonth === minMonth && day < minDay) || - (selectedYear === maxYear && selectedMonth === maxMonth && day > maxDay) - } - > - {day} - - ))} + {Array.from( + new Array(daysInMonth(selectedMonth, selectedYear)).keys(), + (i) => i + 1 + ).map((day) => ( + handleDay(day)} + disabled={ + (selectedYear === minYear && selectedMonth === minMonth && day < minDay) || + (selectedYear === maxYear && selectedMonth === maxMonth && day > maxDay) + } + > + {day} + + ))} - {Array.from(new Array(12).keys()) - .map((i) => i + 1) - .map((month) => ( - handleMonth(month)} - disabled={ - (selectedYear === minYear && month < minMonth) || - (selectedYear === maxYear && month > maxMonth) - } - > - - {dayjs() - .month(month - 1) - .format('MMM')} - - - ))} + {Array.from(new Array(12).keys(), (i) => i + 1).map((month) => ( + handleMonth(month)} + disabled={ + (selectedYear === minYear && month < minMonth) || + (selectedYear === maxYear && month > maxMonth) + } + > + + {dayjs() + .month(month - 1) + .format('MMM')} + + + ))} - {Array.from(new Array(yearsRange).keys()) - .map((i) => minYear + i) - .map((year) => ( - handleYear(year)} - > - {year} - - ))} + {Array.from(new Array(yearsRange).keys(), (i) => minYear + i).map((year) => ( + handleYear(year)} + > + {year} + + ))} diff --git a/src/app/components/time-date/PickerColumn.tsx b/src/app/components/time-date/PickerColumn.tsx index a1fbcb34f..4a74f713d 100644 --- a/src/app/components/time-date/PickerColumn.tsx +++ b/src/app/components/time-date/PickerColumn.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Text, Scroll } from 'folds'; import { CutoutCard } from '$components/cutout-card'; import * as css from './styles.css'; diff --git a/src/app/components/time-date/TimePicker.tsx b/src/app/components/time-date/TimePicker.tsx index c16ad7caa..039d3d5a7 100644 --- a/src/app/components/time-date/TimePicker.tsx +++ b/src/app/components/time-date/TimePicker.tsx @@ -64,7 +64,7 @@ export const TimePicker = forwardRef( {hour24Clock - ? Array.from(new Array(24).keys()).map((hour) => ( + ? Array.from(new Array(24).keys(), (hour) => ( ( {hour < 10 ? `0${hour}` : hour} )) - : Array.from(new Array(12).keys()) - .map((i) => { - if (i === 0) return 12; - return i; - }) - .map((hour) => ( - handleHour(hour)} - disabled={ - (minDay && hour12to24(hour, selectedPM) < minHour24) || - (maxDay && hour12to24(hour, selectedPM) > maxHour24) - } - > - {hour < 10 ? `0${hour}` : hour} - - ))} + : Array.from(new Array(12).keys(), (i) => { + if (i === 0) return 12; + return i; + }).map((hour) => ( + handleHour(hour)} + disabled={ + (minDay && hour12to24(hour, selectedPM) < minHour24) || + (maxDay && hour12to24(hour, selectedPM) > maxHour24) + } + > + {hour < 10 ? `0${hour}` : hour} + + ))} - {Array.from(new Array(60).keys()).map((minute) => ( + {Array.from(new Array(60).keys(), (minute) => ( { const data = await fetch(url).then((resp) => resp.json()); @@ -167,7 +167,7 @@ function parseYoutubeLink(url: Readonly): YoutubeLink | null { // new URL can throw return null; } - const urlHost = parsedURL.host; + const urlHost = parsedURL.hostname.toLowerCase(); const urlSearchParams = parsedURL.searchParams; /** @@ -203,7 +203,7 @@ function parseYoutubeLink(url: Readonly): YoutubeLink | null { videoId, timestamp, playlist, - isMusic: url.includes('music.youtube.com'), + isMusic: urlHost === 'music.youtube.com' || urlHost.endsWith('.music.youtube.com'), }; } diff --git a/src/app/components/url-preview/UrlPreviewCard.tsx b/src/app/components/url-preview/UrlPreviewCard.tsx index 3ac780f74..6bfb9aa61 100644 --- a/src/app/components/url-preview/UrlPreviewCard.tsx +++ b/src/app/components/url-preview/UrlPreviewCard.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { IPreviewUrlResponse } from '$types/matrix-sdk'; +import { type IPreviewUrlResponse } from '$types/matrix-sdk'; import { Box, Icon, IconButton, Icons, Scroll, Spinner, Text, as, color, config } from 'folds'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/user-avatar/UserAvatar.tsx b/src/app/components/user-avatar/UserAvatar.tsx index 3b9606e7b..971ad545d 100644 --- a/src/app/components/user-avatar/UserAvatar.tsx +++ b/src/app/components/user-avatar/UserAvatar.tsx @@ -1,5 +1,5 @@ import { AvatarFallback, AvatarImage, color } from 'folds'; -import { ReactEventHandler, ReactNode, useEffect, useState } from 'react'; +import { type ReactEventHandler, type ReactNode, useEffect, useState } from 'react'; import classNames from 'classnames'; import colorMXID from '$utils/colorMXID'; import * as css from './UserAvatar.css'; diff --git a/src/app/components/user-profile/CreatorChip.tsx b/src/app/components/user-profile/CreatorChip.tsx index f517581cc..6499a36a7 100644 --- a/src/app/components/user-profile/CreatorChip.tsx +++ b/src/app/components/user-profile/CreatorChip.tsx @@ -1,5 +1,5 @@ -import { Chip, config, Icon, Icons, Menu, MenuItem, PopOut, RectCords, Text } from 'folds'; -import { MouseEventHandler, useState } from 'react'; +import { Chip, config, Icon, Icons, Menu, MenuItem, PopOut, type RectCords, Text } from 'folds'; +import { type MouseEventHandler, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; import { useRoomCreatorsTag } from '$hooks/useRoomCreatorsTag'; diff --git a/src/app/components/user-profile/PowerChip.tsx b/src/app/components/user-profile/PowerChip.tsx index d1e4dbfa2..22f5a9f8a 100644 --- a/src/app/components/user-profile/PowerChip.tsx +++ b/src/app/components/user-profile/PowerChip.tsx @@ -15,12 +15,12 @@ import { OverlayBackdrop, OverlayCenter, PopOut, - RectCords, + type RectCords, Spinner, Text, toRem, } from 'folds'; -import { MouseEventHandler, useCallback, useState } from 'react'; +import { type MouseEventHandler, useCallback, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/components/user-profile/UserChips.tsx b/src/app/components/user-profile/UserChips.tsx index 1ce3b26c1..fbcf870f1 100644 --- a/src/app/components/user-profile/UserChips.tsx +++ b/src/app/components/user-profile/UserChips.tsx @@ -1,6 +1,6 @@ import { - KeyboardEventHandler, - MouseEventHandler, + type KeyboardEventHandler, + type MouseEventHandler, useCallback, useEffect, useMemo, @@ -10,7 +10,7 @@ import { import { useNavigate } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { PopOut, Menu, @@ -21,7 +21,7 @@ import { Chip, Icon, Icons, - RectCords, + type RectCords, Spinner, toRem, Box, diff --git a/src/app/components/user-profile/UserHero.tsx b/src/app/components/user-profile/UserHero.tsx index 0acdc3618..dccfab777 100644 --- a/src/app/components/user-profile/UserHero.tsx +++ b/src/app/components/user-profile/UserHero.tsx @@ -18,7 +18,7 @@ import FocusTrap from 'focus-trap-react'; import colorMXID from '$utils/colorMXID'; import { getMxIdLocalPart } from '$utils/matrix'; import { BreakWord, LineClamp3 } from '$styles/Text.css'; -import { UserPresence } from '$hooks/useUserPresence'; +import { type UserPresence } from '$hooks/useUserPresence'; import { stopPropagation } from '$utils/keyboard'; import { useRoom } from '$hooks/useRoom'; import { useSableCosmetics } from '$hooks/useSableCosmetics'; diff --git a/src/app/components/user-profile/UserRoomProfile.tsx b/src/app/components/user-profile/UserRoomProfile.tsx index 124642f84..b756810f9 100644 --- a/src/app/components/user-profile/UserRoomProfile.tsx +++ b/src/app/components/user-profile/UserRoomProfile.tsx @@ -1,9 +1,9 @@ import { Box, Button, config, Icon, Icons, Scroll, Text } from 'folds'; -import { SyntheticEvent, useCallback, useMemo, useState } from 'react'; +import { type SyntheticEvent, useCallback, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAtomValue } from 'jotai'; -import { Opts as LinkifyOpts } from 'linkifyjs'; -import { HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; +import { type HTMLReactParserOptions } from 'html-react-parser'; import { getMxIdServer, mxcUrlToHttp } from '$utils/matrix'; import { getMemberAvatarMxc, getMemberDisplayName } from '$utils/room'; import { useMatrixClient } from '$hooks/useMatrixClient'; @@ -19,9 +19,9 @@ import { useRoomCreators } from '$hooks/useRoomCreators'; import { useRoomPermissions } from '$hooks/useRoomPermissions'; import { useMemberPowerCompare } from '$hooks/useMemberPowerCompare'; import { getDirectCreatePath, withSearchParam } from '$pages/pathUtils'; -import { DirectCreateSearchParams } from '$pages/paths'; +import { type DirectCreateSearchParams } from '$pages/paths'; import { nicknamesAtom } from '$state/nicknames'; -import { UserProfile, useUserProfile } from '$hooks/useUserProfile'; +import { type UserProfile, useUserProfile } from '$hooks/useUserProfile'; import { factoryRenderLinkifyWithMention, getReactCustomHtmlParser, @@ -33,6 +33,7 @@ import { useSpoilerClickHandler } from '$hooks/useSpoilerClickHandler'; import { RenderBody } from '$components/message'; import { getSettings, settingsAtom } from '$state/settings'; import { filterPronounsByLanguage } from '$utils/pronouns'; +import { extractPlainTextFromCustomHtml } from '$utils/sanitize'; import { useSetting } from '$state/hooks/settings'; import { useSettingsLinkBaseUrl } from '$features/settings/useSettingsLinkBaseUrl'; import { CreatorChip } from './CreatorChip'; @@ -134,7 +135,7 @@ function UserExtendedSection({ const safetyTrim = rawBio.length > 2048 ? rawBio.slice(0, 2048) : rawBio; - const visibleText = safetyTrim.replaceAll(/<[^>]*>?/gm, ''); + const visibleText = extractPlainTextFromCustomHtml(safetyTrim); const VISIBLE_LIMIT = 1024; if (visibleText.length <= VISIBLE_LIMIT) { diff --git a/src/app/components/virtualizer/VirtualTile.tsx b/src/app/components/virtualizer/VirtualTile.tsx index b92add55a..22c4faadd 100644 --- a/src/app/components/virtualizer/VirtualTile.tsx +++ b/src/app/components/virtualizer/VirtualTile.tsx @@ -1,4 +1,4 @@ -import { VirtualItem } from '@tanstack/react-virtual'; +import { type VirtualItem } from '@tanstack/react-virtual'; import { as } from 'folds'; import classNames from 'classnames'; import * as css from './style.css'; diff --git a/src/app/features/add-existing/AddExisting.tsx b/src/app/features/add-existing/AddExisting.tsx index 50cfc6717..77fc73cc3 100644 --- a/src/app/features/add-existing/AddExisting.tsx +++ b/src/app/features/add-existing/AddExisting.tsx @@ -20,8 +20,8 @@ import { Text, } from 'folds'; import { - ChangeEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type MouseEventHandler, useCallback, useMemo, useRef, @@ -29,7 +29,7 @@ import { } from 'react'; import { useAtomValue } from 'jotai'; import { useVirtualizer } from '@tanstack/react-virtual'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { stopPropagation } from '$utils/keyboard'; import { useDirects, useRooms, useSpaces } from '$state/hooks/roomList'; import { useMatrixClient } from '$hooks/useMatrixClient'; @@ -43,7 +43,11 @@ import { RoomAvatar, RoomIcon } from '$components/room-avatar'; import { nameInitials } from '$utils/common'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { factoryRoomIdByAtoZ } from '$utils/sort'; -import { SearchItemStrGetter, useAsyncSearch, UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; +import { + type SearchItemStrGetter, + useAsyncSearch, + type UseAsyncSearchOptions, +} from '$hooks/useAsyncSearch'; import { highlightText, makeHighlightRegex } from '$plugins/react-custom-html-parser'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { StateEvent } from '$types/matrix/room'; @@ -104,7 +108,7 @@ export function AddExistingModal({ parentId, space, requestClose }: AddExistingM return true; } - return Array.from(parentIds).some((id) => isAncestor(sourceId, id, visited)); + return [...parentIds].some((id) => isAncestor(sourceId, id, visited)); }, [roomIdToParents] ); diff --git a/src/app/features/call-status/CallControl.tsx b/src/app/features/call-status/CallControl.tsx index 128df221f..d10a28a51 100644 --- a/src/app/features/call-status/CallControl.tsx +++ b/src/app/features/call-status/CallControl.tsx @@ -2,7 +2,7 @@ import { Box, Chip, Icon, IconButton, Icons, Spinner, Text, Tooltip, TooltipProv import { useCallback } from 'react'; import { useSetAtom } from 'jotai'; import { StatusDivider } from './components'; -import { CallEmbed, useCallControlState } from '../../plugins/call'; +import { type CallEmbed, useCallControlState } from '../../plugins/call'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { callEmbedAtom } from '../../state/callEmbed'; diff --git a/src/app/features/call-status/CallRoomName.tsx b/src/app/features/call-status/CallRoomName.tsx index e2cca3b1b..189d5e0ff 100644 --- a/src/app/features/call-status/CallRoomName.tsx +++ b/src/app/features/call-status/CallRoomName.tsx @@ -1,4 +1,4 @@ -import { Room } from 'matrix-js-sdk'; +import { type Room } from 'matrix-js-sdk'; import { Chip, Text } from 'folds'; import { useAtomValue } from 'jotai'; import { useRoomName } from '../../hooks/useRoomMeta'; diff --git a/src/app/features/call-status/CallStatus.tsx b/src/app/features/call-status/CallStatus.tsx index 899bff7d6..4379ac227 100644 --- a/src/app/features/call-status/CallStatus.tsx +++ b/src/app/features/call-status/CallStatus.tsx @@ -9,7 +9,7 @@ import { useCallMembers, useCallSession } from '../../hooks/useCall'; import { ScreenSize, useScreenSize } from '../../hooks/useScreenSize'; import { MemberGlance } from './MemberGlance'; import { StatusDivider } from './components'; -import { CallEmbed } from '../../plugins/call/CallEmbed'; +import { type CallEmbed } from '../../plugins/call/CallEmbed'; import { useCallJoined } from '../../hooks/useCallEmbed'; import { useCallSpeakers } from '../../hooks/useCallSpeakers'; import { MemberSpeaking } from './MemberSpeaking'; diff --git a/src/app/features/call-status/LiveChip.tsx b/src/app/features/call-status/LiveChip.tsx index e62af0e24..6476ab2d4 100644 --- a/src/app/features/call-status/LiveChip.tsx +++ b/src/app/features/call-status/LiveChip.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useState } from 'react'; +import { type MouseEventHandler, useState } from 'react'; import { Avatar, Badge, @@ -10,14 +10,14 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Scroll, Text, toRem, } from 'folds'; -import { CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; +import { type CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; import FocusTrap from 'focus-trap-react'; -import { Room } from 'matrix-js-sdk'; +import { type Room } from 'matrix-js-sdk'; import * as css from './styles.css'; import { stopPropagation } from '../../utils/keyboard'; import { getMemberAvatarMxc, getMemberDisplayName } from '../../utils/room'; diff --git a/src/app/features/call-status/MemberGlance.tsx b/src/app/features/call-status/MemberGlance.tsx index 88c114d51..681e13161 100644 --- a/src/app/features/call-status/MemberGlance.tsx +++ b/src/app/features/call-status/MemberGlance.tsx @@ -1,6 +1,6 @@ import { Box, config, Icon, Icons, Text } from 'folds'; -import { CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; -import { Room } from 'matrix-js-sdk'; +import { type CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; +import { type Room } from 'matrix-js-sdk'; import { UserAvatar } from '../../components/user-avatar'; import { getMemberAvatarMxc, getMemberDisplayName } from '../../utils/room'; import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; diff --git a/src/app/features/call-status/MemberSpeaking.tsx b/src/app/features/call-status/MemberSpeaking.tsx index b80190cd3..b5fc30a2c 100644 --- a/src/app/features/call-status/MemberSpeaking.tsx +++ b/src/app/features/call-status/MemberSpeaking.tsx @@ -1,4 +1,4 @@ -import { Room } from 'matrix-js-sdk'; +import { type Room } from 'matrix-js-sdk'; import { Box, Icon, Icons, Text } from 'folds'; import { getMemberDisplayName } from '../../utils/room'; import { getMxIdLocalPart } from '../../utils/matrix'; @@ -8,7 +8,8 @@ type MemberSpeakingProps = { speakers: Set; }; export function MemberSpeaking({ room, speakers }: MemberSpeakingProps) { - const speakingNames = Array.from(speakers).map( + const speakingNames = Array.from( + speakers, (userId) => getMemberDisplayName(room, userId) ?? getMxIdLocalPart(userId) ?? userId ); return ( diff --git a/src/app/features/call/CallControls.tsx b/src/app/features/call/CallControls.tsx index 6643069c5..f015e684e 100644 --- a/src/app/features/call/CallControls.tsx +++ b/src/app/features/call/CallControls.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useEffect, useRef, useState } from 'react'; +import { type MouseEventHandler, useCallback, useEffect, useRef, useState } from 'react'; import { Box, Button, @@ -9,14 +9,14 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Spinner, Text, toRem, } from 'folds'; import FocusTrap from 'focus-trap-react'; import { SequenceCard } from '$components/sequence-card'; -import { CallEmbed, useCallControlState } from '$plugins/call'; +import { type CallEmbed, useCallControlState } from '$plugins/call'; import { stopPropagation } from '$utils/keyboard'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useRoom } from '$hooks/useRoom'; diff --git a/src/app/features/call/CallMemberCard.tsx b/src/app/features/call/CallMemberCard.tsx index 7d03ac77b..698cc998a 100644 --- a/src/app/features/call/CallMemberCard.tsx +++ b/src/app/features/call/CallMemberCard.tsx @@ -1,4 +1,7 @@ -import { CallMembership, SessionMembershipData } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; +import { + type CallMembership, + type SessionMembershipData, +} from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; import { useState } from 'react'; import { Avatar, Box, Icon, Icons, Text } from 'folds'; import { useMatrixClient } from '../../hooks/useMatrixClient'; @@ -12,11 +15,11 @@ import { UserAvatar } from '../../components/user-avatar'; import { getMouseEventCords } from '../../utils/dom'; import * as css from './styles.css'; -interface MemberWithMembershipData { +type MemberWithMembershipData = { membershipData?: SessionMembershipData & { 'm.call.intent': 'video' | 'audio'; }; -} +}; type CallMemberCardProps = { member: CallMembership; diff --git a/src/app/features/call/CallView.tsx b/src/app/features/call/CallView.tsx index e79a7f6ac..9e802b961 100644 --- a/src/app/features/call/CallView.tsx +++ b/src/app/features/call/CallView.tsx @@ -147,9 +147,9 @@ function CallJoined({ joined, containerRef }: CallJoinedProps) { ); } -interface CallViewProps { +type CallViewProps = { resizable?: boolean; -} +}; export function CallView({ resizable }: CallViewProps) { const room = useRoom(); diff --git a/src/app/features/common-settings/cosmetics/Cosmetics.tsx b/src/app/features/common-settings/cosmetics/Cosmetics.tsx index be793be7b..2533c2b2c 100644 --- a/src/app/features/common-settings/cosmetics/Cosmetics.tsx +++ b/src/app/features/common-settings/cosmetics/Cosmetics.tsx @@ -1,7 +1,7 @@ import { - ChangeEvent, - ChangeEventHandler, - FormEventHandler, + type ChangeEvent, + type ChangeEventHandler, + type FormEventHandler, useCallback, useEffect, useMemo, @@ -41,14 +41,14 @@ import { SequenceCardStyle } from '$features/common-settings/styles.css'; import { UserAvatar } from '$components/user-avatar'; import { nameInitials } from '$utils/common'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; -import { UserProfile, useUserProfile } from '$hooks/useUserProfile'; +import { type UserProfile, useUserProfile } from '$hooks/useUserProfile'; import { getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; -import { Room, RoomMember } from '$types/matrix-sdk'; +import { type Room, type RoomMember } from '$types/matrix-sdk'; import { Command, useCommands } from '$hooks/useCommands'; import { useCapabilities } from '$hooks/useCapabilities'; import { useObjectURL } from '$hooks/useObjectURL'; -import { createUploadAtom, UploadSuccess } from '$state/upload'; +import { createUploadAtom, type UploadSuccess } from '$state/upload'; import { useFilePicker } from '$hooks/useFilePicker'; import { CompactUploadCardRenderer } from '$components/upload-card'; import FocusTrap from 'focus-trap-react'; @@ -57,7 +57,7 @@ import { stopPropagation } from '$utils/keyboard'; import { ModalWide } from '$styles/Modal.css'; import { NameColorEditor } from '$features/settings/account/NameColorEditor'; import { PronounEditor } from '$features/settings/account/PronounEditor'; -import { PronounSet } from '$utils/pronouns'; +import { type PronounSet } from '$utils/pronouns'; const log = createLogger('Cosmetics'); diff --git a/src/app/features/common-settings/developer-tools/DevelopTools.tsx b/src/app/features/common-settings/developer-tools/DevelopTools.tsx index aec2f2ca7..8f3d82cbe 100644 --- a/src/app/features/common-settings/developer-tools/DevelopTools.tsx +++ b/src/app/features/common-settings/developer-tools/DevelopTools.tsx @@ -29,11 +29,11 @@ import { allRoomsAtom } from '$state/room-list/roomList'; import { allInvitesAtom } from '$state/room-list/inviteList'; import { isNotificationEvent } from '$utils/room'; import { CutoutCard } from '$components/cutout-card'; -import { AccountDataEditor, AccountDataSubmitCallback } from '$components/AccountDataEditor'; +import { AccountDataEditor, type AccountDataSubmitCallback } from '$components/AccountDataEditor'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { SequenceCardStyle } from '$features/common-settings/styles.css'; import { SendRoomEvent } from './SendRoomEvent'; -import { StateEventEditor, StateEventInfo } from './StateEventEditor'; +import { StateEventEditor, type StateEventInfo } from './StateEventEditor'; const formatSyncReason = (reason: string): string => { if (reason === 'sliding_active') return 'Sliding Sync active'; @@ -78,15 +78,15 @@ export function DeveloperTools({ requestClose }: DeveloperToolsProps) { const userId = mx.getUserId(); const clientSyncState = mx.getSyncState(); const liveEvents = room.getLiveTimeline().getEvents(); - const latestTimelineEvent = liveEvents[liveEvents.length - 1]; + const latestTimelineEvent = liveEvents.at(-1); const latestTimelineEventId = latestTimelineEvent?.getId() ?? null; - const latestMessageEvent = [...liveEvents].reverse().find((event) => { + const latestMessageEvent = liveEvents.toReversed().find((event) => { const type = event.getType(); return type === 'm.room.message' || type === 'm.room.encrypted' || type === 'm.sticker'; }); const latestMessageEventId = latestMessageEvent?.getId() ?? null; - const latestNotificationEvent = [...liveEvents] - .reverse() + const latestNotificationEvent = liveEvents + .toReversed() .find((event) => isNotificationEvent(event)); const latestNotificationEventId = latestNotificationEvent?.getId() ?? null; const fullyReadEventId = @@ -475,89 +475,85 @@ export function DeveloperTools({ requestClose }: DeveloperToolsProps) { - {Array.from(roomState.keys()) - .sort() - .map((eventType) => { - const expanded = eventType === expandStateType; - const stateKeyToEvents = roomState.get(eventType); - if (!stateKeyToEvents) return null; + {[...roomState.keys()].sort().map((eventType) => { + const expanded = eventType === expandStateType; + const stateKeyToEvents = roomState.get(eventType); + if (!stateKeyToEvents) return null; - return ( - - - setExpandStateType(expanded ? undefined : eventType) - } - variant="Surface" - fill="None" - size="300" - radii="0" - before={ - - } - after={{stateKeyToEvents.size}} + return ( + + + setExpandStateType(expanded ? undefined : eventType) + } + variant="Surface" + fill="None" + size="300" + radii="0" + before={ + + } + after={{stateKeyToEvents.size}} + > + + + {eventType} + + + + {expanded && ( +
- - - {eventType} - - - - {expanded && ( -
+ setComposeEvent({ type: eventType, stateKey: '' }) + } + variant="Surface" + fill="None" + size="300" + radii="0" + before={} > + + + Add New + + + + {[...stateKeyToEvents.keys()].sort().map((stateKey) => ( - setComposeEvent({ type: eventType, stateKey: '' }) - } + onClick={() => { + setOpenStateEvent({ + type: eventType, + stateKey, + }); + }} + key={stateKey} variant="Surface" fill="None" size="300" radii="0" - before={} + after={} > - Add New + {stateKey ? `"${stateKey}"` : 'Default'} - {Array.from(stateKeyToEvents.keys()) - .sort() - .map((stateKey) => ( - { - setOpenStateEvent({ - type: eventType, - stateKey, - }); - }} - key={stateKey} - variant="Surface" - fill="None" - size="300" - radii="0" - after={} - > - - - {stateKey ? `"${stateKey}"` : 'Default'} - - - - ))} -
- )} - - ); - })} + ))} +
+ )} +
+ ); + })}
)} @@ -612,25 +608,23 @@ export function DeveloperTools({ requestClose }: DeveloperToolsProps) { - {Array.from(accountData.keys()) - .sort() - .map((type) => ( - } - onClick={() => setAccountDataType(type)} - > - - - {type} - - - - ))} + {[...accountData.keys()].sort().map((type) => ( + } + onClick={() => setAccountDataType(type)} + > + + + {type} + + + + ))} )} diff --git a/src/app/features/common-settings/developer-tools/SendRoomEvent.tsx b/src/app/features/common-settings/developer-tools/SendRoomEvent.tsx index b246b14b6..fd1a1cff0 100644 --- a/src/app/features/common-settings/developer-tools/SendRoomEvent.tsx +++ b/src/app/features/common-settings/developer-tools/SendRoomEvent.tsx @@ -1,5 +1,5 @@ -import { useCallback, useRef, useState, FormEventHandler, useEffect } from 'react'; -import { MatrixError } from '$types/matrix-sdk'; +import { useCallback, useRef, useState, type FormEventHandler, useEffect } from 'react'; +import { type MatrixError } from '$types/matrix-sdk'; import { Box, Chip, diff --git a/src/app/features/common-settings/developer-tools/StateEventEditor.tsx b/src/app/features/common-settings/developer-tools/StateEventEditor.tsx index 00bbd1125..fe69b5f62 100644 --- a/src/app/features/common-settings/developer-tools/StateEventEditor.tsx +++ b/src/app/features/common-settings/developer-tools/StateEventEditor.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { type FormEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Text, @@ -13,13 +13,13 @@ import { Spinner, Button, } from 'folds'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { Page, PageHeader } from '$components/page'; import { SequenceCard } from '$components/sequence-card'; import { TextViewerContent } from '$components/text-viewer'; import { useStateEvent } from '$hooks/useStateEvent'; import { useRoom } from '$hooks/useRoom'; -import { StateEvent } from '$types/matrix/room'; +import { type StateEvent } from '$types/matrix/room'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useAlive } from '$hooks/useAlive'; import { Cursor } from '$plugins/text-area'; diff --git a/src/app/features/common-settings/emojis-stickers/EmojisStickers.tsx b/src/app/features/common-settings/emojis-stickers/EmojisStickers.tsx index 0ee350a69..6de76d7ac 100644 --- a/src/app/features/common-settings/emojis-stickers/EmojisStickers.tsx +++ b/src/app/features/common-settings/emojis-stickers/EmojisStickers.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { Box, Icon, IconButton, Icons, Scroll, Text } from 'folds'; import { Page, PageContent, PageHeader } from '$components/page'; -import { ImagePack } from '$plugins/custom-emoji'; +import { type ImagePack } from '$plugins/custom-emoji'; import { ImagePackView } from '$components/image-pack-view'; import { RoomPacks } from './RoomPacks'; diff --git a/src/app/features/common-settings/emojis-stickers/RoomPacks.tsx b/src/app/features/common-settings/emojis-stickers/RoomPacks.tsx index 6162e4c0b..6a9e05b35 100644 --- a/src/app/features/common-settings/emojis-stickers/RoomPacks.tsx +++ b/src/app/features/common-settings/emojis-stickers/RoomPacks.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useMemo, useState } from 'react'; +import { type FormEventHandler, useCallback, useMemo, useState } from 'react'; import { Box, Text, @@ -16,14 +16,14 @@ import { IconButton, Menu, } from 'folds'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { SequenceCard } from '$components/sequence-card'; import { - ImagePack, + type ImagePack, ImageUsage, - PackAddress, + type PackAddress, packAddressEqual, - PackContent, + type PackContent, } from '$plugins/custom-emoji'; import { useRoom } from '$hooks/useRoom'; import { useRoomImagePacks } from '$hooks/useImagePacks'; diff --git a/src/app/features/common-settings/general/RoomAddress.tsx b/src/app/features/common-settings/general/RoomAddress.tsx index f1ae66968..80e3247ab 100644 --- a/src/app/features/common-settings/general/RoomAddress.tsx +++ b/src/app/features/common-settings/general/RoomAddress.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useState } from 'react'; +import { type FormEventHandler, useCallback, useState } from 'react'; import { Badge, Box, @@ -14,7 +14,7 @@ import { Text, toRem, } from 'folds'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { SettingTile } from '$components/setting-tile'; import { SequenceCard } from '$components/sequence-card'; import { SequenceCardStyle } from '$features/room-settings/styles.css'; @@ -31,7 +31,7 @@ import { CutoutCard } from '$components/cutout-card'; import { replaceSpaceWithDash } from '$utils/common'; import { useAlive } from '$hooks/useAlive'; import { StateEvent } from '$types/matrix/room'; -import { RoomPermissionsAPI } from '$hooks/useRoomPermissions'; +import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions'; import { getMxIdServer } from '$utils/matrix'; type RoomPublishedAddressesProps = { diff --git a/src/app/features/common-settings/general/RoomEncryption.tsx b/src/app/features/common-settings/general/RoomEncryption.tsx index 3dc87f840..dac24596f 100644 --- a/src/app/features/common-settings/general/RoomEncryption.tsx +++ b/src/app/features/common-settings/general/RoomEncryption.tsx @@ -16,7 +16,7 @@ import { Text, } from 'folds'; import { useCallback, useState } from 'react'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import FocusTrap from 'focus-trap-react'; import { SequenceCard } from '$components/sequence-card'; import { SequenceCardStyle } from '$features/room-settings/styles.css'; @@ -27,7 +27,7 @@ import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useRoom } from '$hooks/useRoom'; import { useStateEvent } from '$hooks/useStateEvent'; import { stopPropagation } from '$utils/keyboard'; -import { RoomPermissionsAPI } from '$hooks/useRoomPermissions'; +import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions'; const ROOM_ENC_ALGO = 'm.megolm.v1.aes-sha2'; diff --git a/src/app/features/common-settings/general/RoomHistoryVisibility.tsx b/src/app/features/common-settings/general/RoomHistoryVisibility.tsx index 2f93472e6..9671110e1 100644 --- a/src/app/features/common-settings/general/RoomHistoryVisibility.tsx +++ b/src/app/features/common-settings/general/RoomHistoryVisibility.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useMemo, useState } from 'react'; +import { type MouseEventHandler, useCallback, useMemo, useState } from 'react'; import { Button, color, @@ -8,14 +8,14 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Spinner, Text, } from 'folds'; import { HistoryVisibility, - MatrixError, - RoomHistoryVisibilityEventContent, + type MatrixError, + type RoomHistoryVisibilityEventContent, } from '$types/matrix-sdk'; import FocusTrap from 'focus-trap-react'; import { SequenceCard } from '$components/sequence-card'; @@ -27,7 +27,7 @@ import { StateEvent } from '$types/matrix/room'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useStateEvent } from '$hooks/useStateEvent'; import { stopPropagation } from '$utils/keyboard'; -import { RoomPermissionsAPI } from '$hooks/useRoomPermissions'; +import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions'; const useVisibilityStr = () => useMemo( diff --git a/src/app/features/common-settings/general/RoomJoinRules.tsx b/src/app/features/common-settings/general/RoomJoinRules.tsx index 5a6c3697f..f69e194db 100644 --- a/src/app/features/common-settings/general/RoomJoinRules.tsx +++ b/src/app/features/common-settings/general/RoomJoinRules.tsx @@ -2,13 +2,13 @@ import { useCallback, useMemo } from 'react'; import { color, Text } from 'folds'; import { JoinRule, - MatrixError, + type MatrixError, RestrictedAllowType, - RoomJoinRulesEventContent, + type RoomJoinRulesEventContent, } from '$types/matrix-sdk'; import { useAtomValue } from 'jotai'; import { - ExtendedJoinRules, + type ExtendedJoinRules, JoinRulesSwitcher, useJoinRuleIcons, useRoomJoinRuleLabel, @@ -27,7 +27,7 @@ import { useRecursiveChildSpaceScopeFactory, useSpaceChildren } from '$state/hoo import { allRoomsAtom } from '$state/room-list/roomList'; import { roomToParentsAtom } from '$state/room/roomToParents'; import { knockRestrictedSupported, knockSupported, restrictedSupported } from '$utils/matrix'; -import { RoomPermissionsAPI } from '$hooks/useRoomPermissions'; +import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions'; type RestrictedRoomAllowContent = { room_id: string; diff --git a/src/app/features/common-settings/general/RoomProfile.tsx b/src/app/features/common-settings/general/RoomProfile.tsx index cd523f32b..b6df8ec3d 100644 --- a/src/app/features/common-settings/general/RoomProfile.tsx +++ b/src/app/features/common-settings/general/RoomProfile.tsx @@ -11,11 +11,11 @@ import { Text, TextArea, } from 'folds'; -import { FormEventHandler, useCallback, useMemo, useState } from 'react'; +import { type FormEventHandler, useCallback, useMemo, useState } from 'react'; import { useAtomValue } from 'jotai'; import Linkify from 'linkify-react'; import classNames from 'classnames'; -import { JoinRule, MatrixError } from '$types/matrix-sdk'; +import { JoinRule, type MatrixError } from '$types/matrix-sdk'; import { SequenceCard } from '$components/sequence-card'; import { SequenceCardStyle } from '$features/room-settings/styles.css'; import { useRoom } from '$hooks/useRoom'; @@ -30,11 +30,11 @@ import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { StateEvent } from '$types/matrix/room'; import { CompactUploadCardRenderer } from '$components/upload-card'; import { useObjectURL } from '$hooks/useObjectURL'; -import { createUploadAtom, UploadSuccess } from '$state/upload'; +import { createUploadAtom, type UploadSuccess } from '$state/upload'; import { useFilePicker } from '$hooks/useFilePicker'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useAlive } from '$hooks/useAlive'; -import { RoomPermissionsAPI } from '$hooks/useRoomPermissions'; +import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions'; type RoomProfileEditProps = { canEditAvatar: boolean; diff --git a/src/app/features/common-settings/general/RoomPublish.tsx b/src/app/features/common-settings/general/RoomPublish.tsx index a4aa47d2a..5739cc5ee 100644 --- a/src/app/features/common-settings/general/RoomPublish.tsx +++ b/src/app/features/common-settings/general/RoomPublish.tsx @@ -1,5 +1,5 @@ import { Box, color, Spinner, Switch, Text } from 'folds'; -import { JoinRule, MatrixError, RoomJoinRulesEventContent } from '$types/matrix-sdk'; +import { JoinRule, type MatrixError, type RoomJoinRulesEventContent } from '$types/matrix-sdk'; import { SequenceCard } from '$components/sequence-card'; import { SequenceCardStyle } from '$features/room-settings/styles.css'; import { SettingTile } from '$components/setting-tile'; @@ -9,8 +9,8 @@ import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { StateEvent } from '$types/matrix/room'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useStateEvent } from '$hooks/useStateEvent'; -import { ExtendedJoinRules } from '$components/JoinRulesSwitcher'; -import { RoomPermissionsAPI } from '$hooks/useRoomPermissions'; +import { type ExtendedJoinRules } from '$components/JoinRulesSwitcher'; +import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions'; type RoomPublishProps = { permissions: RoomPermissionsAPI; diff --git a/src/app/features/common-settings/general/RoomUpgrade.tsx b/src/app/features/common-settings/general/RoomUpgrade.tsx index 073465688..56f0e795f 100644 --- a/src/app/features/common-settings/general/RoomUpgrade.tsx +++ b/src/app/features/common-settings/general/RoomUpgrade.tsx @@ -16,19 +16,19 @@ import { Icons, } from 'folds'; import FocusTrap from 'focus-trap-react'; -import { MatrixError, Method, RoomTombstoneEventContent } from '$types/matrix-sdk'; +import { type MatrixError, Method, type RoomTombstoneEventContent } from '$types/matrix-sdk'; import { SequenceCard } from '$components/sequence-card'; import { SequenceCardStyle } from '$features/room-settings/styles.css'; import { SettingTile } from '$components/setting-tile'; import { useRoom } from '$hooks/useRoom'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; -import { IRoomCreateContent, StateEvent } from '$types/matrix/room'; +import { type IRoomCreateContent, StateEvent } from '$types/matrix/room'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useStateEvent } from '$hooks/useStateEvent'; import { useRoomNavigate } from '$hooks/useRoomNavigate'; import { useCapabilities } from '$hooks/useCapabilities'; import { stopPropagation } from '$utils/keyboard'; -import { RoomPermissionsAPI } from '$hooks/useRoomPermissions'; +import { type RoomPermissionsAPI } from '$hooks/useRoomPermissions'; import { AdditionalCreatorInput, RoomVersionSelector, @@ -55,7 +55,7 @@ function RoomUpgradeDialog({ requestClose }: { requestClose: () => void }) { const allowAdditionalCreators = creatorsSupported(selectedRoomVersion); const { additionalCreators, addAdditionalCreator, removeAdditionalCreator } = - useAdditionalCreators(Array.from(creators)); + useAdditionalCreators([...creators]); const [upgradeState, upgrade] = useAsyncCallback( useCallback( diff --git a/src/app/features/common-settings/members/Members.tsx b/src/app/features/common-settings/members/Members.tsx index 19b8ffc46..57e3ca02f 100644 --- a/src/app/features/common-settings/members/Members.tsx +++ b/src/app/features/common-settings/members/Members.tsx @@ -1,6 +1,6 @@ import { - ChangeEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type MouseEventHandler, useCallback, useMemo, useRef, @@ -15,14 +15,14 @@ import { Icons, Input, PopOut, - RectCords, + type RectCords, Scroll, Spinner, Text, toRem, } from 'folds'; import { useVirtualizer } from '@tanstack/react-virtual'; -import { RoomMember } from '$types/matrix-sdk'; +import { type RoomMember } from '$types/matrix-sdk'; import { Page, PageContent, PageHeader } from '$components/page'; import { useRoom } from '$hooks/useRoom'; import { useRoomMembers } from '$hooks/useRoomMembers'; @@ -34,7 +34,11 @@ import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { getMxIdLocalPart, getMxIdServer } from '$utils/matrix'; import { ServerBadge } from '$components/server-badge'; import { useDebounce } from '$hooks/useDebounce'; -import { SearchItemStrGetter, useAsyncSearch, UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; +import { + type SearchItemStrGetter, + useAsyncSearch, + type UseAsyncSearchOptions, +} from '$hooks/useAsyncSearch'; import { getMemberSearchStr } from '$utils/room'; import { useMembershipFilter, useMembershipFilterMenu } from '$hooks/useMemberFilter'; import { useMemberPowerSort, useMemberSort, useMemberSortMenu } from '$hooks/useMemberSort'; @@ -94,10 +98,7 @@ export function Members({ requestClose }: MembersProps) { const sortedMembers = useMemo( () => - Array.from(members) - .filter(membershipFilter.filterFn) - .sort(memberSort.sortFn) - .sort(memberPowerSort), + [...members].filter(membershipFilter.filterFn).sort(memberSort.sortFn).sort(memberPowerSort), [members, membershipFilter, memberSort, memberPowerSort] ); diff --git a/src/app/features/common-settings/permissions/PermissionGroups.tsx b/src/app/features/common-settings/permissions/PermissionGroups.tsx index 324005819..b29c788e1 100644 --- a/src/app/features/common-settings/permissions/PermissionGroups.tsx +++ b/src/app/features/common-settings/permissions/PermissionGroups.tsx @@ -7,8 +7,8 @@ import { SettingTile } from '$components/setting-tile'; import { applyPermissionPower, getPermissionPower, - IPowerLevels, - PermissionLocation, + type IPowerLevels, + type PermissionLocation, } from '$hooks/usePowerLevels'; import { getPowerLevelTag, getPowers, usePowerLevelTags } from '$hooks/usePowerLevelTags'; import { useRoom } from '$hooks/useRoom'; @@ -18,7 +18,7 @@ import { PowerSwitcher } from '$components/power'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useAlive } from '$hooks/useAlive'; import { SequenceCardStyle } from '$features/common-settings/styles.css'; -import { PermissionGroup } from './types'; +import { type PermissionGroup } from './types'; const USER_DEFAULT_LOCATION: PermissionLocation = { user: true, diff --git a/src/app/features/common-settings/permissions/Powers.tsx b/src/app/features/common-settings/permissions/Powers.tsx index a029ac785..ce1d03674 100644 --- a/src/app/features/common-settings/permissions/Powers.tsx +++ b/src/app/features/common-settings/permissions/Powers.tsx @@ -1,12 +1,12 @@ /* eslint-disable react/no-array-index-key */ -import { useState, MouseEventHandler, ReactNode } from 'react'; +import { useState, type MouseEventHandler, type ReactNode } from 'react'; import FocusTrap from 'focus-trap-react'; import { Box, Button, Chip, Text, - RectCords, + type RectCords, PopOut, Menu, Scroll, @@ -17,7 +17,7 @@ import { import { SequenceCard } from '$components/sequence-card'; import { getPowers, usePowerLevelTags } from '$hooks/usePowerLevelTags'; import { SettingTile } from '$components/setting-tile'; -import { getPermissionPower, IPowerLevels } from '$hooks/usePowerLevels'; +import { getPermissionPower, type IPowerLevels } from '$hooks/usePowerLevels'; import { useRoom } from '$hooks/useRoom'; import { PowerColorBadge, PowerIcon } from '$components/power'; import { useMatrixClient } from '$hooks/useMatrixClient'; @@ -27,7 +27,7 @@ import { getPowerTagIconSrc } from '$hooks/useMemberPowerTag'; import { useRoomCreatorsTag } from '$hooks/useRoomCreatorsTag'; import { useRoomCreators } from '$hooks/useRoomCreators'; import { SequenceCardStyle } from '$features/common-settings/styles.css'; -import { PermissionGroup } from './types'; +import { type PermissionGroup } from './types'; type PeekPermissionsProps = { powerLevels: IPowerLevels; diff --git a/src/app/features/common-settings/permissions/PowersEditor.tsx b/src/app/features/common-settings/permissions/PowersEditor.tsx index f53f3a237..05d18ecec 100644 --- a/src/app/features/common-settings/permissions/PowersEditor.tsx +++ b/src/app/features/common-settings/permissions/PowersEditor.tsx @@ -1,4 +1,10 @@ -import { FormEventHandler, MouseEventHandler, useCallback, useMemo, useState } from 'react'; +import { + type FormEventHandler, + type MouseEventHandler, + useCallback, + useMemo, + useState, +} from 'react'; import { Box, Text, @@ -9,7 +15,7 @@ import { Scroll, Button, Input, - RectCords, + type RectCords, PopOut, Menu, config, @@ -21,13 +27,13 @@ import { import { HexColorPicker } from 'react-colorful'; import { useAtomValue } from 'jotai'; import { Page, PageContent, PageHeader } from '$components/page'; -import { IPowerLevels } from '$hooks/usePowerLevels'; +import { type IPowerLevels } from '$hooks/usePowerLevels'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { getPowers, getUsedPowers, - PowerLevelTags, + type PowerLevelTags, usePowerLevelTags, } from '$hooks/usePowerLevelTags'; import { useRoom } from '$hooks/useRoom'; @@ -41,9 +47,9 @@ import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useFilePicker } from '$hooks/useFilePicker'; import { CompactUploadCardRenderer } from '$components/upload-card'; -import { createUploadAtom, UploadSuccess } from '$state/upload'; +import { createUploadAtom, type UploadSuccess } from '$state/upload'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; -import { MemberPowerTag, MemberPowerTagIcon, StateEvent } from '$types/matrix/room'; +import { type MemberPowerTag, type MemberPowerTagIcon, StateEvent } from '$types/matrix/room'; import { useAlive } from '$hooks/useAlive'; import { BetaNoticeBadge } from '$components/BetaNoticeBadge'; import { getPowerTagIconSrc } from '$hooks/useMemberPowerTag'; @@ -314,7 +320,7 @@ export function PowersEditor({ powerLevels, requestClose }: Readonly { const up = getUsedPowers(powerLevels); - return [up, Math.max(...Array.from(up))]; + return [up, Math.max(...[...up])]; }, [powerLevels]); const powerLevelTags = usePowerLevelTags(room, powerLevels); diff --git a/src/app/features/common-settings/permissions/types.ts b/src/app/features/common-settings/permissions/types.ts index 5d5eaa35d..1f47aa452 100644 --- a/src/app/features/common-settings/permissions/types.ts +++ b/src/app/features/common-settings/permissions/types.ts @@ -1,4 +1,4 @@ -import { PermissionLocation } from '$hooks/usePowerLevels'; +import { type PermissionLocation } from '$hooks/usePowerLevels'; export type PermissionItem = { location: PermissionLocation; diff --git a/src/app/features/create-chat/CreateChat.tsx b/src/app/features/create-chat/CreateChat.tsx index 225ef9b64..c862718b6 100644 --- a/src/app/features/create-chat/CreateChat.tsx +++ b/src/app/features/create-chat/CreateChat.tsx @@ -1,6 +1,6 @@ import { Box, Button, color, config, Icon, Icons, Input, Spinner, Switch, Text } from 'folds'; -import { FormEventHandler, useCallback, useState } from 'react'; -import { ICreateRoomStateEvent, MatrixError, Preset, Visibility } from '$types/matrix-sdk'; +import { type FormEventHandler, useCallback, useState } from 'react'; +import { type ICreateRoomStateEvent, MatrixError, Preset, Visibility } from '$types/matrix-sdk'; import { useNavigate } from 'react-router-dom'; import { SettingTile } from '$components/setting-tile'; import { SequenceCard } from '$components/sequence-card'; diff --git a/src/app/features/create-room/CreateRoom.tsx b/src/app/features/create-room/CreateRoom.tsx index 7347ba586..1f5692018 100644 --- a/src/app/features/create-room/CreateRoom.tsx +++ b/src/app/features/create-room/CreateRoom.tsx @@ -1,5 +1,5 @@ -import { FormEventHandler, useCallback, useEffect, useState } from 'react'; -import { MatrixError, Room, JoinRule } from '$types/matrix-sdk'; +import { type FormEventHandler, useCallback, useEffect, useState } from 'react'; +import { MatrixError, type Room, JoinRule } from '$types/matrix-sdk'; import { Box, Button, @@ -31,7 +31,7 @@ import { AdditionalCreatorInput, createRoom, CreateRoomAliasInput, - CreateRoomData, + type CreateRoomData, CreateRoomAccess, CreateRoomAccessSelector, RoomVersionSelector, diff --git a/src/app/features/create-room/CreateRoomModal.tsx b/src/app/features/create-room/CreateRoomModal.tsx index ad613a520..7297ab150 100644 --- a/src/app/features/create-room/CreateRoomModal.tsx +++ b/src/app/features/create-room/CreateRoomModal.tsx @@ -16,7 +16,7 @@ import FocusTrap from 'focus-trap-react'; import { useAllJoinedRoomsSet, useGetRoom } from '$hooks/useGetRoom'; import { SpaceProvider } from '$hooks/useSpace'; import { useCloseCreateRoomModal, useCreateRoomModalState } from '$state/hooks/createRoomModal'; -import { CreateRoomModalState } from '$state/createRoomModal'; +import { type CreateRoomModalState } from '$state/createRoomModal'; import { stopPropagation } from '$utils/keyboard'; import { CreateRoomType } from '$components/create-room/types'; import { CreateRoomForm } from './CreateRoom'; diff --git a/src/app/features/create-space/CreateSpace.tsx b/src/app/features/create-space/CreateSpace.tsx index 2fbeb198b..e1de7d25c 100644 --- a/src/app/features/create-space/CreateSpace.tsx +++ b/src/app/features/create-space/CreateSpace.tsx @@ -1,5 +1,5 @@ -import { FormEventHandler, useCallback, useEffect, useState } from 'react'; -import { MatrixError, Room } from '$types/matrix-sdk'; +import { type FormEventHandler, useCallback, useEffect, useState } from 'react'; +import { MatrixError, type Room } from '$types/matrix-sdk'; import { Box, Button, @@ -31,7 +31,7 @@ import { AdditionalCreatorInput, createRoom, CreateRoomAliasInput, - CreateRoomData, + type CreateRoomData, CreateRoomAccess, CreateRoomAccessSelector, RoomVersionSelector, diff --git a/src/app/features/create-space/CreateSpaceModal.tsx b/src/app/features/create-space/CreateSpaceModal.tsx index 3ef0da51f..effa38bdd 100644 --- a/src/app/features/create-space/CreateSpaceModal.tsx +++ b/src/app/features/create-space/CreateSpaceModal.tsx @@ -16,7 +16,7 @@ import FocusTrap from 'focus-trap-react'; import { useAllJoinedRoomsSet, useGetRoom } from '$hooks/useGetRoom'; import { SpaceProvider } from '$hooks/useSpace'; import { useCloseCreateSpaceModal, useCreateSpaceModalState } from '$state/hooks/createSpaceModal'; -import { CreateSpaceModalState } from '$state/createSpaceModal'; +import { type CreateSpaceModalState } from '$state/createSpaceModal'; import { stopPropagation } from '$utils/keyboard'; import { CreateSpaceForm } from './CreateSpace'; diff --git a/src/app/features/lobby/DnD.tsx b/src/app/features/lobby/DnD.tsx index fa4584a40..3c6f46d7e 100644 --- a/src/app/features/lobby/DnD.tsx +++ b/src/app/features/lobby/DnD.tsx @@ -1,4 +1,4 @@ -import { RefObject, useEffect, useRef, useState } from 'react'; +import { type RefObject, useEffect, useRef, useState } from 'react'; import { dropTargetForElements, draggable, @@ -8,7 +8,7 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine'; import classNames from 'classnames'; import { Box, Icon, Icons, as } from 'folds'; -import { HierarchyItem } from '$hooks/useSpaceHierarchy'; +import { type HierarchyItem } from '$hooks/useSpaceHierarchy'; import * as css from './DnD.css'; export type DropContainerData = { diff --git a/src/app/features/lobby/HierarchyItemMenu.tsx b/src/app/features/lobby/HierarchyItemMenu.tsx index 1377f33ff..6ffac6de8 100644 --- a/src/app/features/lobby/HierarchyItemMenu.tsx +++ b/src/app/features/lobby/HierarchyItemMenu.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useEffect, useState } from 'react'; +import { type MouseEventHandler, useCallback, useEffect, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { Box, @@ -9,15 +9,15 @@ import { Menu, MenuItem, Text, - RectCords, + type RectCords, config, Line, Spinner, toRem, } from 'folds'; -import { HierarchyItem } from '$hooks/useSpaceHierarchy'; +import { type HierarchyItem } from '$hooks/useSpaceHierarchy'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { MSpaceChildContent, StateEvent } from '$types/matrix/room'; +import { type MSpaceChildContent, StateEvent } from '$types/matrix/room'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { UseStateProvider } from '$components/UseStateProvider'; import { LeaveSpacePrompt } from '$components/leave-space-prompt'; @@ -26,7 +26,7 @@ import { stopPropagation } from '$utils/keyboard'; import { useOpenRoomSettings } from '$state/hooks/roomSettings'; import { useSpaceOptionally } from '$hooks/useSpace'; import { useOpenSpaceSettings } from '$state/hooks/spaceSettings'; -import { IPowerLevels } from '$hooks/usePowerLevels'; +import { type IPowerLevels } from '$hooks/usePowerLevels'; import { getRoomCreatorsForRoomId } from '$hooks/useRoomCreators'; import { getRoomPermissionsAPI } from '$hooks/useRoomPermissions'; import { InviteUserPrompt } from '$components/invite-user-prompt'; diff --git a/src/app/features/lobby/Lobby.tsx b/src/app/features/lobby/Lobby.tsx index c980db462..c3032583d 100644 --- a/src/app/features/lobby/Lobby.tsx +++ b/src/app/features/lobby/Lobby.tsx @@ -1,6 +1,6 @@ import { - MouseEventHandler, - ReactElement, + type MouseEventHandler, + type ReactElement, useCallback, useEffect, useMemo, @@ -20,20 +20,24 @@ import { color, config, } from 'folds'; -import { useVirtualizer, VirtualItem } from '@tanstack/react-virtual'; +import { useVirtualizer, type VirtualItem } from '@tanstack/react-virtual'; import { useAtom, useAtomValue } from 'jotai'; import { useNavigate } from 'react-router-dom'; import { JoinRule, RestrictedAllowType, - Room, - RoomJoinRulesEventContent, - IHierarchyRoom, + type Room, + type RoomJoinRulesEventContent, + type IHierarchyRoom, } from '$types/matrix-sdk'; import { produce } from 'immer'; import { useSpace } from '$hooks/useSpace'; import { Page, PageContent, PageContentCenter, PageHeroSection } from '$components/page'; -import { HierarchyItem, HierarchyItemSpace, useSpaceHierarchy } from '$hooks/useSpaceHierarchy'; +import { + type HierarchyItem, + type HierarchyItemSpace, + useSpaceHierarchy, +} from '$hooks/useSpaceHierarchy'; import { VirtualTile } from '$components/virtualizer'; import { spaceRoomsAtom } from '$state/spaceRooms'; import { useSetting } from '$state/hooks/settings'; @@ -42,7 +46,7 @@ import { settingsAtom } from '$state/settings'; import { ScrollTopContainer } from '$components/scroll-top-container'; import { useElementSizeObserver } from '$hooks/useElementSizeObserver'; import { - IPowerLevels, + type IPowerLevels, PowerLevelsContextProvider, usePowerLevels, useRoomsPowerLevels, @@ -73,7 +77,7 @@ import { getRoomPermissionsAPI } from '$hooks/useRoomPermissions'; import { getRoomCreatorsForRoomId } from '$hooks/useRoomCreators'; import { MembersDrawer } from '$features/room/MembersDrawer'; import { SpaceHierarchyItem } from './SpaceHierarchyItem'; -import { CanDropCallback, useDnDMonitor } from './DnD'; +import { type CanDropCallback, useDnDMonitor } from './DnD'; import { LobbyHero } from './LobbyHero'; import { LobbyHeader } from './LobbyHeader'; import { SpaceHierarchyNavItem } from './SpaceHierarchyNavItem'; @@ -267,7 +271,7 @@ export function Lobby() { // As a subspace can be in multiple spaces, // only return true if all parent spaces are closed. - const allClosed = !Array.from(parentParentIds).some( + const allClosed = ![...parentParentIds].some( (id) => !getInClosedCategories(spaceId, id, parentId, visited) ); visited.delete(categoryId); @@ -291,7 +295,7 @@ export function Lobby() { return false; } - return !Array.from(parentIds).some((id) => !getInClosedCategories(spaceId, id, roomId)); + return ![...parentIds].some((id) => !getInClosedCategories(spaceId, id, roomId)); }; const [subspaceHierarchyLimit] = useSetting(settingsAtom, 'subspaceHierarchyLimit'); @@ -435,9 +439,9 @@ export function Lobby() { } } - const itemSpaces = Array.from( - hierarchy?.find((i) => i.space.roomId === containerParentId)?.rooms ?? [] - ); + const itemSpaces = [ + ...(hierarchy?.find((i) => i.space.roomId === containerParentId)?.rooms ?? []), + ]; const beforeItem: HierarchyItem | undefined = 'space' in containerItem ? undefined : containerItem; diff --git a/src/app/features/lobby/LobbyHeader.tsx b/src/app/features/lobby/LobbyHeader.tsx index 4c3ba7447..d64673b79 100644 --- a/src/app/features/lobby/LobbyHeader.tsx +++ b/src/app/features/lobby/LobbyHeader.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, forwardRef, useState } from 'react'; +import { type MouseEventHandler, forwardRef, useState } from 'react'; import { Avatar, Box, @@ -9,7 +9,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Text, Tooltip, TooltipProvider, @@ -25,7 +25,7 @@ import { useSpace } from '$hooks/useSpace'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { RoomAvatar } from '$components/room-avatar'; import { nameInitials } from '$utils/common'; -import { IPowerLevels } from '$hooks/usePowerLevels'; +import { type IPowerLevels } from '$hooks/usePowerLevels'; import { UseStateProvider } from '$components/UseStateProvider'; import { LeaveSpacePrompt } from '$components/leave-space-prompt'; import { stopPropagation } from '$utils/keyboard'; diff --git a/src/app/features/lobby/RoomItem.tsx b/src/app/features/lobby/RoomItem.tsx index 090871c7b..13addce24 100644 --- a/src/app/features/lobby/RoomItem.tsx +++ b/src/app/features/lobby/RoomItem.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, ReactNode, useCallback, useRef } from 'react'; +import { type MouseEventHandler, type ReactNode, useCallback, useRef } from 'react'; import { Avatar, Badge, @@ -19,11 +19,11 @@ import { toRem, } from 'folds'; import FocusTrap from 'focus-trap-react'; -import { JoinRule, MatrixError, Room, IHierarchyRoom } from '$types/matrix-sdk'; +import { JoinRule, type MatrixError, type Room, type IHierarchyRoom } from '$types/matrix-sdk'; import { RoomAvatar, RoomIcon } from '$components/room-avatar'; import { SequenceCard } from '$components/sequence-card'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { HierarchyItem } from '$hooks/useSpaceHierarchy'; +import { type HierarchyItem } from '$hooks/useSpaceHierarchy'; import { KnockRoomPrompt } from '$components/knock-room-prompt'; import { LocalRoomSummaryLoader } from '$components/RoomSummaryLoader'; import { UseStateProvider } from '$components/UseStateProvider'; diff --git a/src/app/features/lobby/SpaceHierarchyItem.tsx b/src/app/features/lobby/SpaceHierarchyItem.tsx index 2797be88e..9158583ed 100644 --- a/src/app/features/lobby/SpaceHierarchyItem.tsx +++ b/src/app/features/lobby/SpaceHierarchyItem.tsx @@ -1,20 +1,20 @@ -import { forwardRef, MouseEventHandler, useEffect, useMemo } from 'react'; -import { MatrixError, Room, IHierarchyRoom } from '$types/matrix-sdk'; +import { forwardRef, type MouseEventHandler, useEffect, useMemo } from 'react'; +import { MatrixError, type Room, type IHierarchyRoom } from '$types/matrix-sdk'; import { Box, config, Text } from 'folds'; import { - HierarchyItem, - HierarchyItemRoom, - HierarchyItemSpace, + type HierarchyItem, + type HierarchyItemRoom, + type HierarchyItemSpace, useFetchSpaceHierarchyLevel, } from '$hooks/useSpaceHierarchy'; -import { IPowerLevels } from '$hooks/usePowerLevels'; +import { type IPowerLevels } from '$hooks/usePowerLevels'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { RoomType, StateEvent } from '$types/matrix/room'; import { SequenceCard } from '$components/sequence-card'; import { getRoomCreatorsForRoomId } from '$hooks/useRoomCreators'; import { getRoomPermissionsAPI } from '$hooks/useRoomPermissions'; import { SpaceItemCard } from './SpaceItem'; -import { AfterItemDropTarget, CanDropCallback } from './DnD'; +import { AfterItemDropTarget, type CanDropCallback } from './DnD'; import { HierarchyItemMenu } from './HierarchyItemMenu'; import { RoomItemCard } from './RoomItem'; @@ -95,7 +95,7 @@ export const SpaceHierarchyItem = forwardRef { - onSpacesFound(Array.from(subspaces.values())); + onSpacesFound([...subspaces.values()]); }, [subspaces, onSpacesFound]); let childItems: HierarchyItemRoom[] | undefined = roomItems?.filter( diff --git a/src/app/features/lobby/SpaceHierarchyNavItem.tsx b/src/app/features/lobby/SpaceHierarchyNavItem.tsx index def804f62..0a4f3c03e 100644 --- a/src/app/features/lobby/SpaceHierarchyNavItem.tsx +++ b/src/app/features/lobby/SpaceHierarchyNavItem.tsx @@ -1,13 +1,13 @@ import { forwardRef } from 'react'; -import { Room, IHierarchyRoom } from '$types/matrix-sdk'; +import { type Room, type IHierarchyRoom } from '$types/matrix-sdk'; import { Box } from 'folds'; -import { HierarchyItem, HierarchyItemSpace } from '$hooks/useSpaceHierarchy'; -import { IPowerLevels } from '$hooks/usePowerLevels'; +import { type HierarchyItem, type HierarchyItemSpace } from '$hooks/useSpaceHierarchy'; +import { type IPowerLevels } from '$hooks/usePowerLevels'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { StateEvent } from '$types/matrix/room'; import { getRoomCreatorsForRoomId } from '$hooks/useRoomCreators'; import { getRoomPermissionsAPI } from '$hooks/useRoomPermissions'; -import { AfterItemDropTarget, CanDropCallback } from './DnD'; +import { AfterItemDropTarget, type CanDropCallback } from './DnD'; import { HierarchyItemMenu } from './HierarchyItemMenu'; import { SpaceNavItemCard } from './SpaceNavItem'; diff --git a/src/app/features/lobby/SpaceItem.tsx b/src/app/features/lobby/SpaceItem.tsx index 3a95049d7..09c05e914 100644 --- a/src/app/features/lobby/SpaceItem.tsx +++ b/src/app/features/lobby/SpaceItem.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, ReactNode, useCallback, useRef, useState } from 'react'; +import { type MouseEventHandler, type ReactNode, useCallback, useRef, useState } from 'react'; import { Box, Avatar, @@ -14,11 +14,11 @@ import { config, Menu, MenuItem, - RectCords, + type RectCords, } from 'folds'; import classNames from 'classnames'; -import { MatrixError, Room, IHierarchyRoom } from '$types/matrix-sdk'; -import { HierarchyItem } from '$hooks/useSpaceHierarchy'; +import { type MatrixError, type Room, type IHierarchyRoom } from '$types/matrix-sdk'; +import { type HierarchyItem } from '$hooks/useSpaceHierarchy'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { RoomAvatar } from '$components/room-avatar'; import { nameInitials } from '$utils/common'; diff --git a/src/app/features/lobby/SpaceNavItem.tsx b/src/app/features/lobby/SpaceNavItem.tsx index c07ce6cbc..e7f01e9dc 100644 --- a/src/app/features/lobby/SpaceNavItem.tsx +++ b/src/app/features/lobby/SpaceNavItem.tsx @@ -1,11 +1,11 @@ -import { ReactNode, useRef } from 'react'; +import { type ReactNode, useRef } from 'react'; import { Avatar, Badge, Box, Chip, Icon, Icons, as, Text } from 'folds'; import classNames from 'classnames'; -import { IHierarchyRoom, MatrixClient, Room } from '$types/matrix-sdk'; +import { type IHierarchyRoom, type MatrixClient, type Room } from '$types/matrix-sdk'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { getCanonicalAliasOrRoomId, mxcUrlToHttp } from '$utils/matrix'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; -import { HierarchyItem } from '$hooks/useSpaceHierarchy'; +import { type HierarchyItem } from '$hooks/useSpaceHierarchy'; import { LocalRoomSummaryLoader } from '$components/RoomSummaryLoader'; import { getRoomAvatarUrl } from '$utils/room'; import { RoomAvatar } from '$components/room-avatar'; diff --git a/src/app/features/message-search/MessageSearch.tsx b/src/app/features/message-search/MessageSearch.tsx index db9191725..ec03c3dab 100644 --- a/src/app/features/message-search/MessageSearch.tsx +++ b/src/app/features/message-search/MessageSearch.tsx @@ -1,4 +1,4 @@ -import { RefObject, useEffect, useMemo, useRef } from 'react'; +import { type RefObject, useEffect, useMemo, useRef } from 'react'; import { Text, Box, Icon, Icons, config, Spinner, IconButton, Line, toRem } from 'folds'; import { useAtomValue } from 'jotai'; import { useVirtualizer } from '@tanstack/react-virtual'; @@ -7,7 +7,7 @@ import { useSearchParams } from 'react-router-dom'; import { SearchOrderBy } from '$types/matrix-sdk'; import { PageHero, PageHeroEmpty, PageHeroSection } from '$components/page'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { SearchPathSearchParams } from '$pages/paths'; +import { type SearchPathSearchParams } from '$pages/paths'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { SequenceCard } from '$components/sequence-card'; @@ -19,7 +19,7 @@ import { useRooms } from '$state/hooks/roomList'; import { allRoomsAtom } from '$state/room-list/roomList'; import { mDirectAtom } from '$state/mDirectList'; import { VirtualTile } from '$components/virtualizer'; -import { MessageSearchParams, useMessageSearch } from './useMessageSearch'; +import { type MessageSearchParams, useMessageSearch } from './useMessageSearch'; import { SearchResultGroup } from './SearchResultGroup'; import { SearchInput } from './SearchInput'; import { SearchFilters } from './SearchFilters'; @@ -113,7 +113,7 @@ export function MessageSearch({ const groups = useMemo(() => data?.pages.flatMap((result) => result.groups) ?? [], [data]); const highlights = useMemo(() => { const mixed = data?.pages.flatMap((result) => result.highlights); - return Array.from(new Set(mixed)); + return [...new Set(mixed)]; }, [data]); const virtualizer = useVirtualizer({ @@ -253,7 +253,7 @@ export function MessageSearch({ {((msgSearchParams.term && status === 'pending') || (groups.length > 0 && vItems.length === 0)) && ( - {[...new Array(8).keys()].map((key) => ( + {Array.from(new Array(8).keys(), (key) => ( ))} diff --git a/src/app/features/message-search/SearchFilters.tsx b/src/app/features/message-search/SearchFilters.tsx index 4c1abd75d..f955bb921 100644 --- a/src/app/features/message-search/SearchFilters.tsx +++ b/src/app/features/message-search/SearchFilters.tsx @@ -1,6 +1,6 @@ import { - ChangeEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type MouseEventHandler, useCallback, useEffect, useRef, @@ -23,7 +23,7 @@ import { Button, Input, Badge, - RectCords, + type RectCords, } from 'folds'; import { SearchOrderBy } from '$types/matrix-sdk'; import FocusTrap from 'focus-trap-react'; @@ -31,8 +31,12 @@ import { useVirtualizer } from '@tanstack/react-virtual'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { getRoomIconSrc } from '$utils/room'; import { factoryRoomIdByAtoZ } from '$utils/sort'; -import { SearchItemStrGetter, UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; -import { DebounceOptions, useDebounce } from '$hooks/useDebounce'; +import { + type SearchItemStrGetter, + type UseAsyncSearchOptions, + useAsyncSearch, +} from '$hooks/useAsyncSearch'; +import { type DebounceOptions, useDebounce } from '$hooks/useDebounce'; import { VirtualTile } from '$components/virtualizer'; import { stopPropagation } from '$utils/keyboard'; @@ -138,7 +142,7 @@ function SelectRoomButton({ roomList, selectedRooms, onChange }: SelectRoomButto getRoomNameStr, SEARCH_OPTS ); - const rooms = Array.from(searchResult?.items ?? roomList).sort(factoryRoomIdByAtoZ(mx)); + const rooms = (searchResult?.items ?? roomList).toSorted(factoryRoomIdByAtoZ(mx)); const virtualizer = useVirtualizer({ count: rooms.length, diff --git a/src/app/features/message-search/SearchInput.tsx b/src/app/features/message-search/SearchInput.tsx index ad1ca593d..de927c277 100644 --- a/src/app/features/message-search/SearchInput.tsx +++ b/src/app/features/message-search/SearchInput.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, RefObject } from 'react'; +import { type FormEventHandler, type RefObject } from 'react'; import { Box, Text, Input, Icon, Icons, Spinner, Chip, config } from 'folds'; type SearchProps = { diff --git a/src/app/features/message-search/SearchResultGroup.tsx b/src/app/features/message-search/SearchResultGroup.tsx index c3450c6ec..e8b96c610 100644 --- a/src/app/features/message-search/SearchResultGroup.tsx +++ b/src/app/features/message-search/SearchResultGroup.tsx @@ -1,9 +1,9 @@ /* eslint-disable react/destructuring-assignment */ -import { MouseEventHandler, useMemo } from 'react'; -import { IEventWithRoomId, JoinRule, RelationType, Room } from '$types/matrix-sdk'; -import { HTMLReactParserOptions } from 'html-react-parser'; +import { type MouseEventHandler, useMemo } from 'react'; +import { type IEventWithRoomId, JoinRule, RelationType, type Room } from '$types/matrix-sdk'; +import { type HTMLReactParserOptions } from 'html-react-parser'; import { Avatar, Box, Chip, Header, Icon, Icons, Text, config } from 'folds'; -import { Opts as LinkifyOpts } from 'linkifyjs'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { factoryRenderLinkifyWithMention, @@ -15,7 +15,7 @@ import { } from '$plugins/react-custom-html-parser'; import { getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix'; import { useMatrixEventRenderer } from '$hooks/useMatrixEventRenderer'; -import { GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; +import { type GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; import { AvatarBase, ImageContent, @@ -53,7 +53,7 @@ import { import { useRoomCreators } from '$hooks/useRoomCreators'; import { useRoomCreatorsTag } from '$hooks/useRoomCreatorsTag'; import { useSettingsLinkBaseUrl } from '$features/settings/useSettingsLinkBaseUrl'; -import { ResultItem } from './useMessageSearch'; +import { type ResultItem } from './useMessageSearch'; type SearchResultGroupProps = { room: Room; diff --git a/src/app/features/message-search/useMessageSearch.ts b/src/app/features/message-search/useMessageSearch.ts index de1b15585..b860eeea3 100644 --- a/src/app/features/message-search/useMessageSearch.ts +++ b/src/app/features/message-search/useMessageSearch.ts @@ -1,10 +1,10 @@ import { - IEventWithRoomId, - IResultContext, - ISearchRequestBody, - ISearchResponse, - ISearchResult, - SearchOrderBy, + type IEventWithRoomId, + type IResultContext, + type ISearchRequestBody, + type ISearchResponse, + type ISearchResult, + type SearchOrderBy, } from '$types/matrix-sdk'; import { useCallback } from 'react'; import { useMatrixClient } from '$hooks/useMatrixClient'; @@ -37,7 +37,7 @@ const groupSearchResult = (results: ISearchResult[]): ResultGroup[] => { context: item.context, }; - const lastAddedGroup: ResultGroup | undefined = groups[groups.length - 1]; + const lastAddedGroup: ResultGroup | undefined = groups.at(-1); if (lastAddedGroup && roomId === lastAddedGroup.roomId) { lastAddedGroup.items.push(resultItem); return; diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx index f9d279062..b73603bb3 100644 --- a/src/app/features/room-nav/RoomNavItem.tsx +++ b/src/app/features/room-nav/RoomNavItem.tsx @@ -1,5 +1,5 @@ -import { MouseEventHandler, forwardRef, useState, MouseEvent, useEffect } from 'react'; -import { Room, RoomEvent as RoomEventEnum } from '$types/matrix-sdk'; +import { type MouseEventHandler, forwardRef, useState, type MouseEvent, useEffect } from 'react'; +import { type Room, RoomEvent as RoomEventEnum } from '$types/matrix-sdk'; import { Avatar, Box, @@ -13,7 +13,7 @@ import { PopOut, toRem, Line, - RectCords, + type RectCords, Badge, Spinner, Tooltip, diff --git a/src/app/features/room-nav/RoomNavUser.tsx b/src/app/features/room-nav/RoomNavUser.tsx index 7d25d7bf9..ed62ced95 100644 --- a/src/app/features/room-nav/RoomNavUser.tsx +++ b/src/app/features/room-nav/RoomNavUser.tsx @@ -1,7 +1,7 @@ import { Avatar, Box, Icon, Icons, Text } from 'folds'; -import { MouseEventHandler } from 'react'; +import { type MouseEventHandler } from 'react'; import { useAtomValue } from 'jotai'; -import { Room, CallMembership } from '$types/matrix-sdk'; +import { type Room, type CallMembership } from '$types/matrix-sdk'; import { NavButton, NavItem, NavItemContent } from '$components/nav'; import { UserAvatar } from '$components/user-avatar'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/features/room-settings/RoomSettings.tsx b/src/app/features/room-settings/RoomSettings.tsx index 02fb0e849..7b205c0e5 100644 --- a/src/app/features/room-settings/RoomSettings.tsx +++ b/src/app/features/room-settings/RoomSettings.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; import { useAtomValue } from 'jotai'; -import { Avatar, Box, config, Icon, IconButton, Icons, IconSrc, MenuItem, Text } from 'folds'; +import { Avatar, Box, config, Icon, IconButton, Icons, type IconSrc, MenuItem, Text } from 'folds'; import { JoinRule } from '$types/matrix-sdk'; import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '$components/page'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; diff --git a/src/app/features/room-settings/RoomSettingsRenderer.tsx b/src/app/features/room-settings/RoomSettingsRenderer.tsx index 7f2e53e79..d9dcbecf6 100644 --- a/src/app/features/room-settings/RoomSettingsRenderer.tsx +++ b/src/app/features/room-settings/RoomSettingsRenderer.tsx @@ -1,7 +1,7 @@ import { Modal500 } from '$components/Modal500'; import { useCloseRoomSettings, useRoomSettingsState } from '$state/hooks/roomSettings'; import { useAllJoinedRoomsSet, useGetRoom } from '$hooks/useGetRoom'; -import { RoomSettingsState } from '$state/roomSettings'; +import { type RoomSettingsState } from '$state/roomSettings'; import { RoomProvider } from '$hooks/useRoom'; import { SpaceProvider } from '$hooks/useSpace'; import { RoomSettings } from './RoomSettings'; diff --git a/src/app/features/room-settings/abbreviations/RoomAbbreviations.tsx b/src/app/features/room-settings/abbreviations/RoomAbbreviations.tsx index 1354f5691..094a870fc 100644 --- a/src/app/features/room-settings/abbreviations/RoomAbbreviations.tsx +++ b/src/app/features/room-settings/abbreviations/RoomAbbreviations.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useMemo } from 'react'; +import { type FormEventHandler, useCallback, useMemo } from 'react'; import { useAtomValue } from 'jotai'; import { Box, @@ -26,8 +26,8 @@ import { useStateEventCallback } from '$hooks/useStateEventCallback'; import { useForceUpdate } from '$hooks/useForceUpdate'; import { StateEvent } from '$types/matrix/room'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; -import { MatrixError } from '$types/matrix-sdk'; -import { AbbreviationEntry, RoomAbbreviationsContent } from '$utils/abbreviations'; +import { type MatrixError } from '$types/matrix-sdk'; +import { type AbbreviationEntry, type RoomAbbreviationsContent } from '$utils/abbreviations'; import { getAllParents, getStateEvent } from '$utils/room'; import { roomToParentsAtom } from '$state/room/roomToParents'; import { SequenceCardStyle } from '$features/common-settings/styles.css'; @@ -70,7 +70,7 @@ export function RoomAbbreviations({ requestClose, isSpace }: AbbreviationsProps) type SpaceEntryGroup = { spaceId: string; spaceName: string; entries: AbbreviationEntry[] }; const ancestorGroups = useMemo( (): SpaceEntryGroup[] => - Array.from(getAllParents(roomToParents, room.roomId)).reduce( + [...getAllParents(roomToParents, room.roomId)].reduce( (groups, parentId) => { const parentRoom = mx.getRoom(parentId); if (!parentRoom) return groups; diff --git a/src/app/features/room-settings/permissions/usePermissionItems.ts b/src/app/features/room-settings/permissions/usePermissionItems.ts index 20222d45f..7d967c754 100644 --- a/src/app/features/room-settings/permissions/usePermissionItems.ts +++ b/src/app/features/room-settings/permissions/usePermissionItems.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { MessageEvent, StateEvent } from '$types/matrix/room'; -import { PermissionGroup } from '$features/common-settings/permissions'; +import { type PermissionGroup } from '$features/common-settings/permissions'; export const usePermissionGroups = (isCallRoom: boolean): PermissionGroup[] => { const groups: PermissionGroup[] = useMemo(() => { diff --git a/src/app/features/room/CommandAutocomplete.tsx b/src/app/features/room/CommandAutocomplete.tsx index 42ef1374d..76e00e7db 100644 --- a/src/app/features/room/CommandAutocomplete.tsx +++ b/src/app/features/room/CommandAutocomplete.tsx @@ -1,16 +1,16 @@ -import { KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo } from 'react'; -import { Editor } from 'slate'; +import { type KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo } from 'react'; +import { type Editor } from 'slate'; import { Box, config, MenuItem, Text } from 'folds'; -import { Room } from '$types/matrix-sdk'; -import { Command, useCommands } from '$hooks/useCommands'; +import { type Room } from '$types/matrix-sdk'; +import { type Command, useCommands } from '$hooks/useCommands'; import { AutocompleteMenu, - AutocompleteQuery, + type AutocompleteQuery, createCommandElement, moveCursor, replaceWithElement, } from '$components/editor'; -import { UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; +import { type UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useKeyDown } from '$hooks/useKeyDown'; import { onTabPress } from '$utils/keyboard'; diff --git a/src/app/features/room/MembersDrawer.tsx b/src/app/features/room/MembersDrawer.tsx index 0a74acfda..6d5efc64e 100644 --- a/src/app/features/room/MembersDrawer.tsx +++ b/src/app/features/room/MembersDrawer.tsx @@ -1,6 +1,6 @@ import { - ChangeEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type MouseEventHandler, useCallback, useMemo, useRef, @@ -18,7 +18,7 @@ import { Input, MenuItem, PopOut, - RectCords, + type RectCords, Scroll, Spinner, Text, @@ -27,7 +27,7 @@ import { config, toRem, } from 'folds'; -import { MatrixClient, Room, RoomMember } from '$types/matrix-sdk'; +import { type MatrixClient, type Room, type RoomMember } from '$types/matrix-sdk'; import { useVirtualizer } from '@tanstack/react-virtual'; import classNames from 'classnames'; @@ -35,7 +35,11 @@ import { AvatarPresence, PresenceBadge } from '$components/presence'; import { useUserPresence } from '$hooks/useUserPresence'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { UseStateProvider } from '$components/UseStateProvider'; -import { SearchItemStrGetter, UseAsyncSearchOptions, useAsyncSearch } from '$hooks/useAsyncSearch'; +import { + type SearchItemStrGetter, + type UseAsyncSearchOptions, + useAsyncSearch, +} from '$hooks/useAsyncSearch'; import { useDebounce } from '$hooks/useDebounce'; import { TypingIndicator } from '$components/typing-indicator'; import { getMemberDisplayName, getMemberSearchStr } from '$utils/room'; diff --git a/src/app/features/room/RoomCallButton.tsx b/src/app/features/room/RoomCallButton.tsx index ad3c7916d..c12c9a0a5 100644 --- a/src/app/features/room/RoomCallButton.tsx +++ b/src/app/features/room/RoomCallButton.tsx @@ -1,14 +1,14 @@ import { IconButton, Icon, Icons, TooltipProvider, Tooltip, Text } from 'folds'; import { useAtomValue } from 'jotai'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useCallStart, useCallJoined } from '$hooks/useCallEmbed'; import { callEmbedAtom } from '$state/callEmbed'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useCallPreferences } from '$state/hooks/callPreferences'; -interface RoomCallButtonProps { +type RoomCallButtonProps = { room: Room; -} +}; export function RoomCallButton({ room }: RoomCallButtonProps) { const startCall = useCallStart(); diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 70eca4dc2..ab84f552f 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -1,8 +1,8 @@ import { forwardRef, - KeyboardEventHandler, - MouseEvent, - RefObject, + type KeyboardEventHandler, + type MouseEvent, + type RefObject, useCallback, useEffect, useRef, @@ -13,13 +13,13 @@ import { useAtom, useAtomValue } from 'jotai'; import { isKeyHotkey } from 'is-hotkey'; import { EventType, - IContent, - MatrixEvent, + type IContent, + type MatrixEvent, MsgType, RelationType, - Room, - IEventRelation, - StickerEventContent, + type Room, + type IEventRelation, + type StickerEventContent, } from '$types/matrix-sdk'; import { ReactEditor } from 'slate-react'; import { Editor, Point, Range, Transforms } from 'slate'; @@ -38,7 +38,7 @@ import { OverlayBackdrop, OverlayCenter, PopOut, - RectCords, + type RectCords, Scroll, Text, toRem, @@ -47,7 +47,7 @@ import { import { useMatrixClient } from '$hooks/useMatrixClient'; import { AutocompletePrefix, - AutocompleteQuery, + type AutocompleteQuery, createEmoticonElement, CustomEditor, customHtmlEqualsPlainText, @@ -73,7 +73,7 @@ import { import { EmojiBoard, EmojiBoardTab } from '$components/emoji-board'; import { UseStateProvider } from '$components/UseStateProvider'; import { - TUploadContent, + type TUploadContent, encryptFile, getImageInfo, mxcUrlToHttp, @@ -88,18 +88,23 @@ import { roomIdToReplyDraftAtomFamily, roomIdToUploadItemsAtomFamily, roomUploadAtomFamily, - TUploadItem, - TUploadMetadata, - IReplyDraft, + type TUploadItem, + type TUploadMetadata, + type IReplyDraft, } from '$state/room/roomInputDrafts'; import { UploadCardRenderer } from '$components/upload-card'; import { UploadBoard, UploadBoardContent, UploadBoardHeader, - UploadBoardImperativeHandlers, + type UploadBoardImperativeHandlers, } from '$components/upload-board'; -import { Upload, UploadStatus, UploadSuccess, createUploadFamilyObserverAtom } from '$state/upload'; +import { + type Upload, + UploadStatus, + type UploadSuccess, + createUploadFamilyObserverAtom, +} from '$state/upload'; import { getImageUrlBlob, loadImageElement } from '$utils/dom'; import { safeFile } from '$utils/mimeTypes'; import { fulfilledPromiseSettledResult } from '$utils/common'; @@ -158,8 +163,8 @@ import { import { CommandAutocomplete } from './CommandAutocomplete'; import { AudioMessageRecorder, - AudioMessageRecorderHandle, - AudioRecordingCompletePayload, + type AudioMessageRecorderHandle, + type AudioRecordingCompletePayload, } from './AudioMessageRecorder'; // Returns the event ID of the most recent non-reaction/non-edit event in a thread, @@ -171,7 +176,8 @@ const getLatestThreadEventId = (room: Room, threadRootId: string): string => { (ev) => ev.getId() !== threadRootId && !reactionOrEditEvent(ev) ); if (filtered.length > 0) { - return filtered[filtered.length - 1].getId() ?? threadRootId; + const latestThreadEvent = filtered.at(-1); + return latestThreadEvent?.getId() ?? threadRootId; } // Fall back to the live timeline if the Thread object hasn't been registered yet const liveEvents = room @@ -183,7 +189,8 @@ const getLatestThreadEventId = (room: Room, threadRootId: string): string => { ev.threadRootId === threadRootId && ev.getId() !== threadRootId && !reactionOrEditEvent(ev) ); if (liveEvents.length > 0) { - return liveEvents[liveEvents.length - 1].getId() ?? threadRootId; + const latestLiveEvent = liveEvents.at(-1); + return latestLiveEvent?.getId() ?? threadRootId; } return threadRootId; }; @@ -228,18 +235,18 @@ const getReplyContent = (replyDraft: IReplyDraft | undefined, room?: Room): IEve const log = createLogger('RoomInput'); const debugLog = createDebugLogger('RoomInput'); -interface ReplyEventContent { +type ReplyEventContent = { 'm.relates_to'?: IEventRelation; -} +}; -interface RoomInputProps { +type RoomInputProps = { editor: Editor; fileDropContainerRef: RefObject; roomId: string; room: Room; threadRootId?: string; onEditLastMessage?: () => void; -} +}; export const RoomInput = forwardRef( ({ editor, fileDropContainerRef, roomId, room, threadRootId, onEditLastMessage }, ref) => { // When in thread mode, isolate drafts by thread root ID so thread replies @@ -775,7 +782,7 @@ export const RoomInput = forwardRef( mentionData.users.add(replyDraft.userId); } - content['m.mentions'] = getMentionContent(Array.from(mentionData.users), mentionData.room); + content['m.mentions'] = getMentionContent([...mentionData.users], mentionData.room); if (replyDraft || !customHtmlEqualsPlainText(formattedBody, body)) { content.format = 'org.matrix.custom.html'; @@ -1140,20 +1147,18 @@ export const RoomInput = forwardRef( {uploadBoard && ( - {Array.from(selectedFiles) - .reverse() - .map((fileItem, index) => ( - - ))} + {selectedFiles.toReversed().map((fileItem, index) => ( + + ))} )} diff --git a/src/app/features/room/RoomInputPlaceholder.tsx b/src/app/features/room/RoomInputPlaceholder.tsx index fc923d196..319660755 100644 --- a/src/app/features/room/RoomInputPlaceholder.tsx +++ b/src/app/features/room/RoomInputPlaceholder.tsx @@ -1,4 +1,4 @@ -import { ComponentProps } from 'react'; +import { type ComponentProps } from 'react'; import { Box, as } from 'folds'; import classNames from 'classnames'; diff --git a/src/app/features/room/RoomTimeline.css.ts b/src/app/features/room/RoomTimeline.css.ts index 7b355fe1e..df0812b2a 100644 --- a/src/app/features/room/RoomTimeline.css.ts +++ b/src/app/features/room/RoomTimeline.css.ts @@ -1,5 +1,5 @@ import { globalStyle, style } from '@vanilla-extract/css'; -import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; +import { type RecipeVariants, recipe } from '@vanilla-extract/recipes'; import { DefaultReset, color, config, toRem } from 'folds'; export const TimelineFloat = recipe({ diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx index c37c6d385..8a006e23b 100644 --- a/src/app/features/room/RoomTimeline.tsx +++ b/src/app/features/room/RoomTimeline.tsx @@ -1,6 +1,6 @@ import { Fragment, - ReactNode, + type ReactNode, useCallback, useEffect, useLayoutEffect, @@ -8,11 +8,11 @@ import { useRef, useState, } from 'react'; -import { Editor } from 'slate'; +import { type Editor } from 'slate'; import { useAtomValue, useSetAtom } from 'jotai'; -import { PushProcessor, Room, Direction } from '$types/matrix-sdk'; +import { PushProcessor, type Room, Direction } from '$types/matrix-sdk'; import classNames from 'classnames'; -import { VList, VListHandle } from 'virtua'; +import { VList, type VListHandle } from 'virtua'; import { as, Box, @@ -25,7 +25,7 @@ import { color, config, toRem, - ContainerColor, + type ContainerColor, Spinner, } from 'folds'; import { MessageBase, CompactPlaceholder, DefaultPlaceholder } from '$components/message'; @@ -74,7 +74,7 @@ import { } from '$utils/timeline'; import { useTimelineSync } from '$hooks/timeline/useTimelineSync'; import { useTimelineActions } from '$hooks/timeline/useTimelineActions'; -import { ProcessedEvent, useProcessedTimeline } from '$hooks/timeline/useProcessedTimeline'; +import { type ProcessedEvent, useProcessedTimeline } from '$hooks/timeline/useProcessedTimeline'; import { useTimelineEventRenderer } from '$hooks/timeline/useTimelineEventRenderer'; import * as css from './RoomTimeline.css'; @@ -748,8 +748,8 @@ export function RoomTimeline({ const ref = onEditLastMessageRef; ref.current = () => { const myUserId = mx.getUserId(); - const found = [...processedEventsRef.current] - .reverse() + const found = processedEventsRef.current + .toReversed() .find( (e) => e.mEvent.getSender() === myUserId && diff --git a/src/app/features/room/RoomViewFollowing.tsx b/src/app/features/room/RoomViewFollowing.tsx index 6d9a60c68..946c5923b 100644 --- a/src/app/features/room/RoomViewFollowing.tsx +++ b/src/app/features/room/RoomViewFollowing.tsx @@ -11,7 +11,7 @@ import { as, config, } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import classNames from 'classnames'; import FocusTrap from 'focus-trap-react'; import { useAtomValue } from 'jotai'; diff --git a/src/app/features/room/RoomViewHeader.tsx b/src/app/features/room/RoomViewHeader.tsx index 8552332cf..3818c9946 100644 --- a/src/app/features/room/RoomViewHeader.tsx +++ b/src/app/features/room/RoomViewHeader.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, forwardRef, useCallback, useEffect, useState } from 'react'; +import { type MouseEventHandler, forwardRef, useCallback, useEffect, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { useAtom, useAtomValue } from 'jotai'; import { @@ -19,17 +19,17 @@ import { config, Line, PopOut, - RectCords, + type RectCords, Badge, Spinner, } from 'folds'; import { useNavigate } from 'react-router-dom'; import { EventTimeline, - Room, + type Room, ThreadEvent, RoomEvent, - MatrixEvent, + type MatrixEvent, NotificationCountType, } from '$types/matrix-sdk'; @@ -96,20 +96,20 @@ import { RoomCallButton } from './RoomCallButton'; const log = createLogger('RoomViewHeader'); async function getPinsHash(pinnedIds: string[]): Promise { - const sorted = [...pinnedIds].sort().join(','); + const sorted = pinnedIds.toSorted().join(','); const encoder = new TextEncoder(); const data = encoder.encode(sorted); const hashBuffer = await crypto.subtle.digest('SHA-256', data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashArray = [...new Uint8Array(hashBuffer)]; const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); return hashHex.slice(0, 10); } -export interface PinReadMarker { +export type PinReadMarker = { hash: string; count: number; last_seen_id: string; -} +}; type RoomMenuProps = { room: Room; diff --git a/src/app/features/room/RoomViewTyping.tsx b/src/app/features/room/RoomViewTyping.tsx index e75c4a13d..fd3912263 100644 --- a/src/app/features/room/RoomViewTyping.tsx +++ b/src/app/features/room/RoomViewTyping.tsx @@ -1,5 +1,5 @@ import { Box, Icon, IconButton, Icons, Text, as } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import classNames from 'classnames'; import { useSetAtom, useAtomValue } from 'jotai'; import { roomIdToTypingMembersAtom } from '$state/typingMembers'; diff --git a/src/app/features/room/ThreadBrowser.tsx b/src/app/features/room/ThreadBrowser.tsx index 0d9240c38..eaf109002 100644 --- a/src/app/features/room/ThreadBrowser.tsx +++ b/src/app/features/room/ThreadBrowser.tsx @@ -1,6 +1,6 @@ import { - ChangeEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type MouseEventHandler, useCallback, useEffect, useMemo, @@ -20,10 +20,10 @@ import { config, Chip, } from 'folds'; -import { MatrixEvent, Room, Thread, ThreadEvent } from '$types/matrix-sdk'; +import { type MatrixEvent, type Room, type Thread, ThreadEvent } from '$types/matrix-sdk'; import { useAtomValue } from 'jotai'; -import { HTMLReactParserOptions } from 'html-react-parser'; -import { Opts as LinkifyOpts } from 'linkifyjs'; +import { type HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { useSettingsLinkBaseUrl } from '$features/settings/useSettingsLinkBaseUrl'; @@ -44,7 +44,7 @@ import { import { RenderMessageContent } from '$components/RenderMessageContent'; import { settingsAtom } from '$state/settings'; import { useSetting } from '$state/hooks/settings'; -import { GetContentCallback } from '$types/matrix/room'; +import { type GetContentCallback } from '$types/matrix/room'; import { useMentionClickHandler } from '$hooks/useMentionClickHandler'; import { useSpoilerClickHandler } from '$hooks/useSpoilerClickHandler'; import { diff --git a/src/app/features/room/ThreadDrawer.tsx b/src/app/features/room/ThreadDrawer.tsx index 73b6e7787..09e8f7823 100644 --- a/src/app/features/room/ThreadDrawer.tsx +++ b/src/app/features/room/ThreadDrawer.tsx @@ -1,18 +1,18 @@ -import { MouseEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { type MouseEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Header, Icon, IconButton, Icons, Scroll, Text, config } from 'folds'; import { - MatrixEvent, + type MatrixEvent, PushProcessor, ReceiptType, RelationType, - Room, + type Room, RoomEvent, ThreadEvent, } from '$types/matrix-sdk'; import { useAtomValue, useSetAtom } from 'jotai'; import { ReactEditor } from 'slate-react'; -import { HTMLReactParserOptions } from 'html-react-parser'; -import { Opts as LinkifyOpts } from 'linkifyjs'; +import { type HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; import { ImageContent, MSticker, RedactedContent, Reply } from '$components/message'; import { RenderMessageContent } from '$components/RenderMessageContent'; import { Image } from '$components/media'; @@ -37,20 +37,20 @@ import { useMatrixClient } from '$hooks/useMatrixClient'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { useSettingsLinkBaseUrl } from '$features/settings/useSettingsLinkBaseUrl'; import { nicknamesAtom } from '$state/nicknames'; -import { MessageLayout, MessageSpacing, settingsAtom } from '$state/settings'; +import { MessageLayout, type MessageSpacing, settingsAtom } from '$state/settings'; import { useSetting } from '$state/hooks/settings'; import { useRoomAbbreviationsContext } from '$hooks/useRoomAbbreviations'; import { buildAbbrReplaceTextNode } from '$components/message/RenderBody'; import { createMentionElement, moveCursor, useEditor } from '$components/editor'; import { useMentionClickHandler } from '$hooks/useMentionClickHandler'; import { useSpoilerClickHandler } from '$hooks/useSpoilerClickHandler'; -import { GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; +import { type GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; import { usePowerLevelsContext } from '$hooks/usePowerLevels'; import { useRoomPermissions } from '$hooks/useRoomPermissions'; import { useRoomCreators } from '$hooks/useRoomCreators'; import { useImagePackRooms } from '$hooks/useImagePackRooms'; import { useOpenUserRoomProfile } from '$state/hooks/userRoomProfile'; -import { IReplyDraft, roomIdToReplyDraftAtomFamily } from '$state/room/roomInputDrafts'; +import { type IReplyDraft, roomIdToReplyDraftAtomFamily } from '$state/room/roomInputDrafts'; import { roomToParentsAtom } from '$state/room/roomToParents'; import { EncryptedContent, Message, Reactions } from './message'; import { RoomInput } from './RoomInput'; @@ -548,7 +548,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra const events = thread.events || []; if (events.length === 0) return; - const lastEvent = events[events.length - 1]; + const lastEvent = events.at(-1); if (!lastEvent || lastEvent.isSending()) return; const userId = mx.getUserId(); @@ -710,7 +710,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra const isInReplies = replyEventsRef.current.some((e) => e.getId() === targetId); if (!isRoot && !isInReplies) return; setJumpToEventId(targetId); - setTimeout(() => setJumpToEventId(undefined), 2500); + setTimeout(setJumpToEventId, 2500, undefined); const el = drawerRef.current; if (el) { const target = el.querySelector(`[data-message-id="${targetId}"]`); @@ -753,9 +753,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra const threadParticipantIds = new Set( [rootEvent, ...replyEvents].map((ev) => ev?.getSender()).filter(Boolean) as string[] ); - const latestThreadEventId = ( - replyEvents.length > 0 ? replyEvents[replyEvents.length - 1] : rootEvent - )?.getId(); + const latestThreadEventId = (replyEvents.length > 0 ? replyEvents.at(-1) : rootEvent)?.getId(); return ( { const eventId = mEvent.getId(); const pinContent: RoomPinnedEventsEventContent = { - pinned: Array.from(pinnedEvents).filter((id) => id !== eventId), + pinned: [...pinnedEvents].filter((id) => id !== eventId), }; if (!isPinned && eventId) { pinContent.pinned.push(eventId); diff --git a/src/app/features/room/message/MessageEditor.tsx b/src/app/features/room/message/MessageEditor.tsx index e30ebe1b5..4432bf5fd 100644 --- a/src/app/features/room/message/MessageEditor.tsx +++ b/src/app/features/room/message/MessageEditor.tsx @@ -1,6 +1,6 @@ import { - KeyboardEventHandler, - MouseEventHandler, + type KeyboardEventHandler, + type MouseEventHandler, useCallback, useEffect, useMemo, @@ -14,7 +14,7 @@ import { Icons, Line, PopOut, - RectCords, + type RectCords, Spinner, Text, as, @@ -23,19 +23,19 @@ import { import { Editor, Transforms } from 'slate'; import { ReactEditor } from 'slate-react'; import { - IContent, - IMentions, - MatrixEvent, - ReplacementEvent, + type IContent, + type IMentions, + type MatrixEvent, + type ReplacementEvent, RelationType, - Room, - RoomMessageTextEventContent, + type Room, + type RoomMessageTextEventContent, MsgType, } from '$types/matrix-sdk'; import { isKeyHotkey } from 'is-hotkey'; import { AutocompletePrefix, - AutocompleteQuery, + type AutocompleteQuery, CustomEditor, EmoticonAutocomplete, RoomMentionAutocomplete, @@ -69,10 +69,10 @@ import { RenderMessageContent } from '$components/RenderMessageContent'; import { useSettingsLinkBaseUrl } from '$features/settings/useSettingsLinkBaseUrl'; import { getReactCustomHtmlParser, LINKIFY_OPTS } from '$plugins/react-custom-html-parser'; import { useSpoilerClickHandler } from '$hooks/useSpoilerClickHandler'; -import { HTMLReactParserOptions } from 'html-react-parser'; +import { type HTMLReactParserOptions } from 'html-react-parser'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; -import { Opts as LinkifyOpts } from 'linkifyjs'; -import { GetContentCallback } from '$types/matrix/room'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; +import { type GetContentCallback } from '$types/matrix/room'; import { sanitizeText } from '$utils/sanitize'; type MessageEditorProps = { @@ -226,7 +226,7 @@ export const MessageEditor = as<'div', MessageEditorProps>( mentionData.users.add(prevMentionId); }); - const mMentions = getMentionContent(Array.from(mentionData.users), mentionData.room); + const mMentions = getMentionContent([...mentionData.users], mentionData.room); newContent['m.mentions'] = mMentions; contentBody['m.mentions'] = mMentions; diff --git a/src/app/features/room/message/Reactions.tsx b/src/app/features/room/message/Reactions.tsx index efcd74dc9..d0c03c695 100644 --- a/src/app/features/room/message/Reactions.tsx +++ b/src/app/features/room/message/Reactions.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useState } from 'react'; +import { type MouseEventHandler, useCallback, useState } from 'react'; import { Box, Modal, @@ -12,8 +12,7 @@ import { toRem, } from 'folds'; import classNames from 'classnames'; -import { Room } from '$types/matrix-sdk'; -import { type Relations } from '$types/matrix-sdk'; +import { type Room, type Relations } from '$types/matrix-sdk'; import FocusTrap from 'focus-trap-react'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { factoryEventSentBy } from '$utils/matrix'; @@ -72,7 +71,7 @@ export const Reactions = as<'div', ReactionsProps>( ref={ref} > {reactions.map(([key, events]) => { - const rEvents = Array.from(events); + const rEvents = [...events]; if (rEvents.length === 0 || typeof key !== 'string') return null; const myREvent = myUserId ? rEvents.find(factoryEventSentBy(myUserId)) : undefined; const isPressed = !!myREvent?.getRelation(); diff --git a/src/app/features/room/msgContent.ts b/src/app/features/room/msgContent.ts index 0cec05a57..8d85d13db 100644 --- a/src/app/features/room/msgContent.ts +++ b/src/app/features/room/msgContent.ts @@ -1,7 +1,7 @@ -import { IContent, MatrixClient, MsgType } from '$types/matrix-sdk'; +import { type IContent, type MatrixClient, MsgType } from '$types/matrix-sdk'; import to from 'await-to-js'; import { - IThumbnailContent, + type IThumbnailContent, MATRIX_BLUR_HASH_PROPERTY_NAME, MATRIX_SPOILER_PROPERTY_NAME, } from '$types/matrix/common'; @@ -14,7 +14,7 @@ import { loadVideoElement, } from '$utils/dom'; import { encryptFile, getImageInfo, getThumbnailContent, getVideoInfo } from '$utils/matrix'; -import { TUploadItem } from '$state/room/roomInputDrafts'; +import { type TUploadItem } from '$state/room/roomInputDrafts'; import { encodeBlurHash } from '$utils/blurHash'; import { scaleYDimension } from '$utils/common'; import { createLogger } from '$utils/debug'; diff --git a/src/app/features/room/reaction-viewer/ReactionViewer.tsx b/src/app/features/room/reaction-viewer/ReactionViewer.tsx index 9065cfb64..059ea39a5 100644 --- a/src/app/features/room/reaction-viewer/ReactionViewer.tsx +++ b/src/app/features/room/reaction-viewer/ReactionViewer.tsx @@ -14,7 +14,7 @@ import { as, config, } from 'folds'; -import { MatrixEvent, Room, RoomMember, Relations } from '$types/matrix-sdk'; +import { type MatrixEvent, type Room, type RoomMember, type Relations } from '$types/matrix-sdk'; import { getMemberDisplayName } from '$utils/room'; import { eventWithShortcode, getMxIdLocalPart } from '$utils/matrix'; import { useMatrixClient } from '$hooks/useMatrixClient'; @@ -62,7 +62,7 @@ export const ReactionViewer = as<'div', ReactionViewerProps>( const getReactionsForKey = (key: string): MatrixEvent[] => { const reactSet = reactions.find(([k]) => k === key)?.[1]; if (!reactSet) return []; - return Array.from(reactSet); + return [...reactSet]; }; const selectedReactions = getReactionsForKey(selectedKey); diff --git a/src/app/features/room/room-pin-menu/RoomPinMenu.tsx b/src/app/features/room/room-pin-menu/RoomPinMenu.tsx index dd34bb487..38f231630 100644 --- a/src/app/features/room/room-pin-menu/RoomPinMenu.tsx +++ b/src/app/features/room/room-pin-menu/RoomPinMenu.tsx @@ -1,6 +1,13 @@ /* eslint-disable react/no-unused-prop-types, react/destructuring-assignment */ -import { forwardRef, MouseEventHandler, ReactNode, useCallback, useMemo, useRef } from 'react'; -import { MatrixEvent, Room, RoomPinnedEventsEventContent } from '$types/matrix-sdk'; +import { + forwardRef, + type MouseEventHandler, + type ReactNode, + useCallback, + useMemo, + useRef, +} from 'react'; +import { type MatrixEvent, type Room, type RoomPinnedEventsEventContent } from '$types/matrix-sdk'; import { Avatar, Box, @@ -17,8 +24,8 @@ import { Text, toRem, } from 'folds'; -import { Opts as LinkifyOpts } from 'linkifyjs'; -import { HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; +import { type HTMLReactParserOptions } from 'html-react-parser'; import { useAtomValue } from 'jotai'; import { useVirtualizer } from '@tanstack/react-virtual'; import { createLogger } from '$utils/debug'; @@ -49,7 +56,7 @@ import { getMemberDisplayName, getStateEvent, } from '$utils/room'; -import { GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; +import { type GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; import { useMentionClickHandler } from '$hooks/useMentionClickHandler'; import { useSpoilerClickHandler } from '$hooks/useSpoilerClickHandler'; import { useSettingsLinkBaseUrl } from '$features/settings/useSettingsLinkBaseUrl'; @@ -60,7 +67,7 @@ import { makeMentionCustomProps, renderMatrixMention, } from '$plugins/react-custom-html-parser'; -import { RenderMatrixEvent, useMatrixEventRenderer } from '$hooks/useMatrixEventRenderer'; +import { type RenderMatrixEvent, useMatrixEventRenderer } from '$hooks/useMatrixEventRenderer'; import { RenderMessageContent } from '$components/RenderMessageContent'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; @@ -78,7 +85,7 @@ import { PowerIcon } from '$components/power'; import { useRoomCreators } from '$hooks/useRoomCreators'; import { useRoomPermissions } from '$hooks/useRoomPermissions'; import { - GetMemberPowerTag, + type GetMemberPowerTag, getPowerTagIconSrc, useAccessiblePowerTagColors, useGetMemberPowerTag, @@ -304,7 +311,7 @@ export const RoomPinMenu = forwardRef( ); const pinnedEvents = useRoomPinnedEvents(room); - const sortedPinnedEvent = useMemo(() => Array.from(pinnedEvents).reverse(), [pinnedEvents]); + const sortedPinnedEvent = useMemo(() => pinnedEvents.toReversed(), [pinnedEvents]); const useAuthentication = useMediaAuthentication(); const [mediaAutoLoad] = useSetting(settingsAtom, 'mediaAutoLoad'); const [urlPreview] = useSetting(settingsAtom, 'urlPreview'); diff --git a/src/app/features/room/schedule-send/SchedulePickerDialog.tsx b/src/app/features/room/schedule-send/SchedulePickerDialog.tsx index 50f119ef9..a2ab1141a 100644 --- a/src/app/features/room/schedule-send/SchedulePickerDialog.tsx +++ b/src/app/features/room/schedule-send/SchedulePickerDialog.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useState } from 'react'; +import { type MouseEventHandler, useState } from 'react'; import FocusTrap from 'focus-trap-react'; import { Dialog, @@ -16,7 +16,7 @@ import { Button, Chip, PopOut, - RectCords, + type RectCords, } from 'folds'; import { stopPropagation } from '$utils/keyboard'; import { timeDayMonthYear, timeHourMinute, hoursToMs, daysToMs } from '$utils/time'; diff --git a/src/app/features/room/schedule-send/ScheduledMessagesList.tsx b/src/app/features/room/schedule-send/ScheduledMessagesList.tsx index 39f751c9c..94ce0e338 100644 --- a/src/app/features/room/schedule-send/ScheduledMessagesList.tsx +++ b/src/app/features/room/schedule-send/ScheduledMessagesList.tsx @@ -1,7 +1,7 @@ import { useCallback, useState } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Box, Text, Chip, Icon, Icons, IconButton } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { getDelayedEvents, cancelDelayedEvent } from '$utils/delayedEvents'; diff --git a/src/app/features/search/Search.tsx b/src/app/features/search/Search.tsx index 65c79ba83..7b588f99b 100644 --- a/src/app/features/search/Search.tsx +++ b/src/app/features/search/Search.tsx @@ -16,9 +16,9 @@ import { toRem, } from 'folds'; import { - ChangeEventHandler, - KeyboardEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type KeyboardEventHandler, + type MouseEventHandler, useCallback, useEffect, useMemo, @@ -27,12 +27,16 @@ import { } from 'react'; import { isKeyHotkey } from 'is-hotkey'; import { useAtom, useAtomValue } from 'jotai'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useDirects, useOrphanSpaces, useRooms, useSpaces } from '$state/hooks/roomList'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { mDirectAtom } from '$state/mDirectList'; import { allRoomsAtom } from '$state/room-list/roomList'; -import { SearchItemStrGetter, useAsyncSearch, UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; +import { + type SearchItemStrGetter, + useAsyncSearch, + type UseAsyncSearchOptions, +} from '$hooks/useAsyncSearch'; import { useAllJoinedRoomsSet, useGetRoom } from '$hooks/useGetRoom'; import { RoomAvatar, RoomIcon } from '$components/room-avatar'; import { @@ -83,10 +87,10 @@ const useTopActiveRooms = ( return spaces; } if (searchRoomType === SearchRoomType.Directs) { - return [...directs].sort(factoryRoomIdByActivity(mx)).slice(0, 20); + return directs.toSorted(factoryRoomIdByActivity(mx)).slice(0, 20); } if (searchRoomType === SearchRoomType.Rooms) { - return [...rooms].sort(factoryRoomIdByActivity(mx)).slice(0, 20); + return rooms.toSorted(factoryRoomIdByActivity(mx)).slice(0, 20); } return [...rooms, ...directs].sort(factoryRoomIdByActivity(mx)).slice(0, 20); }, [mx, rooms, directs, spaces, searchRoomType]); @@ -174,7 +178,7 @@ export function Search({ requestClose }: SearchProps) { const items = result ? result.items : topActiveRooms; if (!selectedSpaceId) return items; - return [...items].sort((a, b) => { + return items.toSorted((a, b) => { const aInSpace = getAllParents(roomToParents, a)?.has(selectedSpaceId) ? 1 : 0; const bInSpace = getAllParents(roomToParents, b)?.has(selectedSpaceId) ? 1 : 0; return bInSpace - aInSpace; @@ -324,7 +328,7 @@ export function Search({ requestClose }: SearchProps) { const exactParents = roomToParents.get(roomId); const perfectParent = - exactParents && guessPerfectParent(mx, roomId, Array.from(exactParents)); + exactParents && guessPerfectParent(mx, roomId, [...exactParents]); const unread = roomToUnread.get(roomId); diff --git a/src/app/features/settings/Persona/PerMessageProfileEditor.tsx b/src/app/features/settings/Persona/PerMessageProfileEditor.tsx index 00a0ff9fe..d1350a2b1 100644 --- a/src/app/features/settings/Persona/PerMessageProfileEditor.tsx +++ b/src/app/features/settings/Persona/PerMessageProfileEditor.tsx @@ -1,6 +1,6 @@ import { SequenceCard } from '$components/sequence-card'; import { Box, Button, Text, Avatar, config, Icon, IconButton, Icons, Input } from 'folds'; -import { MatrixClient } from 'matrix-js-sdk'; +import { type MatrixClient } from 'matrix-js-sdk'; import { useCallback, useMemo, useState } from 'react'; import { mxcUrlToHttp } from '$utils/matrix'; import { useFilePicker } from '$hooks/useFilePicker'; @@ -14,7 +14,7 @@ import { deletePerMessageProfile, renamePerMessageProfile, } from '$hooks/usePerMessageProfile'; -import { parsePronounsStringToPronounsSetArray, PronounSet } from '$utils/pronouns'; +import { parsePronounsStringToPronounsSetArray, type PronounSet } from '$utils/pronouns'; import { SequenceCardStyle } from '../styles.css'; /** diff --git a/src/app/features/settings/Persona/PerMessageProfileOverview.tsx b/src/app/features/settings/Persona/PerMessageProfileOverview.tsx index 7a3ba574a..93b1523fc 100644 --- a/src/app/features/settings/Persona/PerMessageProfileOverview.tsx +++ b/src/app/features/settings/Persona/PerMessageProfileOverview.tsx @@ -2,7 +2,7 @@ import { useMatrixClient } from '$hooks/useMatrixClient'; import { addOrUpdatePerMessageProfile, getAllPerMessageProfiles, - PerMessageProfile, + type PerMessageProfile, } from '$hooks/usePerMessageProfile'; import { useEffect, useState } from 'react'; import { Box, Button, Text } from 'folds'; diff --git a/src/app/features/settings/Settings.tsx b/src/app/features/settings/Settings.tsx index 796ff2b86..1705043f5 100644 --- a/src/app/features/settings/Settings.tsx +++ b/src/app/features/settings/Settings.tsx @@ -7,7 +7,7 @@ import { Icon, IconButton, Icons, - IconSrc, + type IconSrc, MenuItem, Overlay, OverlayBackdrop, diff --git a/src/app/features/settings/SettingsSectionPage.tsx b/src/app/features/settings/SettingsSectionPage.tsx index ba1a662fe..cff849af1 100644 --- a/src/app/features/settings/SettingsSectionPage.tsx +++ b/src/app/features/settings/SettingsSectionPage.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Icon, IconButton, Icons, Text } from 'folds'; import { Page, PageHeader } from '$components/page'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; diff --git a/src/app/features/settings/account/AnimalCosmetics.tsx b/src/app/features/settings/account/AnimalCosmetics.tsx index 30fe96a92..611f61865 100644 --- a/src/app/features/settings/account/AnimalCosmetics.tsx +++ b/src/app/features/settings/account/AnimalCosmetics.tsx @@ -1,7 +1,7 @@ import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { UserProfile } from '$hooks/useUserProfile'; +import { type UserProfile } from '$hooks/useUserProfile'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { profilesCacheAtom } from '$state/userRoomProfile'; diff --git a/src/app/features/settings/account/BioEditor.tsx b/src/app/features/settings/account/BioEditor.tsx index 6b2500037..d4308c99d 100644 --- a/src/app/features/settings/account/BioEditor.tsx +++ b/src/app/features/settings/account/BioEditor.tsx @@ -1,4 +1,4 @@ -import { KeyboardEventHandler, useCallback, useEffect, useState, useRef } from 'react'; +import { type KeyboardEventHandler, useCallback, useEffect, useState, useRef } from 'react'; import { Box, Chip, @@ -7,7 +7,7 @@ import { Icons, Line, PopOut, - RectCords, + type RectCords, Spinner, Text, config, @@ -17,7 +17,7 @@ import { ReactEditor } from 'slate-react'; import { isKeyHotkey } from 'is-hotkey'; import { AutocompletePrefix, - AutocompleteQuery, + type AutocompleteQuery, CustomEditor, EmoticonAutocomplete, Toolbar, diff --git a/src/app/features/settings/account/IgnoredUserList.tsx b/src/app/features/settings/account/IgnoredUserList.tsx index a898e799e..1b65cfec8 100644 --- a/src/app/features/settings/account/IgnoredUserList.tsx +++ b/src/app/features/settings/account/IgnoredUserList.tsx @@ -1,4 +1,4 @@ -import { ChangeEventHandler, FormEventHandler, useCallback, useState } from 'react'; +import { type ChangeEventHandler, type FormEventHandler, useCallback, useState } from 'react'; import { Box, Button, Chip, Icon, IconButton, Icons, Input, Spinner, Text, config } from 'folds'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; diff --git a/src/app/features/settings/account/Profile.tsx b/src/app/features/settings/account/Profile.tsx index 834bd511d..929bf8eee 100644 --- a/src/app/features/settings/account/Profile.tsx +++ b/src/app/features/settings/account/Profile.tsx @@ -1,6 +1,6 @@ import { - ChangeEventHandler, - FormEventHandler, + type ChangeEventHandler, + type FormEventHandler, useCallback, useEffect, useMemo, @@ -29,7 +29,7 @@ import { useSetAtom } from 'jotai'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { UserProfile, useUserProfile, MSC4440Bio } from '$hooks/useUserProfile'; +import { type UserProfile, useUserProfile, type MSC4440Bio } from '$hooks/useUserProfile'; import { getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix'; import { UserAvatar } from '$components/user-avatar'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; @@ -41,13 +41,13 @@ import { stopPropagation } from '$utils/keyboard'; import { toSettingsFocusIdPart } from '$features/settings/settingsLink'; import { ImageEditor } from '$components/image-editor'; import { ModalWide } from '$styles/Modal.css'; -import { createUploadAtom, UploadSuccess } from '$state/upload'; +import { createUploadAtom, type UploadSuccess } from '$state/upload'; import { CompactUploadCardRenderer } from '$components/upload-card'; import { useCapabilities } from '$hooks/useCapabilities'; import { profilesCacheAtom } from '$state/userRoomProfile'; import { SequenceCardStyle } from '$features/settings/styles.css'; import { useUserPresence } from '$hooks/useUserPresence'; -import { MSC1767Text } from '$types/matrix/common'; +import { type MSC1767Text } from '$types/matrix/common'; import { TimezoneEditor } from './TimezoneEditor'; import { PronounEditor } from './PronounEditor'; import { BioEditor } from './BioEditor'; diff --git a/src/app/features/settings/account/PronounEditor.tsx b/src/app/features/settings/account/PronounEditor.tsx index 37a58aa99..14c6821c5 100644 --- a/src/app/features/settings/account/PronounEditor.tsx +++ b/src/app/features/settings/account/PronounEditor.tsx @@ -1,7 +1,7 @@ -import { useState, useEffect, ChangeEvent } from 'react'; +import { useState, useEffect, type ChangeEvent } from 'react'; import { Input } from 'folds'; import { SettingTile } from '$components/setting-tile'; -import { parsePronounsInput, PronounSet } from '$utils/pronouns'; +import { parsePronounsInput, type PronounSet } from '$utils/pronouns'; type PronounEditorProps = { title: string; diff --git a/src/app/features/settings/account/StatusEditor.tsx b/src/app/features/settings/account/StatusEditor.tsx index dc7baa334..081cfae4a 100644 --- a/src/app/features/settings/account/StatusEditor.tsx +++ b/src/app/features/settings/account/StatusEditor.tsx @@ -1,4 +1,4 @@ -import { ChangeEventHandler, FormEventHandler, useEffect, useState } from 'react'; +import { type ChangeEventHandler, type FormEventHandler, useEffect, useState } from 'react'; import { Box, Text, Button, Input, IconButton, Icon, Icons, Spinner, config } from 'folds'; import { SettingTile } from '$components/setting-tile'; diff --git a/src/app/features/settings/account/TimezoneEditor.tsx b/src/app/features/settings/account/TimezoneEditor.tsx index 2cbade5ba..4773f1be8 100644 --- a/src/app/features/settings/account/TimezoneEditor.tsx +++ b/src/app/features/settings/account/TimezoneEditor.tsx @@ -1,10 +1,10 @@ -import { useMemo, useState, useEffect, ChangeEvent } from 'react'; +import { useMemo, useState, useEffect, type ChangeEvent } from 'react'; import { Box, IconButton, Button, Icon, Icons, Input, Text } from 'folds'; import { SettingTile } from '$components/setting-tile'; -interface IntlWithSupportedValues { +type IntlWithSupportedValues = { supportedValuesOf(key: 'timeZone' | string): string[]; -} +}; type TimezoneEditorProps = { current?: string; diff --git a/src/app/features/settings/cosmetics/Cosmetics.tsx b/src/app/features/settings/cosmetics/Cosmetics.tsx index 3d50d886e..54bf3e921 100644 --- a/src/app/features/settings/cosmetics/Cosmetics.tsx +++ b/src/app/features/settings/cosmetics/Cosmetics.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useState } from 'react'; +import { type MouseEventHandler, useState } from 'react'; import { Box, Button, @@ -8,7 +8,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Scroll, Switch, Text, @@ -17,7 +17,7 @@ import FocusTrap from 'focus-trap-react'; import { PageContent } from '$components/page'; import { SequenceCard } from '$components/sequence-card'; import { useSetting } from '$state/hooks/settings'; -import { JumboEmojiSize, settingsAtom } from '$state/settings'; +import { type JumboEmojiSize, settingsAtom } from '$state/settings'; import { SettingTile } from '$components/setting-tile'; import { stopPropagation } from '$utils/keyboard'; import { SequenceCardStyle } from '$features/settings/styles.css'; diff --git a/src/app/features/settings/cosmetics/Themes.test.tsx b/src/app/features/settings/cosmetics/Themes.test.tsx index ba621537d..e118d6e5b 100644 --- a/src/app/features/settings/cosmetics/Themes.test.tsx +++ b/src/app/features/settings/cosmetics/Themes.test.tsx @@ -1,5 +1,6 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type * as ThemeModule from '$hooks/useTheme'; import { Appearance } from './Themes'; @@ -40,7 +41,7 @@ vi.mock('$state/hooks/settings', () => ({ })); vi.mock('$hooks/useTheme', async () => { - const actual = await vi.importActual('$hooks/useTheme'); + const actual = await vi.importActual('$hooks/useTheme'); return { ...actual, diff --git a/src/app/features/settings/cosmetics/Themes.tsx b/src/app/features/settings/cosmetics/Themes.tsx index 77e692265..5ff8c7a9b 100644 --- a/src/app/features/settings/cosmetics/Themes.tsx +++ b/src/app/features/settings/cosmetics/Themes.tsx @@ -1,4 +1,9 @@ -import { ChangeEventHandler, KeyboardEventHandler, type MouseEventHandler, useState } from 'react'; +import { + type ChangeEventHandler, + type KeyboardEventHandler, + type MouseEventHandler, + useState, +} from 'react'; import { Box, Chip, config, Icon, Icons, Input, Switch, Text, toRem } from 'folds'; import { isKeyHotkey } from 'is-hotkey'; @@ -14,7 +19,7 @@ import { import { DarkTheme, LightTheme, - Theme, + type Theme, ThemeKind, useActiveTheme, useSystemThemeKind, diff --git a/src/app/features/settings/developer-tools/AccountData.tsx b/src/app/features/settings/developer-tools/AccountData.tsx index bc8737e9f..5d7944af2 100644 --- a/src/app/features/settings/developer-tools/AccountData.tsx +++ b/src/app/features/settings/developer-tools/AccountData.tsx @@ -16,14 +16,14 @@ export function AccountData({ expand, onExpandToggle, onSelect }: AccountDataPro const mx = useMatrixClient(); const [accountDataTypes, setAccountDataKeys] = useState(() => // TODO: tighten this once account data event typing is standardized. - Array.from(mx.store.accountData.keys()) + [...mx.store.accountData.keys()] ); useAccountDataCallback( mx, useCallback(() => { // TODO: tighten this once account data event typing is standardized. - setAccountDataKeys(Array.from(mx.store.accountData.keys())); + setAccountDataKeys([...mx.store.accountData.keys()]); }, [mx]) ); diff --git a/src/app/features/settings/developer-tools/DebugLogViewer.tsx b/src/app/features/settings/developer-tools/DebugLogViewer.tsx index 8e5ae01e1..d983cf436 100644 --- a/src/app/features/settings/developer-tools/DebugLogViewer.tsx +++ b/src/app/features/settings/developer-tools/DebugLogViewer.tsx @@ -1,10 +1,21 @@ -import { useEffect, useState, useCallback, useMemo, MouseEventHandler } from 'react'; +import { useEffect, useState, useCallback, useMemo, type MouseEventHandler } from 'react'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; -import { Box, Text, Button, color, config, Badge, Menu, MenuItem, PopOut, RectCords } from 'folds'; +import { + Box, + Text, + Button, + color, + config, + Badge, + Menu, + MenuItem, + PopOut, + type RectCords, +} from 'folds'; import { SequenceCard } from '$components/sequence-card'; import { debugLoggerEnabledAtom, debugLogsAtom, clearDebugLogsAtom } from '$state/debugLogger'; -import { LogEntry, getDebugLogger, LogLevel, LogCategory } from '$utils/debugLogger'; +import { type LogEntry, getDebugLogger, type LogLevel, type LogCategory } from '$utils/debugLogger'; import { SequenceCardStyle } from '$features/settings/styles.css'; const formatTimestamp = (timestamp: number): string => { diff --git a/src/app/features/settings/developer-tools/DevelopTools.tsx b/src/app/features/settings/developer-tools/DevelopTools.tsx index c8ffeb12d..3efc6de06 100644 --- a/src/app/features/settings/developer-tools/DevelopTools.tsx +++ b/src/app/features/settings/developer-tools/DevelopTools.tsx @@ -6,7 +6,7 @@ import { SettingTile } from '$components/setting-tile'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { AccountDataEditor, AccountDataSubmitCallback } from '$components/AccountDataEditor'; +import { AccountDataEditor, type AccountDataSubmitCallback } from '$components/AccountDataEditor'; import { copyToClipboard } from '$utils/dom'; import { SequenceCardStyle } from '$features/settings/styles.css'; import { SettingsSectionPage } from '../SettingsSectionPage'; diff --git a/src/app/features/settings/developer-tools/SentrySettings.tsx b/src/app/features/settings/developer-tools/SentrySettings.tsx index 75bf09c45..552913fad 100644 --- a/src/app/features/settings/developer-tools/SentrySettings.tsx +++ b/src/app/features/settings/developer-tools/SentrySettings.tsx @@ -4,7 +4,8 @@ import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { SequenceCardStyle } from '$features/settings/styles.css'; import { toSettingsFocusIdPart } from '$features/settings/settingsLink'; -import { getDebugLogger, LogCategory } from '$utils/debugLogger'; +import { getSentryEnabled } from '$state/sentryStorage'; +import { getDebugLogger, type LogCategory } from '$utils/debugLogger'; const ALL_CATEGORIES: LogCategory[] = [ 'sync', @@ -51,7 +52,7 @@ export function SentrySettings() { }; const isSentryConfigured = Boolean(import.meta.env.VITE_SENTRY_DSN); - const sentryEnabled = localStorage.getItem('sable_sentry_enabled') === 'true'; + const sentryEnabled = getSentryEnabled(); const environment = import.meta.env.VITE_SENTRY_ENVIRONMENT || import.meta.env.MODE; const isProd = environment === 'production'; const traceSampleRate = isProd ? '10%' : '100%'; diff --git a/src/app/features/settings/developer-tools/SyncDiagnostics.tsx b/src/app/features/settings/developer-tools/SyncDiagnostics.tsx index 1d9631107..7e9f302f9 100644 --- a/src/app/features/settings/developer-tools/SyncDiagnostics.tsx +++ b/src/app/features/settings/developer-tools/SyncDiagnostics.tsx @@ -3,7 +3,7 @@ import { Box, Button, Icon, Icons, Text } from 'folds'; import { SequenceCard } from '$components/sequence-card'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { getClientSyncDiagnostics } from '$client/initMatrix'; -import { Direction, EventType, NotificationCountType, Room } from '$types/matrix-sdk'; +import { Direction, EventType, NotificationCountType, type Room } from '$types/matrix-sdk'; import { Membership } from '$types/matrix/room'; import { SequenceCardStyle } from '$features/settings/styles.css'; import { getUnreadInfo, isNotificationEvent } from '$utils/room'; @@ -77,8 +77,10 @@ const getUnreadDriftRooms = (mx: ReturnType): UnreadDrif if (sdkTotal <= 0 && sdkHighlight <= 0) return driftRooms; if (reconciledUnread.total <= 0 && reconciledUnread.highlight <= 0) return driftRooms; - const latestNotificationEvent = [...room.getLiveTimeline().getEvents()] - .reverse() + const latestNotificationEvent = room + .getLiveTimeline() + .getEvents() + .toReversed() .find((event) => !event.isSending() && isNotificationEvent(event)); const latestNotificationEventId = latestNotificationEvent?.getId() ?? null; if (!latestNotificationEventId) return driftRooms; diff --git a/src/app/features/settings/devices/DeviceTile.tsx b/src/app/features/settings/devices/DeviceTile.tsx index 385f70965..f8e83732d 100644 --- a/src/app/features/settings/devices/DeviceTile.tsx +++ b/src/app/features/settings/devices/DeviceTile.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, ReactNode, useCallback, useEffect, useState } from 'react'; +import { type FormEventHandler, type ReactNode, useCallback, useEffect, useState } from 'react'; import { Box, Text, @@ -15,7 +15,7 @@ import { OverlayBackdrop, OverlayCenter, } from 'folds'; -import { CryptoApi, IMyDevice, MatrixError } from '$types/matrix-sdk'; +import { type CryptoApi, type IMyDevice, type MatrixError } from '$types/matrix-sdk'; import FocusTrap from 'focus-trap-react'; import { SettingTile } from '$components/setting-tile'; import { useMatrixClient } from '$hooks/useMatrixClient'; diff --git a/src/app/features/settings/devices/LocalBackup.tsx b/src/app/features/settings/devices/LocalBackup.tsx index 09d350f8c..aaae765b1 100644 --- a/src/app/features/settings/devices/LocalBackup.tsx +++ b/src/app/features/settings/devices/LocalBackup.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useEffect, useState } from 'react'; +import { type FormEventHandler, useCallback, useEffect, useState } from 'react'; import { Box, Button, color, Icon, Icons, Spinner, Text, toRem } from 'folds'; import FileSaver from 'file-saver'; import { SequenceCard } from '$components/sequence-card'; diff --git a/src/app/features/settings/devices/OtherDevices.tsx b/src/app/features/settings/devices/OtherDevices.tsx index 145be32d3..bbb406019 100644 --- a/src/app/features/settings/devices/OtherDevices.tsx +++ b/src/app/features/settings/devices/OtherDevices.tsx @@ -1,9 +1,9 @@ import { useCallback, useState } from 'react'; import { Box, Button, config, Menu, Spinner, Text } from 'folds'; -import { AuthDict, IMyDevice, MatrixError } from '$types/matrix-sdk'; +import { type AuthDict, type IMyDevice, type MatrixError } from '$types/matrix-sdk'; import { SequenceCard } from '$components/sequence-card'; import { ActionUIA, ActionUIAFlowsLoader } from '$components/ActionUIA'; -import { AsyncState, AsyncStatus, useAsync } from '$hooks/useAsyncCallback'; +import { type AsyncState, AsyncStatus, useAsync } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useUIAMatrixError } from '$hooks/useUIAFlows'; import { DeviceVerificationStatus } from '$components/DeviceVerificationStatus'; @@ -76,7 +76,7 @@ export function OtherDevices({ devices, refreshDeviceList, showVerification }: O const deleteDevices = useAsync( useCallback( async (authDict?: AuthDict) => { - await mx.deleteMultipleDevices(Array.from(deleted), authDict); + await mx.deleteMultipleDevices([...deleted], authDict); }, [mx, deleted] ), diff --git a/src/app/features/settings/devices/Verification.tsx b/src/app/features/settings/devices/Verification.tsx index 34dccf481..7376c4116 100644 --- a/src/app/features/settings/devices/Verification.tsx +++ b/src/app/features/settings/devices/Verification.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useState } from 'react'; +import { type MouseEventHandler, useCallback, useState } from 'react'; import { Badge, Box, @@ -13,18 +13,18 @@ import { OverlayBackdrop, OverlayCenter, IconButton, - RectCords, + type RectCords, PopOut, Menu, MenuItem, } from 'folds'; import FocusTrap from 'focus-trap-react'; -import { CryptoApi, VerificationRequest } from '$types/matrix-sdk'; +import { type CryptoApi, type VerificationRequest } from '$types/matrix-sdk'; import { VerificationStatus } from '$hooks/useDeviceVerificationStatus'; import { InfoCard } from '$components/info-card'; import { ManualVerificationTile } from '$components/ManualVerification'; -import { SecretStorageKeyContent } from '$types/matrix/accountData'; -import { AsyncState, AsyncStatus, useAsync } from '$hooks/useAsyncCallback'; +import { type SecretStorageKeyContent } from '$types/matrix/accountData'; +import { type AsyncState, AsyncStatus, useAsync } from '$hooks/useAsyncCallback'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { DeviceVerification } from '$components/DeviceVerification'; import { diff --git a/src/app/features/settings/emojis-stickers/EmojisStickers.tsx b/src/app/features/settings/emojis-stickers/EmojisStickers.tsx index ea2eba6a9..951131dd5 100644 --- a/src/app/features/settings/emojis-stickers/EmojisStickers.tsx +++ b/src/app/features/settings/emojis-stickers/EmojisStickers.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { Box, Scroll } from 'folds'; import { PageContent } from '$components/page'; -import { ImagePack } from '$plugins/custom-emoji'; +import { type ImagePack } from '$plugins/custom-emoji'; import { ImagePackView } from '$components/image-pack-view'; import { SettingsSectionPage } from '../SettingsSectionPage'; import { GlobalPacks } from './GlobalPacks'; diff --git a/src/app/features/settings/emojis-stickers/GlobalPacks.tsx b/src/app/features/settings/emojis-stickers/GlobalPacks.tsx index 224ed4934..5fff1eebd 100644 --- a/src/app/features/settings/emojis-stickers/GlobalPacks.tsx +++ b/src/app/features/settings/emojis-stickers/GlobalPacks.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useEffect, useMemo, useState } from 'react'; +import { type MouseEventHandler, useCallback, useEffect, useMemo, useState } from 'react'; import { Box, Text, @@ -12,7 +12,7 @@ import { config, Spinner, Menu, - RectCords, + type RectCords, PopOut, Checkbox, toRem, @@ -23,7 +23,7 @@ import { } from 'folds'; import FocusTrap from 'focus-trap-react'; import { useAtomValue } from 'jotai'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useGlobalImagePacks, useRoomsImagePacks } from '$hooks/useImagePacks'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; @@ -32,10 +32,10 @@ import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { toSettingsFocusIdPart } from '$features/settings/settingsLink'; import { - EmoteRoomsContent, - ImagePack, + type EmoteRoomsContent, + type ImagePack, ImageUsage, - PackAddress, + type PackAddress, packAddressEqual, } from '$plugins/custom-emoji'; import { LineClamp2 } from '$styles/Text.css'; @@ -82,9 +82,9 @@ function GlobalPackSelector({ const addSelected = (adds: PackAddress[]) => { setSelected((addresses) => { - const newAddresses = Array.from(addresses); + const newAddresses = [...addresses]; adds.forEach((address) => { - if (newAddresses.find((addr) => packAddressEqual(addr, address))) { + if (newAddresses.some((addr) => packAddressEqual(addr, address))) { return; } newAddresses.push(address); @@ -96,7 +96,7 @@ function GlobalPackSelector({ const removeSelected = (adds: PackAddress[]) => { setSelected((addresses) => { const newAddresses = addresses.filter( - (addr) => !adds.find((address) => packAddressEqual(addr, address)) + (addr) => !adds.some((address) => packAddressEqual(addr, address)) ); return newAddresses; }); @@ -135,7 +135,7 @@ function GlobalPackSelector({ paddingRight: config.space.S100, }} > - {Array.from(roomToPacks.entries()).map(([roomId, roomPacks]) => { + {Array.from(roomToPacks.entries(), ([roomId, roomPacks]) => { const room = mx.getRoom(roomId); if (!room) return null; const roomPackAddresses = roomPacks @@ -175,7 +175,7 @@ function GlobalPackSelector({ const { address } = pack; if (!address) return null; - const added = !!selected.find((addr) => packAddressEqual(addr, address)); + const added = selected.some((addr) => packAddressEqual(addr, address)); return ( roomsImagePack.filter( - (pack) => !globalPacks.find((p) => packAddressEqual(pack.address, p.address)) + (pack) => !globalPacks.some((p) => packAddressEqual(pack.address, p.address)) ), [roomsImagePack, globalPacks] ); @@ -282,7 +282,7 @@ export function GlobalPacks({ onViewPack }: GlobalPacksProps) { const unselectedGlobalPacks = useMemo( () => nonGlobalPacks.filter( - (pack) => !selectedPacks.find((addr) => packAddressEqual(pack.address, addr)) + (pack) => !selectedPacks.some((addr) => packAddressEqual(pack.address, addr)) ), [selectedPacks, nonGlobalPacks] ); @@ -349,7 +349,7 @@ export function GlobalPacks({ onViewPack }: GlobalPacksProps) { const avatarUrl = avatarMxc ? mxcUrlToHttp(mx, avatarMxc, useAuthentication) : undefined; const { address } = pack; if (!address) return null; - const removed = !!removedPacks.find((addr) => packAddressEqual(addr, address)); + const removed = removedPacks.some((addr) => packAddressEqual(addr, address)); return ( {globalPacks.map(renderPack)} {nonGlobalPacks - .filter((pack) => !!selectedPacks.find((addr) => packAddressEqual(pack.address, addr))) + .filter((pack) => selectedPacks.some((addr) => packAddressEqual(pack.address, addr))) .map(renderPack)} {hasChanges && ( diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx index df8b0c6f1..644e2cda2 100644 --- a/src/app/features/settings/general/General.tsx +++ b/src/app/features/settings/general/General.tsx @@ -1,8 +1,8 @@ import { - ChangeEventHandler, - FormEventHandler, - KeyboardEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type FormEventHandler, + type KeyboardEventHandler, + type MouseEventHandler, useEffect, useState, } from 'react'; @@ -20,7 +20,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Scroll, Switch, Text, @@ -31,11 +31,11 @@ import { PageContent } from '$components/page'; import { SequenceCard } from '$components/sequence-card'; import { useSetting } from '$state/hooks/settings'; import { - DateFormat, + type DateFormat, MessageLayout, - MessageSpacing, + type MessageSpacing, RightSwipeAction, - CaptionPosition, + type CaptionPosition, settingsAtom, } from '$state/settings'; import { SettingTile } from '$components/setting-tile'; @@ -48,6 +48,12 @@ import { useMessageSpacingItems } from '$hooks/useMessageSpacing'; import { useDateFormatItems } from '$hooks/useDateFormat'; import { SequenceCardStyle } from '$features/settings/styles.css'; import { sessionsAtom, activeSessionIdAtom } from '$state/sessions'; +import { + getSentryEnabled, + getSentryReplayEnabled, + setSentryEnabled as persistSentryEnabled, + setSentryReplayEnabled, +} from '$state/sentryStorage'; import { useClientConfig } from '$hooks/useClientConfig'; import { resolveSlidingEnabled } from '$client/initMatrix'; import { isKeyHotkey } from 'is-hotkey'; @@ -1287,33 +1293,21 @@ function SettingsSyncSection() { } function DiagnosticsAndPrivacy() { - const [sentryEnabled, setSentryEnabled] = useState( - localStorage.getItem('sable_sentry_enabled') === 'true' - ); - const [sessionReplayEnabled, setSessionReplayEnabled] = useState( - localStorage.getItem('sable_sentry_replay_enabled') === 'true' - ); + const [sentryEnabled, setSentryEnabled] = useState(getSentryEnabled()); + const [sessionReplayEnabled, setSessionReplayEnabled] = useState(getSentryReplayEnabled()); const [needsRefresh, setNeedsRefresh] = useState(false); const isSentryConfigured = Boolean(import.meta.env.VITE_SENTRY_DSN); const handleSentryToggle = (enabled: boolean) => { setSentryEnabled(enabled); - if (enabled) { - localStorage.setItem('sable_sentry_enabled', 'true'); - } else { - localStorage.setItem('sable_sentry_enabled', 'false'); - } + persistSentryEnabled(enabled); setNeedsRefresh(true); }; const handleReplayToggle = (enabled: boolean) => { setSessionReplayEnabled(enabled); - if (enabled) { - localStorage.setItem('sable_sentry_replay_enabled', 'true'); - } else { - localStorage.removeItem('sable_sentry_replay_enabled'); - } + setSentryReplayEnabled(enabled); setNeedsRefresh(true); }; diff --git a/src/app/features/settings/general/SettingsLinkBaseUrlSetting.tsx b/src/app/features/settings/general/SettingsLinkBaseUrlSetting.tsx index deb6b760b..0a47bcae0 100644 --- a/src/app/features/settings/general/SettingsLinkBaseUrlSetting.tsx +++ b/src/app/features/settings/general/SettingsLinkBaseUrlSetting.tsx @@ -1,4 +1,10 @@ -import { ChangeEventHandler, FormEventHandler, useEffect, useMemo, useState } from 'react'; +import { + type ChangeEventHandler, + type FormEventHandler, + useEffect, + useMemo, + useState, +} from 'react'; import { Box, Button, config, Icon, IconButton, Icons, Input, Text } from 'folds'; import { useClientConfig } from '$hooks/useClientConfig'; import { SettingTile } from '$components/setting-tile'; diff --git a/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx b/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx index a0b52da48..be94a1302 100644 --- a/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx +++ b/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx @@ -20,7 +20,7 @@ type ShortcutCategory = { function formatKey(key: string): string { const isMac = - typeof navigator !== 'undefined' && navigator.platform.toUpperCase().indexOf('MAC') >= 0; + typeof navigator !== 'undefined' && navigator.platform.toUpperCase().includes('MAC'); return key .replace(/\bmod\b/g, isMac ? '⌘' : 'Ctrl') .replace(/\balt\b/gi, isMac ? '⌥' : 'Alt') diff --git a/src/app/features/settings/notifications/AllMessages.tsx b/src/app/features/settings/notifications/AllMessages.tsx index ab7f24a8d..4978bfabb 100644 --- a/src/app/features/settings/notifications/AllMessages.tsx +++ b/src/app/features/settings/notifications/AllMessages.tsx @@ -2,8 +2,8 @@ import { useCallback, useMemo } from 'react'; import { Badge, Box, Text } from 'folds'; import { ConditionKind, - IPushRules, - PushRuleCondition, + type IPushRules, + type PushRuleCondition, PushRuleKind, RuleId, } from '$types/matrix-sdk'; @@ -12,7 +12,7 @@ import { AccountDataEvent } from '$types/matrix/accountData'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { SettingMenuSelector } from '$components/setting-menu-selector'; -import { PushRuleData, usePushRule } from '$hooks/usePushRule'; +import { type PushRuleData, usePushRule } from '$hooks/usePushRule'; import { getNotificationModeActions, NotificationMode, diff --git a/src/app/features/settings/notifications/KeywordMessages.tsx b/src/app/features/settings/notifications/KeywordMessages.tsx index 7eab66eb7..4fd2ebd96 100644 --- a/src/app/features/settings/notifications/KeywordMessages.tsx +++ b/src/app/features/settings/notifications/KeywordMessages.tsx @@ -1,5 +1,11 @@ -import { ChangeEventHandler, FormEventHandler, useCallback, useMemo, useState } from 'react'; -import { IPushRule, IPushRules, PushRuleKind } from '$types/matrix-sdk'; +import { + type ChangeEventHandler, + type FormEventHandler, + useCallback, + useMemo, + useState, +} from 'react'; +import { type IPushRule, type IPushRules, PushRuleKind } from '$types/matrix-sdk'; import { Box, Text, Badge, Button, Input, config, IconButton, Icons, Icon, Spinner } from 'folds'; import { useAccountData } from '$hooks/useAccountData'; import { AccountDataEvent } from '$types/matrix/accountData'; @@ -11,7 +17,7 @@ import { toSettingsFocusIdPart } from '$features/settings/settingsLink'; import { getNotificationModeActions, NotificationMode, - NotificationModeOptions, + type NotificationModeOptions, useNotificationActionsMode, useNotificationModeActions, } from '$hooks/useNotificationMode'; diff --git a/src/app/features/settings/notifications/NotificationLevelsHint.tsx b/src/app/features/settings/notifications/NotificationLevelsHint.tsx index 700ca2bdb..9585a57ed 100644 --- a/src/app/features/settings/notifications/NotificationLevelsHint.tsx +++ b/src/app/features/settings/notifications/NotificationLevelsHint.tsx @@ -1,5 +1,16 @@ -import { MouseEventHandler, useState } from 'react'; -import { Box, config, Header, Icon, IconButton, Icons, Menu, PopOut, RectCords, Text } from 'folds'; +import { type MouseEventHandler, useState } from 'react'; +import { + Box, + config, + Header, + Icon, + IconButton, + Icons, + Menu, + PopOut, + type RectCords, + Text, +} from 'folds'; import FocusTrap from 'focus-trap-react'; import { stopPropagation } from '$utils/keyboard'; diff --git a/src/app/features/settings/notifications/PushNotifications.tsx b/src/app/features/settings/notifications/PushNotifications.tsx index 46c0ebb0d..45c6526d7 100644 --- a/src/app/features/settings/notifications/PushNotifications.tsx +++ b/src/app/features/settings/notifications/PushNotifications.tsx @@ -1,6 +1,6 @@ -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { createDebugLogger } from '$utils/debugLogger'; -import { ClientConfig } from '../../../hooks/useClientConfig'; +import { type ClientConfig } from '../../../hooks/useClientConfig'; const debugLog = createDebugLogger('PushNotifications'); diff --git a/src/app/features/settings/notifications/SpecialMessages.tsx b/src/app/features/settings/notifications/SpecialMessages.tsx index 5b7389548..581df0114 100644 --- a/src/app/features/settings/notifications/SpecialMessages.tsx +++ b/src/app/features/settings/notifications/SpecialMessages.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo } from 'react'; -import { ConditionKind, IPushRules, PushRuleKind, RuleId } from '$types/matrix-sdk'; +import { ConditionKind, type IPushRules, PushRuleKind, RuleId } from '$types/matrix-sdk'; import { Box, Text, Badge } from 'folds'; import { useAccountData } from '$hooks/useAccountData'; import { AccountDataEvent } from '$types/matrix/accountData'; @@ -9,11 +9,11 @@ import { SettingMenuSelector } from '$components/setting-menu-selector'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useUserProfile } from '$hooks/useUserProfile'; import { getMxIdLocalPart } from '$utils/matrix'; -import { makePushRuleData, PushRuleData, usePushRule } from '$hooks/usePushRule'; +import { makePushRuleData, type PushRuleData, usePushRule } from '$hooks/usePushRule'; import { getNotificationModeActions, NotificationMode, - NotificationModeOptions, + type NotificationModeOptions, useNotificationActionsMode, useNotificationModeActions, } from '$hooks/useNotificationMode'; diff --git a/src/app/features/settings/notifications/SystemNotification.tsx b/src/app/features/settings/notifications/SystemNotification.tsx index 3a59a96be..b08c1f2e9 100644 --- a/src/app/features/settings/notifications/SystemNotification.tsx +++ b/src/app/features/settings/notifications/SystemNotification.tsx @@ -1,7 +1,7 @@ /* eslint-disable no-nested-ternary */ import { useCallback, useEffect, useState } from 'react'; import { Box, Text, Switch, Button, color, Spinner, config } from 'folds'; -import { IPusherRequest } from '$types/matrix-sdk'; +import { type IPusherRequest } from '$types/matrix-sdk'; import { useAtom } from 'jotai'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; diff --git a/src/app/features/space-nav/SpaceNavItem.tsx b/src/app/features/space-nav/SpaceNavItem.tsx index f319a9da6..91c6d5735 100644 --- a/src/app/features/space-nav/SpaceNavItem.tsx +++ b/src/app/features/space-nav/SpaceNavItem.tsx @@ -1,6 +1,6 @@ -import { MouseEventHandler, useState } from 'react'; -import { Room } from '$types/matrix-sdk'; -import { Box, Icon, Icons, Text, config, RectCords, Avatar } from 'folds'; +import { type MouseEventHandler, useState } from 'react'; +import { type Room } from '$types/matrix-sdk'; +import { Box, Icon, Icons, Text, config, type RectCords, Avatar } from 'folds'; import { useNavigate } from 'react-router-dom'; import { NavButton, NavItem, NavItemContent } from '$components/nav'; import { useRoomName } from '$hooks/useRoomMeta'; diff --git a/src/app/features/space-settings/SpaceSettings.tsx b/src/app/features/space-settings/SpaceSettings.tsx index 586fba3f2..e2377896e 100644 --- a/src/app/features/space-settings/SpaceSettings.tsx +++ b/src/app/features/space-settings/SpaceSettings.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react'; import { useAtomValue } from 'jotai'; -import { Avatar, Box, config, Icon, IconButton, Icons, IconSrc, MenuItem, Text } from 'folds'; +import { Avatar, Box, config, Icon, IconButton, Icons, type IconSrc, MenuItem, Text } from 'folds'; import { JoinRule } from '$types/matrix-sdk'; import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '$components/page'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; diff --git a/src/app/features/space-settings/SpaceSettingsRenderer.tsx b/src/app/features/space-settings/SpaceSettingsRenderer.tsx index 425428685..fe1f95624 100644 --- a/src/app/features/space-settings/SpaceSettingsRenderer.tsx +++ b/src/app/features/space-settings/SpaceSettingsRenderer.tsx @@ -1,7 +1,7 @@ import { Modal500 } from '$components/Modal500'; import { useCloseSpaceSettings, useSpaceSettingsState } from '$state/hooks/spaceSettings'; import { useAllJoinedRoomsSet, useGetRoom } from '$hooks/useGetRoom'; -import { SpaceSettingsState } from '$state/spaceSettings'; +import { type SpaceSettingsState } from '$state/spaceSettings'; import { RoomProvider } from '$hooks/useRoom'; import { SpaceProvider } from '$hooks/useSpace'; import { SpaceSettings } from './SpaceSettings'; diff --git a/src/app/features/space-settings/permissions/usePermissionItems.ts b/src/app/features/space-settings/permissions/usePermissionItems.ts index cdc4f73df..573970a16 100644 --- a/src/app/features/space-settings/permissions/usePermissionItems.ts +++ b/src/app/features/space-settings/permissions/usePermissionItems.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { StateEvent } from '$types/matrix/room'; -import { PermissionGroup } from '$features/common-settings/permissions'; +import { type PermissionGroup } from '$features/common-settings/permissions'; export const usePermissionGroups = (): PermissionGroup[] => { const groups: PermissionGroup[] = useMemo(() => { diff --git a/src/app/features/widgets/GenericWidgetDriver.ts b/src/app/features/widgets/GenericWidgetDriver.ts index ee143b4f3..c58752406 100644 --- a/src/app/features/widgets/GenericWidgetDriver.ts +++ b/src/app/features/widgets/GenericWidgetDriver.ts @@ -6,14 +6,14 @@ import { type IRoomEvent, type Widget, WidgetDriver, - WidgetKind, + type WidgetKind, type IWidgetApiErrorResponseDataDetails, type ISearchUserDirectoryResult, type IGetMediaConfigResult, UpdateDelayedEventAction, OpenIDRequestState, - SimpleObservable, - IOpenIDUpdate, + type SimpleObservable, + type IOpenIDUpdate, } from 'matrix-widget-api'; import { EventType, @@ -24,8 +24,8 @@ import { type SendDelayedEventResponse, type StateEvents, type TimelineEvents, - MatrixClient, - Room, + type MatrixClient, + type Room, } from '$types/matrix-sdk'; export type CapabilityApprovalCallback = (requested: Set) => Promise>; diff --git a/src/app/features/widgets/IntegrationManager.tsx b/src/app/features/widgets/IntegrationManager.tsx index 4bf242a4f..133b87c66 100644 --- a/src/app/features/widgets/IntegrationManager.tsx +++ b/src/app/features/widgets/IntegrationManager.tsx @@ -12,16 +12,16 @@ import { Text, } from 'folds'; import FocusTrap from 'focus-trap-react'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useIntegrationManager, buildIntegrationManagerUrl } from '$hooks/useIntegrationManager'; import * as css from './IntegrationManager.css'; -interface IntegrationManagerProps { +type IntegrationManagerProps = { room: Room; open: boolean; onClose: () => void; -} +}; export function IntegrationManager({ room, open, onClose }: IntegrationManagerProps) { const { managers, scalarToken, loading, error } = useIntegrationManager(); diff --git a/src/app/features/widgets/WidgetIframe.tsx b/src/app/features/widgets/WidgetIframe.tsx index 844debfd2..a7d1eb981 100644 --- a/src/app/features/widgets/WidgetIframe.tsx +++ b/src/app/features/widgets/WidgetIframe.tsx @@ -1,25 +1,31 @@ import { useEffect, useRef, useState } from 'react'; -import { ClientWidgetApi, IWidget, IRoomEvent, Widget, WidgetKind } from 'matrix-widget-api'; +import { + ClientWidgetApi, + type IWidget, + type IRoomEvent, + Widget, + WidgetKind, +} from 'matrix-widget-api'; import { ClientEvent, Direction, - IEvent, - MatrixClient, - MatrixEvent, + type IEvent, + type MatrixClient, + type MatrixEvent, MatrixEventEvent, } from '$types/matrix-sdk'; import { createLogger } from '$utils/debug'; import { resolveWidgetUrl } from '$hooks/useRoomWidgets'; -import { GenericWidgetDriver, CapabilityApprovalCallback } from './GenericWidgetDriver'; +import { GenericWidgetDriver, type CapabilityApprovalCallback } from './GenericWidgetDriver'; const log = createLogger('WidgetIframe'); -interface WidgetIframeProps { +type WidgetIframeProps = { widget: IWidget; roomId: string; mx: MatrixClient; onCapabilityRequest?: CapabilityApprovalCallback; -} +}; export function WidgetIframe({ widget, roomId, mx, onCapabilityRequest }: WidgetIframeProps) { const iframeRef = useRef(null); @@ -78,7 +84,7 @@ export function WidgetIframe({ widget, roomId, mx, onCapabilityRequest }: Widget return; } const stateEvents = state.events?.get(type); - Array.from(stateEvents?.values() ?? []).forEach((eventObject: MatrixEvent) => { + [...(stateEvents?.values() ?? [])].forEach((eventObject: MatrixEvent) => { events.push(eventObject.event); }); messaging.transport.reply(ev.detail, { events }); @@ -87,7 +93,7 @@ export function WidgetIframe({ widget, roomId, mx, onCapabilityRequest }: Widget const readUpToMap: Record = {}; mx.getRooms().forEach((room) => { const roomEvents = room.getLiveTimeline()?.getEvents() || []; - const last = roomEvents[roomEvents.length - 1]; + const last = roomEvents.at(-1); if (last) { const id = last.getId(); if (id) readUpToMap[room.roomId] = id; diff --git a/src/app/features/widgets/WidgetsDrawer.tsx b/src/app/features/widgets/WidgetsDrawer.tsx index ade7699e8..9c66f2832 100644 --- a/src/app/features/widgets/WidgetsDrawer.tsx +++ b/src/app/features/widgets/WidgetsDrawer.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, MouseEventHandler, useState } from 'react'; +import { type FormEventHandler, type MouseEventHandler, useState } from 'react'; import { Box, Header, @@ -15,10 +15,10 @@ import { Button, Line, } from 'folds'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useMatrixClient } from '$hooks/useMatrixClient'; -import { useRoomWidgets, RoomWidget, enrichWidgetUrl } from '$hooks/useRoomWidgets'; +import { useRoomWidgets, type RoomWidget, enrichWidgetUrl } from '$hooks/useRoomWidgets'; import { useSetSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { usePowerLevelsContext } from '$hooks/usePowerLevels'; diff --git a/src/app/hooks/timeline/useProcessedTimeline.ts b/src/app/hooks/timeline/useProcessedTimeline.ts index f1f799308..479a62a53 100644 --- a/src/app/hooks/timeline/useProcessedTimeline.ts +++ b/src/app/hooks/timeline/useProcessedTimeline.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { MatrixEvent, EventTimelineSet, EventTimeline } from '$types/matrix-sdk'; +import { type MatrixEvent, type EventTimelineSet, type EventTimeline } from '$types/matrix-sdk'; import { getTimelineAndBaseIndex, getTimelineRelativeIndex, @@ -8,7 +8,7 @@ import { import { reactionOrEditEvent, isMembershipChanged } from '$utils/room'; import { inSameDay, minuteDifference } from '$utils/time'; -export interface UseProcessedTimelineOptions { +export type UseProcessedTimelineOptions = { items: number[]; linkedTimelines: EventTimeline[]; ignoredUsersSet: Set; @@ -20,9 +20,9 @@ export interface UseProcessedTimelineOptions { hideNickAvatarEvents: boolean; isReadOnly: boolean; hideMemberInReadOnly: boolean; -} +}; -export interface ProcessedEvent { +export type ProcessedEvent = { id: string; itemIndex: number; mEvent: MatrixEvent; @@ -31,7 +31,7 @@ export interface ProcessedEvent { collapsed: boolean; willRenderNewDivider: boolean; willRenderDayDivider: boolean; -} +}; const MESSAGE_EVENT_TYPES = [ 'm.room.message', diff --git a/src/app/hooks/timeline/useTimelineActions.ts b/src/app/hooks/timeline/useTimelineActions.ts index e7bb45f68..434546ac7 100644 --- a/src/app/hooks/timeline/useTimelineActions.ts +++ b/src/app/hooks/timeline/useTimelineActions.ts @@ -1,13 +1,19 @@ -import { useCallback, MouseEventHandler } from 'react'; -import { MatrixClient, Room, MatrixEvent, EventStatus, IContent } from '$types/matrix-sdk'; -import { Editor } from 'slate'; +import { useCallback, type MouseEventHandler } from 'react'; +import { + type MatrixClient, + type Room, + type MatrixEvent, + EventStatus, + type IContent, +} from '$types/matrix-sdk'; +import { type Editor } from 'slate'; import { ReactEditor } from 'slate-react'; import { getMxIdLocalPart, toggleReaction } from '$utils/matrix'; import { getMemberDisplayName, getEditedEvent } from '$utils/room'; import { createMentionElement, isEmptyEditor, moveCursor } from '$components/editor'; -export interface UseTimelineActionsOptions { +export type UseTimelineActionsOptions = { room: Room; mx: MatrixClient; editor: Editor; @@ -30,7 +36,7 @@ export interface UseTimelineActionsOptions { setEditId: (editId: string | undefined) => void; onEditorReset?: () => void; handleOpenEvent: (eventId: string) => void; -} +}; export function useTimelineActions({ room, diff --git a/src/app/hooks/timeline/useTimelineEventRenderer.tsx b/src/app/hooks/timeline/useTimelineEventRenderer.tsx index f7dca411a..60bf9bd63 100644 --- a/src/app/hooks/timeline/useTimelineEventRenderer.tsx +++ b/src/app/hooks/timeline/useTimelineEventRenderer.tsx @@ -1,22 +1,22 @@ -import { MouseEventHandler, useCallback, useMemo } from 'react'; +import { type MouseEventHandler, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useAtomValue } from 'jotai'; import { - MatrixClient, - MatrixEvent, - Room, - PushProcessor, - EventTimelineSet, - IContent, + type MatrixClient, + type MatrixEvent, + type Room, + type PushProcessor, + type EventTimelineSet, + type IContent, } from '$types/matrix-sdk'; -import { SessionMembershipData } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; -import { HTMLReactParserOptions } from 'html-react-parser'; -import { Opts as LinkifyOpts } from 'linkifyjs'; +import { type SessionMembershipData } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; +import { type HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; import { Box, Chip, Avatar, Text, Icons, config, toRem, Icon } from 'folds'; import { MessageLayout } from '$state/settings'; import { nicknamesAtom } from '$state/nicknames'; -import { useGetMemberPowerTag } from '$hooks/useMemberPowerTag'; -import { useMemberEventParser } from '$hooks/useMemberEventParser'; +import { type useGetMemberPowerTag } from '$hooks/useMemberPowerTag'; +import { type useMemberEventParser } from '$hooks/useMemberEventParser'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { useMatrixEventRenderer } from '$hooks/useMatrixEventRenderer'; @@ -35,7 +35,7 @@ import { ImageViewer } from '$components/image-viewer'; import { RenderMessageContent } from '$components/RenderMessageContent'; import { ClientSideHoverFreeze } from '$components/ClientSideHoverFreeze'; import { UserAvatar } from '$components/user-avatar'; -import { MessageEvent, StateEvent, GetContentCallback } from '$types/matrix/room'; +import { MessageEvent, StateEvent, type GetContentCallback } from '$types/matrix/room'; import { getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix'; import { getEditedEvent, @@ -50,7 +50,7 @@ import * as customHtmlCss from '$styles/CustomHtml.css'; import { EncryptedContent, Event, - ForwardedMessageProps, + type ForwardedMessageProps, Message, Reactions, } from '$features/room/message'; @@ -204,7 +204,7 @@ function ThreadReplyChip({ ); } -export interface TimelineEventRendererOptions { +export type TimelineEventRendererOptions = { room: Room; mx: MatrixClient; pushProcessor: PushProcessor; @@ -256,7 +256,7 @@ export interface TimelineEventRendererOptions { getMemberPowerTag: ReturnType; parseMemberEvent: ReturnType; }; -} +}; export function useTimelineEventRenderer({ room, diff --git a/src/app/hooks/timeline/useTimelineSync.test.tsx b/src/app/hooks/timeline/useTimelineSync.test.tsx index d53d74143..334dd524a 100644 --- a/src/app/hooks/timeline/useTimelineSync.test.tsx +++ b/src/app/hooks/timeline/useTimelineSync.test.tsx @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import { act, renderHook } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { Room, RoomEvent } from '$types/matrix-sdk'; +import { type Room, RoomEvent } from '$types/matrix-sdk'; import { useTimelineSync } from './useTimelineSync'; vi.mock('@sentry/react', () => ({ diff --git a/src/app/hooks/timeline/useTimelineSync.ts b/src/app/hooks/timeline/useTimelineSync.ts index 51c85dda8..158b9ac85 100644 --- a/src/app/hooks/timeline/useTimelineSync.ts +++ b/src/app/hooks/timeline/useTimelineSync.ts @@ -1,16 +1,24 @@ -import { useState, useMemo, useCallback, useRef, useEffect, Dispatch, SetStateAction } from 'react'; +import { + useState, + useMemo, + useCallback, + useRef, + useEffect, + type Dispatch, + type SetStateAction, +} from 'react'; import to from 'await-to-js'; import * as Sentry from '@sentry/react'; import { - MatrixClient, - Room, - MatrixEvent, + type MatrixClient, + type Room, + type MatrixEvent, Direction, - EventTimeline, - EventTimelineSetHandlerMap, + type EventTimeline, + type EventTimelineSetHandlerMap, RoomEvent, - IRoomTimelineData, - RoomEventHandlerMap, + type IRoomTimelineData, + type RoomEventHandlerMap, RelationType, ThreadEvent, } from '$types/matrix-sdk'; @@ -192,9 +200,8 @@ const useTimelinePagination = ( const fetched = countAfter - countBefore; if (fetched > 0 && fetched < 5) { - const checkTimeline = backwards - ? freshLTimelines[0] - : freshLTimelines[freshLTimelines.length - 1]; + const checkTimeline = backwards ? freshLTimelines[0] : freshLTimelines.at(-1); + if (!checkTimeline) return; const checkDirection = backwards ? Direction.Backward : Direction.Forward; const stillHasToken = typeof getLinkedTimelines(checkTimeline)[0]?.getPaginationToken(checkDirection) === @@ -345,7 +352,7 @@ const useThreadUpdate = (room: Room, onUpdate: () => void) => { }, [room]); }; -export interface UseTimelineSyncOptions { +export type UseTimelineSyncOptions = { room: Room; mx: MatrixClient; eventId?: string; @@ -356,7 +363,7 @@ export interface UseTimelineSyncOptions { setUnreadInfo: Dispatch>>; hideReadsRef: React.MutableRefObject; readUptoEventIdRef: React.MutableRefObject; -} +}; export function useTimelineSync({ room, diff --git a/src/app/hooks/types.ts b/src/app/hooks/types.ts index 781eb90ca..37c5e42ca 100644 --- a/src/app/hooks/types.ts +++ b/src/app/hooks/types.ts @@ -1,4 +1,4 @@ -import { IRequestTokenResponse } from '$types/matrix-sdk'; +import { type IRequestTokenResponse } from '$types/matrix-sdk'; export type RequestEmailTokenResponse = { email: string; diff --git a/src/app/hooks/useAccountData.ts b/src/app/hooks/useAccountData.ts index 11d3d5b42..ca90d866c 100644 --- a/src/app/hooks/useAccountData.ts +++ b/src/app/hooks/useAccountData.ts @@ -1,5 +1,5 @@ import { useState, useCallback } from 'react'; -import { AccountDataEvents } from '$types/matrix-sdk'; +import { type AccountDataEvents } from '$types/matrix-sdk'; import { useMatrixClient } from './useMatrixClient'; import { useAccountDataCallback } from './useAccountDataCallback'; diff --git a/src/app/hooks/useAccountDataCallback.ts b/src/app/hooks/useAccountDataCallback.ts index 97c350446..7c4995707 100644 --- a/src/app/hooks/useAccountDataCallback.ts +++ b/src/app/hooks/useAccountDataCallback.ts @@ -1,4 +1,4 @@ -import { ClientEvent, ClientEventHandlerMap, MatrixClient } from '$types/matrix-sdk'; +import { ClientEvent, type ClientEventHandlerMap, type MatrixClient } from '$types/matrix-sdk'; import { useEffect } from 'react'; export const useAccountDataCallback = ( diff --git a/src/app/hooks/useAppVisibility.ts b/src/app/hooks/useAppVisibility.ts index 7fd5f2325..4dc9a5be7 100644 --- a/src/app/hooks/useAppVisibility.ts +++ b/src/app/hooks/useAppVisibility.ts @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { useAtom } from 'jotai'; import { togglePusher } from '../features/settings/notifications/PushNotifications'; import { appEvents } from '../utils/appEvents'; diff --git a/src/app/hooks/useAsyncCallback.ts b/src/app/hooks/useAsyncCallback.ts index 70831bea1..f06c0060b 100644 --- a/src/app/hooks/useAsyncCallback.ts +++ b/src/app/hooks/useAsyncCallback.ts @@ -1,4 +1,11 @@ -import { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react'; +import { + type Dispatch, + type SetStateAction, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; import { flushSync } from 'react-dom'; import { useAlive } from './useAlive'; diff --git a/src/app/hooks/useAsyncSearch.ts b/src/app/hooks/useAsyncSearch.ts index ebf78b8be..36e74d550 100644 --- a/src/app/hooks/useAsyncSearch.ts +++ b/src/app/hooks/useAsyncSearch.ts @@ -1,14 +1,14 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { - MatchHandler, + type MatchHandler, AsyncSearch, - AsyncSearchHandler, - AsyncSearchOption, - MatchQueryOption, - NormalizeOption, + type AsyncSearchHandler, + type AsyncSearchOption, + type MatchQueryOption, + type NormalizeOption, normalize, matchQuery, - ResultHandler, + type ResultHandler, } from '$utils/AsyncSearch'; import { sanitizeForRegex } from '$utils/regex'; @@ -52,7 +52,7 @@ export const orderSearchItems = ( getItemStr: SearchItemStrGetter, options?: UseAsyncSearchOptions ): TSearchItem[] => { - const orderedItems: TSearchItem[] = Array.from(items); + const orderedItems: TSearchItem[] = [...items]; // we will consider "_" as word boundary char. // because in more use-cases it is used. (like: emojishortcode) diff --git a/src/app/hooks/useAuthFlows.ts b/src/app/hooks/useAuthFlows.ts index 8d82435e2..a1791b86e 100644 --- a/src/app/hooks/useAuthFlows.ts +++ b/src/app/hooks/useAuthFlows.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { IAuthData, MatrixError, ILoginFlowsResponse } from '$types/matrix-sdk'; +import { type IAuthData, type MatrixError, type ILoginFlowsResponse } from '$types/matrix-sdk'; export enum RegisterFlowStatus { FlowRequired = 401, diff --git a/src/app/hooks/useAuthMetadata.ts b/src/app/hooks/useAuthMetadata.ts index a24859093..42830c443 100644 --- a/src/app/hooks/useAuthMetadata.ts +++ b/src/app/hooks/useAuthMetadata.ts @@ -1,4 +1,4 @@ -import { ValidatedAuthMetadata } from '$types/matrix-sdk'; +import { type ValidatedAuthMetadata } from '$types/matrix-sdk'; import { createContext, useContext } from 'react'; const AuthMetadataContext = createContext(undefined); diff --git a/src/app/hooks/useAutoDiscoveryInfo.ts b/src/app/hooks/useAutoDiscoveryInfo.ts index b2f8bcb56..9d570d467 100644 --- a/src/app/hooks/useAutoDiscoveryInfo.ts +++ b/src/app/hooks/useAutoDiscoveryInfo.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { AutoDiscoveryInfo } from '../cs-api'; +import { type AutoDiscoveryInfo } from '../cs-api'; const AutoDiscoverInfoContext = createContext(null); diff --git a/src/app/hooks/useCall.ts b/src/app/hooks/useCall.ts index 1364e4598..9ec13122d 100644 --- a/src/app/hooks/useCall.ts +++ b/src/app/hooks/useCall.ts @@ -1,9 +1,9 @@ -import { Room } from 'matrix-js-sdk'; +import { type Room } from 'matrix-js-sdk'; import { MatrixRTCSession, MatrixRTCSessionEvent, } from 'matrix-js-sdk/lib/matrixrtc/MatrixRTCSession'; -import { CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; +import { type CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership'; import { useEffect, useState } from 'react'; import { MatrixRTCSessionManagerEvents } from 'matrix-js-sdk/lib/matrixrtc/MatrixRTCSessionManager'; import { useMatrixClient } from './useMatrixClient'; diff --git a/src/app/hooks/useCallEmbed.ts b/src/app/hooks/useCallEmbed.ts index aa3466193..14a90a315 100644 --- a/src/app/hooks/useCallEmbed.ts +++ b/src/app/hooks/useCallEmbed.ts @@ -1,11 +1,11 @@ -import { createContext, RefObject, useCallback, useContext, useEffect, useState } from 'react'; +import { createContext, type RefObject, useCallback, useContext, useEffect, useState } from 'react'; import { MatrixRTCSession } from 'matrix-js-sdk/lib/matrixrtc/MatrixRTCSession'; -import { MatrixClient, Room } from 'matrix-js-sdk'; +import { type MatrixClient, type Room } from 'matrix-js-sdk'; import { useSetAtom } from 'jotai'; import * as Sentry from '@sentry/react'; import { CallEmbed, - ElementCallThemeKind, + type ElementCallThemeKind, ElementWidgetActions, useClientWidgetApiEvent, } from '../plugins/call'; @@ -15,7 +15,7 @@ import { callEmbedAtom } from '../state/callEmbed'; import { useResizeObserver } from './useResizeObserver'; import { CallControlState } from '../plugins/call/CallControlState'; import { useCallMembersChange, useCallSession } from './useCall'; -import { CallPreferences } from '../state/callPreferences'; +import { type CallPreferences } from '../state/callPreferences'; import { createDebugLogger } from '../utils/debugLogger'; const debugLog = createDebugLogger('useCallEmbed'); diff --git a/src/app/hooks/useCallSignaling.ts b/src/app/hooks/useCallSignaling.ts index 16620acb9..66577565a 100644 --- a/src/app/hooks/useCallSignaling.ts +++ b/src/app/hooks/useCallSignaling.ts @@ -14,10 +14,10 @@ const debugLog = createDebugLogger('CallSignaling'); type CallPhase = 'IDLE' | 'RINGING_OUT' | 'RINGING_IN' | 'ACTIVE' | 'ENDED'; -interface SignalState { +type SignalState = { incoming: string | null; outgoing: string | null; -} +}; export function useCallSignaling() { const mx = useMatrixClient(); @@ -100,7 +100,7 @@ export function useCallSignaling() { const myUserId = mx.getUserId(); const now = Date.now(); - const signal = Array.from(mDirects).reduce( + const signal = [...mDirects].reduce( (acc, roomId) => { if (acc.incoming || mutedRoomIdRef.current === roomId) return acc; diff --git a/src/app/hooks/useCallSpeakers.ts b/src/app/hooks/useCallSpeakers.ts index 240036785..4e18fce14 100644 --- a/src/app/hooks/useCallSpeakers.ts +++ b/src/app/hooks/useCallSpeakers.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { CallEmbed } from '../plugins/call'; +import { type CallEmbed } from '../plugins/call'; import { useMutationObserver } from './useMutationObserver'; import { isUserId } from '../utils/matrix'; import { useCallMembers, useCallSession } from './useCall'; diff --git a/src/app/hooks/useCapabilities.ts b/src/app/hooks/useCapabilities.ts index 6e84090e7..3022347b6 100644 --- a/src/app/hooks/useCapabilities.ts +++ b/src/app/hooks/useCapabilities.ts @@ -1,4 +1,4 @@ -import { Capabilities } from '$types/matrix-sdk'; +import { type Capabilities } from '$types/matrix-sdk'; import { createContext, useContext } from 'react'; const CapabilitiesContext = createContext(null); diff --git a/src/app/hooks/useCategoryHandler.ts b/src/app/hooks/useCategoryHandler.ts index 086902d63..434a5120f 100644 --- a/src/app/hooks/useCategoryHandler.ts +++ b/src/app/hooks/useCategoryHandler.ts @@ -1,4 +1,4 @@ -import { MouseEventHandler } from 'react'; +import { type MouseEventHandler } from 'react'; type CategoryAction = | { diff --git a/src/app/hooks/useCommands.ts b/src/app/hooks/useCommands.ts index 8d803d4f7..b402164df 100644 --- a/src/app/hooks/useCommands.ts +++ b/src/app/hooks/useCommands.ts @@ -1,14 +1,14 @@ import { Direction, EventTimeline, - IContextResponse, - MatrixClient, + type IContextResponse, + type MatrixClient, Method, Preset, - Room, - RoomMember, + type Room, + type RoomMember, Visibility, - RoomServerAclEventContent, + type RoomServerAclEventContent, MsgType, } from '$types/matrix-sdk'; import { useMemo } from 'react'; @@ -31,6 +31,7 @@ import { settingsAtom } from '$state/settings'; import { useOpenBugReportModal } from '$state/hooks/bugReportModal'; import { createRoomEncryptionState } from '$components/create-room'; import { parsePronounsInput } from '$utils/pronouns'; +import { extractPlainTextFromCustomHtml } from '$utils/sanitize'; import { sendFeedback } from '$utils/sendFeedbackToUser'; import { PKitCommandMessageHandler } from '$plugins/pluralkit-handler/PKitCommandMessageHandler'; import { useRoomNavigate } from './useRoomNavigate'; @@ -39,7 +40,7 @@ import { useUserProfile } from './useUserProfile'; import { addOrUpdatePerMessageProfile, deletePerMessageProfile, - PerMessageProfile, + type PerMessageProfile, setCurrentlyUsedPerMessageProfileIdForRoom, } from './usePerMessageProfile'; @@ -168,10 +169,7 @@ const hslToHex = (h: number, s: number, l: number): string => { const getAllTextNodes = (root: Node): Node[] => root.nodeType === Node.TEXT_NODE ? [root] - : Array.from(root.childNodes).reduce( - (acc, child) => acc.concat(getAllTextNodes(child)), - [] - ); + : [...root.childNodes].reduce((acc, child) => [...acc, ...getAllTextNodes(child)], []); export const rainbowify = (htmlInput: string): string => { const div = document.createElement('div'); @@ -179,7 +177,7 @@ export const rainbowify = (htmlInput: string): string => { const textNodes = getAllTextNodes(div); const totalTextLen = textNodes.reduce((acc, node) => { const text = node.textContent || ''; - const cleanLen = Array.from(text).filter((c) => c.trim().length > 0).length; + const cleanLen = [...text].filter((c) => c.trim().length > 0).length; return acc + cleanLen; }, 0); @@ -187,7 +185,7 @@ export const rainbowify = (htmlInput: string): string => { const text = node.textContent || ''; if (!text.trim()) return currentGlobalIdx; - const chars = Array.from(text); + const chars = [...text]; const { html: newHtml, count: charsProcessed } = chars.reduce( (acc, char) => { @@ -454,7 +452,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => { const rawIds = splitWithSpace(payload); const userIds = rawIds.filter((id) => isUserId(id)); if (userIds.length > 0) { - let ignoredUsers = mx.getIgnoredUsers().concat(userIds); + let ignoredUsers = [...mx.getIgnoredUsers(), ...userIds]; ignoredUsers = [...new Set(ignoredUsers)]; await mx.setIgnoredUsers(ignoredUsers); } @@ -638,7 +636,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => { if (newAvatar.length === 0) { // no avatar, reset to global newAvatar = profile.avatarUrl; - } else if (!newAvatar.match(/^mxc:\/\/\S+$/)) { + } else if (!/^mxc:\/\/\S+$/.test(newAvatar)) { // bad mxc return; } @@ -1355,14 +1353,7 @@ export const useCommands = (mx: MatrixClient, room: Room): CommandRecord => { exe: async (payload) => { await mx.sendMessage(room.roomId, { msgtype: MsgType.Text, - body: payload - .replaceAll('
', '\n') - .replaceAll('
  • ', '\n- ') - .replaceAll( - /(.*?))"(.*?)>(?(.*?))<\/a>/g, - '[$]($)' - ) - .replaceAll(/<[^>]*>/g, ''), + body: extractPlainTextFromCustomHtml(payload), format: 'org.matrix.custom.html', formatted_body: payload, }); diff --git a/src/app/hooks/useComposingCheck.ts b/src/app/hooks/useComposingCheck.ts index 9208fcf33..a9f508c9d 100644 --- a/src/app/hooks/useComposingCheck.ts +++ b/src/app/hooks/useComposingCheck.ts @@ -2,9 +2,9 @@ import { useCallback, useEffect } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; import { lastCompositionEndAtom } from '$state/lastCompositionEnd'; -interface TimeStamped { +type TimeStamped = { readonly timeStamp: number; -} +}; export function useCompositionEndTracking(): void { const setLastCompositionEnd = useSetAtom(lastCompositionEndAtom); @@ -24,13 +24,13 @@ export function useCompositionEndTracking(): void { }); } -interface IsComposingLike { +type IsComposingLike = { readonly timeStamp: number; readonly keyCode: number; readonly nativeEvent: { readonly isComposing?: boolean; }; -} +}; export function useComposingCheck({ compositionEndThreshold = 500, diff --git a/src/app/hooks/useCrossSigning.ts b/src/app/hooks/useCrossSigning.ts index b280ecfdc..776ce9ddb 100644 --- a/src/app/hooks/useCrossSigning.ts +++ b/src/app/hooks/useCrossSigning.ts @@ -1,4 +1,4 @@ -import { AccountDataEvent, SecretAccountData } from '$types/matrix/accountData'; +import { AccountDataEvent, type SecretAccountData } from '$types/matrix/accountData'; import { useAccountData } from './useAccountData'; export const useCrossSigningActive = (): boolean => { diff --git a/src/app/hooks/useDateFormat.ts b/src/app/hooks/useDateFormat.ts index 497173101..05de45b29 100644 --- a/src/app/hooks/useDateFormat.ts +++ b/src/app/hooks/useDateFormat.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { DateFormat } from '$state/settings'; +import { type DateFormat } from '$state/settings'; export type DateFormatItem = { name: string; diff --git a/src/app/hooks/useDebounce.ts b/src/app/hooks/useDebounce.ts index 5f33976a3..d3ebc7b3d 100644 --- a/src/app/hooks/useDebounce.ts +++ b/src/app/hooks/useDebounce.ts @@ -1,9 +1,9 @@ import { useCallback, useRef } from 'react'; -export interface DebounceOptions { +export type DebounceOptions = { wait?: number; immediate?: boolean; -} +}; export type DebounceCallback = (...args: T) => void; export function useDebounce( diff --git a/src/app/hooks/useDeviceList.ts b/src/app/hooks/useDeviceList.ts index 965784125..06b3bb73f 100644 --- a/src/app/hooks/useDeviceList.ts +++ b/src/app/hooks/useDeviceList.ts @@ -1,5 +1,5 @@ import { useEffect, useCallback, useMemo } from 'react'; -import { IMyDevice, CryptoEvent, CryptoEventHandlerMap } from '$types/matrix-sdk'; +import { type IMyDevice, CryptoEvent, type CryptoEventHandlerMap } from '$types/matrix-sdk'; import { useQuery } from '@tanstack/react-query'; import { useMatrixClient } from './useMatrixClient'; diff --git a/src/app/hooks/useDeviceVerificationStatus.ts b/src/app/hooks/useDeviceVerificationStatus.ts index 4d2ea071f..95e24b27c 100644 --- a/src/app/hooks/useDeviceVerificationStatus.ts +++ b/src/app/hooks/useDeviceVerificationStatus.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from 'react'; -import { CryptoApi } from '$types/matrix-sdk'; +import { type CryptoApi } from '$types/matrix-sdk'; import { verifiedDevice } from '$utils/matrix-crypto'; import { fulfilledPromiseSettledResult } from '$utils/common'; import { useAlive } from './useAlive'; diff --git a/src/app/hooks/useDirectUsers.ts b/src/app/hooks/useDirectUsers.ts index ea412fb65..9f65bfc74 100644 --- a/src/app/hooks/useDirectUsers.ts +++ b/src/app/hooks/useDirectUsers.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { AccountDataEvent, MDirectContent } from '$types/matrix/accountData'; +import { AccountDataEvent, type MDirectContent } from '$types/matrix/accountData'; import { useAccountData } from './useAccountData'; import { useAllJoinedRoomsSet, useGetRoom } from './useGetRoom'; diff --git a/src/app/hooks/useFileDrop.ts b/src/app/hooks/useFileDrop.ts index 7922c336d..ff95f8b62 100644 --- a/src/app/hooks/useFileDrop.ts +++ b/src/app/hooks/useFileDrop.ts @@ -1,4 +1,11 @@ -import { useCallback, DragEventHandler, RefObject, useState, useEffect, useRef } from 'react'; +import { + useCallback, + type DragEventHandler, + type RefObject, + useState, + useEffect, + useRef, +} from 'react'; import { getDataTransferFiles } from '$utils/dom'; export const useFileDropHandler = (onDrop: (file: File[]) => void): DragEventHandler => diff --git a/src/app/hooks/useFilePasteHandler.ts b/src/app/hooks/useFilePasteHandler.ts index 253ad9b3b..197a8f85c 100644 --- a/src/app/hooks/useFilePasteHandler.ts +++ b/src/app/hooks/useFilePasteHandler.ts @@ -1,4 +1,4 @@ -import { useCallback, ClipboardEventHandler } from 'react'; +import { useCallback, type ClipboardEventHandler } from 'react'; import { getDataTransferFiles } from '$utils/dom'; export const useFilePasteHandler = (onPaste: (file: File[]) => void): ClipboardEventHandler => diff --git a/src/app/hooks/useGetRoom.ts b/src/app/hooks/useGetRoom.ts index 6ab37b795..671b72fa4 100644 --- a/src/app/hooks/useGetRoom.ts +++ b/src/app/hooks/useGetRoom.ts @@ -1,4 +1,4 @@ -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useAtomValue } from 'jotai'; import { useCallback, useMemo } from 'react'; import { allRoomsAtom } from '$state/room-list/roomList'; diff --git a/src/app/hooks/useGroupDMMembers.ts b/src/app/hooks/useGroupDMMembers.ts index a3ab796a1..2908e3a0b 100644 --- a/src/app/hooks/useGroupDMMembers.ts +++ b/src/app/hooks/useGroupDMMembers.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { MatrixClient, Room } from '$types/matrix-sdk'; +import { type MatrixClient, type Room } from '$types/matrix-sdk'; export type GroupMemberInfo = { userId: string; diff --git a/src/app/hooks/useImageGestures.ts b/src/app/hooks/useImageGestures.ts index 9c10d676d..f06a9eb42 100644 --- a/src/app/hooks/useImageGestures.ts +++ b/src/app/hooks/useImageGestures.ts @@ -1,14 +1,14 @@ import { useState, useCallback, useRef, useEffect } from 'react'; -interface Vector2 { +type Vector2 = { x: number; y: number; -} +}; -interface Transforms { +type Transforms = { zoom: number; pan: Vector2; -} +}; // calculate pointer position relative to the image center // @@ -108,7 +108,7 @@ export const useImageGestures = (active: boolean, step = 0.2, min = 0.1, max = 5 setCursor('grabbing'); if (activePointers.current.size === 2) { - const points = Array.from(activePointers.current.values()); + const points = [...activePointers.current.values()]; initialDist.current = Math.hypot(points[0].x - points[1].x, points[0].y - points[1].y); } }, @@ -122,7 +122,7 @@ export const useImageGestures = (active: boolean, step = 0.2, min = 0.1, max = 5 activePointers.current.set(e.pointerId, { x: e.clientX, y: e.clientY }); if (activePointers.current.size === 2) { - const points = Array.from(activePointers.current.values()); + const points = [...activePointers.current.values()]; const currentDist = Math.hypot(points[0].x - points[1].x, points[0].y - points[1].y); const delta = currentDist / initialDist.current; diff --git a/src/app/hooks/useImagePackRooms.ts b/src/app/hooks/useImagePackRooms.ts index b2163a726..d9622db45 100644 --- a/src/app/hooks/useImagePackRooms.ts +++ b/src/app/hooks/useImagePackRooms.ts @@ -1,4 +1,4 @@ -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useMemo } from 'react'; import { getAllParents } from '$utils/room'; import { useMatrixClient } from './useMatrixClient'; @@ -10,7 +10,7 @@ export const useImagePackRooms = ( const mx = useMatrixClient(); const imagePackRooms: Room[] = useMemo(() => { - const allParentSpaces = [roomId].concat(Array.from(getAllParents(roomToParents, roomId))); + const allParentSpaces = [...[roomId], ...[...getAllParents(roomToParents, roomId)]]; return allParentSpaces.reduce((list, rId) => { const r = mx.getRoom(rId); if (r) list.push(r); diff --git a/src/app/hooks/useImagePacks.ts b/src/app/hooks/useImagePacks.ts index 7146966b2..b318034a3 100644 --- a/src/app/hooks/useImagePacks.ts +++ b/src/app/hooks/useImagePacks.ts @@ -1,4 +1,4 @@ -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { AccountDataEvent } from '$types/matrix/accountData'; import { StateEvent } from '$types/matrix/room'; @@ -8,8 +8,8 @@ import { getRoomImagePacks, getUserImagePack, globalPacksScope, - ImagePack, - ImageUsage, + type ImagePack, + type ImageUsage, readCachedPack, readCachedPacks, roomPacksScope, @@ -24,8 +24,8 @@ import { useStateEventCallback } from './useStateEventCallback'; const imagePackEqual = (a: ImagePack | undefined, b: ImagePack | undefined): boolean => { if (!a && !b) return true; if (!a || !b) return false; - const aImages = Array.from(a.images.collection.entries()); - const bImages = Array.from(b.images.collection.entries()); + const aImages = [...a.images.collection.entries()]; + const bImages = [...b.images.collection.entries()]; if (aImages.length !== bImages.length) return false; const sameImages = aImages.every(([shortcode, image], index) => { const [otherShortcode, otherImage] = bImages[index]; @@ -136,7 +136,7 @@ export const useGlobalImagePacks = (): ImagePack[] => { const stateKey = mEvent.getStateKey(); if (eventType === StateEvent.PoniesRoomEmotes && roomId && typeof stateKey === 'string') { setGlobalPacks((prev) => { - const global = !!prev.find( + const global = prev.some( (pack) => pack.address && pack.address.roomId === roomId && pack.address.stateKey === stateKey ); @@ -287,7 +287,7 @@ export const useRoomsImagePacks = (rooms: Room[]) => { useCallback( (mEvent) => { if ( - rooms.find((room) => room.roomId === mEvent.getRoomId()) && + rooms.some((room) => room.roomId === mEvent.getRoomId()) && mEvent.getType() === StateEvent.PoniesRoomEmotes ) { setRoomPacks((prev) => { @@ -312,10 +312,11 @@ export const useRelevantImagePacks = (usage: ImageUsage, rooms: Room[]): ImagePa const packs = userPack ? [userPack] : []; const globalPackIds = new Set(globalPacks.map((pack) => pack.id)); - const relPacks = packs.concat( - globalPacks, - roomsPacks.filter((pack) => !globalPackIds.has(pack.id)) - ); + const relPacks = [ + ...packs, + ...globalPacks, + ...roomsPacks.filter((pack) => !globalPackIds.has(pack.id)), + ]; return relPacks.filter((pack) => pack.getImages(usage).length > 0); }, [userPack, globalPacks, roomsPacks, usage]); diff --git a/src/app/hooks/useIntegrationManager.ts b/src/app/hooks/useIntegrationManager.ts index 00feba387..76a978288 100644 --- a/src/app/hooks/useIntegrationManager.ts +++ b/src/app/hooks/useIntegrationManager.ts @@ -1,11 +1,11 @@ import { useCallback, useEffect, useState } from 'react'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { useMatrixClient } from './useMatrixClient'; -export interface IntegrationManager { +export type IntegrationManager = { apiUrl: string; uiUrl: string; -} +}; const DEFAULT_MANAGERS: IntegrationManager[] = [ { diff --git a/src/app/hooks/useKeyBackup.ts b/src/app/hooks/useKeyBackup.ts index 3714ec6be..48b0751c8 100644 --- a/src/app/hooks/useKeyBackup.ts +++ b/src/app/hooks/useKeyBackup.ts @@ -1,9 +1,9 @@ import { - BackupTrustInfo, - CryptoApi, + type BackupTrustInfo, + type CryptoApi, CryptoEvent, - CryptoEventHandlerMap, - KeyBackupInfo, + type CryptoEventHandlerMap, + type KeyBackupInfo, } from '$types/matrix-sdk'; import { useCallback, useEffect, useState } from 'react'; import * as Sentry from '@sentry/react'; diff --git a/src/app/hooks/useLivekitSupport.ts b/src/app/hooks/useLivekitSupport.ts index 3cb2c1d88..b38bc12ca 100644 --- a/src/app/hooks/useLivekitSupport.ts +++ b/src/app/hooks/useLivekitSupport.ts @@ -1,4 +1,4 @@ -import { AutoDiscoveryInfo } from '../cs-api'; +import { type AutoDiscoveryInfo } from '../cs-api'; import { useAutoDiscoveryInfo } from './useAutoDiscoveryInfo'; export const livekitSupport = (autoDiscoveryInfo: AutoDiscoveryInfo): boolean => { diff --git a/src/app/hooks/useLocalRoomSummary.ts b/src/app/hooks/useLocalRoomSummary.ts index 043d09f7b..3cd38330a 100644 --- a/src/app/hooks/useLocalRoomSummary.ts +++ b/src/app/hooks/useLocalRoomSummary.ts @@ -1,4 +1,4 @@ -import { GuestAccess, HistoryVisibility, JoinRule, Room } from '$types/matrix-sdk'; +import { GuestAccess, HistoryVisibility, type JoinRule, type Room } from '$types/matrix-sdk'; import { StateEvent } from '$types/matrix/room'; import { getStateEvent } from '$utils/room'; diff --git a/src/app/hooks/useMatrixClient.ts b/src/app/hooks/useMatrixClient.ts index f12365351..6b1310d7f 100644 --- a/src/app/hooks/useMatrixClient.ts +++ b/src/app/hooks/useMatrixClient.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; const MatrixClientContext = createContext(null); diff --git a/src/app/hooks/useMatrixEventRenderer.ts b/src/app/hooks/useMatrixEventRenderer.ts index 57d56cc1c..f08258009 100644 --- a/src/app/hooks/useMatrixEventRenderer.ts +++ b/src/app/hooks/useMatrixEventRenderer.ts @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; export type EventRenderer = (...args: T) => ReactNode; diff --git a/src/app/hooks/useMediaConfig.ts b/src/app/hooks/useMediaConfig.ts index 929ebd004..d633cd6b7 100644 --- a/src/app/hooks/useMediaConfig.ts +++ b/src/app/hooks/useMediaConfig.ts @@ -1,9 +1,9 @@ import { createContext, useContext } from 'react'; -export interface MediaConfig { +export type MediaConfig = { [key: string]: unknown; 'm.upload.size'?: number; -} +}; const MediaConfigContext = createContext(null); diff --git a/src/app/hooks/useMemberEventParser.tsx b/src/app/hooks/useMemberEventParser.tsx index 2791cef4d..19a1bf57b 100644 --- a/src/app/hooks/useMemberEventParser.tsx +++ b/src/app/hooks/useMemberEventParser.tsx @@ -1,7 +1,7 @@ -import { MouseEventHandler, useCallback, ReactNode } from 'react'; -import { IconSrc, Icons, Text } from 'folds'; -import { MatrixEvent, Room } from '$types/matrix-sdk'; -import { IMemberContent, Membership } from '$types/matrix/room'; +import { type MouseEventHandler, useCallback, type ReactNode } from 'react'; +import { type IconSrc, Icons, Text } from 'folds'; +import { type MatrixEvent, type Room } from '$types/matrix-sdk'; +import { type IMemberContent, Membership } from '$types/matrix/room'; import { getMxIdLocalPart } from '$utils/matrix'; import { isMembershipChanged } from '$utils/room'; import { useOpenUserRoomProfile } from '$state/hooks/userRoomProfile'; diff --git a/src/app/hooks/useMemberFilter.ts b/src/app/hooks/useMemberFilter.ts index 3ce50a5bd..fb40fc2c4 100644 --- a/src/app/hooks/useMemberFilter.ts +++ b/src/app/hooks/useMemberFilter.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { RoomMember } from '$types/matrix-sdk'; +import { type RoomMember } from '$types/matrix-sdk'; import { Membership } from '$types/matrix/room'; export const MembershipFilter = { diff --git a/src/app/hooks/useMemberPowerCompare.ts b/src/app/hooks/useMemberPowerCompare.ts index 72163edfc..3380377b0 100644 --- a/src/app/hooks/useMemberPowerCompare.ts +++ b/src/app/hooks/useMemberPowerCompare.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import { IPowerLevels, readPowerLevel } from './usePowerLevels'; +import { type IPowerLevels, readPowerLevel } from './usePowerLevels'; export const useMemberPowerCompare = (creators: Set, powerLevels: IPowerLevels) => { /** diff --git a/src/app/hooks/useMemberPowerTag.ts b/src/app/hooks/useMemberPowerTag.ts index ac2df17c4..727daad09 100644 --- a/src/app/hooks/useMemberPowerTag.ts +++ b/src/app/hooks/useMemberPowerTag.ts @@ -1,11 +1,11 @@ import { useCallback, useMemo } from 'react'; -import { MatrixClient, Room, RoomMember } from '$types/matrix-sdk'; -import { MemberPowerTag, MemberPowerTagIcon } from '$types/matrix/room'; +import { type MatrixClient, type Room, type RoomMember } from '$types/matrix-sdk'; +import { type MemberPowerTag, type MemberPowerTagIcon } from '$types/matrix/room'; import { accessibleColor } from '$plugins/color'; -import { getPowerLevelTag, PowerLevelTags, usePowerLevelTags } from './usePowerLevelTags'; -import { IPowerLevels, readPowerLevel } from './usePowerLevels'; +import { getPowerLevelTag, type PowerLevelTags, usePowerLevelTags } from './usePowerLevelTags'; +import { type IPowerLevels, readPowerLevel } from './usePowerLevels'; import { useRoomCreatorsTag } from './useRoomCreatorsTag'; -import { ThemeKind } from './useTheme'; +import { type ThemeKind } from './useTheme'; export type GetMemberPowerTag = (userId: string) => MemberPowerTag; diff --git a/src/app/hooks/useMemberSort.ts b/src/app/hooks/useMemberSort.ts index 72e0581da..af82c3a4d 100644 --- a/src/app/hooks/useMemberSort.ts +++ b/src/app/hooks/useMemberSort.ts @@ -1,4 +1,4 @@ -import { RoomMember } from '$types/matrix-sdk'; +import { type RoomMember } from '$types/matrix-sdk'; import { useCallback, useMemo } from 'react'; export const MemberSort = { diff --git a/src/app/hooks/useMembership.ts b/src/app/hooks/useMembership.ts index 47044c381..6957e7fc6 100644 --- a/src/app/hooks/useMembership.ts +++ b/src/app/hooks/useMembership.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Room, RoomMemberEvent, RoomMemberEventHandlerMap } from '$types/matrix-sdk'; +import { type Room, RoomMemberEvent, type RoomMemberEventHandlerMap } from '$types/matrix-sdk'; import { Membership } from '$types/matrix/room'; export const useMembership = (room: Room, userId: string): Membership => { diff --git a/src/app/hooks/useMentionClickHandler.ts b/src/app/hooks/useMentionClickHandler.ts index 350b7c2e8..f6d83dd2c 100644 --- a/src/app/hooks/useMentionClickHandler.ts +++ b/src/app/hooks/useMentionClickHandler.ts @@ -1,8 +1,8 @@ -import { ReactEventHandler, useCallback } from 'react'; +import { type ReactEventHandler, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { isRoomId, isUserId } from '$utils/matrix'; import { getHomeRoomPath, withSearchParam } from '$pages/pathUtils'; -import { RoomSearchParams } from '$pages/paths'; +import { type RoomSearchParams } from '$pages/paths'; import { isSettingsSectionId } from '$features/settings/routes'; import { useOpenSettings } from '$features/settings/useOpenSettings'; import { useOpenUserRoomProfile } from '$state/hooks/userRoomProfile'; diff --git a/src/app/hooks/useMessageSpacing.ts b/src/app/hooks/useMessageSpacing.ts index aab6cb531..33647903f 100644 --- a/src/app/hooks/useMessageSpacing.ts +++ b/src/app/hooks/useMessageSpacing.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { MessageSpacing } from '$state/settings'; +import { type MessageSpacing } from '$state/settings'; export type MessageSpacingItem = { name: string; diff --git a/src/app/hooks/useMutualRooms.ts b/src/app/hooks/useMutualRooms.ts index d0cbefe7e..2c10dfd6a 100644 --- a/src/app/hooks/useMutualRooms.ts +++ b/src/app/hooks/useMutualRooms.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; import { useMatrixClient } from './useMatrixClient'; -import { AsyncState, useAsyncCallbackValue } from './useAsyncCallback'; +import { type AsyncState, useAsyncCallbackValue } from './useAsyncCallback'; import { useSpecVersions } from './useSpecVersions'; export const useMutualRoomsSupport = (): boolean => { diff --git a/src/app/hooks/useNickname.ts b/src/app/hooks/useNickname.ts index 999a668dd..56c65496f 100644 --- a/src/app/hooks/useNickname.ts +++ b/src/app/hooks/useNickname.ts @@ -1,6 +1,6 @@ import { useAtomValue, useSetAtom } from 'jotai'; import { useCallback, useEffect } from 'react'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { AccountDataEvent } from '$types/matrix/accountData'; import { nicknamesAtom, setNicknameAtom } from '$state/nicknames'; import { useAccountDataCallback } from './useAccountDataCallback'; diff --git a/src/app/hooks/useNotificationMode.ts b/src/app/hooks/useNotificationMode.ts index 7901c80a2..d537398fc 100644 --- a/src/app/hooks/useNotificationMode.ts +++ b/src/app/hooks/useNotificationMode.ts @@ -1,4 +1,4 @@ -import { PushRuleAction, PushRuleActionName, TweakName } from '$types/matrix-sdk'; +import { type PushRuleAction, PushRuleActionName, TweakName } from '$types/matrix-sdk'; import { useCallback, useMemo } from 'react'; export enum NotificationMode { diff --git a/src/app/hooks/useParsedLoginFlows.ts b/src/app/hooks/useParsedLoginFlows.ts index 8095ef37a..85605ef7e 100644 --- a/src/app/hooks/useParsedLoginFlows.ts +++ b/src/app/hooks/useParsedLoginFlows.ts @@ -1,5 +1,10 @@ import { useMemo } from 'react'; -import { ILoginFlow, IPasswordFlow, ISSOFlow, LoginFlow } from '$types/matrix-sdk'; +import { + type ILoginFlow, + type IPasswordFlow, + type ISSOFlow, + type LoginFlow, +} from '$types/matrix-sdk'; export const getSSOFlow = (loginFlows: LoginFlow[]): ISSOFlow | undefined => loginFlows.find((flow) => flow.type === 'm.login.sso' || flow.type === 'm.login.cas') as diff --git a/src/app/hooks/usePasswordEmail.ts b/src/app/hooks/usePasswordEmail.ts index dbe39010d..bc2169cf5 100644 --- a/src/app/hooks/usePasswordEmail.ts +++ b/src/app/hooks/usePasswordEmail.ts @@ -1,7 +1,7 @@ -import { MatrixClient, MatrixError } from '$types/matrix-sdk'; +import { type MatrixClient, type MatrixError } from '$types/matrix-sdk'; import { useCallback, useRef } from 'react'; -import { AsyncState, useAsyncCallback } from './useAsyncCallback'; -import { RequestEmailTokenCallback, RequestEmailTokenResponse } from './types'; +import { type AsyncState, useAsyncCallback } from './useAsyncCallback'; +import { type RequestEmailTokenCallback, type RequestEmailTokenResponse } from './types'; export const usePasswordEmail = ( mx: MatrixClient diff --git a/src/app/hooks/usePerMessageProfile.ts b/src/app/hooks/usePerMessageProfile.ts index 3f9ff455b..a2964163d 100644 --- a/src/app/hooks/usePerMessageProfile.ts +++ b/src/app/hooks/usePerMessageProfile.ts @@ -1,6 +1,6 @@ -import { AccountDataCompatVersion, AccountDataEvent } from '$types/matrix/accountData'; -import { PronounSet } from '$utils/pronouns'; -import { MatrixClient } from 'matrix-js-sdk'; +import { type AccountDataCompatVersion, AccountDataEvent } from '$types/matrix/accountData'; +import { type PronounSet } from '$utils/pronouns'; +import { type MatrixClient } from 'matrix-js-sdk'; const ACCOUNT_DATA_PREFIX = AccountDataEvent.SablePerProfileMessageProfiles; @@ -314,7 +314,7 @@ async function getRoomsUsingProfile(mx: MatrixClient, profileId: string): Promis const content: PerMessageProfileRoomAssociationWrapper | undefined = accountData?.getContent(); const associations = getAssociationsMap(content); const roomsUsingProfile: string[] = []; - Array.from(associations.entries()).forEach(([roomId, assoc]) => { + [...associations.entries()].forEach(([roomId, assoc]) => { if (assoc?.profileId === profileId) roomsUsingProfile.push(roomId); }); return roomsUsingProfile; diff --git a/src/app/hooks/usePowerLevelTags.ts b/src/app/hooks/usePowerLevelTags.ts index eab9cc1f7..05cff7f6d 100644 --- a/src/app/hooks/usePowerLevelTags.ts +++ b/src/app/hooks/usePowerLevelTags.ts @@ -1,7 +1,7 @@ -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useMemo } from 'react'; -import { MemberPowerTag, StateEvent } from '$types/matrix/room'; -import { IPowerLevels } from './usePowerLevels'; +import { type MemberPowerTag, StateEvent } from '$types/matrix/room'; +import { type IPowerLevels } from './usePowerLevels'; import { useStateEvent } from './useStateEvent'; export type PowerLevelTags = Record; @@ -95,7 +95,7 @@ export const usePowerLevelTags = (room: Room, powerLevels: IPowerLevels): PowerL const powerToTags: PowerLevelTags = { ...content }; const powers = getUsedPowers(powerLevels); - Array.from(powers).forEach((power) => { + [...powers].forEach((power) => { if (powerToTags[power]?.name === undefined) { powerToTags[power] = DEFAULT_TAGS[power] ?? generateFallbackTag(DEFAULT_TAGS, power); } diff --git a/src/app/hooks/usePowerLevels.ts b/src/app/hooks/usePowerLevels.ts index 4b1b874b4..a99f2d5b6 100644 --- a/src/app/hooks/usePowerLevels.ts +++ b/src/app/hooks/usePowerLevels.ts @@ -1,4 +1,4 @@ -import { MatrixEvent, Room } from '$types/matrix-sdk'; +import { type MatrixEvent, type Room } from '$types/matrix-sdk'; import { createContext, useCallback, useContext, useMemo, useState } from 'react'; import { produce } from 'immer'; import { StateEvent } from '$types/matrix/room'; @@ -109,7 +109,7 @@ export const useRoomsPowerLevels = (rooms: Room[]): Map => roomId && event.getType() === StateEvent.RoomPowerLevels && event.getStateKey() === '' && - rooms.find((r) => r.roomId === roomId) + rooms.some((r) => r.roomId === roomId) ) { setRoomToPowerLevels(getRoomsPowerLevels()); } diff --git a/src/app/hooks/usePushRule.ts b/src/app/hooks/usePushRule.ts index 32c9c3133..6a845115d 100644 --- a/src/app/hooks/usePushRule.ts +++ b/src/app/hooks/usePushRule.ts @@ -1,10 +1,10 @@ import { - IPushRule, - IPushRules, - PushRuleAction, - PushRuleCondition, + type IPushRule, + type IPushRules, + type PushRuleAction, + type PushRuleCondition, PushRuleKind, - RuleId, + type RuleId, } from '$types/matrix-sdk'; import { useMemo } from 'react'; diff --git a/src/app/hooks/useRecentEmoji.ts b/src/app/hooks/useRecentEmoji.ts index 09c356795..44609c506 100644 --- a/src/app/hooks/useRecentEmoji.ts +++ b/src/app/hooks/useRecentEmoji.ts @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react'; -import { ClientEvent, MatrixClient, MatrixEvent } from '$types/matrix-sdk'; +import { ClientEvent, type MatrixClient, type MatrixEvent } from '$types/matrix-sdk'; import { AccountDataEvent } from '$types/matrix/accountData'; import { getRecentEmojis } from '$plugins/recent-emoji'; -import { IEmoji } from '$plugins/emoji'; +import { type IEmoji } from '$plugins/emoji'; export const useRecentEmoji = (mx: MatrixClient, limit?: number): IEmoji[] => { const [recentEmoji, setRecentEmoji] = useState(() => getRecentEmojis(mx, limit)); diff --git a/src/app/hooks/useRegisterEmail.ts b/src/app/hooks/useRegisterEmail.ts index 9124f116f..d311d9b0e 100644 --- a/src/app/hooks/useRegisterEmail.ts +++ b/src/app/hooks/useRegisterEmail.ts @@ -1,7 +1,7 @@ -import { MatrixClient, MatrixError } from '$types/matrix-sdk'; +import { type MatrixClient, type MatrixError } from '$types/matrix-sdk'; import { useCallback, useRef } from 'react'; -import { AsyncState, useAsyncCallback } from './useAsyncCallback'; -import { RequestEmailTokenCallback, RequestEmailTokenResponse } from './types'; +import { type AsyncState, useAsyncCallback } from './useAsyncCallback'; +import { type RequestEmailTokenCallback, type RequestEmailTokenResponse } from './types'; export const useRegisterEmail = ( mx: MatrixClient diff --git a/src/app/hooks/useRoom.ts b/src/app/hooks/useRoom.ts index 80b148115..5de2f999c 100644 --- a/src/app/hooks/useRoom.ts +++ b/src/app/hooks/useRoom.ts @@ -1,4 +1,4 @@ -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { createContext, useContext } from 'react'; const RoomContext = createContext(null); diff --git a/src/app/hooks/useRoomAbbreviations.ts b/src/app/hooks/useRoomAbbreviations.ts index e2641b341..34a93d27c 100644 --- a/src/app/hooks/useRoomAbbreviations.ts +++ b/src/app/hooks/useRoomAbbreviations.ts @@ -1,8 +1,8 @@ import { createContext, useContext, useCallback, useMemo } from 'react'; import { useAtomValue } from 'jotai'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { StateEvent } from '$types/matrix/room'; -import { buildAbbreviationsMap, RoomAbbreviationsContent } from '$utils/abbreviations'; +import { buildAbbreviationsMap, type RoomAbbreviationsContent } from '$utils/abbreviations'; import { getAllParents, getStateEvent } from '$utils/room'; import { roomToParentsAtom } from '$state/room/roomToParents'; import { useMatrixClient } from './useMatrixClient'; @@ -54,7 +54,7 @@ export const useMergedAbbreviations = (room: Room): Map => { ); return useMemo(() => { - const allParentIds = Array.from(getAllParents(roomToParents, room.roomId)); + const allParentIds = [...getAllParents(roomToParents, room.roomId)]; const ancestorEntries = allParentIds.flatMap((parentId) => { const parentRoom = mx.getRoom(parentId); if (!parentRoom) return []; diff --git a/src/app/hooks/useRoomAccountData.ts b/src/app/hooks/useRoomAccountData.ts index 4d246fe49..fe66645d3 100644 --- a/src/app/hooks/useRoomAccountData.ts +++ b/src/app/hooks/useRoomAccountData.ts @@ -1,11 +1,11 @@ -import { Room, RoomEvent, RoomEventHandlerMap } from '$types/matrix-sdk'; +import { type Room, RoomEvent, type RoomEventHandlerMap } from '$types/matrix-sdk'; import { useCallback, useEffect, useState } from 'react'; export const useRoomAccountData = (room: Room): Map => { const getAccountData = useCallback((): Map => { const accountData = new Map(); - Array.from(room.accountData.entries()).forEach(([type, mEvent]) => { + [...room.accountData.entries()].forEach(([type, mEvent]) => { const content = mEvent.getContent(); accountData.set(type, content); }); diff --git a/src/app/hooks/useRoomAliases.ts b/src/app/hooks/useRoomAliases.ts index 36fff1ad9..ab6fcabae 100644 --- a/src/app/hooks/useRoomAliases.ts +++ b/src/app/hooks/useRoomAliases.ts @@ -1,8 +1,12 @@ import { useCallback, useEffect, useMemo } from 'react'; -import { MatrixError, Room, RoomCanonicalAliasEventContent } from '$types/matrix-sdk'; +import { + type MatrixError, + type Room, + type RoomCanonicalAliasEventContent, +} from '$types/matrix-sdk'; import { StateEvent } from '$types/matrix/room'; import { getStateEvent } from '$utils/room'; -import { AsyncState, useAsyncCallback } from './useAsyncCallback'; +import { type AsyncState, useAsyncCallback } from './useAsyncCallback'; import { useMatrixClient } from './useMatrixClient'; import { useAlive } from './useAlive'; import { useStateEvent } from './useStateEvent'; diff --git a/src/app/hooks/useRoomCreators.ts b/src/app/hooks/useRoomCreators.ts index 6c4f740c1..f041d0682 100644 --- a/src/app/hooks/useRoomCreators.ts +++ b/src/app/hooks/useRoomCreators.ts @@ -1,6 +1,6 @@ -import { MatrixClient, MatrixEvent, Room } from '$types/matrix-sdk'; +import { type MatrixClient, type MatrixEvent, type Room } from '$types/matrix-sdk'; import { useMemo } from 'react'; -import { IRoomCreateContent, StateEvent } from '$types/matrix/room'; +import { type IRoomCreateContent, StateEvent } from '$types/matrix/room'; import { creatorsSupported } from '$utils/matrix'; import { getStateEvent } from '$utils/room'; import { useStateEvent } from './useStateEvent'; diff --git a/src/app/hooks/useRoomCreatorsTag.ts b/src/app/hooks/useRoomCreatorsTag.ts index f1a31de15..081d61d43 100644 --- a/src/app/hooks/useRoomCreatorsTag.ts +++ b/src/app/hooks/useRoomCreatorsTag.ts @@ -1,4 +1,4 @@ -import { MemberPowerTag } from '$types/matrix/room'; +import { type MemberPowerTag } from '$types/matrix/room'; const DEFAULT_TAG: MemberPowerTag = { name: 'Founder', diff --git a/src/app/hooks/useRoomEvent.ts b/src/app/hooks/useRoomEvent.ts index 703353669..f5bf22dcb 100644 --- a/src/app/hooks/useRoomEvent.ts +++ b/src/app/hooks/useRoomEvent.ts @@ -1,10 +1,10 @@ import { - IEvent, + type IEvent, MatrixEvent, MatrixEventEvent, - Room, + type Room, RoomEvent, - CryptoBackend, + type CryptoBackend, } from '$types/matrix-sdk'; import { useCallback, useEffect, useMemo, useState } from 'react'; import to from 'await-to-js'; diff --git a/src/app/hooks/useRoomEventReaders.ts b/src/app/hooks/useRoomEventReaders.ts index 8a0236685..52147c60d 100644 --- a/src/app/hooks/useRoomEventReaders.ts +++ b/src/app/hooks/useRoomEventReaders.ts @@ -1,4 +1,4 @@ -import { Room, RoomEvent, RoomEventHandlerMap } from '$types/matrix-sdk'; +import { type Room, RoomEvent, type RoomEventHandlerMap } from '$types/matrix-sdk'; import { useEffect, useState } from 'react'; const getEventReaders = (room: Room, evtId?: string) => { diff --git a/src/app/hooks/useRoomLatestRenderedEvent.ts b/src/app/hooks/useRoomLatestRenderedEvent.ts index 07b2a28d9..9c5aa4526 100644 --- a/src/app/hooks/useRoomLatestRenderedEvent.ts +++ b/src/app/hooks/useRoomLatestRenderedEvent.ts @@ -1,5 +1,10 @@ /* eslint-disable no-continue */ -import { MatrixEvent, Room, RoomEvent, RoomEventHandlerMap } from '$types/matrix-sdk'; +import { + type MatrixEvent, + type Room, + RoomEvent, + type RoomEventHandlerMap, +} from '$types/matrix-sdk'; import { useEffect, useState } from 'react'; import { MessageEvent, StateEvent } from '$types/matrix/room'; import { settingsAtom } from '$state/settings'; diff --git a/src/app/hooks/useRoomMembers.ts b/src/app/hooks/useRoomMembers.ts index fbfdbdd3c..e7271b039 100644 --- a/src/app/hooks/useRoomMembers.ts +++ b/src/app/hooks/useRoomMembers.ts @@ -1,4 +1,9 @@ -import { MatrixClient, MatrixEvent, RoomMember, RoomMemberEvent } from '$types/matrix-sdk'; +import { + type MatrixClient, + type MatrixEvent, + type RoomMember, + RoomMemberEvent, +} from '$types/matrix-sdk'; import { useEffect, useState } from 'react'; export const useRoomMembers = (mx: MatrixClient, roomId: string): RoomMember[] => { diff --git a/src/app/hooks/useRoomMeta.ts b/src/app/hooks/useRoomMeta.ts index eee1385fd..6cc70b2e6 100644 --- a/src/app/hooks/useRoomMeta.ts +++ b/src/app/hooks/useRoomMeta.ts @@ -1,5 +1,10 @@ import { useEffect, useState } from 'react'; -import { RoomJoinRulesEventContent, Room, RoomEvent, RoomStateEvent } from '$types/matrix-sdk'; +import { + type RoomJoinRulesEventContent, + type Room, + RoomEvent, + RoomStateEvent, +} from '$types/matrix-sdk'; import { StateEvent } from '$types/matrix/room'; import { useStateEvent } from './useStateEvent'; import { useNickname } from './useNickname'; diff --git a/src/app/hooks/useRoomNavigate.ts b/src/app/hooks/useRoomNavigate.ts index 51555125c..387657b3f 100644 --- a/src/app/hooks/useRoomNavigate.ts +++ b/src/app/hooks/useRoomNavigate.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import { NavigateOptions, useNavigate } from 'react-router-dom'; +import { type NavigateOptions, useNavigate } from 'react-router-dom'; import { useAtomValue } from 'jotai'; import { getCanonicalAliasOrRoomId } from '$utils/matrix'; import { diff --git a/src/app/hooks/useRoomPermissions.ts b/src/app/hooks/useRoomPermissions.ts index cb6f69a25..5046d6a10 100644 --- a/src/app/hooks/useRoomPermissions.ts +++ b/src/app/hooks/useRoomPermissions.ts @@ -1,8 +1,8 @@ import { useMemo } from 'react'; import { - IPowerLevels, - PowerLevelActions, - PowerLevelNotificationsAction, + type IPowerLevels, + type PowerLevelActions, + type PowerLevelNotificationsAction, readPowerLevel, } from './usePowerLevels'; diff --git a/src/app/hooks/useRoomPinnedEvents.ts b/src/app/hooks/useRoomPinnedEvents.ts index cb6106461..ae6dfeb4c 100644 --- a/src/app/hooks/useRoomPinnedEvents.ts +++ b/src/app/hooks/useRoomPinnedEvents.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { RoomPinnedEventsEventContent, Room } from '$types/matrix-sdk'; +import { type RoomPinnedEventsEventContent, type Room } from '$types/matrix-sdk'; import { StateEvent } from '$types/matrix/room'; import { useStateEvent } from './useStateEvent'; diff --git a/src/app/hooks/useRoomState.ts b/src/app/hooks/useRoomState.ts index 532bfb7d8..3142e63bf 100644 --- a/src/app/hooks/useRoomState.ts +++ b/src/app/hooks/useRoomState.ts @@ -1,9 +1,9 @@ import { Direction, - MatrixEvent, - Room, + type MatrixEvent, + type Room, RoomStateEvent, - RoomStateEventHandlerMap, + type RoomStateEventHandlerMap, } from '$types/matrix-sdk'; import { useCallback, useEffect, useState } from 'react'; import { StateEvent } from '$types/matrix/room'; diff --git a/src/app/hooks/useRoomTypingMembers.ts b/src/app/hooks/useRoomTypingMembers.ts index 5d06baded..424bb7e20 100644 --- a/src/app/hooks/useRoomTypingMembers.ts +++ b/src/app/hooks/useRoomTypingMembers.ts @@ -2,8 +2,8 @@ import { useAtomValue } from 'jotai'; import { selectAtom } from 'jotai/utils'; import { useCallback } from 'react'; import { - IRoomIdToTypingMembers, - TypingReceipt, + type IRoomIdToTypingMembers, + type TypingReceipt, roomIdToTypingMembersAtom, } from '$state/typingMembers'; diff --git a/src/app/hooks/useRoomWidgets.ts b/src/app/hooks/useRoomWidgets.ts index 65381ddd3..e2cbde94f 100644 --- a/src/app/hooks/useRoomWidgets.ts +++ b/src/app/hooks/useRoomWidgets.ts @@ -1,15 +1,15 @@ -import { Room, MatrixEvent, MatrixClient } from '$types/matrix-sdk'; +import { type Room, type MatrixEvent, type MatrixClient } from '$types/matrix-sdk'; import { useCallback, useMemo } from 'react'; -import { IWidget } from 'matrix-widget-api'; +import { type IWidget } from 'matrix-widget-api'; import { StateEvent } from '$types/matrix/room'; import { getStateEvents } from '$utils/room'; import { useStateEventCallback } from './useStateEventCallback'; import { useForceUpdate } from './useForceUpdate'; -export interface RoomWidget extends IWidget { +export type RoomWidget = { eventId?: string; sender?: string; -} +} & IWidget; export const resolveWidgetUrl = ( url: string, diff --git a/src/app/hooks/useRoomsNotificationPreferences.ts b/src/app/hooks/useRoomsNotificationPreferences.ts index 65626bc12..e56ae0bbd 100644 --- a/src/app/hooks/useRoomsNotificationPreferences.ts +++ b/src/app/hooks/useRoomsNotificationPreferences.ts @@ -1,6 +1,6 @@ import { createContext, useCallback, useContext, useMemo } from 'react'; -import { ConditionKind, IPushRules, MatrixClient, PushRuleKind } from '$types/matrix-sdk'; -import { Icons, IconSrc } from 'folds'; +import { ConditionKind, type IPushRules, type MatrixClient, PushRuleKind } from '$types/matrix-sdk'; +import { Icons, type IconSrc } from 'folds'; import { AccountDataEvent } from '$types/matrix/accountData'; import { isRoomId } from '$utils/matrix'; import { useAccountData } from './useAccountData'; diff --git a/src/app/hooks/useSableCosmetics.ts b/src/app/hooks/useSableCosmetics.ts index b9b54c7e3..52a7d44fa 100644 --- a/src/app/hooks/useSableCosmetics.ts +++ b/src/app/hooks/useSableCosmetics.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { usePowerLevels } from './usePowerLevels'; import { useRoomCreators } from './useRoomCreators'; import { useAccessiblePowerTagColors, useGetMemberPowerTag } from './useMemberPowerTag'; diff --git a/src/app/hooks/useSecretStorage.ts b/src/app/hooks/useSecretStorage.ts index 53829cf8d..970b430be 100644 --- a/src/app/hooks/useSecretStorage.ts +++ b/src/app/hooks/useSecretStorage.ts @@ -1,7 +1,7 @@ import { AccountDataEvent, - SecretStorageDefaultKeyContent, - SecretStorageKeyContent, + type SecretStorageDefaultKeyContent, + type SecretStorageKeyContent, } from '$types/matrix/accountData'; import { useAccountData } from './useAccountData'; diff --git a/src/app/hooks/useSessionProfiles.ts b/src/app/hooks/useSessionProfiles.ts index 2cb03c358..5effad9ea 100644 --- a/src/app/hooks/useSessionProfiles.ts +++ b/src/app/hooks/useSessionProfiles.ts @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from 'react'; -import { Session } from '$state/sessions'; +import { type Session } from '$state/sessions'; export type SessionProfile = { displayName?: string; diff --git a/src/app/hooks/useSettingsSync.ts b/src/app/hooks/useSettingsSync.ts index 9fd452963..86cdd392a 100644 --- a/src/app/hooks/useSettingsSync.ts +++ b/src/app/hooks/useSettingsSync.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef } from 'react'; import { atom, useAtom, useSetAtom } from 'jotai'; -import { MatrixEvent } from '$types/matrix-sdk'; +import { type MatrixEvent } from '$types/matrix-sdk'; import { AccountDataEvent } from '$types/matrix/accountData'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useAccountDataCallback } from '$hooks/useAccountDataCallback'; diff --git a/src/app/hooks/useSidebarItems.ts b/src/app/hooks/useSidebarItems.ts index dba7b2f28..49a0e673c 100644 --- a/src/app/hooks/useSidebarItems.ts +++ b/src/app/hooks/useSidebarItems.ts @@ -1,5 +1,5 @@ -import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type Dispatch, type SetStateAction, useCallback, useEffect, useState } from 'react'; +import { type MatrixClient } from '$types/matrix-sdk'; import { AccountDataEvent } from '$types/matrix/accountData'; import { Membership } from '$types/matrix/room'; import { getAccountData, isSpace } from '$utils/room'; @@ -48,13 +48,13 @@ export const parseSidebar = ( typeof item === 'object' && typeof item.id === 'string' && Array.isArray(item.content) && - !items.find((i) => (typeof i === 'string' ? false : i.id === item.id)) + !items.some((i) => (typeof i === 'string' ? false : i.id === item.id)) ) { const safeContent = item.content.filter(safeToAdd); safeContent.forEach((i) => orphans.delete(i)); items.push({ ...item, - content: Array.from(new Set(safeContent)), + content: [...new Set(safeContent)], }); } }); diff --git a/src/app/hooks/useSpace.ts b/src/app/hooks/useSpace.ts index ea53a0d0a..d9976a5ba 100644 --- a/src/app/hooks/useSpace.ts +++ b/src/app/hooks/useSpace.ts @@ -1,4 +1,4 @@ -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { createContext, useContext } from 'react'; const SpaceContext = createContext(null); diff --git a/src/app/hooks/useSpaceHierarchy.ts b/src/app/hooks/useSpaceHierarchy.ts index b5c088750..bea8a2a40 100644 --- a/src/app/hooks/useSpaceHierarchy.ts +++ b/src/app/hooks/useSpaceHierarchy.ts @@ -1,12 +1,12 @@ import { atom, useAtom, useAtomValue } from 'jotai'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { MatrixError, MatrixEvent, Room, IHierarchyRoom } from '$types/matrix-sdk'; -import { QueryFunction, useInfiniteQuery } from '@tanstack/react-query'; -import { MSpaceChildContent, StateEvent } from '$types/matrix/room'; +import { MatrixError, type MatrixEvent, type Room, type IHierarchyRoom } from '$types/matrix-sdk'; +import { type QueryFunction, useInfiniteQuery } from '@tanstack/react-query'; +import { type MSpaceChildContent, StateEvent } from '$types/matrix/room'; import { roomToParentsAtom } from '$state/room/roomToParents'; import { getAllParents, getStateEvents, isValidChild } from '$utils/room'; import { isRoomId } from '$utils/matrix'; -import { SortFunc, byOrderKey, byTsOldToNew, factoryRoomIdByActivity } from '$utils/sort'; +import { type SortFunc, byOrderKey, byTsOldToNew, factoryRoomIdByActivity } from '$utils/sort'; import { useMatrixClient } from './useMatrixClient'; import { makeLobbyCategoryId } from '../state/closedLobbyCategories'; import { useStateEventCallback } from './useStateEventCallback'; diff --git a/src/app/hooks/useSpecVersions.ts b/src/app/hooks/useSpecVersions.ts index 42403c61c..20d17c98a 100644 --- a/src/app/hooks/useSpecVersions.ts +++ b/src/app/hooks/useSpecVersions.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { SpecVersions } from '../cs-api'; +import { type SpecVersions } from '../cs-api'; const SpecVersionsContext = createContext(null); diff --git a/src/app/hooks/useSpoilerClickHandler.ts b/src/app/hooks/useSpoilerClickHandler.ts index b21011880..b92a788b7 100644 --- a/src/app/hooks/useSpoilerClickHandler.ts +++ b/src/app/hooks/useSpoilerClickHandler.ts @@ -1,4 +1,4 @@ -import { ReactEventHandler, useCallback } from 'react'; +import { type ReactEventHandler, useCallback } from 'react'; export const useSpoilerClickHandler = (): ReactEventHandler => { const handleClick: ReactEventHandler = useCallback((evt) => { diff --git a/src/app/hooks/useStateEvent.ts b/src/app/hooks/useStateEvent.ts index ba99eff51..f50b8f28c 100644 --- a/src/app/hooks/useStateEvent.ts +++ b/src/app/hooks/useStateEvent.ts @@ -1,6 +1,6 @@ -import { Room } from '$types/matrix-sdk'; +import { type Room } from '$types/matrix-sdk'; import { useCallback, useMemo } from 'react'; -import { StateEvent } from '$types/matrix/room'; +import { type StateEvent } from '$types/matrix/room'; import { getStateEvent } from '$utils/room'; import { useStateEventCallback } from './useStateEventCallback'; import { useForceUpdate } from './useForceUpdate'; diff --git a/src/app/hooks/useStateEventCallback.ts b/src/app/hooks/useStateEventCallback.ts index 512378c3e..031af47aa 100644 --- a/src/app/hooks/useStateEventCallback.ts +++ b/src/app/hooks/useStateEventCallback.ts @@ -1,4 +1,9 @@ -import { MatrixClient, MatrixEvent, RoomState, RoomStateEvent } from '$types/matrix-sdk'; +import { + type MatrixClient, + type MatrixEvent, + type RoomState, + RoomStateEvent, +} from '$types/matrix-sdk'; import { useEffect } from 'react'; export type StateEventCallback = ( diff --git a/src/app/hooks/useSyncState.ts b/src/app/hooks/useSyncState.ts index d7763050d..d8ee3c89b 100644 --- a/src/app/hooks/useSyncState.ts +++ b/src/app/hooks/useSyncState.ts @@ -1,4 +1,4 @@ -import { ClientEvent, ClientEventHandlerMap, MatrixClient } from '$types/matrix-sdk'; +import { ClientEvent, type ClientEventHandlerMap, type MatrixClient } from '$types/matrix-sdk'; import { useEffect } from 'react'; export const useSyncState = ( diff --git a/src/app/hooks/useTextAreaCodeEditor.ts b/src/app/hooks/useTextAreaCodeEditor.ts index ff44e934e..c8c673782 100644 --- a/src/app/hooks/useTextAreaCodeEditor.ts +++ b/src/app/hooks/useTextAreaCodeEditor.ts @@ -1,7 +1,7 @@ -import { useMemo, useCallback, KeyboardEventHandler, MutableRefObject } from 'react'; +import { useMemo, useCallback, type KeyboardEventHandler, type MutableRefObject } from 'react'; import { isKeyHotkey } from 'is-hotkey'; import { TextArea, Intent, TextAreaOperations, Cursor } from '$plugins/text-area'; -import { GetTarget } from '$plugins/text-area/type'; +import { type GetTarget } from '$plugins/text-area/type'; import { useTextAreaIntentHandler } from './useTextAreaIntent'; export const useTextAreaCodeEditor = ( diff --git a/src/app/hooks/useTextAreaIntent.ts b/src/app/hooks/useTextAreaIntent.ts index e3e355145..4acf7d68f 100644 --- a/src/app/hooks/useTextAreaIntent.ts +++ b/src/app/hooks/useTextAreaIntent.ts @@ -1,6 +1,6 @@ import { isKeyHotkey } from 'is-hotkey'; -import { KeyboardEventHandler, useCallback } from 'react'; -import { Cursor, Intent, Operations, TextArea } from '$plugins/text-area'; +import { type KeyboardEventHandler, useCallback } from 'react'; +import { Cursor, type Intent, type Operations, type TextArea } from '$plugins/text-area'; export const useTextAreaIntentHandler = ( textArea: TextArea, diff --git a/src/app/hooks/useThrottle.ts b/src/app/hooks/useThrottle.ts index 12b249f29..3d2875fd4 100644 --- a/src/app/hooks/useThrottle.ts +++ b/src/app/hooks/useThrottle.ts @@ -1,9 +1,9 @@ import { useCallback, useRef } from 'react'; -export interface ThrottleOptions { +export type ThrottleOptions = { wait?: number; immediate?: boolean; -} +}; export type ThrottleCallback = (...args: T) => void; diff --git a/src/app/hooks/useTypingStatusUpdater.ts b/src/app/hooks/useTypingStatusUpdater.ts index 9bbda0900..b1ccaee1b 100644 --- a/src/app/hooks/useTypingStatusUpdater.ts +++ b/src/app/hooks/useTypingStatusUpdater.ts @@ -1,4 +1,4 @@ -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { useMemo, useRef } from 'react'; import { TYPING_TIMEOUT_MS } from '$state/typingMembers'; diff --git a/src/app/hooks/useUIAFlows.ts b/src/app/hooks/useUIAFlows.ts index f7e04f2e3..d2f94d956 100644 --- a/src/app/hooks/useUIAFlows.ts +++ b/src/app/hooks/useUIAFlows.ts @@ -1,4 +1,4 @@ -import { AuthType, IAuthData, MatrixError, UIAFlow } from '$types/matrix-sdk'; +import { AuthType, type IAuthData, type MatrixError, type UIAFlow } from '$types/matrix-sdk'; import { useCallback, useMemo } from 'react'; import { getSupportedUIAFlows, diff --git a/src/app/hooks/useUserPresence.ts b/src/app/hooks/useUserPresence.ts index f1b858422..3541b205f 100644 --- a/src/app/hooks/useUserPresence.ts +++ b/src/app/hooks/useUserPresence.ts @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from 'react'; -import { User, UserEvent, UserEventHandlerMap } from '$types/matrix-sdk'; +import { type User, UserEvent, type UserEventHandlerMap } from '$types/matrix-sdk'; import { useMatrixClient } from './useMatrixClient'; export enum Presence { diff --git a/src/app/hooks/useUserProfile.ts b/src/app/hooks/useUserProfile.ts index 3308e6813..2bf7c9a9b 100644 --- a/src/app/hooks/useUserProfile.ts +++ b/src/app/hooks/useUserProfile.ts @@ -1,13 +1,13 @@ import { useEffect, useMemo } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; import { selectAtom } from 'jotai/utils'; -import { EventTimeline, Room } from '$types/matrix-sdk'; +import { EventTimeline, type Room } from '$types/matrix-sdk'; import { StateEvent } from '$types/matrix/room'; import colorMXID from '$utils/colorMXID'; import { profilesCacheAtom } from '$state/userRoomProfile'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; -import { MSC1767Text } from '$types/matrix/common'; +import { type MSC1767Text } from '$types/matrix/common'; import { useMatrixClient } from './useMatrixClient'; const inFlightProfiles = new Map>(); diff --git a/src/app/hooks/useVerificationRequest.ts b/src/app/hooks/useVerificationRequest.ts index 38d08d859..a571a1539 100644 --- a/src/app/hooks/useVerificationRequest.ts +++ b/src/app/hooks/useVerificationRequest.ts @@ -1,14 +1,14 @@ import { useCallback, useEffect, useState } from 'react'; import { CryptoEvent, - CryptoEventHandlerMap, - VerificationPhase, - VerificationRequest, + type CryptoEventHandlerMap, + type VerificationPhase, + type VerificationRequest, VerificationRequestEvent, - VerificationRequestEventHandlerMap, - Verifier, + type VerificationRequestEventHandlerMap, + type Verifier, VerifierEvent, - VerifierEventHandlerMap, + type VerifierEventHandlerMap, } from '$types/matrix-sdk'; import { useMatrixClient } from './useMatrixClient'; diff --git a/src/app/i18n.ts b/src/app/i18n.ts index 9e83805d3..08750eeb7 100644 --- a/src/app/i18n.ts +++ b/src/app/i18n.ts @@ -1,6 +1,6 @@ import i18n from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; -import Backend, { HttpBackendOptions } from 'i18next-http-backend'; +import Backend, { type HttpBackendOptions } from 'i18next-http-backend'; import { initReactI18next } from 'react-i18next'; import { trimTrailingSlash } from './utils/common'; diff --git a/src/app/pages/FeatureCheck.tsx b/src/app/pages/FeatureCheck.tsx index dd621c90a..64eb54981 100644 --- a/src/app/pages/FeatureCheck.tsx +++ b/src/app/pages/FeatureCheck.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useEffect } from 'react'; +import { type ReactNode, useEffect } from 'react'; import { Box, Dialog, Text, config } from 'folds'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { checkIndexedDBSupport } from '$utils/featureCheck'; diff --git a/src/app/pages/MobileFriendly.tsx b/src/app/pages/MobileFriendly.tsx index 2fe174fe5..f49327ff2 100644 --- a/src/app/pages/MobileFriendly.tsx +++ b/src/app/pages/MobileFriendly.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useMatch } from 'react-router-dom'; import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize'; import { DIRECT_PATH, EXPLORE_PATH, HOME_PATH, INBOX_PATH, SPACE_PATH } from './paths'; diff --git a/src/app/pages/Router.tsx b/src/app/pages/Router.tsx index 28f8c7efb..c8e4f373a 100644 --- a/src/app/pages/Router.tsx +++ b/src/app/pages/Router.tsx @@ -8,7 +8,7 @@ import { } from 'react-router-dom'; import * as Sentry from '@sentry/react'; -import { ClientConfig } from '$hooks/useClientConfig'; +import { type ClientConfig } from '$hooks/useClientConfig'; import { ErrorPage } from '$components/DefaultErrorPage'; import { SettingsRoute } from '$features/settings'; import { SettingsShallowRouteRenderer } from '$features/settings/SettingsShallowRouteRenderer'; @@ -24,7 +24,7 @@ import { UserRoomProfileRenderer } from '$components/UserRoomProfileRenderer'; import { CreateRoomModalRenderer } from '$features/create-room'; import { CreateSpaceModalRenderer } from '$features/create-space'; import { BugReportModalRenderer } from '$features/bug-report'; -import { getFallbackSession, MATRIX_SESSIONS_KEY, Sessions } from '$state/sessions'; +import { getFallbackSession, MATRIX_SESSIONS_KEY, type Sessions } from '$state/sessions'; import { getLocalStorageItem } from '$state/utils/atomWithLocalStorage'; import { NotificationJumper } from '$hooks/useNotificationJumper'; import { SearchModalRenderer } from '$features/search'; @@ -98,7 +98,10 @@ const getFirstSession = () => { return getFallbackSession(); }; -export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => { +export const createRouter = ( + clientConfig: ClientConfig, + screenSize: ScreenSize +): ReturnType => { const { hashRouter } = clientConfig; const mobile = screenSize === ScreenSize.Mobile; diff --git a/src/app/pages/ThemeManager.tsx b/src/app/pages/ThemeManager.tsx index d47b73cbf..04c3d695e 100644 --- a/src/app/pages/ThemeManager.tsx +++ b/src/app/pages/ThemeManager.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useEffect } from 'react'; +import { type ReactNode, useEffect } from 'react'; import { configClass, varsClass } from 'folds'; import { DarkTheme, diff --git a/src/app/pages/auth/SSOLogin.tsx b/src/app/pages/auth/SSOLogin.tsx index eabb23602..436bf2e5c 100644 --- a/src/app/pages/auth/SSOLogin.tsx +++ b/src/app/pages/auth/SSOLogin.tsx @@ -1,5 +1,5 @@ import { Avatar, AvatarImage, Box, Button, Text } from 'folds'; -import { IIdentityProvider, SSOAction, createClient } from '$types/matrix-sdk'; +import { type IIdentityProvider, type SSOAction, createClient } from '$types/matrix-sdk'; import { useMemo } from 'react'; import { useAutoDiscoveryInfo } from '$hooks/useAutoDiscoveryInfo'; diff --git a/src/app/pages/auth/ServerPicker.tsx b/src/app/pages/auth/ServerPicker.tsx index e8a4984b2..3542ee2f2 100644 --- a/src/app/pages/auth/ServerPicker.tsx +++ b/src/app/pages/auth/ServerPicker.tsx @@ -1,8 +1,8 @@ import { - ChangeEventHandler, - FocusEventHandler, - KeyboardEventHandler, - MouseEventHandler, + type ChangeEventHandler, + type FocusEventHandler, + type KeyboardEventHandler, + type MouseEventHandler, useEffect, useRef, useState, @@ -16,7 +16,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Text, config, } from 'folds'; diff --git a/src/app/pages/auth/login/Login.tsx b/src/app/pages/auth/login/Login.tsx index fe895332b..2a2da65c9 100644 --- a/src/app/pages/auth/login/Login.tsx +++ b/src/app/pages/auth/login/Login.tsx @@ -7,7 +7,7 @@ import { useAuthServer } from '$hooks/useAuthServer'; import { useParsedLoginFlows } from '$hooks/useParsedLoginFlows'; import { getLoginPath, getRegisterPath, withSearchParam } from '$pages/pathUtils'; import { usePathWithOrigin } from '$hooks/usePathWithOrigin'; -import { LoginPathSearchParams } from '$pages/paths'; +import { type LoginPathSearchParams } from '$pages/paths'; import { useClientConfig } from '$hooks/useClientConfig'; import { SSOLogin } from '$pages/auth/SSOLogin'; import { OrDivider } from '$pages/auth/OrDivider'; diff --git a/src/app/pages/auth/login/PasswordLoginForm.tsx b/src/app/pages/auth/login/PasswordLoginForm.tsx index ba888bda5..f4875177f 100644 --- a/src/app/pages/auth/login/PasswordLoginForm.tsx +++ b/src/app/pages/auth/login/PasswordLoginForm.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, MouseEventHandler, useCallback, useState } from 'react'; +import { type FormEventHandler, type MouseEventHandler, useCallback, useState } from 'react'; import { Box, Button, @@ -12,14 +12,14 @@ import { OverlayBackdrop, OverlayCenter, PopOut, - RectCords, + type RectCords, Spinner, Text, config, } from 'folds'; import FocusTrap from 'focus-trap-react'; import { Link } from 'react-router-dom'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { getMxIdLocalPart, getMxIdServer, isUserId } from '$utils/matrix'; import { EMAIL_REGEX } from '$utils/regex'; import { useAutoDiscoveryInfo } from '$hooks/useAutoDiscoveryInfo'; @@ -32,7 +32,7 @@ import { stopPropagation } from '$utils/keyboard'; import { FieldError } from '$pages/auth/FiledError'; import { deviceDisplayName } from '$utils/user-agent'; import { - CustomLoginResponse, + type CustomLoginResponse, LoginError, factoryGetBaseUrl, login, diff --git a/src/app/pages/auth/login/TokenLogin.tsx b/src/app/pages/auth/login/TokenLogin.tsx index 7acf3e9b8..50ba7892e 100644 --- a/src/app/pages/auth/login/TokenLogin.tsx +++ b/src/app/pages/auth/login/TokenLogin.tsx @@ -11,11 +11,11 @@ import { config, } from 'folds'; import { useCallback, useEffect } from 'react'; -import { MatrixError } from '$types/matrix-sdk'; +import { type MatrixError } from '$types/matrix-sdk'; import { useAutoDiscoveryInfo } from '$hooks/useAutoDiscoveryInfo'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { deviceDisplayName } from '$utils/user-agent'; -import { CustomLoginResponse, LoginError, login, useLoginComplete } from './loginUtil'; +import { type CustomLoginResponse, LoginError, login, useLoginComplete } from './loginUtil'; function LoginTokenError({ message }: { message: string }) { return ( diff --git a/src/app/pages/auth/login/loginUtil.ts b/src/app/pages/auth/login/loginUtil.ts index 81371a614..fc7ace99d 100644 --- a/src/app/pages/auth/login/loginUtil.ts +++ b/src/app/pages/auth/login/loginUtil.ts @@ -1,10 +1,15 @@ import to from 'await-to-js'; -import { createClient, LoginRequest, LoginResponse, MatrixError } from '$types/matrix-sdk'; +import { + createClient, + type LoginRequest, + type LoginResponse, + MatrixError, +} from '$types/matrix-sdk'; import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useSetAtom } from 'jotai'; import * as Sentry from '@sentry/react'; -import { clientAllowedServer, ClientConfig } from '$hooks/useClientConfig'; +import { clientAllowedServer, type ClientConfig } from '$hooks/useClientConfig'; import { deleteAfterLoginRedirectPath, getAfterLoginRedirectPath, diff --git a/src/app/pages/auth/register/PasswordRegisterForm.tsx b/src/app/pages/auth/register/PasswordRegisterForm.tsx index e8e991022..660a1d3f7 100644 --- a/src/app/pages/auth/register/PasswordRegisterForm.tsx +++ b/src/app/pages/auth/register/PasswordRegisterForm.tsx @@ -10,14 +10,14 @@ import { Text, color, } from 'folds'; -import { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; +import { type ChangeEventHandler, useCallback, useMemo, useState } from 'react'; import { - AuthDict, + type AuthDict, AuthType, - IAuthData, - MatrixError, - RegisterRequest, - UIAFlow, + type IAuthData, + type MatrixError, + type RegisterRequest, + type UIAFlow, createClient, } from '$types/matrix-sdk'; import { PasswordInput } from '$components/password-input'; @@ -28,7 +28,7 @@ import { requiredStageInFlows, } from '$utils/matrix-uia'; import { useUIACompleted, useUIAFlow, useUIAParams } from '$hooks/useUIAFlows'; -import { AsyncState, AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; +import { type AsyncState, AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useAutoDiscoveryInfo } from '$hooks/useAutoDiscoveryInfo'; import { AutoDummyStageDialog, @@ -40,10 +40,10 @@ import { import { useRegisterEmail } from '$hooks/useRegisterEmail'; import { ConfirmPasswordMatch } from '$components/ConfirmPasswordMatch'; import { UIAFlowOverlay } from '$components/UIAFlowOverlay'; -import { RequestEmailTokenCallback, RequestEmailTokenResponse } from '$hooks/types'; +import { type RequestEmailTokenCallback, type RequestEmailTokenResponse } from '$hooks/types'; import { FieldError } from '$pages/auth/FiledError'; import { deviceDisplayName } from '$utils/user-agent'; -import { RegisterError, RegisterResult, register, useRegisterComplete } from './registerUtil'; +import { RegisterError, type RegisterResult, register, useRegisterComplete } from './registerUtil'; export const SUPPORTED_REGISTER_STAGES = [ AuthType.RegistrationToken, diff --git a/src/app/pages/auth/register/Register.tsx b/src/app/pages/auth/register/Register.tsx index 11bee338e..5e48cc819 100644 --- a/src/app/pages/auth/register/Register.tsx +++ b/src/app/pages/auth/register/Register.tsx @@ -8,7 +8,7 @@ import { useParsedLoginFlows } from '$hooks/useParsedLoginFlows'; import { SupportedUIAFlowsLoader } from '$components/SupportedUIAFlowsLoader'; import { getLoginPath } from '$pages/pathUtils'; import { usePathWithOrigin } from '$hooks/usePathWithOrigin'; -import { RegisterPathSearchParams } from '$pages/paths'; +import { type RegisterPathSearchParams } from '$pages/paths'; import { SSOLogin } from '$pages/auth/SSOLogin'; import { OrDivider } from '$pages/auth/OrDivider'; import { PasswordRegisterForm, SUPPORTED_REGISTER_STAGES } from './PasswordRegisterForm'; diff --git a/src/app/pages/auth/register/registerUtil.ts b/src/app/pages/auth/register/registerUtil.ts index 513aabfc0..2013312c2 100644 --- a/src/app/pages/auth/register/registerUtil.ts +++ b/src/app/pages/auth/register/registerUtil.ts @@ -1,15 +1,15 @@ import to from 'await-to-js'; import { - IAuthData, - MatrixClient, + type IAuthData, + type MatrixClient, MatrixError, - RegisterRequest, - RegisterResponse, + type RegisterRequest, + type RegisterResponse, } from '$types/matrix-sdk'; import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useSetAtom } from 'jotai'; -import { LoginPathSearchParams } from '$pages/paths'; +import { type LoginPathSearchParams } from '$pages/paths'; import { deleteAfterLoginRedirectPath, getAfterLoginRedirectPath, diff --git a/src/app/pages/auth/reset-password/PasswordResetForm.tsx b/src/app/pages/auth/reset-password/PasswordResetForm.tsx index af4c57526..8ed1d654a 100644 --- a/src/app/pages/auth/reset-password/PasswordResetForm.tsx +++ b/src/app/pages/auth/reset-password/PasswordResetForm.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useEffect, useMemo, useState } from 'react'; +import { type FormEventHandler, useCallback, useEffect, useMemo, useState } from 'react'; import { Box, Button, @@ -14,7 +14,7 @@ import { } from 'folds'; import { useNavigate } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; -import { AuthDict, AuthType, MatrixError, createClient } from '$types/matrix-sdk'; +import { type AuthDict, AuthType, type MatrixError, createClient } from '$types/matrix-sdk'; import { useAutoDiscoveryInfo } from '$hooks/useAutoDiscoveryInfo'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { useAuthServer } from '$hooks/useAuthServer'; @@ -24,10 +24,10 @@ import { ConfirmPasswordMatch } from '$components/ConfirmPasswordMatch'; import { UIAFlowOverlay } from '$components/UIAFlowOverlay'; import { EmailStageDialog } from '$components/uia-stages'; import { getLoginPath, withSearchParam } from '$pages/pathUtils'; -import { LoginPathSearchParams } from '$pages/paths'; +import { type LoginPathSearchParams } from '$pages/paths'; import { getUIAError, getUIAErrorCode } from '$utils/matrix-uia'; import { FieldError } from '$pages/auth/FiledError'; -import { ResetPasswordResult, resetPassword } from './resetPasswordUtil'; +import { type ResetPasswordResult, resetPassword } from './resetPasswordUtil'; type FormData = { email: string; diff --git a/src/app/pages/auth/reset-password/ResetPassword.tsx b/src/app/pages/auth/reset-password/ResetPassword.tsx index 718101fab..439e35af4 100644 --- a/src/app/pages/auth/reset-password/ResetPassword.tsx +++ b/src/app/pages/auth/reset-password/ResetPassword.tsx @@ -3,7 +3,7 @@ import { useMemo } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; import { getLoginPath } from '$pages/pathUtils'; import { useAuthServer } from '$hooks/useAuthServer'; -import { ResetPasswordPathSearchParams } from '$pages/paths'; +import { type ResetPasswordPathSearchParams } from '$pages/paths'; import { PasswordResetForm } from './PasswordResetForm'; const useResetPasswordSearchParams = ( diff --git a/src/app/pages/auth/reset-password/resetPasswordUtil.ts b/src/app/pages/auth/reset-password/resetPasswordUtil.ts index 65f0c7e7c..98e0891b7 100644 --- a/src/app/pages/auth/reset-password/resetPasswordUtil.ts +++ b/src/app/pages/auth/reset-password/resetPasswordUtil.ts @@ -1,5 +1,10 @@ import to from 'await-to-js'; -import { AuthDict, IAuthData, MatrixClient, MatrixError } from '$types/matrix-sdk'; +import { + type AuthDict, + type IAuthData, + type MatrixClient, + type MatrixError, +} from '$types/matrix-sdk'; export type ResetPasswordResponse = Record; export type ResetPasswordResult = [IAuthData, undefined] | [undefined, ResetPasswordResponse]; diff --git a/src/app/pages/client/AutoDiscovery.tsx b/src/app/pages/client/AutoDiscovery.tsx index c39f16cd3..797d55248 100644 --- a/src/app/pages/client/AutoDiscovery.tsx +++ b/src/app/pages/client/AutoDiscovery.tsx @@ -1,7 +1,7 @@ -import { ReactNode, useCallback, useMemo } from 'react'; +import { type ReactNode, useCallback, useMemo } from 'react'; import { AutoDiscoveryInfoProvider } from '../../hooks/useAutoDiscoveryInfo'; import { AsyncStatus, useAsyncCallbackValue } from '../../hooks/useAsyncCallback'; -import { autoDiscovery, AutoDiscoveryInfo } from '../../cs-api'; +import { autoDiscovery, type AutoDiscoveryInfo } from '../../cs-api'; import { getMxIdServer } from '../../utils/matrix'; type AutoDiscoveryProps = { diff --git a/src/app/pages/client/BackgroundNotifications.tsx b/src/app/pages/client/BackgroundNotifications.tsx index 395718223..8b5c60688 100644 --- a/src/app/pages/client/BackgroundNotifications.tsx +++ b/src/app/pages/client/BackgroundNotifications.tsx @@ -2,10 +2,10 @@ import { useEffect, useMemo, useRef } from 'react'; import { ClientEvent, createClient, - MatrixClient, - MatrixEvent, + type MatrixClient, + type MatrixEvent, MatrixEventEvent, - Room, + type Room, RoomEvent, SyncState, PushProcessor, @@ -14,7 +14,7 @@ import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { sessionsAtom, activeSessionIdAtom, - Session, + type Session, pendingNotificationAtom, backgroundUnreadCountsAtom, inAppBannerAtom, @@ -144,7 +144,7 @@ export function BackgroundNotifications() { const inactiveSessionsRef = useRef(inactiveSessions); inactiveSessionsRef.current = inactiveSessions; - interface NotifyOptions { + type NotifyOptions = { /** Title shown in the notification banner. */ title: string; /** Body text. */ @@ -162,7 +162,7 @@ export function BackgroundNotifications() { /** Optional callback invoked when the user clicks the notification (window.Notification * fallback path only; the SW path routes via its own notificationclick handler). */ onClick?: () => void; - } + }; useEffect(() => { if (!shouldRunBackgroundNotifications) { diff --git a/src/app/pages/client/ClientBindAtoms.ts b/src/app/pages/client/ClientBindAtoms.ts index aac351062..5ddfb67dc 100644 --- a/src/app/pages/client/ClientBindAtoms.ts +++ b/src/app/pages/client/ClientBindAtoms.ts @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useBindAtoms } from '$state/hooks/useBindAtoms'; diff --git a/src/app/pages/client/ClientInitStorageAtom.tsx b/src/app/pages/client/ClientInitStorageAtom.tsx index 90f6d914f..1eab6ceee 100644 --- a/src/app/pages/client/ClientInitStorageAtom.tsx +++ b/src/app/pages/client/ClientInitStorageAtom.tsx @@ -1,4 +1,4 @@ -import { ReactNode, useMemo } from 'react'; +import { type ReactNode, useMemo } from 'react'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { makeClosedNavCategoriesAtom } from '$state/closedNavCategories'; import { ClosedNavCategoriesProvider } from '$state/hooks/closedNavCategories'; diff --git a/src/app/pages/client/ClientLayout.tsx b/src/app/pages/client/ClientLayout.tsx index b22abcb3b..3b537a163 100644 --- a/src/app/pages/client/ClientLayout.tsx +++ b/src/app/pages/client/ClientLayout.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box } from 'folds'; import { matchPath, useLocation } from 'react-router-dom'; import { useScreenSizeContext } from '$hooks/useScreenSize'; diff --git a/src/app/pages/client/ClientNonUIFeatures.tsx b/src/app/pages/client/ClientNonUIFeatures.tsx index 26ac2f431..fc0eba86c 100644 --- a/src/app/pages/client/ClientNonUIFeatures.tsx +++ b/src/app/pages/client/ClientNonUIFeatures.tsx @@ -1,13 +1,13 @@ import { useAtomValue, useSetAtom } from 'jotai'; import * as Sentry from '@sentry/react'; -import { ReactNode, useCallback, useEffect, useRef } from 'react'; +import { type ReactNode, useCallback, useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { MatrixEvent, MatrixEventEvent, PushProcessor, RoomEvent, - RoomEventHandlerMap, + type RoomEventHandlerMap, SetPresence, } from '$types/matrix-sdk'; import parse from 'html-react-parser'; diff --git a/src/app/pages/client/ClientRoomsNotificationPreferences.tsx b/src/app/pages/client/ClientRoomsNotificationPreferences.tsx index c6ef04a4c..056072097 100644 --- a/src/app/pages/client/ClientRoomsNotificationPreferences.tsx +++ b/src/app/pages/client/ClientRoomsNotificationPreferences.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { RoomsNotificationPreferencesProvider, useRoomsNotificationPreferences, diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx index 1a653e950..3cd91d205 100644 --- a/src/app/pages/client/ClientRoot.tsx +++ b/src/app/pages/client/ClientRoot.tsx @@ -9,13 +9,20 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Spinner, Text, } from 'folds'; -import { HttpApiEvent, HttpApiEventHandlerMap, MatrixClient } from '$types/matrix-sdk'; +import { HttpApiEvent, type HttpApiEventHandlerMap, type MatrixClient } from '$types/matrix-sdk'; import FocusTrap from 'focus-trap-react'; -import { useRef, MouseEventHandler, ReactNode, useCallback, useEffect, useState } from 'react'; +import { + useRef, + type MouseEventHandler, + type ReactNode, + useCallback, + useEffect, + useState, +} from 'react'; import * as Sentry from '@sentry/react'; import { useNavigate } from 'react-router-dom'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; @@ -323,8 +330,7 @@ export function ClientRoot({ children }: ClientRootProps) { 'SHA-256', new TextEncoder().encode(matrixUserId) ); - const hashHex = Array.from(new Uint8Array(hashBuffer)) - .map((b) => b.toString(16).padStart(2, '0')) + const hashHex = Array.from(new Uint8Array(hashBuffer), (b) => b.toString(16).padStart(2, '0')) .join('') .slice(0, 16); // Include the homeserver domain as a custom attribute — it is not PII (it is the diff --git a/src/app/pages/client/SidebarNav.tsx b/src/app/pages/client/SidebarNav.tsx index 507fa37c4..b563216c7 100644 --- a/src/app/pages/client/SidebarNav.tsx +++ b/src/app/pages/client/SidebarNav.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useRef, useState } from 'react'; +import { type MouseEventHandler, useRef, useState } from 'react'; import { Box, Checkbox, config, Line, Menu, MenuItem, PopOut, Scroll, Text, toRem } from 'folds'; import FocusTrap from 'focus-trap-react'; import { stopPropagation } from '$utils/keyboard'; diff --git a/src/app/pages/client/SpecVersions.tsx b/src/app/pages/client/SpecVersions.tsx index 75cb3cafc..f5c16a855 100644 --- a/src/app/pages/client/SpecVersions.tsx +++ b/src/app/pages/client/SpecVersions.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { Box, Dialog, config, Text, Button, Spinner } from 'folds'; import { SpecVersionsLoader } from '$components/SpecVersionsLoader'; import { SpecVersionsProvider } from '$hooks/useSpecVersions'; diff --git a/src/app/pages/client/SyncStatus.tsx b/src/app/pages/client/SyncStatus.tsx index f55fe5e59..4c3398bda 100644 --- a/src/app/pages/client/SyncStatus.tsx +++ b/src/app/pages/client/SyncStatus.tsx @@ -1,4 +1,4 @@ -import { MatrixClient, SyncState } from '$types/matrix-sdk'; +import { type MatrixClient, SyncState } from '$types/matrix-sdk'; import { useCallback, useState } from 'react'; import { Box, config, Line, Text } from 'folds'; import * as Sentry from '@sentry/react'; diff --git a/src/app/pages/client/direct/Direct.tsx b/src/app/pages/client/direct/Direct.tsx index 43ccb6be1..83da5056d 100644 --- a/src/app/pages/client/direct/Direct.tsx +++ b/src/app/pages/client/direct/Direct.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, forwardRef, useEffect, useMemo, useRef, useState } from 'react'; +import { type MouseEventHandler, forwardRef, useEffect, useMemo, useRef, useState } from 'react'; import { useAtom, useAtomValue } from 'jotai'; import { Avatar, @@ -10,7 +10,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Text, config, toRem, @@ -212,7 +212,7 @@ export function Direct() { }, [mx, directs]); const sortedDirects = useMemo(() => { - const items = Array.from(directs).sort(factoryRoomIdByActivity(mx)); + const items = directs.toSorted(factoryRoomIdByActivity(mx)); const hasUnread = (roomId: string) => { const unread = roomToUnread.get(roomId); return !!unread && (unread.total > 0 || unread.highlight > 0); diff --git a/src/app/pages/client/direct/RoomProvider.tsx b/src/app/pages/client/direct/RoomProvider.tsx index 1780fa728..5a00678aa 100644 --- a/src/app/pages/client/direct/RoomProvider.tsx +++ b/src/app/pages/client/direct/RoomProvider.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useParams } from 'react-router-dom'; import { useSelectedRoom } from '$hooks/router/useSelectedRoom'; import { IsDirectRoomProvider, RoomProvider } from '$hooks/useRoom'; diff --git a/src/app/pages/client/explore/Explore.tsx b/src/app/pages/client/explore/Explore.tsx index 9ac55e2d2..dcdb670bd 100644 --- a/src/app/pages/client/explore/Explore.tsx +++ b/src/app/pages/client/explore/Explore.tsx @@ -1,4 +1,4 @@ -import { FormEventHandler, useCallback, useRef, useState } from 'react'; +import { type FormEventHandler, useCallback, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; import { diff --git a/src/app/pages/client/explore/Server.tsx b/src/app/pages/client/explore/Server.tsx index d58aa2a10..1b0c44400 100644 --- a/src/app/pages/client/explore/Server.tsx +++ b/src/app/pages/client/explore/Server.tsx @@ -1,7 +1,7 @@ import { - FormEventHandler, - MouseEventHandler, - RefObject, + type FormEventHandler, + type MouseEventHandler, + type RefObject, useCallback, useEffect, useMemo, @@ -20,7 +20,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Scroll, Spinner, Text, @@ -31,12 +31,12 @@ import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; import { useAtomValue } from 'jotai'; import { useQuery } from '@tanstack/react-query'; -import { MatrixClient, Method, RoomType } from '$types/matrix-sdk'; +import { type MatrixClient, Method, RoomType } from '$types/matrix-sdk'; import { Page, PageContent, PageContentCenter, PageHeader } from '$components/page'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { RoomTopicViewer } from '$components/room-topic-viewer'; import { RoomCard, RoomCardBase, RoomCardGrid } from '$components/room-card'; -import { ExploreServerPathSearchParams } from '$pages/paths'; +import { type ExploreServerPathSearchParams } from '$pages/paths'; import { getExploreServerPath, withSearchParam } from '$pages/pathUtils'; import { allRoomsAtom } from '$state/room-list/roomList'; import { useRoomNavigate } from '$hooks/useRoomNavigate'; @@ -574,7 +574,7 @@ export function PublicRooms() { {isLoading && ( - {[...new Array(currentLimit).keys()].map((item) => ( + {Array.from(new Array(currentLimit).keys(), (item) => ( ))} diff --git a/src/app/pages/client/home/Home.tsx b/src/app/pages/client/home/Home.tsx index c25d99e30..031bb28f1 100644 --- a/src/app/pages/client/home/Home.tsx +++ b/src/app/pages/client/home/Home.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, forwardRef, useMemo, useRef, useState } from 'react'; +import { type MouseEventHandler, forwardRef, useMemo, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Avatar, @@ -10,7 +10,7 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Text, config, toRem, @@ -60,7 +60,7 @@ import { } from '$hooks/useRoomsNotificationPreferences'; import { UseStateProvider } from '$components/UseStateProvider'; import { JoinAddressPrompt } from '$components/join-address-prompt'; -import { RoomSearchParams } from '$pages/paths'; +import { type RoomSearchParams } from '$pages/paths'; import { useHomeRooms } from './useHomeRooms'; type HomeMenuProps = { @@ -207,7 +207,7 @@ export function Home() { const [closedCategories, setClosedCategories] = useAtom(useClosedNavCategoriesAtom()); const sortedRooms = useMemo(() => { - const items = Array.from(rooms).sort( + const items = rooms.toSorted( closedCategories.has(DEFAULT_CATEGORY_ID) ? factoryRoomIdByActivity(mx) : factoryRoomIdByAtoZ(mx) diff --git a/src/app/pages/client/home/RoomProvider.tsx b/src/app/pages/client/home/RoomProvider.tsx index b6f7ba7e3..6d04c8c60 100644 --- a/src/app/pages/client/home/RoomProvider.tsx +++ b/src/app/pages/client/home/RoomProvider.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useParams } from 'react-router-dom'; import { useSelectedRoom } from '$hooks/router/useSelectedRoom'; import { IsDirectRoomProvider, RoomProvider } from '$hooks/useRoom'; diff --git a/src/app/pages/client/inbox/Invites.tsx b/src/app/pages/client/inbox/Invites.tsx index 8eeff6ad4..a895a99ed 100644 --- a/src/app/pages/client/inbox/Invites.tsx +++ b/src/app/pages/client/inbox/Invites.tsx @@ -19,7 +19,12 @@ import { } from 'folds'; import { useAtomValue } from 'jotai'; import { nicknamesAtom } from '$state/nicknames'; -import { RoomTopicEventContent, MatrixClient, MatrixError, Room } from '$types/matrix-sdk'; +import { + type RoomTopicEventContent, + type MatrixClient, + type MatrixError, + type Room, +} from '$types/matrix-sdk'; import FocusTrap from 'focus-trap-react'; import { Page, @@ -573,7 +578,7 @@ function SpamInvites({ ); const ignoredUsers = useIgnoredUsers(); - const unignoredUsers = Array.from(new Set(invites.map((invite) => invite.senderId))).filter( + const unignoredUsers = [...new Set(invites.map((invite) => invite.senderId))].filter( (user) => !ignoredUsers.includes(user) ); const [blockAllStatus, blockAll] = useAsyncCallback( diff --git a/src/app/pages/client/inbox/Notifications.tsx b/src/app/pages/client/inbox/Notifications.tsx index 61ae4096c..d7b76f459 100644 --- a/src/app/pages/client/inbox/Notifications.tsx +++ b/src/app/pages/client/inbox/Notifications.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/destructuring-assignment */ -import { MouseEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { type MouseEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Avatar, Box, @@ -15,23 +15,23 @@ import { } from 'folds'; import { useSearchParams } from 'react-router-dom'; import { - INotification, - INotificationsResponse, - IRoomEvent, + type INotification, + type INotificationsResponse, + type IRoomEvent, JoinRule, Method, RelationType, - Room, + type Room, } from '$types/matrix-sdk'; import { useVirtualizer } from '@tanstack/react-virtual'; -import { HTMLReactParserOptions } from 'html-react-parser'; -import { Opts as LinkifyOpts } from 'linkifyjs'; +import { type HTMLReactParserOptions } from 'html-react-parser'; +import { type Opts as LinkifyOpts } from 'linkifyjs'; import { useAtomValue } from 'jotai'; import { nicknamesAtom } from '$state/nicknames'; import { Page, PageContent, PageContentCenter, PageHeader } from '$components/page'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { getMxIdLocalPart, mxcUrlToHttp } from '$utils/matrix'; -import { InboxNotificationsPathSearchParams } from '$pages/paths'; +import { type InboxNotificationsPathSearchParams } from '$pages/paths'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { SequenceCard } from '$components/sequence-card'; import { RoomAvatar, RoomIcon } from '$components/room-avatar'; @@ -68,7 +68,7 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Image } from '$components/media'; import { ImageViewer } from '$components/image-viewer'; -import { GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; +import { type GetContentCallback, MessageEvent, StateEvent } from '$types/matrix/room'; import { useMatrixEventRenderer } from '$hooks/useMatrixEventRenderer'; import * as customHtmlCss from '$styles/CustomHtml.css'; import { useRoomNavigate } from '$hooks/useRoomNavigate'; @@ -174,7 +174,7 @@ const useNotificationTimeline = ( if (currentTimeline.nextToken === from) { return { nextToken: data.next_token, - groups: from ? currentTimeline.groups.concat(groups) : groups, + groups: from ? [...currentTimeline.groups, ...groups] : groups, }; } return currentTimeline; @@ -780,7 +780,7 @@ export function Notifications() { {timelineState.status === AsyncStatus.Loading && ( - {[...new Array(8).keys()].map((key) => ( + {Array.from(new Array(8).keys(), (key) => ( ) { if (typeof containerItem === 'string') { const folder: ISidebarFolder = { id: randomStr(), - content: [containerItem].concat(child), + content: [...[containerItem], ...child], }; newItems.push(folder); return; } newItems.push({ ...containerItem.folder, - content: containerItem.folder.content.concat(child), + content: [...containerItem.folder.content, ...child], }); return; } diff --git a/src/app/pages/client/space/RoomProvider.tsx b/src/app/pages/client/space/RoomProvider.tsx index 411efc163..26df3f26e 100644 --- a/src/app/pages/client/space/RoomProvider.tsx +++ b/src/app/pages/client/space/RoomProvider.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useParams } from 'react-router-dom'; import { useAtom, useAtomValue } from 'jotai'; import { useSelectedRoom } from '$hooks/router/useSelectedRoom'; diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx index 4eb7b117c..82ac8d739 100644 --- a/src/app/pages/client/space/Space.tsx +++ b/src/app/pages/client/space/Space.tsx @@ -1,6 +1,6 @@ import { - MouseEventHandler, - ReactElement, + type MouseEventHandler, + type ReactElement, forwardRef, useCallback, useEffect, @@ -20,17 +20,17 @@ import { Menu, MenuItem, PopOut, - RectCords, + type RectCords, Spinner, Text, color, config, toRem, } from 'folds'; -import { useVirtualizer, VirtualItem } from '@tanstack/react-virtual'; +import { useVirtualizer, type VirtualItem } from '@tanstack/react-virtual'; import FocusTrap from 'focus-trap-react'; import { useNavigate } from 'react-router-dom'; -import { JoinRule, Room, RoomJoinRulesEventContent } from '$types/matrix-sdk'; +import { JoinRule, type Room, type RoomJoinRulesEventContent } from '$types/matrix-sdk'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { mDirectAtom } from '$state/mDirectList'; import { NavCategory, NavCategoryHeader, NavItem, NavItemContent, NavLink } from '$components/nav'; @@ -48,7 +48,7 @@ import { roomToUnreadAtom } from '$state/room/roomToUnread'; import { useCategoryHandler } from '$hooks/useCategoryHandler'; import { useNavToActivePathMapper } from '$hooks/useNavToActivePathMapper'; import { useRoomName } from '$hooks/useRoomMeta'; -import { HierarchyItem, useSpaceJoinedHierarchy } from '$hooks/useSpaceHierarchy'; +import { type HierarchyItem, useSpaceJoinedHierarchy } from '$hooks/useSpaceHierarchy'; import { allRoomsAtom } from '$state/room-list/roomList'; import { PageNav, PageNavContent, PageNavHeader } from '$components/page'; import { usePowerLevels } from '$hooks/usePowerLevels'; @@ -466,7 +466,7 @@ export function Space() { // As a subspace can be in multiple spaces, // only return true if all parent spaces are closed. - const allClosed = !Array.from(parentParentIds).some( + const allClosed = ![...parentParentIds].some( (id) => !getInClosedCategories(spaceId, id, parentId, visited) ); visited.delete(categoryId); @@ -498,7 +498,7 @@ export function Space() { return false; } - return Array.from(childIds).some((id) => getContainsShowRoom(id, visited)); + return [...childIds].some((id) => getContainsShowRoom(id, visited)); }, [roomToUnread, selectedRoomId, roomToChildren] ); @@ -522,9 +522,7 @@ export function Space() { return false; } - const allCollapsed = !Array.from(parentIds).some( - (id) => !getInClosedCategories(spaceId, id, roomId) - ); + const allCollapsed = ![...parentIds].some((id) => !getInClosedCategories(spaceId, id, roomId)); ancestorsCollapsedCache.current.set(categoryId, allCollapsed); return allCollapsed; }; @@ -585,7 +583,10 @@ export function Space() { connectorStack = [{ aX: Math.round(renderDepth * PADDING_LEFT_DEPTH_OFFSET), aY: 0 }]; } - const lastConnector = connectorStack[connectorStack.length - 1]; + const lastConnector = connectorStack.at(-1) ?? { + aX: Math.round(renderDepth * PADDING_LEFT_DEPTH_OFFSET), + aY: 0, + }; // aX: numeric x where the vertical connector starts // aY: end of parent (already numeric) diff --git a/src/app/pages/client/space/SpaceProvider.tsx b/src/app/pages/client/space/SpaceProvider.tsx index cc2eb5bde..16ec9c199 100644 --- a/src/app/pages/client/space/SpaceProvider.tsx +++ b/src/app/pages/client/space/SpaceProvider.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { useParams } from 'react-router-dom'; import { useMatrixClient } from '$hooks/useMatrixClient'; import { useSpaces } from '$state/hooks/roomList'; diff --git a/src/app/pages/pathSearchParam.ts b/src/app/pages/pathSearchParam.ts index 813abc3d9..9bfedc3ea 100644 --- a/src/app/pages/pathSearchParam.ts +++ b/src/app/pages/pathSearchParam.ts @@ -1,4 +1,4 @@ -import { RoomSearchParams, DirectCreateSearchParams } from './paths'; +import { type RoomSearchParams, type DirectCreateSearchParams } from './paths'; type SearchParamsGetter = (searchParams: URLSearchParams) => T; diff --git a/src/app/pages/pathUtils.ts b/src/app/pages/pathUtils.ts index 120a1a70c..f07d6526c 100644 --- a/src/app/pages/pathUtils.ts +++ b/src/app/pages/pathUtils.ts @@ -1,6 +1,6 @@ -import { generatePath, Path } from 'react-router-dom'; +import { generatePath, type Path } from 'react-router-dom'; import { trimLeadingSlash, trimTrailingSlash } from '$utils/common'; -import { HashRouterConfig } from '$hooks/useClientConfig'; +import { type HashRouterConfig } from '$hooks/useClientConfig'; import { DIRECT_CREATE_PATH, DIRECT_PATH, @@ -26,7 +26,7 @@ import { SPACE_ROOM_PATH, SPACE_SEARCH_PATH, CREATE_PATH, - SettingsPathSearchParams, + type SettingsPathSearchParams, } from './paths'; export const joinPathComponent = (path: Path): string => path.pathname + path.search + path.hash; diff --git a/src/app/plugins/arborium/runtime.test.ts b/src/app/plugins/arborium/runtime.test.ts index 3c6d89ef9..df4685666 100644 --- a/src/app/plugins/arborium/runtime.test.ts +++ b/src/app/plugins/arborium/runtime.test.ts @@ -1,8 +1,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; +import type * as Arborium from '@arborium/arborium'; import type { HighlightResult } from '.'; -type ArboriumModule = typeof import('@arborium/arborium'); +type ArboriumModule = typeof Arborium; afterEach(() => { vi.resetModules(); diff --git a/src/app/plugins/arborium/runtime.ts b/src/app/plugins/arborium/runtime.ts index 30d22031d..2ad79c9cc 100644 --- a/src/app/plugins/arborium/runtime.ts +++ b/src/app/plugins/arborium/runtime.ts @@ -1,4 +1,6 @@ -type ArboriumModule = typeof import('@arborium/arborium'); +import type * as Arborium from '@arborium/arborium'; + +type ArboriumModule = typeof Arborium; type ArboriumModuleWithAvailability = ArboriumModule & { availableLanguages?: string[]; isLanguageAvailable?: (language: string) => boolean | Promise; @@ -9,19 +11,19 @@ const LANGUAGE_COMPATIBILITY: Record = { markup: 'html', }; -export interface HighlightCodeInput { +export type HighlightCodeInput = { code: string; language?: string | null; allowDetect?: boolean; -} +}; export type HighlightResult = | { mode: 'plain'; html: string; language?: string } | { mode: 'highlighted'; html: string; language: string }; -export interface HighlightCodeDeps { +export type HighlightCodeDeps = { loadModule?: () => Promise; -} +}; let arboriumModulePromise: Promise | null = null; diff --git a/src/app/plugins/bad-words.ts b/src/app/plugins/bad-words.ts index 74fcaddb7..65c13ec02 100644 --- a/src/app/plugins/bad-words.ts +++ b/src/app/plugins/bad-words.ts @@ -3,9 +3,10 @@ import { sanitizeForRegex } from '$utils/regex'; const additionalBadWords: string[] = ['torture', 't0rture']; -const fullBadWordList = additionalBadWords.concat( - badWords.array.filter((word) => !additionalBadWords.includes(word)) -); +const fullBadWordList = [ + ...additionalBadWords, + ...badWords.array.filter((word) => !additionalBadWords.includes(word)), +]; export const BAD_WORDS_REGEX = new RegExp( `(\\b|_)(${fullBadWordList.map((word) => sanitizeForRegex(word)).join('|')})(\\b|_)`, diff --git a/src/app/plugins/call/CallControl.ts b/src/app/plugins/call/CallControl.ts index c30ffc1c7..84c571cc1 100644 --- a/src/app/plugins/call/CallControl.ts +++ b/src/app/plugins/call/CallControl.ts @@ -1,7 +1,11 @@ -import { ClientWidgetApi } from 'matrix-widget-api'; +import { type ClientWidgetApi } from 'matrix-widget-api'; import EventEmitter from 'eventemitter3'; import { CallControlState } from './CallControlState'; -import { ElementMediaStateDetail, ElementMediaStatePayload, ElementWidgetActions } from './types'; +import { + type ElementMediaStateDetail, + type ElementMediaStatePayload, + ElementWidgetActions, +} from './types'; export enum CallControlEvent { StateUpdate = 'state_update', diff --git a/src/app/plugins/call/CallEmbed.ts b/src/app/plugins/call/CallEmbed.ts index 1a12211d4..124abc8d2 100644 --- a/src/app/plugins/call/CallEmbed.ts +++ b/src/app/plugins/call/CallEmbed.ts @@ -1,28 +1,28 @@ import { ClientEvent, KnownMembership, - MatrixClient, - MatrixEvent, + type MatrixClient, + type MatrixEvent, MatrixEventEvent, - Room, + type Room, RoomStateEvent, } from 'matrix-js-sdk'; import { ClientWidgetApi, type IWidgetApiRequest, - IRoomEvent, - IWidget, + type IRoomEvent, + type IWidget, Widget, WidgetApiFromWidgetAction, WidgetApiToWidgetAction, - WidgetDriver, + type WidgetDriver, } from 'matrix-widget-api'; import { CallWidgetDriver } from './CallWidgetDriver'; import { trimTrailingSlash } from '../../utils/common'; import { ElementCallIntent, - ElementCallThemeKind, - ElementMediaStateDetail, + type ElementCallThemeKind, + type ElementMediaStateDetail, ElementWidgetActions, } from './types'; import { CallControl } from './CallControl'; @@ -234,7 +234,7 @@ export class CallEmbed { this.mx.getRooms().forEach((room) => { // Timelines are most recent last const events = room.getLiveTimeline()?.getEvents() || []; - const roomEvent = events[events.length - 1]; + const roomEvent = events.at(-1); if (!roomEvent) return; // force later code to think the room is fresh this.readUpToMap[room.roomId] = roomEvent.getId()!; }); @@ -360,7 +360,7 @@ export class CallEmbed { // Timelines are most recent last, so reverse the order and limit ourselves to 100 events // to avoid overusing the CPU. const timeline = room.getLiveTimeline(); - const events = [...timeline.getEvents()].reverse().slice(0, 100); + const events = timeline.getEvents().toReversed().slice(0, 100); function isRelevantTimelineEvent(timelineEvent: MatrixEvent): boolean { return timelineEvent.getId() === upToEventId || timelineEvent.getId() === ev.getId(); } diff --git a/src/app/plugins/call/CallWidgetDriver.ts b/src/app/plugins/call/CallWidgetDriver.ts index 6d94891f8..a75818fce 100644 --- a/src/app/plugins/call/CallWidgetDriver.ts +++ b/src/app/plugins/call/CallWidgetDriver.ts @@ -10,8 +10,8 @@ import { type IGetMediaConfigResult, UpdateDelayedEventAction, OpenIDRequestState, - SimpleObservable, - IOpenIDUpdate, + type SimpleObservable, + type IOpenIDUpdate, } from 'matrix-widget-api'; import { EventType, @@ -22,7 +22,7 @@ import { type SendDelayedEventResponse, type StateEvents, type TimelineEvents, - MatrixClient, + type MatrixClient, } from 'matrix-js-sdk'; import { getCallCapabilities } from './utils'; import { downloadMedia, mxcUrlToHttp } from '../../utils/matrix'; @@ -53,7 +53,7 @@ export class CallWidgetDriver extends WidgetDriver { } public async validateCapabilities(requested: Set): Promise> { - const allow = Array.from(requested).filter((cap) => this.allowedCapabilities.has(cap)); + const allow = [...requested].filter((cap) => this.allowedCapabilities.has(cap)); return new Set(allow); } diff --git a/src/app/plugins/call/hooks.ts b/src/app/plugins/call/hooks.ts index f5b3ff933..25cf04f39 100644 --- a/src/app/plugins/call/hooks.ts +++ b/src/app/plugins/call/hooks.ts @@ -1,11 +1,11 @@ import { - ClientWidgetApi, - IWidgetApiAcknowledgeResponseData, - IWidgetApiRequestData, + type ClientWidgetApi, + type IWidgetApiAcknowledgeResponseData, + type IWidgetApiRequestData, } from 'matrix-widget-api'; import { useCallback, useEffect, useState } from 'react'; -import { CallControl, CallControlEvent } from './CallControl'; -import { CallControlState } from './CallControlState'; +import { type CallControl, CallControlEvent } from './CallControl'; +import { type CallControlState } from './CallControlState'; export const useClientWidgetApiEvent = ( api: ClientWidgetApi | undefined, diff --git a/src/app/plugins/custom-emoji/ImagePack.ts b/src/app/plugins/custom-emoji/ImagePack.ts index d7aa9941a..58e5227c1 100644 --- a/src/app/plugins/custom-emoji/ImagePack.ts +++ b/src/app/plugins/custom-emoji/ImagePack.ts @@ -1,9 +1,9 @@ -import { MatrixEvent } from '$types/matrix-sdk'; +import { type MatrixEvent } from '$types/matrix-sdk'; import { PackAddress } from './PackAddress'; -import { PackImageReader } from './PackImageReader'; +import { type PackImageReader } from './PackImageReader'; import { PackImagesReader } from './PackImagesReader'; import { PackMetaReader } from './PackMetaReader'; -import { ImageUsage, PackContent } from './types'; +import { ImageUsage, type PackContent } from './types'; export class ImagePack { public readonly id: string; @@ -53,7 +53,7 @@ export class ImagePack { return this.stickerMemo; } - const images = Array.from(this.images.collection.values()).filter((image) => { + const images = [...this.images.collection.values()].filter((image) => { const usg = image.usage ?? this.meta.usage; return usg.includes(usage); }); diff --git a/src/app/plugins/custom-emoji/PackImageReader.ts b/src/app/plugins/custom-emoji/PackImageReader.ts index 51e5114f2..0b083fcef 100644 --- a/src/app/plugins/custom-emoji/PackImageReader.ts +++ b/src/app/plugins/custom-emoji/PackImageReader.ts @@ -1,5 +1,5 @@ -import { IImageInfo } from '$types/matrix/common'; -import { ImageUsage, PackImage } from './types'; +import { type IImageInfo } from '$types/matrix/common'; +import { ImageUsage, type PackImage } from './types'; export class PackImageReader { public readonly shortcode: string; diff --git a/src/app/plugins/custom-emoji/PackImagesReader.ts b/src/app/plugins/custom-emoji/PackImagesReader.ts index 1531218ae..a75657d10 100644 --- a/src/app/plugins/custom-emoji/PackImagesReader.ts +++ b/src/app/plugins/custom-emoji/PackImagesReader.ts @@ -1,5 +1,5 @@ import { PackImageReader } from './PackImageReader'; -import { PackImages } from './types'; +import { type PackImages } from './types'; export class PackImagesReader { private readonly rawImages: PackImages; diff --git a/src/app/plugins/custom-emoji/PackMetaReader.ts b/src/app/plugins/custom-emoji/PackMetaReader.ts index 537addf46..5153e2deb 100644 --- a/src/app/plugins/custom-emoji/PackMetaReader.ts +++ b/src/app/plugins/custom-emoji/PackMetaReader.ts @@ -1,4 +1,4 @@ -import { PackMeta, ImageUsage } from './types'; +import { type PackMeta, ImageUsage } from './types'; export class PackMetaReader { private readonly meta: PackMeta; diff --git a/src/app/plugins/custom-emoji/imagePackCache.ts b/src/app/plugins/custom-emoji/imagePackCache.ts index f15f1fba0..3773d00f0 100644 --- a/src/app/plugins/custom-emoji/imagePackCache.ts +++ b/src/app/plugins/custom-emoji/imagePackCache.ts @@ -15,7 +15,7 @@ import { PackAddress } from './PackAddress'; import { ImagePack } from './ImagePack'; -import { PackContent, PackImages } from './types'; +import { type PackContent, type PackImages } from './types'; // -------------------------------------------------------------------------- // Types stored in localStorage diff --git a/src/app/plugins/custom-emoji/types.ts b/src/app/plugins/custom-emoji/types.ts index 8794beda5..8d84f4c37 100644 --- a/src/app/plugins/custom-emoji/types.ts +++ b/src/app/plugins/custom-emoji/types.ts @@ -1,4 +1,4 @@ -import { IImageInfo } from '$types/matrix/common'; +import { type IImageInfo } from '$types/matrix/common'; // https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md diff --git a/src/app/plugins/custom-emoji/utils.ts b/src/app/plugins/custom-emoji/utils.ts index 4b35cad19..7e7f3d04b 100644 --- a/src/app/plugins/custom-emoji/utils.ts +++ b/src/app/plugins/custom-emoji/utils.ts @@ -1,11 +1,11 @@ -import { MatrixClient, MatrixEvent, Room } from '$types/matrix-sdk'; +import { type MatrixClient, type MatrixEvent, type Room } from '$types/matrix-sdk'; import { StateEvent } from '$types/matrix/room'; import { getAccountData, getStateEvent, getStateEvents } from '$utils/room'; import { AccountDataEvent } from '$types/matrix/accountData'; -import { ImageUsage } from './types'; +import { type ImageUsage } from './types'; import { ImagePack } from './ImagePack'; -import { PackMetaReader } from './PackMetaReader'; -import { PackAddress } from './PackAddress'; +import { type PackMetaReader } from './PackMetaReader'; +import { type PackAddress } from './PackAddress'; export function packAddressEqual(a1?: PackAddress, a2?: PackAddress): boolean { if (!a1 && !a2) return true; diff --git a/src/app/plugins/emoji.ts b/src/app/plugins/emoji.ts index 2462b7ffe..d75491b38 100644 --- a/src/app/plugins/emoji.ts +++ b/src/app/plugins/emoji.ts @@ -1,4 +1,4 @@ -import { CompactEmoji, fromUnicodeToHexcode } from 'emojibase'; +import { type CompactEmoji, fromUnicodeToHexcode } from 'emojibase'; import emojisData from 'emojibase-data/en/compact.json'; import joypixels from 'emojibase-data/en/shortcodes/joypixels.json'; import emojibase from 'emojibase-data/en/shortcodes/emojibase.json'; diff --git a/src/app/plugins/markdown/block/parser.ts b/src/app/plugins/markdown/block/parser.ts index b56d3e3b6..75b457df5 100644 --- a/src/app/plugins/markdown/block/parser.ts +++ b/src/app/plugins/markdown/block/parser.ts @@ -10,7 +10,7 @@ import { HorizontalRuleRule, } from './rules'; import { runBlockRule } from './runner'; -import { BlockMDParser } from './type'; +import { type BlockMDParser } from './type'; /** * Parses block-level markdown text into HTML using defined block rules. diff --git a/src/app/plugins/markdown/block/rules.ts b/src/app/plugins/markdown/block/rules.ts index a5bfe2635..38d3d3f82 100644 --- a/src/app/plugins/markdown/block/rules.ts +++ b/src/app/plugins/markdown/block/rules.ts @@ -1,4 +1,4 @@ -import { BlockMDRule } from './type'; +import { type BlockMDRule } from './type'; const HEADING_REG_1 = /^(#{1,6}) +(.+)\n?/m; export const HeadingRule: BlockMDRule = { diff --git a/src/app/plugins/markdown/block/runner.ts b/src/app/plugins/markdown/block/runner.ts index 8e80c95ff..a151ab5d0 100644 --- a/src/app/plugins/markdown/block/runner.ts +++ b/src/app/plugins/markdown/block/runner.ts @@ -1,5 +1,5 @@ import { replaceMatch } from '$plugins/markdown/internal'; -import { BlockMDParser, BlockMDRule } from './type'; +import { type BlockMDParser, type BlockMDRule } from './type'; /** * Parses block-level markdown text into HTML using defined block rules. diff --git a/src/app/plugins/markdown/block/type.ts b/src/app/plugins/markdown/block/type.ts index 9462e1d50..8bebe6f2c 100644 --- a/src/app/plugins/markdown/block/type.ts +++ b/src/app/plugins/markdown/block/type.ts @@ -1,4 +1,4 @@ -import { MatchResult, MatchRule } from '$plugins/markdown/internal'; +import { type MatchResult, type MatchRule } from '$plugins/markdown/internal'; /** * Type for a function that parses block-level markdown into HTML. diff --git a/src/app/plugins/markdown/inline/parser.ts b/src/app/plugins/markdown/inline/parser.ts index 37c71a661..f8ded2459 100644 --- a/src/app/plugins/markdown/inline/parser.ts +++ b/src/app/plugins/markdown/inline/parser.ts @@ -10,7 +10,7 @@ import { UnderlineRule, } from './rules'; import { runInlineRule, runInlineRules } from './runner'; -import { InlineMDParser } from './type'; +import { type InlineMDParser } from './type'; const LeveledRules = [ BoldRule, diff --git a/src/app/plugins/markdown/inline/rules.ts b/src/app/plugins/markdown/inline/rules.ts index 77bcbd57a..fae82a458 100644 --- a/src/app/plugins/markdown/inline/rules.ts +++ b/src/app/plugins/markdown/inline/rules.ts @@ -1,4 +1,4 @@ -import { InlineMDRule } from './type'; +import { type InlineMDRule } from './type'; const MIN_ANY = '(.+?)'; const URL_NEG_LB = '(? !(child instanceof DOMText)) + const content = children.some((child) => !(child instanceof DOMText)) ? undefined : children.map((c) => (c instanceof DOMText ? c.data : '')).join(); diff --git a/src/app/plugins/recent-emoji.ts b/src/app/plugins/recent-emoji.ts index ca7b25cc1..0b6d9bed2 100644 --- a/src/app/plugins/recent-emoji.ts +++ b/src/app/plugins/recent-emoji.ts @@ -1,7 +1,7 @@ -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { AccountDataEvent } from '$types/matrix/accountData'; import { getAccountData } from '$utils/room'; -import { IEmoji, emojis } from './emoji'; +import { type IEmoji, emojis } from './emoji'; type EmojiUnicode = string; type EmojiUsageCount = number; diff --git a/src/app/plugins/text-area/Operations.ts b/src/app/plugins/text-area/Operations.ts index a4c813ad6..2630eefdf 100644 --- a/src/app/plugins/text-area/Operations.ts +++ b/src/app/plugins/text-area/Operations.ts @@ -1,7 +1,7 @@ -import { Cursor } from './Cursor'; +import { type Cursor } from './Cursor'; -export interface Operations { +export type Operations = { select(cursor: Cursor): void; deselect(cursor: Cursor): void; insert(cursor: Cursor, text: string): Cursor; -} +}; diff --git a/src/app/plugins/text-area/TextArea.ts b/src/app/plugins/text-area/TextArea.ts index 2e8a08553..ad4e072a0 100644 --- a/src/app/plugins/text-area/TextArea.ts +++ b/src/app/plugins/text-area/TextArea.ts @@ -1,5 +1,5 @@ import { Cursor } from './Cursor'; -import { GetTarget } from './type'; +import { type GetTarget } from './type'; export class TextArea { private readonly getTarget: GetTarget; diff --git a/src/app/plugins/text-area/TextAreaOperations.ts b/src/app/plugins/text-area/TextAreaOperations.ts index 3bc1b4f8d..511c5184b 100644 --- a/src/app/plugins/text-area/TextAreaOperations.ts +++ b/src/app/plugins/text-area/TextAreaOperations.ts @@ -1,6 +1,6 @@ import { Cursor } from './Cursor'; -import { Operations } from './Operations'; -import { GetTarget } from './type'; +import { type Operations } from './Operations'; +import { type GetTarget } from './type'; export class TextAreaOperations implements Operations { private readonly getTarget: GetTarget; diff --git a/src/app/plugins/text-area/TextUtils.ts b/src/app/plugins/text-area/TextUtils.ts index 43d05837f..b599a01f3 100644 --- a/src/app/plugins/text-area/TextUtils.ts +++ b/src/app/plugins/text-area/TextUtils.ts @@ -1,5 +1,5 @@ export class TextUtils { static multiline(str: string) { - return str.indexOf('\n') !== -1; + return str.includes('\n'); } } diff --git a/src/app/plugins/text-area/mods/Intent.ts b/src/app/plugins/text-area/mods/Intent.ts index 9ad8533d7..c7519327c 100644 --- a/src/app/plugins/text-area/mods/Intent.ts +++ b/src/app/plugins/text-area/mods/Intent.ts @@ -1,6 +1,6 @@ import { Cursor } from '$plugins/text-area/Cursor'; -import { Operations } from '$plugins/text-area/Operations'; -import { TextArea } from '$plugins/text-area/TextArea'; +import { type Operations } from '$plugins/text-area/Operations'; +import { type TextArea } from '$plugins/text-area/TextArea'; export class Intent { public readonly textArea: TextArea; diff --git a/src/app/plugins/utils.ts b/src/app/plugins/utils.ts index fc84b3921..00f4609c6 100644 --- a/src/app/plugins/utils.ts +++ b/src/app/plugins/utils.ts @@ -1,6 +1,6 @@ -import { SearchItemStrGetter } from '$hooks/useAsyncSearch'; +import { type SearchItemStrGetter } from '$hooks/useAsyncSearch'; import { PackImageReader } from './custom-emoji'; -import { IEmoji } from './emoji'; +import { type IEmoji } from './emoji'; export const getEmoticonSearchStr: SearchItemStrGetter = (item) => { const shortcode = `:${item.shortcode}:`; @@ -13,7 +13,7 @@ export const getEmoticonSearchStr: SearchItemStrGetter const names = [shortcode, item.label]; if (Array.isArray(item.shortcodes)) { - return names.concat(item.shortcodes); + return [...names, ...item.shortcodes]; } return names; }; diff --git a/src/app/plugins/via-servers.ts b/src/app/plugins/via-servers.ts index e789e5f8f..404c0cdf2 100644 --- a/src/app/plugins/via-servers.ts +++ b/src/app/plugins/via-servers.ts @@ -1,6 +1,6 @@ -import { Room } from '$types/matrix-sdk'; -import { IRoomCreateContent, StateEvent } from '$types/matrix/room'; -import { IPowerLevels } from '$hooks/usePowerLevels'; +import { type Room } from '$types/matrix-sdk'; +import { type IRoomCreateContent, StateEvent } from '$types/matrix/room'; +import { type IPowerLevels } from '$hooks/usePowerLevels'; import { creatorsSupported, getMxIdServer } from '$utils/matrix'; import { getStateEvent } from '$utils/room'; @@ -69,5 +69,5 @@ export const getViaServers = (room: Room): string[] => { if (mostPop3.includes(via[0])) { mostPop3.splice(mostPop3.indexOf(via[0]), 1); } - return via.concat(mostPop3.slice(0, 2)); + return [...via, ...mostPop3.slice(0, 2)]; }; diff --git a/src/app/plugins/voice-recorder-kit/useVoiceRecorder.ts b/src/app/plugins/voice-recorder-kit/useVoiceRecorder.ts index e623c1b17..222cc491c 100644 --- a/src/app/plugins/voice-recorder-kit/useVoiceRecorder.ts +++ b/src/app/plugins/voice-recorder-kit/useVoiceRecorder.ts @@ -13,6 +13,8 @@ const WAVEFORM_POINT_COUNT = 100; let sharedAudioContext: AudioContext | null = null; +const createSilenceLevels = (count: number): number[] => Array(count).fill(0.15); + function getSharedAudioContext(): AudioContext { if (!sharedAudioContext || sharedAudioContext.state === 'closed') { sharedAudioContext = new AudioContext(); @@ -22,7 +24,7 @@ function getSharedAudioContext(): AudioContext { // downsample an array of samples to a target count by averaging blocks of samples together function downsampleWaveform(samples: number[], targetCount: number): number[] { - if (samples.length === 0) return Array.from({ length: targetCount }, () => 0.15); + if (samples.length === 0) return createSilenceLevels(targetCount); if (samples.length <= targetCount) { const step = (samples.length - 1) / (targetCount - 1); return Array.from({ length: targetCount }, (_, i) => { @@ -60,9 +62,7 @@ export function useVoiceRecorder(options: UseVoiceRecorderOptions = {}): UseVoic const [isPlaying, setIsPlaying] = useState(false); const [isPaused, setIsPaused] = useState(false); const [seconds, setSeconds] = useState(0); - const [levels, setLevels] = useState(() => - Array.from({ length: BAR_COUNT }, () => 0.15) - ); + const [levels, setLevels] = useState(() => createSilenceLevels(BAR_COUNT)); const [error, setError] = useState(null); const [audioUrl, setAudioUrl] = useState(null); const [audioFile, setAudioFile] = useState(null); @@ -460,7 +460,7 @@ export function useVoiceRecorder(options: UseVoiceRecorderOptions = {}): UseVoic audioContextRef.current.suspend().catch(() => {}); } - setLevels(Array.from({ length: BAR_COUNT }, () => 0.15)); + setLevels(createSilenceLevels(BAR_COUNT)); } catch { setError('Error pausing recording'); } @@ -831,7 +831,7 @@ export function useVoiceRecorder(options: UseVoiceRecorderOptions = {}): UseVoic setSeconds(0); pausedTimeRef.current = 0; startTimeRef.current = null; - setLevels(Array.from({ length: BAR_COUNT }, () => 0.15)); + setLevels(createSilenceLevels(BAR_COUNT)); previousChunksRef.current = []; chunksRef.current = []; waveformSamplesRef.current = []; @@ -873,7 +873,7 @@ export function useVoiceRecorder(options: UseVoiceRecorderOptions = {}): UseVoic setSeconds(0); pausedTimeRef.current = 0; startTimeRef.current = null; - setLevels(Array.from({ length: BAR_COUNT }, () => 0.15)); + setLevels(createSilenceLevels(BAR_COUNT)); previousChunksRef.current = []; chunksRef.current = []; isResumingRef.current = false; diff --git a/src/app/state/backupRestore.ts b/src/app/state/backupRestore.ts index 9c1c11479..223a8e0fc 100644 --- a/src/app/state/backupRestore.ts +++ b/src/app/state/backupRestore.ts @@ -1,5 +1,5 @@ import { atom } from 'jotai'; -import { ImportRoomKeyProgressData, ImportRoomKeyStage } from '$types/matrix-sdk'; +import { type ImportRoomKeyProgressData, ImportRoomKeyStage } from '$types/matrix-sdk'; export enum BackupProgressStatus { Idle, diff --git a/src/app/state/callEmbed.ts b/src/app/state/callEmbed.ts index b05055c43..126520122 100644 --- a/src/app/state/callEmbed.ts +++ b/src/app/state/callEmbed.ts @@ -1,6 +1,6 @@ import { atom } from 'jotai'; import * as Sentry from '@sentry/react'; -import { CallEmbed } from '../plugins/call'; +import { type CallEmbed } from '../plugins/call'; const baseCallEmbedAtom = atom(undefined); diff --git a/src/app/state/callPreferences.ts b/src/app/state/callPreferences.ts index a8e82067f..541f610c3 100644 --- a/src/app/state/callPreferences.ts +++ b/src/app/state/callPreferences.ts @@ -1,4 +1,4 @@ -import { WritableAtom } from 'jotai'; +import { type WritableAtom } from 'jotai'; import { atomWithLocalStorage, getLocalStorageItem, diff --git a/src/app/state/closedLobbyCategories.ts b/src/app/state/closedLobbyCategories.ts index 3c5c99e16..195706443 100644 --- a/src/app/state/closedLobbyCategories.ts +++ b/src/app/state/closedLobbyCategories.ts @@ -1,4 +1,4 @@ -import { WritableAtom, atom } from 'jotai'; +import { type WritableAtom, atom } from 'jotai'; import { produce } from 'immer'; import { atomWithLocalStorage, @@ -34,7 +34,7 @@ export const makeClosedLobbyCategoriesAtom = (userId: string): ClosedLobbyCatego return new Set(arrayValue); }, (key, value) => { - const arrayValue = Array.from(value); + const arrayValue = [...value]; setLocalStorageItem(key, arrayValue); } ); diff --git a/src/app/state/closedNavCategories.ts b/src/app/state/closedNavCategories.ts index 8c2348902..9ba711b8b 100644 --- a/src/app/state/closedNavCategories.ts +++ b/src/app/state/closedNavCategories.ts @@ -1,4 +1,4 @@ -import { WritableAtom, atom } from 'jotai'; +import { type WritableAtom, atom } from 'jotai'; import { produce } from 'immer'; import { atomWithLocalStorage, @@ -34,7 +34,7 @@ export const makeClosedNavCategoriesAtom = (userId: string): ClosedNavCategories return new Set(arrayValue); }, (key, value) => { - const arrayValue = Array.from(value); + const arrayValue = [...value]; setLocalStorageItem(key, arrayValue); } ); diff --git a/src/app/state/createRoomModal.ts b/src/app/state/createRoomModal.ts index 16757ec71..d341da2bc 100644 --- a/src/app/state/createRoomModal.ts +++ b/src/app/state/createRoomModal.ts @@ -1,5 +1,5 @@ import { atom } from 'jotai'; -import { CreateRoomType } from '$components/create-room/types'; +import { type CreateRoomType } from '$components/create-room/types'; export type CreateRoomModalState = { spaceId?: string; diff --git a/src/app/state/hooks/callPreferences.ts b/src/app/state/hooks/callPreferences.ts index d02700f05..ce75555a3 100644 --- a/src/app/state/hooks/callPreferences.ts +++ b/src/app/state/hooks/callPreferences.ts @@ -1,6 +1,6 @@ import { createContext, useCallback, useContext } from 'react'; import { useAtom } from 'jotai'; -import { CallPreferences, CallPreferencesAtom } from '../callPreferences'; +import { type CallPreferences, type CallPreferencesAtom } from '../callPreferences'; const CallPreferencesAtomContext = createContext(null); export const CallPreferencesProvider = CallPreferencesAtomContext.Provider; diff --git a/src/app/state/hooks/closedLobbyCategories.ts b/src/app/state/hooks/closedLobbyCategories.ts index 19843b6e6..eed808009 100644 --- a/src/app/state/hooks/closedLobbyCategories.ts +++ b/src/app/state/hooks/closedLobbyCategories.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { ClosedLobbyCategoriesAtom } from '$state/closedLobbyCategories'; +import { type ClosedLobbyCategoriesAtom } from '$state/closedLobbyCategories'; const ClosedLobbyCategoriesAtomContext = createContext(null); export const ClosedLobbyCategoriesProvider = ClosedLobbyCategoriesAtomContext.Provider; diff --git a/src/app/state/hooks/closedNavCategories.ts b/src/app/state/hooks/closedNavCategories.ts index 38a962e48..ee95e19b6 100644 --- a/src/app/state/hooks/closedNavCategories.ts +++ b/src/app/state/hooks/closedNavCategories.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { ClosedNavCategoriesAtom } from '$state/closedNavCategories'; +import { type ClosedNavCategoriesAtom } from '$state/closedNavCategories'; const ClosedNavCategoriesAtomContext = createContext(null); export const ClosedNavCategoriesProvider = ClosedNavCategoriesAtomContext.Provider; diff --git a/src/app/state/hooks/createRoomModal.ts b/src/app/state/hooks/createRoomModal.ts index 8c1ec8b70..cb7f076b9 100644 --- a/src/app/state/hooks/createRoomModal.ts +++ b/src/app/state/hooks/createRoomModal.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; -import { CreateRoomType } from '$components/create-room/types'; -import { createRoomModalAtom, CreateRoomModalState } from '$state/createRoomModal'; +import { type CreateRoomType } from '$components/create-room/types'; +import { createRoomModalAtom, type CreateRoomModalState } from '$state/createRoomModal'; export const useCreateRoomModalState = (): CreateRoomModalState | undefined => { const data = useAtomValue(createRoomModalAtom); diff --git a/src/app/state/hooks/createSpaceModal.ts b/src/app/state/hooks/createSpaceModal.ts index 4661712d0..7edf1a0e1 100644 --- a/src/app/state/hooks/createSpaceModal.ts +++ b/src/app/state/hooks/createSpaceModal.ts @@ -1,6 +1,6 @@ import { useCallback } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; -import { createSpaceModalAtom, CreateSpaceModalState } from '$state/createSpaceModal'; +import { createSpaceModalAtom, type CreateSpaceModalState } from '$state/createSpaceModal'; export const useCreateSpaceModalState = (): CreateSpaceModalState | undefined => { const data = useAtomValue(createSpaceModalAtom); diff --git a/src/app/state/hooks/navToActivePath.ts b/src/app/state/hooks/navToActivePath.ts index 412247600..cdd22253c 100644 --- a/src/app/state/hooks/navToActivePath.ts +++ b/src/app/state/hooks/navToActivePath.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { NavToActivePathAtom } from '$state/navToActivePath'; +import { type NavToActivePathAtom } from '$state/navToActivePath'; const NavToActivePathAtomContext = createContext(null); export const NavToActivePathProvider = NavToActivePathAtomContext.Provider; diff --git a/src/app/state/hooks/openedSidebarFolder.ts b/src/app/state/hooks/openedSidebarFolder.ts index 6a5e61f92..1d8f32339 100644 --- a/src/app/state/hooks/openedSidebarFolder.ts +++ b/src/app/state/hooks/openedSidebarFolder.ts @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react'; -import { OpenedSidebarFolderAtom } from '$state/openedSidebarFolder'; +import { type OpenedSidebarFolderAtom } from '$state/openedSidebarFolder'; const OpenedSidebarFolderAtomContext = createContext(null); export const OpenedSidebarFolderProvider = OpenedSidebarFolderAtomContext.Provider; diff --git a/src/app/state/hooks/roomList.ts b/src/app/state/hooks/roomList.ts index 708909266..7de175f6a 100644 --- a/src/app/state/hooks/roomList.ts +++ b/src/app/state/hooks/roomList.ts @@ -1,9 +1,9 @@ -import { Atom, useAtomValue } from 'jotai'; +import { type Atom, useAtomValue } from 'jotai'; import { selectAtom } from 'jotai/utils'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { useCallback, useMemo } from 'react'; import { getAllParents, isRoom, isSpace, isUnsupportedRoom } from '$utils/room'; -import { RoomToParents } from '$types/matrix/room'; +import { type RoomToParents } from '$types/matrix/room'; import { compareRoomsEqual } from '$state/room-list/utils'; export type RoomsAtom = Atom; diff --git a/src/app/state/hooks/roomSettings.ts b/src/app/state/hooks/roomSettings.ts index 8a0784870..f53b29f93 100644 --- a/src/app/state/hooks/roomSettings.ts +++ b/src/app/state/hooks/roomSettings.ts @@ -1,6 +1,10 @@ import { useCallback } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; -import { roomSettingsAtom, RoomSettingsPage, RoomSettingsState } from '$state/roomSettings'; +import { + roomSettingsAtom, + type RoomSettingsPage, + type RoomSettingsState, +} from '$state/roomSettings'; export const useRoomSettingsState = (): RoomSettingsState | undefined => { const data = useAtomValue(roomSettingsAtom); diff --git a/src/app/state/hooks/settings.ts b/src/app/state/hooks/settings.ts index d28c48336..2f49f98fd 100644 --- a/src/app/state/hooks/settings.ts +++ b/src/app/state/hooks/settings.ts @@ -1,7 +1,7 @@ import { atom, useAtomValue, useSetAtom } from 'jotai'; import { selectAtom } from 'jotai/utils'; import { useMemo } from 'react'; -import { Settings, settingsAtom as sAtom } from '$state/settings'; +import { type Settings, type settingsAtom as sAtom } from '$state/settings'; export type SettingSetter = | Settings[K] diff --git a/src/app/state/hooks/spaceSettings.ts b/src/app/state/hooks/spaceSettings.ts index ab3b8da20..e5672efc6 100644 --- a/src/app/state/hooks/spaceSettings.ts +++ b/src/app/state/hooks/spaceSettings.ts @@ -1,6 +1,10 @@ import { useCallback } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; -import { spaceSettingsAtom, SpaceSettingsPage, SpaceSettingsState } from '$state/spaceSettings'; +import { + spaceSettingsAtom, + type SpaceSettingsPage, + type SpaceSettingsState, +} from '$state/spaceSettings'; export const useSpaceSettingsState = (): SpaceSettingsState | undefined => { const data = useAtomValue(spaceSettingsAtom); diff --git a/src/app/state/hooks/unread.ts b/src/app/state/hooks/unread.ts index ac94bb6a3..87e705a37 100644 --- a/src/app/state/hooks/unread.ts +++ b/src/app/state/hooks/unread.ts @@ -1,8 +1,8 @@ import { useCallback } from 'react'; import { useAtomValue } from 'jotai'; import { selectAtom } from 'jotai/utils'; -import { RoomToUnread, Unread } from '$types/matrix/room'; -import { roomToUnreadAtom, unreadEqual } from '$state/room/roomToUnread'; +import { type RoomToUnread, type Unread } from '$types/matrix/room'; +import { type roomToUnreadAtom, unreadEqual } from '$state/room/roomToUnread'; const compareUnreadEqual = (u1?: Unread, u2?: Unread): boolean => { if (!u1 || !u2) return false; diff --git a/src/app/state/hooks/useBindAtoms.ts b/src/app/state/hooks/useBindAtoms.ts index 1d68295d0..6c1d602ed 100644 --- a/src/app/state/hooks/useBindAtoms.ts +++ b/src/app/state/hooks/useBindAtoms.ts @@ -1,4 +1,4 @@ -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { allInvitesAtom, useBindAllInvitesAtom } from '$state/room-list/inviteList'; import { allRoomsAtom, useBindAllRoomsAtom } from '$state/room-list/roomList'; import { mDirectAtom, useBindMDirectAtom } from '$state/mDirectList'; diff --git a/src/app/state/hooks/userRoomProfile.ts b/src/app/state/hooks/userRoomProfile.ts index c0e8edcb6..fe8a2df00 100644 --- a/src/app/state/hooks/userRoomProfile.ts +++ b/src/app/state/hooks/userRoomProfile.ts @@ -1,8 +1,8 @@ import { useCallback } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; -import { Position, RectCords } from 'folds'; -import { UserProfile } from '$hooks/useUserProfile'; -import { userRoomProfileAtom, UserRoomProfileState } from '$state/userRoomProfile'; +import { type Position, type RectCords } from 'folds'; +import { type UserProfile } from '$hooks/useUserProfile'; +import { userRoomProfileAtom, type UserRoomProfileState } from '$state/userRoomProfile'; export const useUserRoomProfileState = (): UserRoomProfileState | undefined => { const data = useAtomValue(userRoomProfileAtom); diff --git a/src/app/state/mDirectList.ts b/src/app/state/mDirectList.ts index 89b3721f4..a80b085b2 100644 --- a/src/app/state/mDirectList.ts +++ b/src/app/state/mDirectList.ts @@ -1,5 +1,5 @@ import { atom, useSetAtom } from 'jotai'; -import { ClientEvent, MatrixClient, MatrixEvent } from '$types/matrix-sdk'; +import { ClientEvent, type MatrixClient, type MatrixEvent } from '$types/matrix-sdk'; import { useEffect } from 'react'; import { AccountDataEvent } from '$types/matrix/accountData'; import { getAccountData, getMDirects } from '$utils/room'; diff --git a/src/app/state/mediaVolume.ts b/src/app/state/mediaVolume.ts new file mode 100644 index 000000000..83372b04a --- /dev/null +++ b/src/app/state/mediaVolume.ts @@ -0,0 +1,21 @@ +/** + * Centralized access to the persisted media volume preference. + * + * Plain functions rather than a Jotai atom because the value is applied + * directly to DOM element refs, not read reactively in JSX. + */ + +const MEDIA_VOLUME_KEY = 'mediaVolume'; + +/** Returns the persisted volume (0–1), or undefined if never set. */ +export const getMediaVolume = (): number | undefined => { + const stored = localStorage.getItem(MEDIA_VOLUME_KEY); + if (stored === null) return undefined; + const parsed = parseFloat(stored); + return Number.isNaN(parsed) ? undefined : parsed; +}; + +/** Persist the current volume (0–1). */ +export const setMediaVolume = (volume: number): void => { + localStorage.setItem(MEDIA_VOLUME_KEY, String(volume)); +}; diff --git a/src/app/state/modal.ts b/src/app/state/modal.ts index 5b54dc637..bc027ec7d 100644 --- a/src/app/state/modal.ts +++ b/src/app/state/modal.ts @@ -1,5 +1,5 @@ import { atom } from 'jotai'; -import { MatrixEvent, Room, Relations } from '$types/matrix-sdk'; +import { type MatrixEvent, type Room, type Relations } from '$types/matrix-sdk'; export enum ModalType { Delete = 'delete', diff --git a/src/app/state/navToActivePath.ts b/src/app/state/navToActivePath.ts index affc313da..47191f177 100644 --- a/src/app/state/navToActivePath.ts +++ b/src/app/state/navToActivePath.ts @@ -1,6 +1,6 @@ -import { WritableAtom, atom } from 'jotai'; +import { type WritableAtom, atom } from 'jotai'; import { produce } from 'immer'; -import { Path } from 'react-router-dom'; +import { type Path } from 'react-router-dom'; import { atomWithLocalStorage, getLocalStorageItem, diff --git a/src/app/state/nicknames.ts b/src/app/state/nicknames.ts index ca67302a1..d55f73620 100644 --- a/src/app/state/nicknames.ts +++ b/src/app/state/nicknames.ts @@ -1,5 +1,5 @@ import { atom } from 'jotai'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { AccountDataEvent } from '$types/matrix/accountData'; export const NICKNAMES_KEY = 'sableNicknames'; diff --git a/src/app/state/openedSidebarFolder.ts b/src/app/state/openedSidebarFolder.ts index 743160d4b..8c104b71b 100644 --- a/src/app/state/openedSidebarFolder.ts +++ b/src/app/state/openedSidebarFolder.ts @@ -1,4 +1,4 @@ -import { WritableAtom, atom } from 'jotai'; +import { type WritableAtom, atom } from 'jotai'; import { produce } from 'immer'; import { atomWithLocalStorage, @@ -34,7 +34,7 @@ export const makeOpenedSidebarFolderAtom = (userId: string): OpenedSidebarFolder return new Set(arrayValue); }, (key, value) => { - const arrayValue = Array.from(value); + const arrayValue = [...value]; setLocalStorageItem(key, arrayValue); } ); diff --git a/src/app/state/room-list/inviteList.ts b/src/app/state/room-list/inviteList.ts index 4d33b3323..132856039 100644 --- a/src/app/state/room-list/inviteList.ts +++ b/src/app/state/room-list/inviteList.ts @@ -1,8 +1,8 @@ -import { atom, WritableAtom } from 'jotai'; -import { MatrixClient } from '$types/matrix-sdk'; +import { atom, type WritableAtom } from 'jotai'; +import { type MatrixClient } from '$types/matrix-sdk'; import { useMemo } from 'react'; import { Membership } from '$types/matrix/room'; -import { RoomsAction, useBindRoomsWithMembershipsAtom } from './utils'; +import { type RoomsAction, useBindRoomsWithMembershipsAtom } from './utils'; const baseRoomsAtom = atom([]); export const allInvitesAtom = atom( diff --git a/src/app/state/room-list/roomList.ts b/src/app/state/room-list/roomList.ts index c3437d930..5e1fce982 100644 --- a/src/app/state/room-list/roomList.ts +++ b/src/app/state/room-list/roomList.ts @@ -1,8 +1,8 @@ import { atom } from 'jotai'; -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; import { useMemo } from 'react'; import { Membership } from '$types/matrix/room'; -import { RoomsAction, useBindRoomsWithMembershipsAtom } from './utils'; +import { type RoomsAction, useBindRoomsWithMembershipsAtom } from './utils'; const baseRoomsAtom = atom([]); export const allRoomsAtom = atom( diff --git a/src/app/state/room-list/utils.ts b/src/app/state/room-list/utils.ts index 9758a1ba6..9acd27a25 100644 --- a/src/app/state/room-list/utils.ts +++ b/src/app/state/room-list/utils.ts @@ -1,7 +1,7 @@ -import { useSetAtom, WritableAtom } from 'jotai'; -import { ClientEvent, MatrixClient, Room, RoomEvent } from '$types/matrix-sdk'; +import { useSetAtom, type WritableAtom } from 'jotai'; +import { ClientEvent, type MatrixClient, type Room, RoomEvent } from '$types/matrix-sdk'; import { useEffect } from 'react'; -import { Membership } from '$types/matrix/room'; +import { type Membership } from '$types/matrix/room'; export type RoomsAction = | { @@ -22,7 +22,7 @@ export const useBindRoomsWithMembershipsAtom = ( useEffect(() => { const satisfyMembership = (room: Room): boolean => - !!memberships.find((membership) => membership === room.getMyMembership()); + memberships.some((membership) => membership === room.getMyMembership()); setRoomsAtom({ type: 'INITIALIZE', rooms: mx diff --git a/src/app/state/room/roomInputDrafts.ts b/src/app/state/room/roomInputDrafts.ts index 4b167f220..852c84ac2 100644 --- a/src/app/state/room/roomInputDrafts.ts +++ b/src/app/state/room/roomInputDrafts.ts @@ -1,9 +1,9 @@ import { atom } from 'jotai'; import { atomFamily } from 'jotai/utils'; -import { Descendant } from 'slate'; -import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; -import { IEventRelation } from '$types/matrix-sdk'; -import { TUploadContent } from '$utils/matrix'; +import { type Descendant } from 'slate'; +import { type EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; +import { type IEventRelation } from '$types/matrix-sdk'; +import { type TUploadContent } from '$utils/matrix'; import { createUploadAtomFamily } from '$state/upload'; import { createListAtom } from '$state/list'; diff --git a/src/app/state/room/roomToParents.ts b/src/app/state/room/roomToParents.ts index 65b0a3a36..eda09678f 100644 --- a/src/app/state/room/roomToParents.ts +++ b/src/app/state/room/roomToParents.ts @@ -2,15 +2,15 @@ import { produce } from 'immer'; import { atom, useSetAtom } from 'jotai'; import { ClientEvent, - MatrixClient, - MatrixEvent, - Room, + type MatrixClient, + type MatrixEvent, + type Room, RoomEvent, RoomStateEvent, SyncState, } from '$types/matrix-sdk'; import { useCallback, useEffect } from 'react'; -import { Membership, RoomToParents, StateEvent } from '$types/matrix/room'; +import { Membership, type RoomToParents, StateEvent } from '$types/matrix/room'; import { getRoomToParents, getSpaceChildren, diff --git a/src/app/state/room/roomToUnread.ts b/src/app/state/room/roomToUnread.ts index e70271d2c..06a4b8421 100644 --- a/src/app/state/room/roomToUnread.ts +++ b/src/app/state/room/roomToUnread.ts @@ -1,14 +1,14 @@ import { produce } from 'immer'; import { atom, useAtomValue, useSetAtom } from 'jotai'; import { - IRoomTimelineData, - MatrixClient, - MatrixEvent, - Room, + type IRoomTimelineData, + type MatrixClient, + type MatrixEvent, + type Room, RoomEvent, SyncState, - ReceiptContent, - ReceiptType, + type ReceiptContent, + type ReceiptType, EventType, ClientEvent, } from '$types/matrix-sdk'; @@ -16,9 +16,9 @@ import { useCallback, useEffect, useRef } from 'react'; import { Membership, NotificationType, - RoomToUnread, - UnreadInfo, - Unread, + type RoomToUnread, + type UnreadInfo, + type Unread, StateEvent, } from '$types/matrix/room'; import { diff --git a/src/app/state/sentryStorage.ts b/src/app/state/sentryStorage.ts new file mode 100644 index 000000000..223c84bd8 --- /dev/null +++ b/src/app/state/sentryStorage.ts @@ -0,0 +1,28 @@ +/** + * Centralized access to the Sentry opt-in preference stored in localStorage. + * + * These must be plain functions because src/instrument.ts reads them + * synchronously at module load time, before React and Jotai mount. + */ + +export const SENTRY_ENABLED_KEY = 'sable_sentry_enabled'; +export const SENTRY_REPLAY_ENABLED_KEY = 'sable_sentry_replay_enabled'; + +export const getSentryEnabled = (): boolean => localStorage.getItem(SENTRY_ENABLED_KEY) === 'true'; + +export const isSentryDecided = (): boolean => localStorage.getItem(SENTRY_ENABLED_KEY) !== null; + +export const setSentryEnabled = (enabled: boolean): void => { + localStorage.setItem(SENTRY_ENABLED_KEY, String(enabled)); +}; + +export const getSentryReplayEnabled = (): boolean => + localStorage.getItem(SENTRY_REPLAY_ENABLED_KEY) === 'true'; + +export const setSentryReplayEnabled = (enabled: boolean): void => { + if (enabled) { + localStorage.setItem(SENTRY_REPLAY_ENABLED_KEY, 'true'); + } else { + localStorage.removeItem(SENTRY_REPLAY_ENABLED_KEY); + } +}; diff --git a/src/app/state/sessions.ts b/src/app/state/sessions.ts index aadf20ce9..b90e2e650 100644 --- a/src/app/state/sessions.ts +++ b/src/app/state/sessions.ts @@ -1,4 +1,4 @@ -import { ReactNode } from 'react'; +import { type ReactNode } from 'react'; import { atom } from 'jotai'; import { createLogger } from '$utils/debug'; import { diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts index 8a4956394..78037c5b5 100644 --- a/src/app/state/settings.ts +++ b/src/app/state/settings.ts @@ -23,7 +23,7 @@ export enum CaptionPosition { } export type JumboEmojiSize = 'none' | 'extraSmall' | 'small' | 'normal' | 'large' | 'extraLarge'; -export interface Settings { +export type Settings = { themeId?: string; useSystemTheme: boolean; lightThemeId?: string; @@ -121,7 +121,7 @@ export interface Settings { // furry stuff renderAnimals: boolean; -} +}; const defaultSettings: Settings = { themeId: undefined, diff --git a/src/app/state/spaceRooms.ts b/src/app/state/spaceRooms.ts index 572fade3e..ea6d1c486 100644 --- a/src/app/state/spaceRooms.ts +++ b/src/app/state/spaceRooms.ts @@ -15,7 +15,7 @@ const baseSpaceRoomsAtom = atomWithLocalStorage>( return new Set(arrayValue); }, (key, value) => { - const arrayValue = Array.from(value); + const arrayValue = [...value]; setLocalStorageItem(key, arrayValue); } ); @@ -36,7 +36,7 @@ export const spaceRoomsAtom = atom, [SpaceRoomsAction], undefined>( const current = get(baseSpaceRoomsAtom); const { type, roomIds } = action; - if (type === 'DELETE' && roomIds.find((roomId) => current.has(roomId))) { + if (type === 'DELETE' && roomIds.some((roomId) => current.has(roomId))) { set( baseSpaceRoomsAtom, produce(current, (draft) => { diff --git a/src/app/state/typingMembers.ts b/src/app/state/typingMembers.ts index 9f2a357b7..1e31cab0b 100644 --- a/src/app/state/typingMembers.ts +++ b/src/app/state/typingMembers.ts @@ -1,6 +1,10 @@ import { produce } from 'immer'; import { atom, useSetAtom } from 'jotai'; -import { MatrixClient, RoomMemberEvent, RoomMemberEventHandlerMap } from '$types/matrix-sdk'; +import { + type MatrixClient, + RoomMemberEvent, + type RoomMemberEventHandlerMap, +} from '$types/matrix-sdk'; import { useEffect } from 'react'; import { useSetting } from './hooks/settings'; import { settingsAtom } from './settings'; diff --git a/src/app/state/upload.ts b/src/app/state/upload.ts index 827e4bfd4..125db34f2 100644 --- a/src/app/state/upload.ts +++ b/src/app/state/upload.ts @@ -1,9 +1,14 @@ import { atom, useAtom } from 'jotai'; import { atomFamily } from 'jotai/utils'; -import { MatrixClient, UploadResponse, UploadProgress, MatrixError } from '$types/matrix-sdk'; +import { + type MatrixClient, + type UploadResponse, + type UploadProgress, + type MatrixError, +} from '$types/matrix-sdk'; import { useCallback } from 'react'; import { useThrottle } from '$hooks/useThrottle'; -import { uploadContent, TUploadContent } from '$utils/matrix'; +import { uploadContent, type TUploadContent } from '$utils/matrix'; export enum UploadStatus { Idle = 'idle', diff --git a/src/app/state/userRoomProfile.ts b/src/app/state/userRoomProfile.ts index 04540fcd2..1ed20a3a0 100644 --- a/src/app/state/userRoomProfile.ts +++ b/src/app/state/userRoomProfile.ts @@ -1,4 +1,4 @@ -import { Position, RectCords } from 'folds'; +import { type Position, type RectCords } from 'folds'; import { atom } from 'jotai'; import type { UserProfile } from '$hooks/useUserProfile'; diff --git a/src/app/styles/ContainerColor.css.ts b/src/app/styles/ContainerColor.css.ts index cefc5256a..2f8310751 100644 --- a/src/app/styles/ContainerColor.css.ts +++ b/src/app/styles/ContainerColor.css.ts @@ -1,6 +1,6 @@ -import { ComplexStyleRule } from '@vanilla-extract/css'; -import { RecipeVariants, recipe } from '@vanilla-extract/recipes'; -import { ContainerColor as TContainerColor, DefaultReset, color, config } from 'folds'; +import { type ComplexStyleRule } from '@vanilla-extract/css'; +import { type RecipeVariants, recipe } from '@vanilla-extract/recipes'; +import { type ContainerColor as TContainerColor, DefaultReset, color, config } from 'folds'; const getVariant = (variant: TContainerColor): ComplexStyleRule => ({ vars: { diff --git a/src/app/utils/AsyncSearch.ts b/src/app/utils/AsyncSearch.ts index c799c60bb..b6cd24f75 100644 --- a/src/app/utils/AsyncSearch.ts +++ b/src/app/utils/AsyncSearch.ts @@ -78,7 +78,7 @@ export const AsyncSearch = ( if (findingCount !== currentFindingCount) onResult(resultList, query); searchIndex += 1; - sessionScheduleId = globalThis.setTimeout(() => find(query, thisSessionTimestamp), 1); + sessionScheduleId = globalThis.setTimeout(find, 1, query, thisSessionTimestamp); return; } } diff --git a/src/app/utils/MegolmExportEncryption.ts b/src/app/utils/MegolmExportEncryption.ts index 96250e5bd..b9e499360 100644 --- a/src/app/utils/MegolmExportEncryption.ts +++ b/src/app/utils/MegolmExportEncryption.ts @@ -43,7 +43,7 @@ function toArrayBufferView(data: Uint8Array): Uint8Array { function encodeBase64(uint8Array: Uint8Array): string { // Misinterpt the Uint8Array as Latin-1. // window.btoa expects a unicode string with codepoints in the range 0-255. - const latin1String = String.fromCodePoint.apply(null, Array.from(uint8Array)); + const latin1String = String.fromCodePoint(...[...uint8Array]); // Use the builtin base64 encoder. return globalThis.btoa(latin1String); } diff --git a/src/app/utils/abbreviations.ts b/src/app/utils/abbreviations.ts index e6dfcfa3e..718db9d93 100644 --- a/src/app/utils/abbreviations.ts +++ b/src/app/utils/abbreviations.ts @@ -44,7 +44,7 @@ export const splitByAbbreviations = (text: string, abbrMap: Map) const segments: TextSegment[] = []; let lastIndex = 0; - Array.from(text.matchAll(pattern)).forEach((match) => { + [...text.matchAll(pattern)].forEach((match) => { const matchIndex = match.index; if (matchIndex > lastIndex) { segments.push({ id: `txt-${segments.length}`, text: text.slice(lastIndex, matchIndex) }); diff --git a/src/app/utils/addStickerToDefaultStickerPack.ts b/src/app/utils/addStickerToDefaultStickerPack.ts index a9715a05f..d0d40d8b0 100644 --- a/src/app/utils/addStickerToDefaultStickerPack.ts +++ b/src/app/utils/addStickerToDefaultStickerPack.ts @@ -1,7 +1,7 @@ -import { PackContent, ImageUsage } from '$plugins/custom-emoji'; +import { type PackContent, ImageUsage } from '$plugins/custom-emoji'; import { AccountDataEvent } from '$types/matrix/accountData'; -import { IImageInfo } from '$types/matrix/common'; -import { MatrixClient } from 'matrix-js-sdk'; +import { type IImageInfo } from '$types/matrix/common'; +import { type MatrixClient } from 'matrix-js-sdk'; // Utility function to add a sticker to the default sticker pack // For now this only works for unencrypted stickers diff --git a/src/app/utils/bgColorImg.d.ts b/src/app/utils/bgColorImg.d.ts new file mode 100644 index 000000000..f38db24f9 --- /dev/null +++ b/src/app/utils/bgColorImg.d.ts @@ -0,0 +1,3 @@ +declare function bgColorImg(img: HTMLImageElement): string; + +export default bgColorImg; diff --git a/src/app/utils/common.ts b/src/app/utils/common.ts index fb1e0b16b..8a7fd79a5 100644 --- a/src/app/utils/common.ts +++ b/src/app/utils/common.ts @@ -1,4 +1,4 @@ -import { IconName, IconSrc } from 'folds'; +import { type IconName, type IconSrc } from 'folds'; export const bytesToSize = (bytes: number): string => { const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; diff --git a/src/app/utils/debugLogger.ts b/src/app/utils/debugLogger.ts index 1e1c7e74b..301dfa785 100644 --- a/src/app/utils/debugLogger.ts +++ b/src/app/utils/debugLogger.ts @@ -20,14 +20,14 @@ export type LogCategory = | 'error' | 'general'; -export interface LogEntry { +export type LogEntry = { timestamp: number; level: LogLevel; category: LogCategory; namespace: string; message: string; data?: unknown; -} +}; type LogListener = (entry: LogEntry) => void; @@ -136,7 +136,7 @@ class DebugLoggerService { } else { this.disabledBreadcrumbCategories.add(category); } - const disabledArray = Array.from(this.disabledBreadcrumbCategories); + const disabledArray = [...this.disabledBreadcrumbCategories]; if (disabledArray.length > 0) { localStorage.setItem(BREADCRUMB_DISABLED_KEY, JSON.stringify(disabledArray)); } else { diff --git a/src/app/utils/delayedEvents.ts b/src/app/utils/delayedEvents.ts index 488c51d1c..e893a3f27 100644 --- a/src/app/utils/delayedEvents.ts +++ b/src/app/utils/delayedEvents.ts @@ -1,17 +1,18 @@ import { EventType, - IContent, - MatrixClient, + type IContent, + type MatrixClient, MatrixEvent, - Room, + type Room, UpdateDelayedEventAction, + type DelayedEventInfo, + type SendDelayedEventResponse, } from '$types/matrix-sdk'; -import type { DelayedEventInfo, SendDelayedEventResponse } from '$types/matrix-sdk'; // Grab types needed for encryption -interface EncryptableBackend { +type EncryptableBackend = { encryptEvent(event: MatrixEvent, room: Room): Promise; -} +}; export async function supportsDelayedEvents(mx: MatrixClient): Promise { try { diff --git a/src/app/utils/featureCheck.ts b/src/app/utils/featureCheck.ts index a9474c317..e5f2dcaed 100644 --- a/src/app/utils/featureCheck.ts +++ b/src/app/utils/featureCheck.ts @@ -1,5 +1,5 @@ export const checkIndexedDBSupport = async (): Promise => { - const ts = new Date().getTime(); + const ts = Date.now(); const dbName = `checkIndexedDBSupport-${ts}`; return new Promise((resolve) => { let db; diff --git a/src/app/utils/keyboard.ts b/src/app/utils/keyboard.ts index ad0463dd1..861d60156 100644 --- a/src/app/utils/keyboard.ts +++ b/src/app/utils/keyboard.ts @@ -1,7 +1,7 @@ import { isKeyHotkey } from 'is-hotkey'; -import { KeyboardEventHandler } from 'react'; +import { type KeyboardEventHandler } from 'react'; -export interface KeyboardEventLike { +export type KeyboardEventLike = { key: string; which: number; altKey: boolean; @@ -9,7 +9,7 @@ export interface KeyboardEventLike { metaKey: boolean; shiftKey: boolean; preventDefault(): void; -} +}; export const onTabPress = (evt: KeyboardEventLike, callback: () => void) => { if (isKeyHotkey('tab', evt)) { diff --git a/src/app/utils/matrix-crypto.ts b/src/app/utils/matrix-crypto.ts index 144a51dfe..da69e01cc 100644 --- a/src/app/utils/matrix-crypto.ts +++ b/src/app/utils/matrix-crypto.ts @@ -1,4 +1,4 @@ -import { CryptoApi } from '$types/matrix-sdk'; +import { type CryptoApi } from '$types/matrix-sdk'; export const verifiedDevice = async ( api: CryptoApi, diff --git a/src/app/utils/matrix-uia.ts b/src/app/utils/matrix-uia.ts index 4fc10e448..6a6af369f 100644 --- a/src/app/utils/matrix-uia.ts +++ b/src/app/utils/matrix-uia.ts @@ -1,4 +1,4 @@ -import { AuthType, IAuthData, UIAFlow } from '$types/matrix-sdk'; +import { AuthType, type IAuthData, type UIAFlow } from '$types/matrix-sdk'; export const getSupportedUIAFlows = (uiaFlows: UIAFlow[], supportedStages: string[]): UIAFlow[] => { const supportedUIAFlows = uiaFlows.filter((flow) => diff --git a/src/app/utils/matrix.ts b/src/app/utils/matrix.ts index f04f71d90..b1d952931 100644 --- a/src/app/utils/matrix.ts +++ b/src/app/utils/matrix.ts @@ -1,21 +1,21 @@ import { - EncryptedAttachmentInfo, + type EncryptedAttachmentInfo, decryptAttachment, encryptAttachment, } from 'browser-encrypt-attachment'; import { EventTimeline, - EventTimelineSet, - MatrixClient, + type EventTimelineSet, + type MatrixClient, MatrixError, - MatrixEvent, - Room, - RoomMember, - UploadProgress, - UploadResponse, + type MatrixEvent, + type Room, + type RoomMember, + type UploadProgress, + type UploadResponse, } from '$types/matrix-sdk'; import to from 'await-to-js'; -import { IImageInfo, IThumbnailContent, IVideoInfo } from '$types/matrix/common'; +import { type IImageInfo, type IThumbnailContent, type IVideoInfo } from '$types/matrix/common'; import { AccountDataEvent } from '$types/matrix/accountData'; import { Membership, MessageEvent, StateEvent } from '$types/matrix/room'; import * as Sentry from '@sentry/react'; @@ -279,7 +279,7 @@ export const addRoomIdToMDirect = async ( }); const roomIds = userIdToRoomIds[userId] || []; - if (roomIds.indexOf(roomId) === -1) { + if (!roomIds.includes(roomId)) { roomIds.push(roomId); } userIdToRoomIds[userId] = roomIds; @@ -415,7 +415,7 @@ export const toggleReaction = ( ); const allReactions = relations?.getSortedAnnotationsByKey() ?? []; const [, reactionsSet] = allReactions.find(([k]: [string, any]) => k === key) ?? []; - const reactions: MatrixEvent[] = reactionsSet ? Array.from(reactionsSet) : []; + const reactions: MatrixEvent[] = reactionsSet ? [...reactionsSet] : []; const myReaction = reactions.find(factoryEventSentBy(mx.getUserId()!)); if (myReaction && !!(myReaction as any)?.isRelation()) { diff --git a/src/app/utils/notifications.ts b/src/app/utils/notifications.ts index a6d47b7a8..2454b4f32 100644 --- a/src/app/utils/notifications.ts +++ b/src/app/utils/notifications.ts @@ -1,4 +1,4 @@ -import { MatrixClient, ReceiptType } from '$types/matrix-sdk'; +import { type MatrixClient, ReceiptType } from '$types/matrix-sdk'; export async function markAsRead(mx: MatrixClient, roomId: string, privateReceipt: boolean) { const room = mx.getRoom(roomId); diff --git a/src/app/utils/room.ts b/src/app/utils/room.ts index 34fb527d8..64192c2f3 100644 --- a/src/app/utils/room.ts +++ b/src/app/utils/room.ts @@ -1,34 +1,34 @@ -import { IconName, IconSrc } from 'folds'; +import { type IconName, type IconSrc } from 'folds'; import { EventTimeline, - EventTimelineSet, + type EventTimelineSet, EventType, - IMentions, - IPowerLevelsContent, - IPushRule, - IPushRules, + type IMentions, + type IPowerLevelsContent, + type IPushRule, + type IPushRules, JoinRule, - MatrixClient, - MatrixEvent, + type MatrixClient, + type MatrixEvent, NotificationCountType, PushProcessor, RelationType, - Room, - RoomMember, - CryptoBackend, + type Room, + type RoomMember, + type CryptoBackend, MsgType, } from '$types/matrix-sdk'; -import { AccountDataEvent } from '$types/matrix/accountData'; +import { type AccountDataEvent } from '$types/matrix/accountData'; import { - IRoomCreateContent, + type IRoomCreateContent, Membership, NotificationType, - RoomToParents, + type RoomToParents, RoomType, MessageEvent, StateEvent, - UnreadInfo, + type UnreadInfo, } from '$types/matrix/room'; import * as Sentry from '@sentry/react'; @@ -176,7 +176,7 @@ export const getRoomToParents = (mx: MatrixClient): RoomToParents => { export const getOrphanParents = (roomToParents: RoomToParents, roomId: string): string[] => { const parents = getAllParents(roomToParents, roomId); - return Array.from(parents).filter((parentRoomId) => !roomToParents.has(parentRoomId)); + return [...parents].filter((parentRoomId) => !roomToParents.has(parentRoomId)); }; export const isMutedRule = (rule: IPushRule) => @@ -264,7 +264,7 @@ export const roomHaveUnread = (mx: MatrixClient, room: Room) => { return false; } - const latestEvent = liveEvents[liveEvents.length - 1]; + const latestEvent = liveEvents.at(-1); if (latestEvent?.getSender() === userId) { return false; @@ -316,8 +316,8 @@ export const getUnreadInfo = (room: Room, options?: UnreadInfoOptions): UnreadIn const liveEvents = room.getLiveTimeline().getEvents(); // Exclude the user's own messages: own sent events are always "read" (hasUserReadEvent // returns true for them), which would cause the clamp to fire incorrectly. - const latestNotification = [...liveEvents] - .reverse() + const latestNotification = liveEvents + .toReversed() .find( (event) => !event.isSending() && @@ -538,7 +538,7 @@ export const getMemberSearchStr = ( mxIdToName: (mxId: string) => string ): string[] => [ member.rawDisplayName === member.userId ? mxIdToName(member.userId) : member.rawDisplayName, - query.startsWith('@') || query.indexOf(':') > -1 ? member.userId : mxIdToName(member.userId), + query.startsWith('@') || query.includes(':') ? member.userId : mxIdToName(member.userId), ]; export const getMemberAvatarMxc = (room: Room, userId: string): string | undefined => { @@ -745,7 +745,7 @@ export const guessPerfectParent = ( } }); - return Array.from(specialUsers); + return [...specialUsers]; }; let perfectParent: string | undefined; diff --git a/src/app/utils/sanitize.test.ts b/src/app/utils/sanitize.test.ts index c1332cfc7..fce7c1f4e 100644 --- a/src/app/utils/sanitize.test.ts +++ b/src/app/utils/sanitize.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { sanitizeCustomHtml } from './sanitize'; +import { extractPlainTextFromCustomHtml, sanitizeCustomHtml } from './sanitize'; describe('sanitizeCustomHtml', () => { it('keeps permitted Matrix v1.18 tags', () => { @@ -138,3 +138,13 @@ describe('sanitizeCustomHtml', () => { expect((result.match(/
    /g) ?? []).length).toBeLessThanOrEqual(100); }); }); + +describe('extractPlainTextFromCustomHtml', () => { + it('converts sanitized html into readable plain text', () => { + const result = extractPlainTextFromCustomHtml( + '

    Hello
    world

    • One
    • Two
    ' + ); + + expect(result).toBe('Hello\nworld\n- One\n- Two'); + }); +}); diff --git a/src/app/utils/sanitize.ts b/src/app/utils/sanitize.ts index 301b942c5..1111e3dce 100644 --- a/src/app/utils/sanitize.ts +++ b/src/app/utils/sanitize.ts @@ -1,4 +1,4 @@ -import DOMPurify from 'dompurify'; +import DOMPurify, { type UponSanitizeAttributeHookEvent } from 'dompurify'; import { isMatrixHexColor } from './matrixHtml'; const MAX_TAG_NESTING = 100; @@ -70,7 +70,7 @@ const permittedTagToAttributes = { hr: ['data-md'], } as const satisfies Record; -const permittedHtmlAttributes = Array.from(new Set(Object.values(permittedTagToAttributes).flat())); +const permittedHtmlAttributes = [...new Set(Object.values(permittedTagToAttributes).flat())]; const allowedLinkSchemes = new Set(['https', 'http', 'ftp', 'mailto', 'magnet']); const forbiddenContentTags = ['mx-reply', 'script', 'style', 'textarea', 'option', 'noscript']; @@ -78,6 +78,31 @@ const forbiddenContentTags = ['mx-reply', 'script', 'style', 'textarea', 'option const codeLanguageClassRegex = /^language-[A-Za-z0-9_-]+$/; const orderedListStartRegex = /^-?\d+$/; const allowedUriRegex = /^(?:https?|ftp|mailto|magnet|mxc):/i; +const textBlockTags = new Set([ + 'blockquote', + 'caption', + 'details', + 'div', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'li', + 'ol', + 'p', + 'pre', + 'summary', + 'table', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr', + 'ul', +]); export function sanitizeText(body: string): string { const tagsToReplace: Record = { @@ -235,7 +260,7 @@ const enforceNestingLimit = (fragment: DocumentFragment): void => { const overlyNestedElements: Array<{ depth: number; element: Element }> = []; const collect = (node: ParentNode, depth: number) => { - Array.from(node.childNodes).forEach((child) => { + [...node.childNodes].forEach((child) => { if (!(child instanceof Element)) return; const childDepth = depth + 1; @@ -253,7 +278,7 @@ const enforceNestingLimit = (fragment: DocumentFragment): void => { .sort((a, b) => b.depth - a.depth) .forEach(({ element }) => { if (!element.parentNode) return; - element.replaceWith(...Array.from(element.childNodes)); + element.replaceWith(...[...element.childNodes]); }); }; @@ -273,7 +298,7 @@ const pruneInvalidEmptyElements = ( }); }; -export const sanitizeCustomHtml = (customHtml: string): string => { +export function sanitizeCustomHtml(customHtml: string): string { if (typeof window === 'undefined') { return sanitizeText(customHtml); } @@ -282,41 +307,44 @@ export const sanitizeCustomHtml = (customHtml: string): string => { const purify = DOMPurify(window); const allowedHtmlAttributes = [...permittedHtmlAttributes, INTERNAL_IMG_SRC_ATTR]; - purify.addHook('uponSanitizeAttribute', (currentNode, hookEvent) => { - const tagName = currentNode.tagName.toLowerCase(); - const attrName = hookEvent.attrName.toLowerCase(); + purify.addHook( + 'uponSanitizeAttribute', + (currentNode: Element, hookEvent: UponSanitizeAttributeHookEvent) => { + const tagName = currentNode.tagName.toLowerCase(); + const attrName = hookEvent.attrName.toLowerCase(); + + if (tagName === 'img' && attrName === INTERNAL_IMG_SRC_ATTR) { + if (!protectedSources.has(hookEvent.attrValue)) { + // eslint-disable-next-line no-param-reassign + hookEvent.keepAttr = false; + return; + } - if (tagName === 'img' && attrName === INTERNAL_IMG_SRC_ATTR) { - if (!protectedSources.has(hookEvent.attrValue)) { + // eslint-disable-next-line no-param-reassign + hookEvent.forceKeepAttr = true; + return; + } + + if (!tagAllowsAttribute(tagName, attrName)) { + // DOMPurify exposes attribute decisions by mutating the hook event. // eslint-disable-next-line no-param-reassign hookEvent.keepAttr = false; return; } - // eslint-disable-next-line no-param-reassign - hookEvent.forceKeepAttr = true; - return; - } + const validatedAttrValue = getValidatedAttributeValue(tagName, attrName, hookEvent.attrValue); + if (validatedAttrValue === undefined) { + // eslint-disable-next-line no-param-reassign + hookEvent.keepAttr = false; + return; + } - if (!tagAllowsAttribute(tagName, attrName)) { - // DOMPurify exposes attribute decisions by mutating the hook event. // eslint-disable-next-line no-param-reassign - hookEvent.keepAttr = false; - return; - } - - const validatedAttrValue = getValidatedAttributeValue(tagName, attrName, hookEvent.attrValue); - if (validatedAttrValue === undefined) { + hookEvent.attrValue = validatedAttrValue; // eslint-disable-next-line no-param-reassign - hookEvent.keepAttr = false; - return; + hookEvent.forceKeepAttr = true; } - - // eslint-disable-next-line no-param-reassign - hookEvent.attrValue = validatedAttrValue; - // eslint-disable-next-line no-param-reassign - hookEvent.forceKeepAttr = true; - }); + ); const sanitizedNode = purify.sanitize(protectedHtml, { ALLOWED_TAGS: [...permittedHtmlTags], @@ -342,4 +370,57 @@ export const sanitizeCustomHtml = (customHtml: string): string => { const container = document.createElement('div'); container.append(fragment); return restoreProtectedImageSources(container.innerHTML, protectedSources); -}; +} + +function appendPlainTextLineBreak(parts: string[]) { + const previous = parts.at(-1); + if (previous?.endsWith('\n')) return; + parts.push('\n'); +} + +function collectPlainTextFromNode(node: Node, parts: string[]): void { + if (node.nodeType === Node.TEXT_NODE) { + const text = node.textContent; + if (text) parts.push(text); + return; + } + + if (!(node instanceof Element)) return; + + const tagName = node.tagName.toLowerCase(); + + if (tagName === 'br') { + appendPlainTextLineBreak(parts); + return; + } + + if (tagName === 'li') { + parts.push('- '); + } + + [...node.childNodes].forEach((child) => collectPlainTextFromNode(child, parts)); + + if (textBlockTags.has(tagName)) { + appendPlainTextLineBreak(parts); + } +} + +export function extractPlainTextFromCustomHtml(customHtml: string): string { + if (typeof DOMParser === 'undefined') { + return customHtml; + } + + const parser = new DOMParser(); + const doc = parser.parseFromString(sanitizeCustomHtml(customHtml), 'text/html'); + const parts: string[] = []; + + [...doc.body.childNodes].forEach((child) => collectPlainTextFromNode(child, parts)); + + return parts + .join('') + .replace(/\r\n?/g, '\n') + .replace(/\u00a0/g, ' ') + .replace(/[ \t]+\n/g, '\n') + .replace(/\n{3,}/g, '\n\n') + .trim(); +} diff --git a/src/app/utils/sendFeedbackToUser.ts b/src/app/utils/sendFeedbackToUser.ts index c1fa7ab42..8f44b53b3 100644 --- a/src/app/utils/sendFeedbackToUser.ts +++ b/src/app/utils/sendFeedbackToUser.ts @@ -1,4 +1,4 @@ -import { MatrixEvent, Room } from 'matrix-js-sdk'; +import { MatrixEvent, type Room } from 'matrix-js-sdk'; export function sendFeedback(msg: string, room: Room, userId: string) { const localNotice = new MatrixEvent({ diff --git a/src/app/utils/settingsSync.ts b/src/app/utils/settingsSync.ts index 0f6b25888..e825d8c9b 100644 --- a/src/app/utils/settingsSync.ts +++ b/src/app/utils/settingsSync.ts @@ -1,4 +1,4 @@ -import { Settings } from '$state/settings'; +import { type Settings } from '$state/settings'; /** * Keys excluded from cross-device sync. diff --git a/src/app/utils/sort.ts b/src/app/utils/sort.ts index 2f99e757a..42e12801e 100644 --- a/src/app/utils/sort.ts +++ b/src/app/utils/sort.ts @@ -1,4 +1,4 @@ -import { MatrixClient } from '$types/matrix-sdk'; +import { type MatrixClient } from '$types/matrix-sdk'; export type SortFunc = (a: T, b: T) => number; diff --git a/src/app/utils/timeline.ts b/src/app/utils/timeline.ts index 0934e5b02..a6e2791dd 100644 --- a/src/app/utils/timeline.ts +++ b/src/app/utils/timeline.ts @@ -1,4 +1,4 @@ -import { Direction, EventTimeline, MatrixEvent, Room } from '$types/matrix-sdk'; +import { Direction, type EventTimeline, type MatrixEvent, type Room } from '$types/matrix-sdk'; import { roomHaveNotification, roomHaveUnread, reactionOrEditEvent } from '$utils/room'; export const PAGINATION_LIMIT = 60; diff --git a/src/client/initMatrix.ts b/src/client/initMatrix.ts index 093093ba2..a00aaf798 100644 --- a/src/client/initMatrix.ts +++ b/src/client/initMatrix.ts @@ -1,18 +1,18 @@ import { ClientEvent, createClient, - MatrixClient, + type MatrixClient, IndexedDBStore, IndexedDBCryptoStore, SyncState, - ISyncStateData, + type ISyncStateData, } from '$types/matrix-sdk'; import { clearNavToActivePathStore } from '$state/navToActivePath'; import { - Session, - Sessions, - SessionStoreName, + type Session, + type Sessions, + type SessionStoreName, getSessionStoreName, MATRIX_SESSIONS_KEY, } from '$state/sessions'; @@ -22,7 +22,11 @@ import { createDebugLogger } from '$utils/debugLogger'; import * as Sentry from '@sentry/react'; import { pushSessionToSW } from '../sw-session'; import { cryptoCallbacks } from './secretStorageKeys'; -import { SlidingSyncConfig, SlidingSyncDiagnostics, SlidingSyncManager } from './slidingSync'; +import { + type SlidingSyncConfig, + type SlidingSyncDiagnostics, + SlidingSyncManager, +} from './slidingSync'; const log = createLogger('initMatrix'); const debugLog = createDebugLogger('initMatrix'); @@ -226,7 +230,7 @@ export const clearMismatchedStores = async (): Promise => { allDbs.map(async ({ name }) => { if (!name) return; - const containsKnownUser = Array.from(knownUserIds).some((uid) => name.includes(uid)); + const containsKnownUser = [...knownUserIds].some((uid) => name.includes(uid)); const looksLikeUserDb = name.includes('@'); if (looksLikeUserDb && !containsKnownUser && !knownStoreNames.has(name)) { log.warn(`clearMismatchedStores: "${name}" has unknown user — deleting`); diff --git a/src/client/secretStorageKeys.d.ts b/src/client/secretStorageKeys.d.ts new file mode 100644 index 000000000..9b14490f6 --- /dev/null +++ b/src/client/secretStorageKeys.d.ts @@ -0,0 +1,8 @@ +import type { CryptoCallbacks } from '$types/matrix-sdk'; + +export function storePrivateKey(keyId: string, privateKey: Uint8Array): void; +export function clearSecretStorageKeys(): void; +export const cryptoCallbacks: Pick< + CryptoCallbacks, + 'getSecretStorageKey' | 'cacheSecretStorageKey' +>; diff --git a/src/client/slidingSync.test.ts b/src/client/slidingSync.test.ts index bd0643e5d..4e5515a82 100644 --- a/src/client/slidingSync.test.ts +++ b/src/client/slidingSync.test.ts @@ -15,6 +15,7 @@ * instant (matching Element Web's model). */ import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { type MatrixClient } from '$types/matrix-sdk'; import { SlidingSyncManager, type SlidingSyncConfig } from './slidingSync'; // ── vi.hoisted mocks ───────────────────────────────────────────────────────── @@ -75,7 +76,7 @@ function makeMockMx(overrides: Record = {}) { off: vi.fn(), removeListener: vi.fn(), ...overrides, - } as unknown as import('$types/matrix-sdk').MatrixClient; + } as unknown as MatrixClient; } function makeManager(mx: ReturnType): SlidingSyncManager { diff --git a/src/client/slidingSync.ts b/src/client/slidingSync.ts index 43fdf39ea..b46681143 100644 --- a/src/client/slidingSync.ts +++ b/src/client/slidingSync.ts @@ -1,14 +1,14 @@ /* eslint-disable max-classes-per-file */ import { ClientEvent, - Extension, + type Extension, ExtensionState, KnownMembership, - MatrixClient, - MSC3575List, - MSC3575RoomData, - MSC3575RoomSubscription, - MSC3575SlidingSyncResponse, + type MatrixClient, + type MSC3575List, + type MSC3575RoomData, + type MSC3575RoomSubscription, + type MSC3575SlidingSyncResponse, MSC3575_WILDCARD, RoomMemberEvent, SlidingSync, @@ -306,7 +306,7 @@ export class SlidingSyncManager { const defaultSubscription = buildEncryptedSubscription(roomTimelineLimit); const lists = buildLists(listPageSize, includeInviteList); - this.listKeys = Array.from(lists.keys()); + this.listKeys = [...lists.keys()]; this.slidingSync = new SlidingSync(proxyBaseUrl, lists, defaultSubscription, mx, pollTimeoutMs); // Register the presence extension so m.presence events from the server are fed diff --git a/src/index.tsx b/src/index.tsx index 4f2e57245..614ff3dbb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -21,7 +21,7 @@ import { pushSessionToSW } from './sw-session'; import { getFallbackSession, MATRIX_SESSIONS_KEY, - Sessions, + type Sessions, ACTIVE_SESSION_KEY, } from './app/state/sessions'; import { createLogger } from './app/utils/debug'; diff --git a/src/sw.ts b/src/sw.ts index bd09cd8d3..f3266b7a5 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -133,7 +133,7 @@ async function cleanupDeadClients() { const activeClients = await self.clients.matchAll(); const activeIds = new Set(activeClients.map((c) => c.id)); - Array.from(sessions.keys()).forEach((id) => { + [...sessions.keys()].forEach((id) => { if (!activeIds.has(id)) { sessions.delete(id); clientToResolve.delete(id); @@ -164,7 +164,7 @@ function setSession(clientId: string, accessToken: unknown, baseUrl: unknown, us } const resolveSession = clientToResolve.get(clientId); - if (resolveSession) { + if (typeof resolveSession === 'function') { resolveSession(sessions.get(clientId)); clientToResolve.delete(clientId); clientToSessionPromise.delete(clientId); @@ -370,7 +370,7 @@ async function requestDecryptionFromClient( const eventId = rawEvent.event_id as string; // Chain clients sequentially using reduce to avoid await-in-loop and for-of. - return Array.from(windowClients).reduce( + return [...windowClients].reduce( async (prevPromise, client) => { const prev = await prevPromise; if (prev?.success) return prev; @@ -562,7 +562,7 @@ self.addEventListener('message', (event: ExtendableMessageEvent) => { const { eventId } = data as { eventId?: string }; if (typeof eventId === 'string') { const resolve = decryptionPendingMap.get(eventId); - if (resolve) { + if (typeof resolve === 'function') { decryptionPendingMap.delete(eventId); resolve(data as DecryptionResult); } diff --git a/src/types/matrix/common.ts b/src/types/matrix/common.ts index e91993578..e0f3a064e 100644 --- a/src/types/matrix/common.ts +++ b/src/types/matrix/common.ts @@ -1,5 +1,5 @@ -import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; -import { MsgType } from '$types/matrix-sdk'; +import { type EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; +import { type MsgType } from '$types/matrix-sdk'; export const MATRIX_BLUR_HASH_PROPERTY_NAME = 'xyz.amorgan.blurhash'; export const MATRIX_SPOILER_PROPERTY_NAME = 'page.codeberg.everypizza.msc4193.spoiler'; diff --git a/src/types/matrix/room.ts b/src/types/matrix/room.ts index f851b9e42..eac297412 100644 --- a/src/types/matrix/room.ts +++ b/src/types/matrix/room.ts @@ -1,4 +1,4 @@ -import { IImageInfo } from './common'; +import { type IImageInfo } from './common'; export enum Membership { Invite = 'invite', diff --git a/tsconfig.json b/tsconfig.json index a18b41f91..a41f5c356 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,36 +1,13 @@ { - "compilerOptions": { - "sourceMap": true, - "jsx": "react-jsx", - "target": "ES2022", - "module": "ES2022", - "allowJs": true, - "strict": true, - "strictNullChecks": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "baseUrl": ".", - "paths": { - "$hooks/*": ["src/app/hooks/*"], - "$plugins/*": ["src/app/plugins/*"], - "$components/*": ["src/app/components/*"], - "$features/*": ["src/app/features/*"], - "$state/*": ["src/app/state/*"], - "$styles/*": ["src/app/styles/*"], - "$utils/*": ["src/app/utils/*"], - "$pages/*": ["src/app/pages/*"], - "$types/*": ["src/types/*"], - "$public/*": ["public/*"], - "$client/*": ["src/client/*"] + "extends": "./tsconfig.web.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.web.json" }, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "noEmit": true, - "resolveJsonModule": true, - "outDir": "dist", - "skipLibCheck": true, - "lib": ["ES2022", "DOM"] - }, - "exclude": ["node_modules", "dist"], - "include": ["src", "vite.config.ts", "vitest.config.ts"] + { + "path": "./tsconfig.node.json" + } + ] } diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 000000000..8278dad8d --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "lib": ["ESNext"], + "types": ["node"], + "allowJs": true, + "checkJs": true, + "strict": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "moduleResolution": "NodeNext", + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "erasableSyntaxOnly": true, + "verbatimModuleSyntax": true, + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./.tsbuildinfo/tsconfig.node.tsbuildinfo" + }, + "include": [ + "build.config.ts", + "eslint.config.js", + "vite.config.ts", + "vitest.config.ts", + "scripts/**/*.js" + ], + "exclude": ["node_modules", "dist", "coverage"] +} diff --git a/tsconfig.web.json b/tsconfig.web.json new file mode 100644 index 000000000..80d6c8ece --- /dev/null +++ b/tsconfig.web.json @@ -0,0 +1,51 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "allowJs": false, + "checkJs": false, + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "composite": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "tsBuildInfoFile": "./.tsbuildinfo/tsconfig.tsbuildinfo", + "baseUrl": ".", + "paths": { + "$hooks": ["src/app/hooks"], + "$hooks/*": ["src/app/hooks/*"], + "$plugins": ["src/app/plugins"], + "$plugins/*": ["src/app/plugins/*"], + "$components": ["src/app/components"], + "$components/*": ["src/app/components/*"], + "$features": ["src/app/features"], + "$features/*": ["src/app/features/*"], + "$state": ["src/app/state"], + "$state/*": ["src/app/state/*"], + "$styles": ["src/app/styles"], + "$styles/*": ["src/app/styles/*"], + "$utils": ["src/app/utils"], + "$utils/*": ["src/app/utils/*"], + "$pages": ["src/app/pages"], + "$pages/*": ["src/app/pages/*"], + "$generated": ["src/app/generated"], + "$generated/*": ["src/app/generated/*"], + "$types": ["src/types"], + "$types/*": ["src/types/*"], + "$public": ["public"], + "$public/*": ["public/*"], + "$client": ["src/client"], + "$client/*": ["src/client/*"] + } + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.js", "src/**/*.jsx"], + "exclude": ["node_modules", "dist", "coverage"] +} diff --git a/vite.config.ts b/vite.config.ts index a130df507..88815ee98 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,14 +1,16 @@ import { defineConfig } from 'vite'; import type { ViteDevServer, PluginOption } from 'vite'; import { execSync } from 'child_process'; +import type { RollupInjectOptions } from '@rollup/plugin-inject'; import react from '@vitejs/plugin-react'; import svgr from 'vite-plugin-svgr'; import { wasm } from '@rollup/plugin-wasm'; import { viteStaticCopy } from 'vite-plugin-static-copy'; import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'; -import inject from '@rollup/plugin-inject'; -import topLevelAwait from 'vite-plugin-top-level-await'; +import * as injectModule from '@rollup/plugin-inject'; +import * as topLevelAwaitModule from 'vite-plugin-top-level-await'; +import type { Options as TopLevelAwaitOptions } from 'vite-plugin-top-level-await'; import { VitePWA } from 'vite-plugin-pwa'; import { compression, defineAlgorithm } from 'vite-plugin-compression2'; import { constants as zlibConstants } from 'zlib'; @@ -17,7 +19,12 @@ import path from 'path'; import { cloudflare } from '@cloudflare/vite-plugin'; import { createRequire } from 'module'; import { sentryVitePlugin } from '@sentry/vite-plugin'; -import buildConfig from './build.config'; +import buildConfig from './build.config.ts'; + +const inject = injectModule.default as unknown as (options?: RollupInjectOptions) => PluginOption; +const topLevelAwait = topLevelAwaitModule.default as unknown as ( + options?: TopLevelAwaitOptions +) => PluginOption; const packageJson = JSON.parse( fs.readFileSync(path.resolve(__dirname, 'package.json'), 'utf8') @@ -133,19 +140,7 @@ export default defineConfig(({ command }) => ({ IS_RELEASE_TAG: JSON.stringify(isReleaseTag), }, resolve: { - alias: { - $hooks: path.resolve(__dirname, 'src/app/hooks'), - $plugins: path.resolve(__dirname, 'src/app/plugins'), - $components: path.resolve(__dirname, 'src/app/components'), - $features: path.resolve(__dirname, 'src/app/features'), - $state: path.resolve(__dirname, 'src/app/state'), - $styles: path.resolve(__dirname, 'src/app/styles'), - $utils: path.resolve(__dirname, 'src/app/utils'), - $pages: path.resolve(__dirname, 'src/app/pages'), - $types: path.resolve(__dirname, 'src/types'), - $public: path.resolve(__dirname, 'public'), - $client: path.resolve(__dirname, 'src/client'), - }, + tsconfigPaths: true, }, server: { port: 8080, @@ -162,7 +157,7 @@ export default defineConfig(({ command }) => ({ // The export name of top-level await promise for each chunk module promiseExportName: '__tla', // The function to generate import names of top-level await promise in each chunk module - promiseImportName: (i) => `__tla_${i}`, + promiseImportName: (i: number) => `__tla_${i}`, }), viteStaticCopy(copyFiles), vanillaExtractPlugin({ identifiers: 'debug' }), @@ -231,7 +226,7 @@ export default defineConfig(({ command }) => ({ '@vanilla-extract/recipes/createRuntimeFn', ], needsInterop: ['matrix-widget-api'], - esbuildOptions: { + rolldownOptions: { define: { global: 'globalThis', }, @@ -249,7 +244,7 @@ export default defineConfig(({ command }) => ({ sourcemap: true, copyPublicDir: false, rollupOptions: { - plugins: [inject({ Buffer: ['buffer', 'Buffer'] }) as PluginOption], + plugins: [inject({ Buffer: ['buffer', 'Buffer'] })], }, }, })); diff --git a/vitest.config.ts b/vitest.config.ts index fedea1151..497c539f9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,26 +1,13 @@ import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; -import path from 'path'; // Standalone Vitest config — intentionally excludes Cloudflare, PWA, compression, // and other production-only Vite plugins that don't apply to unit tests. export default defineConfig({ plugins: [react(), vanillaExtractPlugin()], resolve: { - alias: { - $hooks: path.resolve(__dirname, 'src/app/hooks'), - $plugins: path.resolve(__dirname, 'src/app/plugins'), - $components: path.resolve(__dirname, 'src/app/components'), - $features: path.resolve(__dirname, 'src/app/features'), - $state: path.resolve(__dirname, 'src/app/state'), - $styles: path.resolve(__dirname, 'src/app/styles'), - $utils: path.resolve(__dirname, 'src/app/utils'), - $pages: path.resolve(__dirname, 'src/app/pages'), - $types: path.resolve(__dirname, 'src/types'), - $public: path.resolve(__dirname, 'public'), - $client: path.resolve(__dirname, 'src/client'), - }, + tsconfigPaths: true, }, define: { APP_VERSION: JSON.stringify('test'), @@ -31,7 +18,7 @@ export default defineConfig({ environment: 'jsdom', globals: true, setupFiles: ['./src/test/setup.ts'], - include: ['src/**/*.{test,spec}.{ts,tsx}'], + include: ['src/**/*.{test,spec}.{ts,tsx}', 'scripts/**/*.{test,spec}.{js,ts}'], coverage: { provider: 'v8', reporter: ['text', 'html', 'lcov'], @@ -46,6 +33,14 @@ export default defineConfig({ 'src/**/*.test.{ts,tsx}', 'src/**/*.spec.{ts,tsx}', ], + // Baseline locked at current coverage. Raise these thresholds as test + // coverage improves, never lower them. + thresholds: { + statements: 1.5, + branches: 1, + functions: 1.5, + lines: 1.5, + }, }, }, });