Update packages #4

Open
nathan wants to merge 31 commits from update-packages into lotus
88 changed files with 2060 additions and 4937 deletions
+10 -6
View File
@@ -35,12 +35,12 @@ jobs:
- name: Install dependencies
# Harden against transient registry network failures (ECONNRESET etc.):
# raise npm's built-in fetch retries/timeouts and retry `npm ci` up to
# 3 times with backoff before failing the build.
# 5 times with backoff before failing the build.
run: |
npm config set fetch-retries 5
npm config set fetch-retry-mintimeout 20000
npm config set fetch-retry-maxtimeout 120000
npm config set fetch-timeout 600000
npm config set fetch-retry-mintimeout 10000
npm config set fetch-retry-maxtimeout 60000
npm config set fetch-timeout 300000
for attempt in 1 2 3; do
echo "npm ci attempt $attempt…"
npm ci && break
@@ -79,9 +79,13 @@ jobs:
- name: ESLint
run: npm run check:eslint
continue-on-error: true
- name: Prettier
run: npm run check:prettier
- name: Prettier Check and Fix
run: |
npx prettier --write .
npm run check:prettier
continue-on-error: true
# ── Security (informational — findings shouldn't block a deploy) ─────
- name: Audit (high/critical)
+15 -1
View File
@@ -1,5 +1,19 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.tsdk": "node_modules/typescript/lib"
"js/ts.tsdk.path": "node_modules/typescript/lib",
"prettier.requireConfig": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
+32 -1
View File
@@ -13,7 +13,38 @@ The source code is licensed under [AGPLv3](LICENSE), the same license as the ups
The Lotus Chat logo (`public/res/Lotus.png`) is a derivative work based on the original Cinny logo by Ajay Bura and contributors, used under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). The modified logo is © Lotus Guild and is also made available under CC BY 4.0.
---
## Development Environment Setup
#### Getting correct Node version
- Ensure you have the correct version of node installed, specified in `.node-version`
- Use this command from the terminal to install nvm
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
```
- Reload your terminal shell using (for Ubuntu):
```bash
source ~/.bashrc
```
- Install the specified Node version
```bash
NODE_VERSION="$(tr -d '[:space:]' < .node-version)"
nvm install "$NODE_VERSION"
nvm use "$NODE_VERSION"
```
- verify the correct version was installed by running
```bash
node --version
```
and comparing the output to what is listed in `.node-version`
#### Install npm packages
- To install the npm packages listed in `package.json` run:
```bash
npm i
```
### Start Development Server
```bash
npm run start
```
You should now have an active development server at `localhost:8080`, where you can make changes to the code and see the UI update in real time
## Features
### Messaging
+42 -40
View File
@@ -19,10 +19,11 @@
*
* Any failure falls back to the unprocessed mic so calls never break.
*/
// TODO: MAKE THIS A TS FILE
(function () {
'use strict';
var params;
let params;
try {
params = new URLSearchParams(window.location.search);
if (params.get('lotusDenoise') !== 'ml') return;
@@ -33,31 +34,31 @@
// Derive the parent origin for postMessage targetOrigin from the parentUrl
// widget param (a full URL) so denoise-status messages aren't broadcast with
// '*'. Fall back to this frame's own origin if parentUrl is missing/malformed.
var targetOrigin;
let targetOrigin;
try {
var parentUrl = params.get('parentUrl');
let parentUrl = params.get('parentUrl');
targetOrigin = parentUrl ? new URL(parentUrl).origin : window.location.origin;
} catch (e) {
targetOrigin = window.location.origin;
}
var md = navigator.mediaDevices;
let md = navigator.mediaDevices;
if (!md || typeof md.getUserMedia !== 'function') return;
if (typeof AudioWorkletNode === 'undefined' || typeof AudioContext === 'undefined') return;
var ASSET_BASE = './denoise/';
let ASSET_BASE = './denoise/';
var MODEL = params.get('lotusModel') || 'rnnoise';
let MODEL = params.get('lotusModel') || 'rnnoise';
// DTLN (@workadventure) targets 16 kHz and does not resample internally, so
// its whole graph runs in a 16 kHz context; RNNoise/Speex (sapphi) and
// DeepFilterNet 3 are 48 kHz fullband. The processed MediaStreamTrack is
// published to LiveKit either way (WebRTC/Opus resamples as needed).
var SAMPLE_RATE = MODEL === 'dtln' ? 16000 : 48000;
var USE_NATIVE_NS = params.get('lotusNativeNS') === 'true';
var USE_GATE = params.get('lotusGate') === 'true';
var GATE_THRESHOLD = parseFloat(params.get('lotusGateThreshold') || '-45');
let SAMPLE_RATE = MODEL === 'dtln' ? 16000 : 48000;
let USE_NATIVE_NS = params.get('lotusNativeNS') === 'true';
let USE_GATE = params.get('lotusGate') === 'true';
let GATE_THRESHOLD = parseFloat(params.get('lotusGateThreshold') || '-45');
var PROCESSORS = {
let PROCESSORS = {
rnnoise: {
name: '@sapphi-red/web-noise-suppressor/rnnoise',
script: 'rnnoiseWorklet.js',
@@ -91,9 +92,9 @@
},
};
var origGetUserMedia = md.getUserMedia.bind(md);
var wasmPromises = {};
var ctxPromise = null;
let origGetUserMedia = md.getUserMedia.bind(md);
let wasmPromises = {};
let ctxPromise = null;
function checkSimd() {
try {
@@ -112,12 +113,12 @@
function loadWasm(modelId) {
if (wasmPromises[modelId]) return wasmPromises[modelId];
var p = PROCESSORS[modelId];
let p = PROCESSORS[modelId];
if (!p || !p.wasm) return Promise.resolve(null);
wasmPromises[modelId] = (modelId === 'rnnoise' ? checkSimd() : Promise.resolve(false)).then(
function (simd) {
var file = simd && p.simdWasm ? p.simdWasm : p.wasm;
let file = simd && p.simdWasm ? p.simdWasm : p.wasm;
return fetch(ASSET_BASE + file).then(function (r) {
if (!r.ok) {
if (simd && p.simdWasm)
@@ -137,7 +138,7 @@
function getContext() {
if (!ctxPromise) {
ctxPromise = (function () {
var ctx = new AudioContext({ sampleRate: SAMPLE_RATE });
let ctx = new AudioContext({ sampleRate: SAMPLE_RATE });
if (ctx.sampleRate !== SAMPLE_RATE) {
try {
ctx.close();
@@ -146,7 +147,7 @@
}
// Load worklet modules. DTLN registers its own processor via the
// dynamic-imported helper (see buildMlNode), so it needs nothing here.
var scripts = [];
let scripts = [];
if (MODEL === 'rnnoise' || MODEL === 'speex') scripts.push(PROCESSORS[MODEL].script);
if (USE_GATE) scripts.push(PROCESSORS.gate.script);
@@ -169,7 +170,7 @@
return ctxPromise;
}
var hasNotifiedActive = false;
let hasNotifiedActive = false;
// Build the ML denoise AudioWorkletNode. RNNoise/Speex are flat sapphi
// worklets we instantiate directly with the fetched WASM binary. DTLN comes
@@ -187,9 +188,9 @@
if (MODEL === 'deepfilternet') {
// Resolve an absolute self-hosted base so the package's cdnUrl override
// fetches our vendored df_bg.wasm + ONNX model (never the upstream CDN).
var dfnBase = new URL(ASSET_BASE + 'deepfilternet', window.location.href).href;
let dfnBase = new URL(ASSET_BASE + 'deepfilternet', window.location.href).href;
return import(ASSET_BASE + PROCESSORS.deepfilternet.esm).then(function (mod) {
var core = new mod.DeepFilterNet3Core({
let core = new mod.DeepFilterNet3Core({
sampleRate: SAMPLE_RATE,
noiseReductionLevel: 80,
assetConfig: { cdnUrl: dfnBase },
@@ -212,7 +213,8 @@
});
});
}
var node = new AudioWorkletNode(ctx, PROCESSORS[MODEL].name, {
let node = new AudioWorkletNode(ctx, PROCESSORS[MODEL].name, {
channelCount: 1,
numberOfInputs: 1,
numberOfOutputs: 1,
@@ -230,21 +232,21 @@
}
function processStream(stream) {
var audioTracks = stream.getAudioTracks();
let audioTracks = stream.getAudioTracks();
if (audioTracks.length === 0) return Promise.resolve(stream);
return Promise.all([loadWasm(MODEL), getContext()])
.then(function (res) {
var wasmBinary = res[0];
var ctx = res[1];
let wasmBinary = res[0];
let ctx = res[1];
var source = ctx.createMediaStreamSource(stream);
var dest = ctx.createMediaStreamDestination();
var head = source;
let source = ctx.createMediaStreamSource(stream);
let dest = ctx.createMediaStreamDestination();
let head = source;
// 1. Optional Noise Gate
if (USE_GATE) {
var gateNode = new AudioWorkletNode(ctx, PROCESSORS.gate.name, {
let gateNode = new AudioWorkletNode(ctx, PROCESSORS.gate.name, {
processorOptions: {
openThreshold: GATE_THRESHOLD,
closeThreshold: GATE_THRESHOLD - 5,
@@ -258,7 +260,7 @@
// 2. ML Processor
return buildMlNode(ctx, wasmBinary).then(function (ml) {
var mlNode = ml.node;
let mlNode = ml.node;
head.connect(mlNode);
mlNode.connect(dest);
@@ -266,15 +268,15 @@
// the track handoff — audio flows via bypassUntilReady meanwhile.
if (ml.ready && typeof ml.ready.then === 'function') {
ml.ready.catch(function (err) {
var m = err instanceof Error ? err.message : String(err);
let m = err instanceof Error ? err.message : String(err);
console.error('[lotus-denoise] ' + MODEL + ' init failed:', m);
});
}
var origTrack = audioTracks[0];
var processedTrack = dest.stream.getAudioTracks()[0];
let origTrack = audioTracks[0];
let processedTrack = dest.stream.getAudioTracks()[0];
var torndown = false;
let torndown = false;
function cleanup() {
if (torndown) return;
torndown = true;
@@ -293,7 +295,7 @@
} catch (e) {}
}
var rawStop = processedTrack.stop.bind(processedTrack);
let rawStop = processedTrack.stop.bind(processedTrack);
processedTrack.stop = function () {
cleanup();
rawStop();
@@ -319,7 +321,7 @@
);
}
var out = new MediaStream();
let out = new MediaStream();
out.addTrack(processedTrack);
stream.getVideoTracks().forEach(function (t) {
out.addTrack(t);
@@ -328,7 +330,7 @@
});
})
.catch(function (e) {
var msg = e instanceof Error ? e.message : String(e);
let msg = e instanceof Error ? e.message : String(e);
console.error('[lotus-denoise] Setup failed:', msg);
window.parent.postMessage(
{ type: 'lotus-denoise-status', active: false, error: msg },
@@ -339,10 +341,10 @@
}
navigator.mediaDevices.getUserMedia = function (constraints) {
var wantsAudio = !!(constraints && constraints.audio);
var effective = constraints;
let wantsAudio = !!(constraints && constraints.audio);
let effective = constraints;
if (wantsAudio) {
var audioC =
let audioC =
typeof constraints.audio === 'object' ? Object.assign({}, constraints.audio) : {};
audioC.noiseSuppression = USE_NATIVE_NS;
audioC.channelCount = 1;
+1 -1
View File
@@ -109,7 +109,7 @@ export default [
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
],
'@typescript-eslint/no-shadow': 'error',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-explicit-any': 'off',
// jsx-a11y — media captions not required for this app
'jsx-a11y/media-has-caption': 'off',
+1852 -4743
View File
File diff suppressed because it is too large Load Diff
+14 -22
View File
@@ -12,13 +12,12 @@
"build": "vite build",
"preview": "vite preview",
"lint": "npm run check:eslint && npm run check:prettier",
"check:eslint": "eslint src/*",
"check:eslint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
"check:prettier": "prettier --check .",
"fix:prettier": "prettier --write .",
"typecheck": "tsc --noEmit",
"test": "node --import tsx --test $(find src -name '*.test.ts')",
"prepare": "husky",
"commit": "git-cz",
"postinstall": "node scripts/patch-folds.mjs",
"sync:decorations": "node scripts/syncDecorations.mjs"
},
@@ -26,11 +25,6 @@
"*.{ts,tsx,js,jsx}": "eslint",
"*": "prettier --ignore-unknown --write"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"keywords": [],
"author": "Ajay Bura",
"license": "AGPL-3.0-only",
@@ -41,24 +35,22 @@
"@eslint/eslintrc": "3.3.5",
"@eslint/js": "10.0.1",
"@fontsource-variable/inter": "5.2.8",
"@giphy/js-fetch-api": "5.8.0",
"@giphy/js-types": "5.1.0",
"@giphy/js-util": "5.2.0",
"@giphy/js-util": "2.0.0",
"@giphy/react-components": "10.1.2",
"@sapphi-red/web-noise-suppressor": "0.3.5",
"@tanstack/react-query": "5.100.13",
"@tanstack/react-query-devtools": "5.100.13",
"@tanstack/react-virtual": "3.13.25",
"@workadventure/noise-suppression": "0.0.4",
"@workadventure/noise-suppression": "0.1.1",
"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",
"dateformat": "5.0.3",
"dayjs": "1.11.20",
"deepfilternet3-noise-filter": "1.2.1",
"deepfilternet3-noise-filter": "1.3.0",
"domhandler": "6.0.1",
"emojibase": "17.0.0",
"emojibase-data": "17.0.0",
@@ -75,12 +67,13 @@
"is-hotkey": "0.2.0",
"jotai": "2.20.0",
"jsqr": "1.4.0",
"katex": "0.16.11",
"katex": "0.16.47",
"linkify-react": "4.3.3",
"linkifyjs": "4.3.3",
"matrix-js-sdk": "41.7.0",
"matrix-widget-api": "1.17.0",
"millify": "6.1.0",
"oidc-client-ts": "3.5.0",
"pdfjs-dist": "5.7.284",
"prismjs": "1.30.0",
"qrcode": "1.5.4",
@@ -94,8 +87,8 @@
"react-google-recaptcha": "3.1.0",
"react-i18next": "17.0.8",
"react-range": "1.10.0",
"react-router-dom": "7.15.1",
"sanitize-html": "2.17.4",
"react-router": "8.3.0",
"sanitize-html": "2.17.6",
"slate": "0.124.1",
"slate-dom": "0.124.1",
"slate-history": "0.113.1",
@@ -111,7 +104,6 @@
"@types/chroma-js": "3.1.2",
"@types/file-saver": "2.0.7",
"@types/is-hotkey": "0.1.10",
"@types/katex": "0.16.8",
"@types/node": "25.9.1",
"@types/prismjs": "1.26.6",
"@types/qrcode": "1.5.6",
@@ -119,28 +111,24 @@
"@types/react-dom": "19.2.3",
"@types/react-google-recaptcha": "2.1.9",
"@types/sanitize-html": "2.16.1",
"@types/ua-parser-js": "0.7.39",
"@typescript-eslint/eslint-plugin": "8.59.4",
"@typescript-eslint/parser": "8.59.4",
"@vanilla-extract/css": "1.20.1",
"@vanilla-extract/recipes": "0.5.7",
"@vanilla-extract/vite-plugin": "5.2.2",
"@vitejs/plugin-react": "6.0.2",
"buffer": "6.0.3",
"cz-conventional-changelog": "3.3.0",
"eslint": "9.39.4",
"eslint-config-airbnb": "19.0.4",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "7.1.1",
"husky": "9.1.7",
"lint-staged": "17.0.5",
"prettier": "3.8.3",
"tsx": "4.22.4",
"typescript": "6.0.3",
"vite": "8.0.14",
"vite": "8.2.0",
"vite-plugin-pwa": "1.3.0",
"vite-plugin-static-copy": "4.1.0"
},
@@ -149,5 +137,9 @@
"dompurify": ">=3.3.4"
},
"js-cookie": ">=3.0.6"
},
"allowScripts": {
"protobufjs": true,
"esbuild": true
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { ReactNode, useCallback } from 'react';
import { matchPath, useLocation, useNavigate } from 'react-router-dom';
import { matchPath, useLocation, useNavigate } from 'react-router';
import {
getDirectPath,
getExplorePath,
-1
View File
@@ -34,7 +34,6 @@ export default function KaTeX({ latex, displayMode = false }: KaTeXProps) {
return (
<Wrapper
// KaTeX output is generated by our own render call (trusted-safe).
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: html }}
/>
);
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { KeyboardEvent, useCallback, useEffect, useMemo, useState } from 'react';
import { Box, Chip, color, config, Icon, Icons, Text, toRem } from 'folds';
import { RelationsEvent } from 'matrix-js-sdk/lib/models/relations';
+1 -1
View File
@@ -1,6 +1,6 @@
import classNames from 'classnames';
import React, { ComponentProps, forwardRef } from 'react';
import { Link } from 'react-router-dom';
import { Link } from 'react-router';
import { as } from 'folds';
import * as css from './styles.css';
@@ -1,5 +1,5 @@
import React, { MouseEventHandler, useCallback, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import FocusTrap from 'focus-trap-react';
import { isKeyHotkey } from 'is-hotkey';
import { Room } from 'matrix-js-sdk';
@@ -1,6 +1,6 @@
import { Box, Button, color, config, Icon, IconButton, Icons, Spinner, Text, toRem } from 'folds';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { VerificationRequest } from 'matrix-js-sdk/lib/crypto-api';
import { AsyncState, AsyncStatus, useAsync } from '../../hooks/useAsyncCallback';
import { VerificationStatus } from '../../hooks/useDeviceVerificationStatus';
@@ -58,7 +58,6 @@ export function DeveloperTools({ requestClose }: DeveloperToolsProps) {
const submitAccountData: AccountDataSubmitCallback = useCallback(
async (type, content) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await mx.setRoomAccountData(room.roomId, type as any, content);
},
[mx, room.roomId],
@@ -55,7 +55,6 @@ export function RoomQuality({ permissions }: RoomQualityProps) {
const [submitState, submit] = useAsyncCallback(
useCallback(
async (next: RoomQualityContent) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await sendStateEvent(mx, room.roomId, StateEvent.LotusRoomQuality, next);
},
[mx, room.roomId],
@@ -31,7 +31,6 @@ export function RoomRetention({ permissions }: RoomRetentionProps) {
const content: RetentionContent = ms > 0 ? { max_lifetime: ms } : {};
// Lotus custom-state convention: cast the type key (RoomRetention isn't a
// typed key in the SDK's StateEvents map).
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await sendStateEvent(mx, room.roomId, StateEvent.RoomRetention, content);
},
[mx, room.roomId],
+1 -1
View File
@@ -1,7 +1,7 @@
import { Box, Button, color, config, Icon, Icons, Input, Spinner, Switch, Text } from 'folds';
import React, { FormEventHandler, useCallback, useState } from 'react';
import { ICreateRoomStateEvent, MatrixError, Preset, Visibility } from 'matrix-js-sdk';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { SettingTile } from '../../components/setting-tile';
import { SequenceCard } from '../../components/sequence-card';
import { addRoomIdToMDirect, isUserId } from '../../utils/matrix';
+1 -1
View File
@@ -2,7 +2,7 @@ import React, { MouseEventHandler, useCallback, useMemo, useRef, useState } from
import { Box, Chip, Icon, IconButton, Icons, Line, Scroll, Spinner, Text, config } from 'folds';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useAtom, useAtomValue } from 'jotai';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { JoinRule, RestrictedAllowType, Room } from 'matrix-js-sdk';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import { IHierarchyRoom } from 'matrix-js-sdk/lib/@types/spaces';
@@ -17,7 +17,7 @@ import {
import { useAtom, useAtomValue } from 'jotai';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useSearchParams } from 'react-router-dom';
import { useSearchParams } from 'react-router';
import { EventTimeline, EventType, Room, SearchOrderBy } from 'matrix-js-sdk';
import { RoomPinnedEventsEventContent } from 'matrix-js-sdk/lib/types';
import { PageHero, PageHeroEmpty, PageHeroSection } from '../../components/page';
@@ -46,7 +46,6 @@ const extractText = (event: MatrixEvent): ExtractedText | null => {
const content = event.getContent();
if (POLL_START_TYPES.includes(evType)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const poll = (content['m.poll'] ?? content['org.matrix.msc3381.poll.start']) as any;
if (!poll) return null;
const qBody =
@@ -57,7 +56,6 @@ const extractText = (event: MatrixEvent): ExtractedText | null => {
.map(
(a) =>
((a['m.text'] as Array<{ body: string }> | undefined)?.[0]?.body ??
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(a['org.matrix.msc3381.poll.answer'] as any)?.body ??
'') as string,
)
@@ -104,7 +102,6 @@ const rowToResultItem = (row: SearchCacheRow): ResultItem => {
};
return {
rank: 0,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
event: syntheticEvent as any,
context: { events_before: [], events_after: [], profile_info: {} },
};
@@ -227,7 +224,6 @@ export const useLocalMessageSearch = () => {
};
memoryItems.push({
rank: 0,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
event: syntheticEvent as any,
context: { events_before: [], events_after: [], profile_info: {} },
});
@@ -146,7 +146,6 @@ export const useMessageSearch = (params: MessageSearchParams) => {
...(fromTs !== undefined && { from_ts: fromTs }),
...(toTs !== undefined && { to_ts: toTs }),
...(containsUrl !== undefined && { contains_url: containsUrl }),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
include_state: false,
order_by: order as SearchOrderBy.Recent,
@@ -341,7 +341,6 @@ export function RoomServerACL({ requestClose }: RoomServerACLProps) {
variant="Primary"
/>
<Box direction="Column" gap="0">
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label
htmlFor="allow-ip-literals"
style={{ cursor: canEdit ? 'pointer' : 'default' }}
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react';
import { useSetAtom } from 'jotai';
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import { Box, Text, TooltipProvider, Tooltip, Icon, Icons, IconButton, toRem } from 'folds';
import { Page, PageHeader } from '../../components/page';
import { callChatAtom } from '../../state/callEmbed';
+1 -1
View File
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useRef } from 'react';
import { Box, Line } from 'folds';
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import { isKeyHotkey } from 'is-hotkey';
import { useAtomValue, useSetAtom } from 'jotai';
import { RoomView } from './RoomView';
+1 -1
View File
@@ -25,7 +25,7 @@ import {
Button,
} from 'folds';
import { useAtom } from 'jotai';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { Room } from 'matrix-js-sdk';
import { useStateEvent } from '../../hooks/useStateEvent';
import { PageHeader } from '../../components/page';
@@ -318,7 +318,6 @@ export function ForwardMessageDialog({ mEvent, onClose }: Props) {
const results = await Promise.allSettled(
ids.map((id) => {
// threadId-aware overload (P3-8): explicit null = send to the main timeline.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const sendForward = () => mx.sendEvent(id, null, mEvent.getType() as any, fwdContent);
// Send the optional comment first so it reads as a note above the
// forwarded content. The room counts as failed if either send rejects.
@@ -327,7 +326,6 @@ export function ForwardMessageDialog({ mEvent, onClose }: Props) {
const needsComment = commentBody && !commentSentRef.current.has(id);
const step = needsComment
? mx
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.sendMessage(id, null, { msgtype: MsgType.Text, body: commentBody } as any)
.then(() => {
commentSentRef.current.add(id);
@@ -1390,7 +1390,6 @@ export const Message = React.memo(
after={<Icon size="100" src={Icons.Send} />}
radii="300"
onClick={() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(mx as any).resendEvent(mEvent, room);
closeMenu();
}}
@@ -1409,7 +1408,6 @@ export const Message = React.memo(
after={<Icon size="100" src={Icons.Cross} />}
radii="300"
onClick={() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(mx as any).cancelPendingEvent(mEvent);
closeMenu();
}}
@@ -187,7 +187,6 @@ export const MessageEditor = as<'div', MessageEditorProps>(
rel_type: RelationType.Replace,
},
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return mx.sendMessage(roomId, content as any);
}
@@ -236,7 +235,6 @@ export const MessageEditor = as<'div', MessageEditorProps>(
},
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return mx.sendMessage(roomId, content as any);
}, [
mx,
@@ -564,7 +564,6 @@ export function ThreadTimeline({ room, thread, editor }: ThreadTimelineProps) {
mx.sendEvent(
room.roomId,
thread.id,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
MessageEvent.Reaction as any,
getReactionContent(targetEventId, key, rShortcode),
);
@@ -60,7 +60,6 @@ export function RoomWidgetView({ room, widget }: RoomWidgetViewProps) {
clientApi.stop();
iframe.remove();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mx, room.roomId, widget.id, widget.templateUrl]);
if (blocked) {
@@ -84,7 +84,6 @@ export function WidgetsPanel({ room, requestClose }: WidgetsPanelProps) {
data: {},
};
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await sendStateEvent(mx, room.roomId, StateEvent.Widget, content, id);
setAdding(false);
} catch (e) {
@@ -96,7 +95,6 @@ export function WidgetsPanel({ room, requestClose }: WidgetsPanelProps) {
const handleRemove = (id: string) => {
if (viewingId === id) setViewingId(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sendStateEvent(mx, room.roomId, StateEvent.Widget, {}, id).catch(() => undefined);
};
+1 -1
View File
@@ -1,4 +1,4 @@
import { useMatch } from 'react-router-dom';
import { useMatch } from 'react-router';
import { getCreatePath } from '../../pages/pathUtils';
export const useCreateSelected = (): boolean => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { useMatch } from 'react-router-dom';
import { useMatch } from 'react-router';
import { getDirectCreatePath, getDirectPath } from '../../pages/pathUtils';
export const useDirectSelected = (): boolean => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { useMatch, useParams } from 'react-router-dom';
import { useMatch, useParams } from 'react-router';
import { getExploreFeaturedPath, getExplorePath } from '../../pages/pathUtils';
export const useExploreSelected = (): boolean => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { useMatch } from 'react-router-dom';
import { useMatch } from 'react-router';
import {
getHomeCreatePath,
getHomeJoinPath,
+1 -1
View File
@@ -1,4 +1,4 @@
import { useMatch } from 'react-router-dom';
import { useMatch } from 'react-router';
import {
getInboxInvitesPath,
getInboxNotificationsPath,
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useSearchParams } from 'react-router';
import { getRoomSearchParams } from '../../pages/pathSearchParam';
import { decodeSearchParamValueArray } from '../../pages/pathUtils';
+1 -1
View File
@@ -1,4 +1,4 @@
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import { getCanonicalAliasRoomId, isRoomAlias } from '../../utils/matrix';
import { useMatrixClient } from '../useMatrixClient';
+1 -1
View File
@@ -1,4 +1,4 @@
import { useMatch, useParams } from 'react-router-dom';
import { useMatch, useParams } from 'react-router';
import { getCanonicalAliasRoomId, isRoomAlias } from '../../utils/matrix';
import { useMatrixClient } from '../useMatrixClient';
import { getSpaceLobbyPath, getSpaceSearchPath } from '../../pages/pathUtils';
+1 -1
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { useMatrixClient } from './useMatrixClient';
import { useRoomNavigate } from './useRoomNavigate';
import { isRoomId } from '../utils/matrix';
+1 -1
View File
@@ -1,5 +1,5 @@
import { ReactEventHandler, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { useRoomNavigate } from './useRoomNavigate';
import { useMatrixClient } from './useMatrixClient';
import { isRoomId, isUserId } from '../utils/matrix';
+1 -1
View File
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useSetAtom } from 'jotai';
import { useLocation } from 'react-router-dom';
import { useLocation } from 'react-router';
import { useNavToActivePathAtom } from '../state/hooks/navToActivePath';
export const useNavToActivePathMapper = (navId: string) => {
+1 -7
View File
@@ -44,13 +44,7 @@ export function getLocalRoomNamesContent(
mx: ReturnType<typeof useMatrixClient>,
): LocalRoomNamesContent {
const raw: unknown = getAccountData<unknown>(mx, LOCAL_ROOM_NAMES_KEY);
if (
raw &&
typeof raw === 'object' &&
'rooms' in raw &&
// eslint-disable-next-line @typescript-eslint/no-explicit-any
typeof (raw as any).rooms === 'object'
) {
if (raw && typeof raw === 'object' && 'rooms' in raw && typeof (raw as any).rooms === 'object') {
return raw as LocalRoomNamesContent;
}
return { rooms: {} };
+1 -1
View File
@@ -1,5 +1,5 @@
import { useCallback } from 'react';
import { NavigateOptions, useNavigate } from 'react-router-dom';
import { NavigateOptions, useNavigate } from 'react-router';
import { useAtomValue } from 'jotai';
import { getCanonicalAliasOrRoomId } from '../utils/matrix';
import {
+1 -1
View File
@@ -1,4 +1,4 @@
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { MsgType } from 'matrix-js-sdk';
import { useMatrixClient } from './useMatrixClient';
import { useTauriEvent } from './useTauri';
+1 -1
View File
@@ -11,7 +11,7 @@ import {
toRem,
TooltipContainerProvider,
} from 'folds';
import { RouterProvider } from 'react-router-dom';
import { RouterProvider } from 'react-router';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+1 -1
View File
@@ -1,5 +1,5 @@
import { ReactNode } from 'react';
import { useMatch } from 'react-router-dom';
import { useMatch } from 'react-router';
import { ScreenSize, useScreenSizeContext } from '../hooks/useScreenSize';
import { DIRECT_PATH, EXPLORE_PATH, HOME_PATH, INBOX_PATH, SPACE_PATH } from './paths';
+1 -1
View File
@@ -1,5 +1,5 @@
import React from 'react';
import { useRouteError, isRouteErrorResponse } from 'react-router-dom';
import { useRouteError, isRouteErrorResponse } from 'react-router';
import { Box, Button, config, Text, toRem } from 'folds';
export function RouteError() {
+1 -1
View File
@@ -6,7 +6,7 @@ import {
createHashRouter,
createRoutesFromElements,
redirect,
} from 'react-router-dom';
} from 'react-router';
import { RoomSkeleton } from '../components/RoomSkeleton';
import { LobbySkeleton } from '../components/LobbySkeleton';
import { AuthSkeleton } from '../components/AuthSkeleton';
+1 -8
View File
@@ -1,13 +1,6 @@
import React, { useCallback, useEffect } from 'react';
import { Box, Header, Scroll, Spinner, Text, color } from 'folds';
import {
Outlet,
generatePath,
matchPath,
useLocation,
useNavigate,
useParams,
} from 'react-router-dom';
import { Outlet, generatePath, matchPath, useLocation, useNavigate, useParams } from 'react-router';
import classNames from 'classnames';
import { AuthFooter } from './AuthFooter';
+1 -1
View File
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { Box, Text, color } from 'folds';
import { Link, useSearchParams } from 'react-router-dom';
import { Link, useSearchParams } from 'react-router';
import { SSOAction } from 'matrix-js-sdk';
import { useAuthFlows } from '../../../hooks/useAuthFlows';
import { useAuthServer } from '../../../hooks/useAuthServer';
@@ -18,7 +18,7 @@ import {
config,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { Link } from 'react-router-dom';
import { Link } from 'react-router';
import { MatrixError } from 'matrix-js-sdk';
import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../../utils/matrix';
import { EMAIL_REGEX } from '../../../utils/regex';
+1 -1
View File
@@ -1,7 +1,7 @@
import to from 'await-to-js';
import { LoginRequest, LoginResponse, MatrixError, createClient } from 'matrix-js-sdk';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { ClientConfig, clientAllowedServer } from '../../../hooks/useClientConfig';
import { autoDiscovery, specVersions } from '../../../cs-api';
import { ErrorCode } from '../../../cs-errorcode';
+1 -1
View File
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { Box, Text, color } from 'folds';
import { Link, useSearchParams } from 'react-router-dom';
import { Link, useSearchParams } from 'react-router';
import { SSOAction } from 'matrix-js-sdk';
import { useAuthServer } from '../../../hooks/useAuthServer';
import { RegisterFlowStatus, useAuthFlows } from '../../../hooks/useAuthFlows';
+1 -1
View File
@@ -7,7 +7,7 @@ import {
RegisterResponse,
} from 'matrix-js-sdk';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { LoginPathSearchParams } from '../../paths';
import { ErrorCode } from '../../../cs-errorcode';
import {
@@ -12,7 +12,7 @@ import {
color,
config,
} from 'folds';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import FocusTrap from 'focus-trap-react';
import { AuthDict, AuthType, MatrixError, createClient } from 'matrix-js-sdk';
import { useAutoDiscoveryInfo } from '../../../hooks/useAutoDiscoveryInfo';
@@ -1,6 +1,6 @@
import { Box, Text } from 'folds';
import React, { useMemo } from 'react';
import { Link, useSearchParams } from 'react-router-dom';
import { Link, useSearchParams } from 'react-router';
import { getLoginPath } from '../../pathUtils';
import { useAuthServer } from '../../../hooks/useAuthServer';
import { PasswordResetForm } from './PasswordResetForm';
+3 -3
View File
@@ -1,6 +1,6 @@
import { useAtomValue, useSetAtom } from 'jotai';
import React, { ReactNode, useCallback, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import {
ClientEvent,
ClientEventHandlerMap,
@@ -70,12 +70,12 @@ import {
THREAD_NOTIFICATIONS_FALLBACK_BEHAVIOR,
} from '../../utils/threadNotifications';
// Grace period after the initial sync settles before invite notifications arm, so
// the async invite-atom population lands first and isn't mistaken for new invites.
const LogoSVG = withOriginBaseUrl(getOriginBaseUrl(), '/public/res/lotus.png');
const LogoUnreadSVG = withOriginBaseUrl(getOriginBaseUrl(), '/public/res/lotus-unread.png');
const LogoHighlightSVG = withOriginBaseUrl(getOriginBaseUrl(), '/public/res/lotus-highlight.png');
// Grace period after the initial sync settles before invite notifications arm, so
// the async invite-atom population lands first and isn't mistaken for new invites.
const INVITE_NOTIFY_ARM_DELAY_MS = 3000;
function SystemEmojiFeature() {
+1 -1
View File
@@ -19,7 +19,7 @@ import {
} from 'folds';
import { useVirtualizer } from '@tanstack/react-virtual';
import FocusTrap from 'focus-trap-react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { factoryRoomIdByActivity } from '../../../utils/sort';
import {
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useNavigate, useSearchParams } from 'react-router';
import { Box, Icon, IconButton, Icons, Scroll } from 'folds';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { getDirectCreateSearchParams } from '../../pathSearchParam';
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { FormEventHandler, useCallback, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import FocusTrap from 'focus-trap-react';
import {
Avatar,
+1 -1
View File
@@ -27,7 +27,7 @@ import {
config,
toRem,
} from 'folds';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useNavigate, useParams, useSearchParams } from 'react-router';
import FocusTrap from 'focus-trap-react';
import { useAtomValue } from 'jotai';
import { useQuery } from '@tanstack/react-query';
+1 -1
View File
@@ -6,7 +6,7 @@ import React, {
useRef,
useState,
} from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import {
Avatar,
Box,
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
+1 -1
View File
@@ -12,7 +12,7 @@ import {
config,
toRem,
} from 'folds';
import { useSearchParams } from 'react-router-dom';
import { useSearchParams } from 'react-router';
import {
INotification,
INotificationsResponse,
+1 -1
View File
@@ -1,7 +1,7 @@
import React, { MouseEventHandler, useState } from 'react';
import { Box, config, Icon, Icons, Menu, PopOut, RectCords, Text } from 'folds';
import FocusTrap from 'focus-trap-react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar';
import { stopPropagation } from '../../../utils/keyboard';
import { SequenceCard } from '../../../components/sequence-card';
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { MouseEventHandler, forwardRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { Box, Icon, Icons, Menu, MenuItem, PopOut, RectCords, Text, config, toRem } from 'folds';
import FocusTrap from 'focus-trap-react';
import { useAtomValue } from 'jotai';
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react';
import { Icon, Icons } from 'folds';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { useAtomValue } from 'jotai';
import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar';
import { useExploreSelected } from '../../../hooks/router/useExploreSelected';
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { MouseEventHandler, forwardRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { Box, Icon, Icons, Menu, MenuItem, PopOut, RectCords, Text, config, toRem } from 'folds';
import { useAtomValue } from 'jotai';
import FocusTrap from 'focus-trap-react';
+1 -1
View File
@@ -1,5 +1,5 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { Icon, Icons } from 'folds';
import { useAtomValue } from 'jotai';
import {
+1 -1
View File
@@ -9,7 +9,7 @@ import React, {
useRef,
useState,
} from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import {
Box,
Icon,
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import { useAtom, useAtomValue } from 'jotai';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { IsDirectRoomProvider, RoomProvider } from '../../../hooks/useRoom';
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
import { useParams } from 'react-router';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useSpaces } from '../../../state/hooks/roomList';
import { allRoomsAtom } from '../../../state/room-list/roomList';
+1 -1
View File
@@ -1,4 +1,4 @@
import { generatePath, Path } from 'react-router-dom';
import { generatePath, Path } from 'react-router';
import {
DIRECT_CREATE_PATH,
DIRECT_PATH,
+1 -1
View File
@@ -2,7 +2,7 @@ import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createStore } from 'jotai';
import { enableMapSet } from 'immer';
import type { Path } from 'react-router-dom';
import type { Path } from 'react-router';
// `makeNavToActivePathAtom(userId)` is a factory: localStorage is read when the
// returned atom is first created/accessed (not at module load), but we still
+1 -1
View File
@@ -1,6 +1,6 @@
import { WritableAtom, atom } from 'jotai';
import { produce } from 'immer';
import { Path } from 'react-router-dom';
import { Path } from 'react-router';
import {
atomWithLocalStorage,
getLocalStorageItem,
-2
View File
@@ -25,10 +25,8 @@ export const setMarkedUnread = (
unread: boolean,
): Promise<unknown> =>
Promise.all([
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mx.setRoomAccountData(roomId, AccountDataEvent.MarkedUnread as any, { unread }),
// Best-effort mirror for older servers; never fail the primary write on it.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mx.setRoomAccountData(roomId, UNSTABLE_MARKED_UNREAD as any, { unread }).catch(() => undefined),
]);
-2
View File
@@ -13,7 +13,6 @@ export function getAccountData<T>(
mx: MatrixClient,
eventType: AccountDataEvent | string,
): T | undefined {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const event = (mx as any).getAccountData(eventType) as MatrixEvent | undefined;
return event?.getContent() as T | undefined;
}
@@ -23,6 +22,5 @@ export function setAccountData<T>(
eventType: AccountDataEvent | string,
content: T,
): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (mx as any).setAccountData(eventType, content);
}
-2
View File
@@ -70,7 +70,6 @@ export async function buildModelNode(
model: DenoiseModelId,
): Promise<DenoiseNode> {
if (model === 'dtln') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mod: any = await import(/* @vite-ignore */ `${BASE}workadventure/audio-worklet.js`);
const handle = await mod.createNoiseSuppressionAudioWorklet(ctx, { bypassUntilReady: true });
return { node: handle.node, dispose: () => handle.dispose() };
@@ -81,7 +80,6 @@ export async function buildModelNode(
// deepfilternet/v2/... Override its cdnUrl to our absolute base so nothing
// hits the upstream CDN. DeepFilterNet3Core builds the worklet node directly.
const dfnBase = new URL(`${BASE}deepfilternet`, window.location.href).href;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mod: any = await import(/* @vite-ignore */ `${BASE}deepfilternet/index.esm.js`);
const core = new mod.DeepFilterNet3Core({
sampleRate: sampleRateFor(model),
-5
View File
@@ -44,12 +44,10 @@ test('onTabPress fires only on Tab', () => {
test('preventScrollWithArrowKey prevents default only on arrows', () => {
const up = evt('ArrowUp', 38);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
preventScrollWithArrowKey(up as any);
assert.equal(up.prevented, true);
const a = evt('a', 65);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
preventScrollWithArrowKey(a as any);
assert.equal(a.prevented, false);
});
@@ -95,14 +93,12 @@ test('stopPropagation: stops unless an editable element is focused', () => {
// nothing focused → stops, returns true
withActive(null);
let k = makeKeyEvt();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assert.equal(stopPropagation(k.ev as any), true);
assert.equal(k.wasStopped(), true);
// input focused → does not stop, returns false
withActive({ nodeName: 'INPUT', getAttribute: () => null });
k = makeKeyEvt();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assert.equal(stopPropagation(k.ev as any), false);
assert.equal(k.wasStopped(), false);
@@ -112,6 +108,5 @@ test('stopPropagation: stops unless an editable element is focused', () => {
getAttribute: (a: string) => (a === 'contenteditable' ? 'true' : null),
});
k = makeKeyEvt();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assert.equal(stopPropagation(k.ev as any), false);
});
@@ -22,7 +22,6 @@ const makeMx = (
if (opts.forgetRejects) throw new Error('forget failed');
return {};
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
return { mx, calls };
};
-1
View File
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { M_POLL_KIND_DISCLOSED } from 'matrix-js-sdk';
// Pure helpers for poll display. matrix-js-sdk 41.7.0's PollStartEvent /
-1
View File
@@ -58,7 +58,6 @@ export function sendStateEvent<T extends object>(
content: T,
stateKey = '',
): Promise<ISendEventResponse> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return mx.sendStateEvent(roomId, eventType as any, content, stateKey);
}
+2 -2
View File
@@ -69,7 +69,7 @@ export interface ChromeLanguageDetectorFactory {
declare global {
// eslint-disable-next-line vars-on-top
var Translator: ChromeTranslatorFactory | undefined;
let Translator: ChromeTranslatorFactory | undefined;
// eslint-disable-next-line vars-on-top
var LanguageDetector: ChromeLanguageDetectorFactory | undefined;
let LanguageDetector: ChromeLanguageDetectorFactory | undefined;
}
+28 -6
View File
@@ -22,18 +22,40 @@ document.body.classList.add(configClass, varsClass);
// Register Service Worker
if ('serviceWorker' in navigator) {
const swUrl =
import.meta.env.MODE === 'production'
? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
: `/dev-sw.js?dev-sw`;
const isProduction = import.meta.env.PROD;
const swUrl = isProduction
? `${trimTrailingSlash(import.meta.env.BASE_URL)}/sw.js`
: `/dev-sw.js?dev-sw`;
const sendSessionToSW = () => {
const session = getFallbackSession();
pushSessionToSW(session?.baseUrl, session?.accessToken);
};
navigator.serviceWorker.register(swUrl).then(sendSessionToSW);
navigator.serviceWorker.ready.then(sendSessionToSW);
const registerServiceWorker = async () => {
try {
const registration = await navigator.serviceWorker.register(
swUrl,
isProduction
? undefined
: {
type: 'module',
scope: '/',
},
);
sendSessionToSW();
await navigator.serviceWorker.ready;
sendSessionToSW();
console.info('Service worker registered:', registration.scope);
} catch (error) {
console.error('Service worker registration failed:', error);
}
};
registerServiceWorker();
navigator.serviceWorker.addEventListener('message', (ev) => {
const { type } = ev.data ?? {};
-1
View File
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { globalStyle, keyframes, style } from '@vanilla-extract/css';
const glitch1 = keyframes({
+6 -8
View File
@@ -7,7 +7,7 @@ import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import { VitePWA } from 'vite-plugin-pwa';
import fs from 'fs';
import path from 'path';
import buildConfig from './build.config';
import buildConfig from './build.config.ts';
const copyFiles = {
targets: [
@@ -227,7 +227,7 @@ const vendorChunks = (id) => {
if (id.includes('node_modules/matrix-js-sdk')) return 'matrix-sdk';
if (id.includes('node_modules/react-dom')) return 'react-dom';
if (
id.includes('node_modules/react-router-dom') ||
id.includes('node_modules/react-router') ||
id.includes('node_modules/@remix-run') ||
id.includes('node_modules/react-router/')
)
@@ -246,7 +246,7 @@ const vendorChunks = (id) => {
export default defineConfig({
appType: 'spa',
publicDir: false,
publicDir: './public/res',
base: buildConfig.base,
server: {
port: 8080,
@@ -282,7 +282,7 @@ export default defineConfig({
dontCacheBustURLsMatching: /assets\//,
// Raised above the 2 MB default so the ~5.5 MB matrix-sdk crypto wasm
// (hash-busted and hot on every session) is precached deliberately.
maximumFileSizeToCacheInBytes: 6 * 1024 * 1024,
maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
// codeSplitting: false is not yet supported by vite-plugin-pwa 1.3.0;
// the inlineDynamicImports deprecation warning from Vite is from pwa internal build
},
@@ -293,10 +293,8 @@ export default defineConfig({
}),
],
optimizeDeps: {
rolldownOptions: {
define: {
global: 'globalThis',
},
define: {
global: 'globalThis',
},
},
build: {