Merge branch 'bwindels/fix-tracker-changed-key-check' into bwindels/calls

This commit is contained in:
Bruno Windels 2022-05-31 13:46:10 +02:00
commit 9384fdc885
25 changed files with 720 additions and 111 deletions

View File

@ -10,13 +10,34 @@ Hydrogen's goals are:
- It is a standalone webapp, but can also be easily embedded into an existing website/webapp to add chat capabilities.
- Loading (unused) parts of the application after initial page load should be supported
For embedded usage, see the [SDK instructions](doc/SDK.md).
If you find this interesting, come and discuss on [`#hydrogen:matrix.org`](https://matrix.to/#/#hydrogen:matrix.org).
# How to use
Hydrogen is deployed to [hydrogen.element.io](https://hydrogen.element.io). You can run it locally `yarn install` (only the first time) and `yarn start` in the terminal, and point your browser to `http://localhost:3000`. If you prefer, you can also [use docker](doc/docker.md).
Hydrogen is deployed to [hydrogen.element.io](https://hydrogen.element.io). You can also deploy Hydrogen on your own web server:
Hydrogen uses symbolic links in the codebase, so if you are on Windows, have a look at [making git & symlinks work](https://github.com/git-for-windows/git/wiki/Symbolic-Links) there.
1. Download the [latest release package](https://github.com/vector-im/hydrogen-web/releases).
1. Extract the package to the public directory of your web server.
1. If this is your first deploy:
1. copy `config.sample.json` to `config.json` and if needed, make any modifications (unless you've set up your own [sygnal](https://github.com/matrix-org/sygnal) instance, you don't need to change anything in the `push` section).
1. Disable caching entirely on the server for:
- `index.html`
- `sw.js`
- `config.json`
- All theme manifests referenced in the `themeManifests` of `config.json`, these files are typically called `theme-{name}.json`.
These resources will still be cached client-side by the service worker. Because of this; you'll still need to refresh the app twice before config.json changes are applied.
## Set up a dev environment
You can run Hydrogen locally by the following commands in the terminal:
- `yarn install` (only the first time)
- `yarn start` in the terminal
Now point your browser to `http://localhost:3000`. If you prefer, you can also [use docker](doc/docker.md).
# FAQ

View File

@ -1,6 +1,6 @@
{
"name": "hydrogen-web",
"version": "0.2.28",
"version": "0.2.29",
"description": "A javascript matrix client prototype, trying to minize RAM usage by offloading as much as possible to IndexedDB",
"directories": {
"doc": "doc"
@ -11,9 +11,11 @@
"lint-ci": "eslint src/",
"test": "impunity --entry-point src/platform/web/main.js src/platform/web/Platform.js --force-esm-dirs lib/ src/ --root-dir src/",
"test:postcss": "impunity --entry-point scripts/postcss/tests/css-compile-variables.test.js scripts/postcss/tests/css-url-to-variables.test.js",
"test:sdk": "yarn build:sdk && cd ./scripts/sdk/test/ && yarn --no-lockfile && node test-sdk-in-esm-vite-build-env.js && node test-sdk-in-commonjs-env.js",
"start": "vite --port 3000",
"build": "vite build",
"build:sdk": "./scripts/sdk/build.sh"
"build": "vite build && ./scripts/cleanup.sh",
"build:sdk": "./scripts/sdk/build.sh",
"watch:sdk": "./scripts/sdk/build.sh && yarn run vite build -c vite.sdk-lib-config.js --watch"
},
"repository": {
"type": "git",
@ -48,7 +50,7 @@
"regenerator-runtime": "^0.13.7",
"text-encoding": "^0.7.0",
"typescript": "^4.4",
"vite": "^2.6.14",
"vite": "^2.9.8",
"xxhashjs": "^0.2.2"
},
"dependencies": {

View File

@ -31,6 +31,18 @@ function appendVariablesToCSS(variables, cssSource) {
return cssSource + getRootSectionWithVariables(variables);
}
function addThemesToConfig(bundle, manifestLocations, defaultThemes) {
for (const [fileName, info] of Object.entries(bundle)) {
if (fileName === "config.json") {
const source = new TextDecoder().decode(info.source);
const config = JSON.parse(source);
config["themeManifests"] = manifestLocations;
config["defaultTheme"] = defaultThemes;
info.source = new TextEncoder().encode(JSON.stringify(config, undefined, 2));
}
}
}
function parseBundle(bundle) {
const chunkMap = new Map();
const assetMap = new Map();
@ -72,7 +84,7 @@ function parseBundle(bundle) {
}
module.exports = function buildThemes(options) {
let manifest, variants, defaultDark, defaultLight;
let manifest, variants, defaultDark, defaultLight, defaultThemes = {};
let isDevelopment = false;
const virtualModuleId = '@theme/'
const resolvedVirtualModuleId = '\0' + virtualModuleId;
@ -99,9 +111,11 @@ module.exports = function buildThemes(options) {
// This is the default theme, stash the file name for later
if (details.dark) {
defaultDark = fileName;
defaultThemes["dark"] = `${name}-${variant}`;
}
else {
defaultLight = fileName;
defaultThemes["light"] = `${name}-${variant}`;
}
}
// emit the css as built theme bundle
@ -215,6 +229,7 @@ module.exports = function buildThemes(options) {
type: "text/css",
media: "(prefers-color-scheme: dark)",
href: `./${darkThemeLocation}`,
class: "theme",
}
},
{
@ -224,6 +239,7 @@ module.exports = function buildThemes(options) {
type: "text/css",
media: "(prefers-color-scheme: light)",
href: `./${lightThemeLocation}`,
class: "theme",
}
},
];
@ -231,24 +247,36 @@ module.exports = function buildThemes(options) {
generateBundle(_, bundle) {
const { assetMap, chunkMap, runtimeThemeChunk } = parseBundle(bundle);
const manifestLocations = [];
for (const [location, chunkArray] of chunkMap) {
const manifest = require(`${location}/manifest.json`);
const compiledVariables = options.compiledVariables.get(location);
const derivedVariables = compiledVariables["derived-variables"];
const icon = compiledVariables["icon"];
const builtAssets = {};
/**
* Generate a mapping from theme name to asset hashed location of said theme in build output.
* This can be used to enumerate themes during runtime.
*/
for (const chunk of chunkArray) {
const [, name, variant] = chunk.fileName.match(/theme-(.+)-(.+)\.css/);
builtAssets[`${name}-${variant}`] = assetMap.get(chunk.fileName).fileName;
}
manifest.source = {
"built-asset": chunkArray.map(chunk => assetMap.get(chunk.fileName).fileName),
"built-assets": builtAssets,
"runtime-asset": assetMap.get(runtimeThemeChunk.fileName).fileName,
"derived-variables": derivedVariables,
"icon": icon
};
const name = `theme-${manifest.name}.json`;
manifestLocations.push(`assets/${name}`);
this.emitFile({
type: "asset",
name,
source: JSON.stringify(manifest),
});
}
addThemesToConfig(bundle, manifestLocations, defaultThemes);
},
}
}

View File

@ -8,7 +8,7 @@ function contentHash(str) {
return hasher.digest();
}
function injectServiceWorker(swFile, otherUnhashedFiles, placeholdersPerChunk) {
function injectServiceWorker(swFile, findUnhashedFileNamesFromBundle, placeholdersPerChunk) {
const swName = path.basename(swFile);
let root;
let version;
@ -31,6 +31,7 @@ function injectServiceWorker(swFile, otherUnhashedFiles, placeholdersPerChunk) {
logger = config.logger;
},
generateBundle: async function(options, bundle) {
const otherUnhashedFiles = findUnhashedFileNamesFromBundle(bundle);
const unhashedFilenames = [swName].concat(otherUnhashedFiles);
const unhashedFileContentMap = unhashedFilenames.reduce((map, fileName) => {
const chunkOrAsset = bundle[fileName];

3
scripts/cleanup.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
# Remove icons created in .tmp
rm -rf .tmp

View File

@ -2,6 +2,9 @@ VERSION=$(jq -r ".version" package.json)
PACKAGE=hydrogen-web-$VERSION.tar.gz
yarn build
pushd target
# move config file so we don't override it
# when deploying a new version
mv config.json config.sample.json
tar -czvf ../$PACKAGE ./
popd
echo $PACKAGE

View File

@ -2,6 +2,18 @@
"name": "hydrogen-view-sdk",
"description": "Embeddable matrix client library, including view components",
"version": "0.0.12",
"main": "./hydrogen.es.js",
"type": "module"
"main": "./lib-build/hydrogen.cjs.js",
"exports": {
".": {
"import": "./lib-build/hydrogen.es.js",
"require": "./lib-build/hydrogen.cjs.js"
},
"./paths/vite": "./paths/vite.js",
"./style.css": "./asset-build/assets/theme-element-light.css",
"./theme-element-light.css": "./asset-build/assets/theme-element-light.css",
"./theme-element-dark.css": "./asset-build/assets/theme-element-dark.css",
"./main.js": "./asset-build/assets/main.js",
"./download-sandbox.html": "./asset-build/assets/download-sandbox.html",
"./assets/*": "./asset-build/assets/*"
}
}

View File

@ -2,8 +2,12 @@
# Exit whenever one of the commands fail with a non-zero exit code
set -e
set -o pipefail
# Enable extended globs so we can use the `!(filename)` glob syntax
shopt -s extglob
rm -rf target
# Only remove the directory contents instead of the whole directory to maintain
# the `npm link`/`yarn link` symlink
rm -rf target/*
yarn run vite build -c vite.sdk-assets-config.js
yarn run vite build -c vite.sdk-lib-config.js
yarn tsc -p tsconfig-declaration.json
@ -12,19 +16,10 @@ mkdir target/paths
# this doesn't work, the ?url imports need to be in the consuming project, so disable for now
# ./scripts/sdk/transform-paths.js ./src/platform/web/sdk/paths/vite.js ./target/paths/vite.js
cp doc/SDK.md target/README.md
pushd target
pushd asset-build/assets
mv main.*.js ../../main.js
# Create a copy of light theme for backwards compatibility
cp theme-element-light.*.css ../../style.css
# Remove asset hash from css files
mv theme-element-light.*.css ../../theme-element-light.css
mv theme-element-dark.*.css ../../theme-element-dark.css
mv download-sandbox.*.html ../../download-sandbox.html
rm *.js *.wasm
mv ./* ../../
pushd target/asset-build
rm index.html
popd
rm -rf asset-build
mv lib-build/* .
rm -rf lib-build
pushd target/asset-build/assets
# Remove all `*.wasm` and `*.js` files except for `main.js`
rm !(main).js *.wasm
popd

View File

@ -3,21 +3,7 @@ const fs = require("fs");
const appManifest = require("../../package.json");
const baseSDKManifest = require("./base-manifest.json");
/*
need to leave exports out of base-manifest.json because of #vite-bug,
with the downside that we can't support environments that support
both esm and commonjs modules, so we pick just esm.
```
"exports": {
".": {
"import": "./hydrogen.es.js",
"require": "./hydrogen.cjs.js"
},
"./paths/vite": "./paths/vite.js",
"./style.css": "./style.css"
},
```
Also need to leave typescript type definitions out until the
Need to leave typescript type definitions out until the
typescript conversion is complete and all imports in the d.ts files
exists.
```

3
scripts/sdk/test/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
dist
yarn.lock

2
scripts/sdk/test/deps.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
// Keep TypeScripts from complaining about hydrogen-view-sdk not having types yet
declare module "hydrogen-view-sdk";

View File

@ -0,0 +1,21 @@
import * as hydrogenViewSdk from "hydrogen-view-sdk";
import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
import workerPath from 'hydrogen-view-sdk/main.js?url';
import olmWasmPath from '@matrix-org/olm/olm.wasm?url';
import olmJsPath from '@matrix-org/olm/olm.js?url';
import olmLegacyJsPath from '@matrix-org/olm/olm_legacy.js?url';
const assetPaths = {
downloadSandbox: downloadSandboxPath,
worker: workerPath,
olm: {
wasm: olmWasmPath,
legacyBundle: olmLegacyJsPath,
wasmBundle: olmJsPath
}
};
import "hydrogen-view-sdk/theme-element-light.css";
console.log('hydrogenViewSdk', hydrogenViewSdk);
console.log('assetPaths', assetPaths);
console.log('Entry ESM works ✅');

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app" class="hydrogen"></div>
<script type="module" src="./esm-entry.ts"></script>
</body>
</html>

View File

@ -0,0 +1,8 @@
{
"name": "test-sdk",
"version": "0.0.0",
"description": "",
"dependencies": {
"hydrogen-view-sdk": "link:../../../target"
}
}

View File

@ -0,0 +1,13 @@
// Make sure the SDK can be used in a CommonJS environment.
// Usage: node scripts/sdk/test/test-sdk-in-commonjs-env.js
const hydrogenViewSdk = require('hydrogen-view-sdk');
// Test that the "exports" are available:
// Worker
require.resolve('hydrogen-view-sdk/main.js');
// Styles
require.resolve('hydrogen-view-sdk/theme-element-light.css');
// Can access files in the assets/* directory
require.resolve('hydrogen-view-sdk/assets/main.js');
console.log('SDK works in CommonJS ✅');

View File

@ -0,0 +1,19 @@
const { resolve } = require('path');
const { build } = require('vite');
async function main() {
await build({
outDir: './dist',
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html')
}
}
}
});
console.log('SDK works in Vite build ✅');
}
main();

View File

@ -50,6 +50,7 @@ export class SettingsViewModel extends ViewModel {
this.minSentImageSizeLimit = 400;
this.maxSentImageSizeLimit = 4000;
this.pushNotifications = new PushNotificationStatus();
this._activeTheme = undefined;
}
get _session() {
@ -76,6 +77,9 @@ export class SettingsViewModel extends ViewModel {
this.sentImageSizeLimit = await this.platform.settingsStorage.getInt("sentImageSizeLimit");
this.pushNotifications.supported = await this.platform.notificationService.supportsPush();
this.pushNotifications.enabled = await this._session.arePushNotificationsEnabled();
if (!import.meta.env.DEV) {
this._activeTheme = await this.platform.themeLoader.getActiveTheme();
}
this.emitChange("");
}
@ -127,6 +131,18 @@ export class SettingsViewModel extends ViewModel {
return this._formatBytes(this._estimate?.usage);
}
get themes() {
return this.platform.themeLoader.themes;
}
get activeTheme() {
return this._activeTheme;
}
setTheme(name) {
this.platform.themeLoader.setTheme(name);
}
_formatBytes(n) {
if (typeof n === "number") {
return Math.round(n / (1024 * 1024)).toFixed(1) + " MB";

View File

@ -214,11 +214,12 @@ export class DeviceTracker {
const allDeviceIdentities = [];
const deviceIdentitiesToStore = [];
// filter out devices that have changed their ed25519 key since last time we queried them
deviceIdentities = await Promise.all(deviceIdentities.map(async deviceIdentity => {
await Promise.all(deviceIdentities.map(async deviceIdentity => {
if (knownDeviceIds.includes(deviceIdentity.deviceId)) {
const existingDevice = await txn.deviceIdentities.get(deviceIdentity.userId, deviceIdentity.deviceId);
if (existingDevice.ed25519Key !== deviceIdentity.ed25519Key) {
allDeviceIdentities.push(existingDevice);
return;
}
}
allDeviceIdentities.push(deviceIdentity);
@ -363,3 +364,154 @@ export class DeviceTracker {
return await txn.deviceIdentities.getByCurve25519Key(curve25519Key);
}
}
import {createMockStorage} from "../../mocks/Storage";
import {Instance as NullLoggerInstance} from "../../logging/NullLogger";
export function tests() {
function createUntrackedRoomMock(roomId, joinedUserIds, invitedUserIds = []) {
return {
isTrackingMembers: false,
isEncrypted: true,
loadMemberList: () => {
const joinedMembers = joinedUserIds.map(userId => {return {membership: "join", roomId, userId};});
const invitedMembers = invitedUserIds.map(userId => {return {membership: "invite", roomId, userId};});
const members = joinedMembers.concat(invitedMembers);
const memberMap = members.reduce((map, member) => {
map.set(member.userId, member);
return map;
}, new Map());
return {members: memberMap, release() {}}
},
writeIsTrackingMembers(isTrackingMembers) {
if (this.isTrackingMembers !== isTrackingMembers) {
return isTrackingMembers;
}
return undefined;
},
applyIsTrackingMembersChanges(isTrackingMembers) {
if (isTrackingMembers !== undefined) {
this.isTrackingMembers = isTrackingMembers;
}
},
}
}
function createQueryKeysHSApiMock(createKey = (algorithm, userId, deviceId) => `${algorithm}:${userId}:${deviceId}:key`) {
return {
queryKeys(payload) {
const {device_keys: deviceKeys} = payload;
const userKeys = Object.entries(deviceKeys).reduce((userKeys, [userId, deviceIds]) => {
if (deviceIds.length === 0) {
deviceIds = ["device1"];
}
userKeys[userId] = deviceIds.filter(d => d === "device1").reduce((deviceKeys, deviceId) => {
deviceKeys[deviceId] = {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": deviceId,
"keys": {
[`curve25519:${deviceId}`]: createKey("curve25519", userId, deviceId),
[`ed25519:${deviceId}`]: createKey("ed25519", userId, deviceId),
},
"signatures": {
[userId]: {
[`ed25519:${deviceId}`]: `ed25519:${userId}:${deviceId}:signature`
}
},
"unsigned": {
"device_display_name": `${userId} Phone`
},
"user_id": userId
};
return deviceKeys;
}, {});
return userKeys;
}, {});
const response = {device_keys: userKeys};
return {
async response() {
return response;
}
};
}
};
}
const roomId = "!abc:hs.tld";
return {
"trackRoom only writes joined members": async assert => {
const storage = await createMockStorage();
const tracker = new DeviceTracker({
storage,
getSyncToken: () => "token",
olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw
ownUserId: "@alice:hs.tld",
ownDeviceId: "ABCD",
});
const room = createUntrackedRoomMock(roomId, ["@alice:hs.tld", "@bob:hs.tld"], ["@charly:hs.tld"]);
await tracker.trackRoom(room, NullLoggerInstance.item);
const txn = await storage.readTxn([storage.storeNames.userIdentities]);
assert.deepEqual(await txn.userIdentities.get("@alice:hs.tld"), {
userId: "@alice:hs.tld",
roomIds: [roomId],
deviceTrackingStatus: TRACKING_STATUS_OUTDATED
});
assert.deepEqual(await txn.userIdentities.get("@bob:hs.tld"), {
userId: "@bob:hs.tld",
roomIds: [roomId],
deviceTrackingStatus: TRACKING_STATUS_OUTDATED
});
assert.equal(await txn.userIdentities.get("@charly:hs.tld"), undefined);
},
"getting devices for tracked room yields correct keys": async assert => {
const storage = await createMockStorage();
const tracker = new DeviceTracker({
storage,
getSyncToken: () => "token",
olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw
ownUserId: "@alice:hs.tld",
ownDeviceId: "ABCD",
});
const room = createUntrackedRoomMock(roomId, ["@alice:hs.tld", "@bob:hs.tld"]);
await tracker.trackRoom(room, NullLoggerInstance.item);
const hsApi = createQueryKeysHSApiMock();
const devices = await tracker.devicesForRoomMembers(roomId, ["@alice:hs.tld", "@bob:hs.tld"], hsApi, NullLoggerInstance.item);
assert.equal(devices.length, 2);
assert.equal(devices.find(d => d.userId === "@alice:hs.tld").ed25519Key, "ed25519:@alice:hs.tld:device1:key");
assert.equal(devices.find(d => d.userId === "@bob:hs.tld").ed25519Key, "ed25519:@bob:hs.tld:device1:key");
},
"device with changed key is ignored": async assert => {
const storage = await createMockStorage();
const tracker = new DeviceTracker({
storage,
getSyncToken: () => "token",
olmUtil: {ed25519_verify: () => {}}, // valid if it does not throw
ownUserId: "@alice:hs.tld",
ownDeviceId: "ABCD",
});
const room = createUntrackedRoomMock(roomId, ["@alice:hs.tld", "@bob:hs.tld"]);
await tracker.trackRoom(room, NullLoggerInstance.item);
const hsApi = createQueryKeysHSApiMock();
// query devices first time
await tracker.devicesForRoomMembers(roomId, ["@alice:hs.tld", "@bob:hs.tld"], hsApi, NullLoggerInstance.item);
const txn = await storage.readWriteTxn([storage.storeNames.userIdentities]);
// mark alice as outdated, so keys will be fetched again
tracker.writeDeviceChanges(["@alice:hs.tld"], txn, NullLoggerInstance.item);
await txn.complete();
const hsApiWithChangedAliceKey = createQueryKeysHSApiMock((algo, userId, deviceId) => {
return `${algo}:${userId}:${deviceId}:${userId === "@alice:hs.tld" ? "newKey" : "key"}`;
});
const devices = await tracker.devicesForRoomMembers(roomId, ["@alice:hs.tld", "@bob:hs.tld"], hsApiWithChangedAliceKey, NullLoggerInstance.item);
assert.equal(devices.length, 2);
assert.equal(devices.find(d => d.userId === "@alice:hs.tld").ed25519Key, "ed25519:@alice:hs.tld:device1:key");
assert.equal(devices.find(d => d.userId === "@bob:hs.tld").ed25519Key, "ed25519:@bob:hs.tld:device1:key");
const txn2 = await storage.readTxn([storage.storeNames.deviceIdentities]);
// also check the modified key was not stored
assert.equal((await txn2.deviceIdentities.get("@alice:hs.tld", "device1")).ed25519Key, "ed25519:@alice:hs.tld:device1:key");
}
}
}

View File

@ -41,6 +41,7 @@ import {parseHTML} from "./parsehtml.js";
import {handleAvatarError} from "./ui/avatar";
import {MediaDevicesWrapper} from "./dom/MediaDevices";
import {DOMWebRTC} from "./dom/WebRTC";
import {ThemeLoader} from "./ThemeLoader";
function addScript(src) {
return new Promise(function (resolve, reject) {
@ -169,20 +170,36 @@ export class Platform {
this._workerPromise = undefined;
this.mediaDevices = new MediaDevicesWrapper(navigator.mediaDevices);
this.webRTC = new DOMWebRTC();
this._themeLoader = import.meta.env.DEV? null: new ThemeLoader(this);
}
async init() {
if (!this._config) {
if (!this._configURL) {
throw new Error("Neither config nor configURL was provided!");
}
const {body}= await this.request(this._configURL, {method: "GET", format: "json", cache: true}).response();
this._config = body;
try {
await this.logger.run("Platform init", async (log) => {
if (!this._config) {
if (!this._configURL) {
throw new Error("Neither config nor configURL was provided!");
}
const {status, body}= await this.request(this._configURL, {method: "GET", format: "json", cache: true}).response();
if (status === 404) {
throw new Error(`Could not find ${this._configURL}. Did you copy over config.sample.json?`);
} else if (status >= 400) {
throw new Error(`Got status ${status} while trying to fetch ${this._configURL}`);
}
this._config = body;
}
this.notificationService = new NotificationService(
this._serviceWorkerHandler,
this._config.push
);
const manifests = this.config["themeManifests"];
await this._themeLoader?.init(manifests);
this._themeLoader?.setTheme(await this._themeLoader.getActiveTheme(), log);
});
} catch (err) {
this._container.innerText = err.message;
throw err;
}
this.notificationService = new NotificationService(
this._serviceWorkerHandler,
this._config.push
);
}
_createLogger(isDevelopment) {
@ -314,6 +331,23 @@ export class Platform {
return DEFINE_VERSION;
}
get themeLoader() {
return this._themeLoader;
}
replaceStylesheet(newPath) {
const head = document.querySelector("head");
// remove default theme
document.querySelectorAll(".theme").forEach(e => e.remove());
// add new theme
const styleTag = document.createElement("link");
styleTag.href = `./${newPath}`;
styleTag.rel = "stylesheet";
styleTag.type = "text/css";
styleTag.className = "theme";
head.appendChild(styleTag);
}
dispose() {
this._disposables.dispose();
}

View File

@ -0,0 +1,75 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import type {ILogItem} from "../../logging/types.js";
import type {Platform} from "./Platform.js";
export class ThemeLoader {
private _platform: Platform;
private _themeMapping: Record<string, string> = {};
constructor(platform: Platform) {
this._platform = platform;
}
async init(manifestLocations: string[]): Promise<void> {
for (const manifestLocation of manifestLocations) {
const { body } = await this._platform
.request(manifestLocation, {
method: "GET",
format: "json",
cache: true,
})
.response();
/*
After build has finished, the source section of each theme manifest
contains `built-assets` which is a mapping from the theme-name to the
location of the css file in build.
*/
Object.assign(this._themeMapping, body["source"]["built-assets"]);
}
}
setTheme(themeName: string, log?: ILogItem) {
this._platform.logger.wrapOrRun(log, {l: "change theme", id: themeName}, () => {
const themeLocation = this._themeMapping[themeName];
if (!themeLocation) {
throw new Error( `Cannot find theme location for theme "${themeName}"!`);
}
this._platform.replaceStylesheet(themeLocation);
this._platform.settingsStorage.setString("theme", themeName);
});
}
get themes(): string[] {
return Object.keys(this._themeMapping);
}
async getActiveTheme(): Promise<string|undefined> {
// check if theme is set via settings
let theme = await this._platform.settingsStorage.getString("theme");
if (theme) {
return theme;
}
// return default theme
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
return this._platform.config["defaultTheme"].dark;
} else if (window.matchMedia("(prefers-color-scheme: light)").matches) {
return this._platform.config["defaultTheme"].light;
}
return undefined;
}
}

View File

@ -95,8 +95,8 @@ let pendingFetchAbortController = new AbortController();
async function handleRequest(request) {
try {
if (request.url.includes("config.json")) {
return handleConfigRequest(request);
if (request.url.includes("config.json") || /theme-.+\.json/.test(request.url)) {
return handleStaleWhileRevalidateRequest(request);
}
const url = new URL(request.url);
// rewrite / to /index.html so it hits the cache
@ -123,9 +123,13 @@ async function handleRequest(request) {
}
}
async function handleConfigRequest(request) {
/**
* Stale-while-revalidate caching for certain files
* see https://developer.chrome.com/docs/workbox/caching-strategies-overview/#stale-while-revalidate
*/
async function handleStaleWhileRevalidateRequest(request) {
let response = await readCache(request);
const networkResponsePromise = fetchAndUpdateConfig(request);
const networkResponsePromise = fetchAndUpdateCache(request);
if (response) {
return response;
} else {
@ -133,7 +137,7 @@ async function handleConfigRequest(request) {
}
}
async function fetchAndUpdateConfig(request) {
async function fetchAndUpdateCache(request) {
const response = await fetch(request, {
signal: pendingFetchAbortController.signal,
headers: {
@ -156,8 +160,14 @@ async function updateCache(request, response) {
cache.put(request, response.clone());
} else if (request.url.startsWith(baseURL)) {
let assetName = request.url.substr(baseURL.length);
let cacheName;
if (HASHED_CACHED_ON_REQUEST_ASSETS.includes(assetName)) {
const cache = await caches.open(hashedCacheName);
cacheName = hashedCacheName;
} else if (UNHASHED_PRECACHED_ASSETS.includes(assetName)) {
cacheName = unhashedCacheName;
}
if (cacheName) {
const cache = await caches.open(cacheName);
await cache.put(request, response.clone());
}
}

View File

@ -93,10 +93,13 @@ export class SettingsView extends TemplateView {
]);
})
);
settingNodes.push(
t.h3("Preferences"),
row(t, vm.i18n`Scale down images when sending`, this._imageCompressionRange(t, vm)),
t.if(vm => !import.meta.env.DEV && vm.activeTheme, (t, vm) => {
return row(t, vm.i18n`Use the following theme`, this._themeOptions(t, vm));
}),
);
const logButtons = [t.button({onClick: () => vm.exportLogs()}, "Export")];
if (import.meta.env.DEV) {
@ -140,6 +143,15 @@ export class SettingsView extends TemplateView {
vm.i18n`no resizing`;
})];
}
_themeOptions(t, vm) {
const activeTheme = vm.activeTheme;
const optionTags = [];
for (const name of vm.themes) {
optionTags.push(t.option({value: name, selected: name === activeTheme}, name));
}
return t.select({onChange: (e) => vm.setTheme(e.target.value)}, optionTags);
}
}

View File

@ -16,25 +16,39 @@ export default defineConfig(({mode}) => {
sourcemap: true,
rollupOptions: {
output: {
assetFileNames: (asset) => asset.name.includes("config.json") ? "assets/[name][extname]": "assets/[name].[hash][extname]",
assetFileNames: (asset) => {
if (asset.name.includes("config.json")) {
return "[name][extname]";
}
else if (asset.name.match(/theme-.+\.json/)) {
return "assets/[name][extname]";
}
else {
return "assets/[name].[hash][extname]";
}
}
},
},
},
plugins: [
themeBuilder({
themeConfig: {
themes: {"element": "./src/platform/web/ui/css/themes/element"},
themes: {
element: "./src/platform/web/ui/css/themes/element",
},
default: "element",
},
compiledVariables
compiledVariables,
}),
// important this comes before service worker
// otherwise the manifest and the icons it refers to won't be cached
injectWebManifest("assets/manifest.json"),
injectServiceWorker("./src/platform/web/sw.js", ["index.html"], {
injectServiceWorker("./src/platform/web/sw.js", findUnhashedFileNamesFromBundle, {
// placeholders to replace at end of build by chunk name
"index": {DEFINE_GLOBAL_HASH: definePlaceholders.DEFINE_GLOBAL_HASH},
"sw": definePlaceholders
index: {
DEFINE_GLOBAL_HASH: definePlaceholders.DEFINE_GLOBAL_HASH,
},
sw: definePlaceholders,
}),
],
define: Object.assign({
@ -42,3 +56,16 @@ export default defineConfig(({mode}) => {
}, definePlaceholders),
});
});
function findUnhashedFileNamesFromBundle(bundle) {
const names = ["index.html"];
for (const fileName of Object.keys(bundle)) {
if (fileName.includes("config.json")) {
names.push(fileName);
}
if (/theme-.+\.json/.test(fileName)) {
names.push(fileName);
}
}
return names;
}

View File

@ -3,11 +3,33 @@ const mergeOptions = require('merge-options');
const themeBuilder = require("./scripts/build-plugins/rollup-plugin-build-themes");
const {commonOptions, compiledVariables} = require("./vite.common-config.js");
// These paths will be saved without their hash so they havea consisent path to
// reference
const pathsToExport = [
"main.js",
"download-sandbox.html",
"theme-element-light.css",
"theme-element-dark.css",
];
export default mergeOptions(commonOptions, {
root: "src/",
base: "./",
build: {
outDir: "../target/asset-build/",
rollupOptions: {
output: {
assetFileNames: (chunkInfo) => {
// Get rid of the hash so we can consistently reference these
// files in our `package.json` `exports`
if(pathsToExport.includes(path.basename(chunkInfo.name))) {
return "assets/[name].[ext]";
}
return "assets/[name]-[hash][extname]";
}
}
}
},
plugins: [
themeBuilder({

224
yarn.lock
View File

@ -503,66 +503,141 @@ entities@^2.0.0:
version "4.2.8"
resolved "https://github.com/bwindels/es6-promise.git#112f78f5829e627055b0ff56a52fecb63f6003b1"
esbuild-android-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.31.tgz#4b7dfbbeee62b3495ba78220b85fb590eb68d5bf"
integrity sha512-MYkuJ91w07nGmr4EouejOZK2j/f5TCnsKxY8vRr2+wpKKfHD1LTJK28VbZa+y1+AL7v1V9G98ezTUwsV3CmXNw==
esbuild-android-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.15.tgz#3fc3ff0bab76fe35dd237476b5d2b32bb20a3d44"
integrity sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==
esbuild-android-arm64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.31.tgz#24c3d693924e044fb0d23206c3e627502b10b930"
integrity sha512-0rkH/35s7ZVcsw6nS0IAkR0dekSbjZGWdlOAf3jV0lGoPqqw0x6/TmaV9w7DQgUERTH1ApmPlpAMU4kVkCq9Jg==
esbuild-darwin-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.15.tgz#8e9169c16baf444eacec60d09b24d11b255a8e72"
integrity sha512-ihOQRGs2yyp7t5bArCwnvn2Atr6X4axqPpEdCFPVp7iUj4cVSdisgvEKdNR7yH3JDjW6aQDw40iQFoTqejqxvQ==
esbuild-darwin-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.31.tgz#285fbdb6dc74d4410f43dee59e6a14ebff82a9d7"
integrity sha512-kP6xPZHxtJa36Hb0jC05L3VzQSZBW2f3bpnQS20czXTRGEmM2GDiYpGdI5g2QYaw6vC4PYXjnigq8usd9g9jnQ==
esbuild-darwin-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.15.tgz#1b07f893b632114f805e188ddfca41b2b778229a"
integrity sha512-i1FZssTVxUqNlJ6cBTj5YQj4imWy3m49RZRnHhLpefFIh0To05ow9DTrXROTE1urGTQCloFUXTX8QfGJy1P8dQ==
esbuild-darwin-arm64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.31.tgz#b39c471a8134ce2c7811eb96fab9c500b256261c"
integrity sha512-1ZMog4hkNsdBGtDDtsftUqX6S9N52gEx4vX5aVehsSptgoBFIar1XrPiBTQty7YNH+bJasTpSVaZQgElCVvPKQ==
esbuild-freebsd-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.15.tgz#0b8b7eca1690c8ec94c75680c38c07269c1f4a85"
integrity sha512-G3dLBXUI6lC6Z09/x+WtXBXbOYQZ0E8TDBqvn7aMaOCzryJs8LyVXKY4CPnHFXZAbSwkCbqiPuSQ1+HhrNk7EA==
esbuild-freebsd-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.31.tgz#7ca700ef60ae12154bae63094ad41b21c6ae1a23"
integrity sha512-Zo0BYj7QpVFWoUpkv6Ng0RO2eJ4zk/WDaHMO88+jr5HuYmxsOre0imgwaZVPquTuJnCvL1G48BFucJ3tFflSeQ==
esbuild-freebsd-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.15.tgz#2e1a6c696bfdcd20a99578b76350b41db1934e52"
integrity sha512-KJx0fzEDf1uhNOZQStV4ujg30WlnwqUASaGSFPhznLM/bbheu9HhqZ6mJJZM32lkyfGJikw0jg7v3S0oAvtvQQ==
esbuild-freebsd-arm64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.31.tgz#f793085c7184fcd08649b8d185edc5c2ce112e82"
integrity sha512-t85bS6jbRpmdjr4pdr/FY/fpx8lo1vv9S7BAs2EsXKJQhRDMIiC3QW+k2acYJoRuqirlvJcJVFQGCq/PfyC1kA==
esbuild-linux-32@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.15.tgz#6fd39f36fc66dd45b6b5f515728c7bbebc342a69"
integrity sha512-ZvTBPk0YWCLMCXiFmD5EUtB30zIPvC5Itxz0mdTu/xZBbbHJftQgLWY49wEPSn2T/TxahYCRDWun5smRa0Tu+g==
esbuild-linux-32@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.31.tgz#cac97ec7da6fbde0c21dbe08babd0d2a034f317d"
integrity sha512-XYtOk/GodSkv+UOYVwryGpGPuFnszsMvRMKq6cIUfFfdssHuKDsU9IZveyCG44J106J39ABenQ5EetbYtVJHUw==
esbuild-linux-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.15.tgz#9cb8e4bcd7574e67946e4ee5f1f1e12386bb6dd3"
integrity sha512-eCKzkNSLywNeQTRBxJRQ0jxRCl2YWdMB3+PkWFo2BBQYC5mISLIVIjThNtn6HUNqua1pnvgP5xX0nHbZbPj5oA==
esbuild-linux-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.31.tgz#ec94cd5228e6777d2feb3c24a1fe1cbf8817d6da"
integrity sha512-Zf9CZxAxaXWHLqCg/QZ/hs0RU0XV3IBxV+ENQzy00S4QOTnZAvSLgPciILHHrVJ0lPIlb4XzAqlLM5y6iI2LIw==
esbuild-linux-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.15.tgz#3891aa3704ec579a1b92d2a586122e5b6a2bfba1"
integrity sha512-bYpuUlN6qYU9slzr/ltyLTR9YTBS7qUDymO8SV7kjeNext61OdmqFAzuVZom+OLW1HPHseBfJ/JfdSlx8oTUoA==
esbuild-linux-arm64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.31.tgz#d119188fccd6384db5c703de24c46dacaee3e9e8"
integrity sha512-V/H0tv+xpQ9IOHM+o85oCKNNidIEc5CcnDWl0V+hPd2F03dqdbFkWPBGphx8rD4JSQn6UefUQ1iH7y1qIzO8Fw==
esbuild-linux-arm@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.15.tgz#8a00e99e6a0c6c9a6b7f334841364d8a2b4aecfe"
integrity sha512-wUHttDi/ol0tD8ZgUMDH8Ef7IbDX+/UsWJOXaAyTdkT7Yy9ZBqPg8bgB/Dn3CZ9SBpNieozrPRHm0BGww7W/jA==
esbuild-linux-arm@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.31.tgz#63e10846886901e5632a591d44160f95c5d12ba7"
integrity sha512-RpiaeHPRlgCCDskxoyIsI49BhcDtZ4cl8+SLffizDm0yMNWP538SUg0ezQ2TTOPj3/svaGIbkRDwYtAon0Sjkg==
esbuild-linux-mips64le@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.15.tgz#36b07cc47c3d21e48db3bb1f4d9ef8f46aead4f7"
integrity sha512-KlVjIG828uFPyJkO/8gKwy9RbXhCEUeFsCGOJBepUlpa7G8/SeZgncUEz/tOOUJTcWMTmFMtdd3GElGyAtbSWg==
esbuild-linux-mips64le@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.31.tgz#1cd44f72cde6489a5d6deea7c54efa6f3d6590ee"
integrity sha512-9/oBfAckInRuUg6AEgdCLLn6KJ6UOJDOLmUinTsReVSg6AfV6wxYQJq9iQM2idRogP7GUpomJ+bvCdWXpotQRQ==
esbuild-linux-ppc64le@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.15.tgz#f7e6bba40b9a11eb9dcae5b01550ea04670edad2"
integrity sha512-h6gYF+OsaqEuBjeesTBtUPw0bmiDu7eAeuc2OEH9S6mV9/jPhPdhOWzdeshb0BskRZxPhxPOjqZ+/OqLcxQwEQ==
esbuild-linux-ppc64le@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.31.tgz#3b5ccc05e5b8ef5c494f30a61fdd27811d2bbeeb"
integrity sha512-NMcb14Pg+8q8raGkzor9/R3vQwKzgxE3694BtO2SDLBwJuL2C1dQ1ZtM1t7ZvArQBgT8RiZVxb0/3fD+qGNk7g==
esbuild-linux-riscv64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.31.tgz#d74ca78c8ed1d9b40bc907a9e3ef6e83fc06189c"
integrity sha512-l13yvmsVfawAnoYfcpuvml+nTlrOmtdceXYufSkXl2DOb0JKcuR6ARlAzuQCDcpo49SOJy1cCxpwlOIsUQBfzA==
esbuild-linux-s390x@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.31.tgz#1bd547b8b027e323b77a838d265cb56ece2543af"
integrity sha512-GIwV9mY3koYja9MCSkKLk1P7rj+MkPV0UsGsZ575hEcIBrXeKN9jBi6X/bxDDPEN/SUAH35cJhBNrZU4x9lEfg==
esbuild-netbsd-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.15.tgz#a2fedc549c2b629d580a732d840712b08d440038"
integrity sha512-3+yE9emwoevLMyvu+iR3rsa+Xwhie7ZEHMGDQ6dkqP/ndFzRHkobHUKTe+NCApSqG5ce2z4rFu+NX/UHnxlh3w==
esbuild-netbsd-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.31.tgz#964a45dbad4fac92aa0a15056e38a182735bd6c6"
integrity sha512-bJ+pyLvKQm+Obp5k7/Wk8e9Gdkls56F1aiI3uptoIfOIUqsZImH7pDyTrSufwqsFp62kO9LRuwXnjDwQtPyhFQ==
esbuild-node-loader@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/esbuild-node-loader/-/esbuild-node-loader-0.6.3.tgz#3b90012f8bc2fcbb2ef76a659482c2c99840c5e8"
@ -575,27 +650,52 @@ esbuild-openbsd-64@0.13.15:
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.15.tgz#b22c0e5806d3a1fbf0325872037f885306b05cd7"
integrity sha512-wTfvtwYJYAFL1fSs8yHIdf5GEE4NkbtbXtjLWjM3Cw8mmQKqsg8kTiqJ9NJQe5NX/5Qlo7Xd9r1yKMMkHllp5g==
esbuild-openbsd-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.31.tgz#7d2a1d46450321b0459263d3e7072e6d3924ce46"
integrity sha512-NRAAPPca05H9j9Xab0kVXK0V6/pyZGGy8d2Y8KS0BMwWEydlD4KCJDmH8/7bWCKYLRGOOCE9/GPBJyPWHFW3sg==
esbuild-sunos-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.15.tgz#d0b6454a88375ee8d3964daeff55c85c91c7cef4"
integrity sha512-lbivT9Bx3t1iWWrSnGyBP9ODriEvWDRiweAs69vI+miJoeKwHWOComSRukttbuzjZ8r1q0mQJ8Z7yUsDJ3hKdw==
esbuild-sunos-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.31.tgz#3b3e4363951cd1dda14a14fee6d94ca426108e0c"
integrity sha512-9uA+V8w9Eehu4ldb95lPWdgCMcMO5HH6pXmfkk5usn3JsSZxKdLKsXB4hYgP80wscZvVYXJl2G+KNxsUTfPhZw==
esbuild-windows-32@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.15.tgz#c96d0b9bbb52f3303322582ef8e4847c5ad375a7"
integrity sha512-fDMEf2g3SsJ599MBr50cY5ve5lP1wyVwTe6aLJsM01KtxyKkB4UT+fc5MXQFn3RLrAIAZOG+tHC+yXObpSn7Nw==
esbuild-windows-32@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.31.tgz#997026a41c04535bfb7c014a0458940b49145820"
integrity sha512-VGdncQTqoxD9q3v/dk0Yugbmx2FzOkcs0OemBYc1X9KXOLQYH0uQbLJIckZdZOC3J+JKSExbYFrzYCOwWPuNyA==
esbuild-windows-64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.15.tgz#1f79cb9b1e1bb02fb25cd414cb90d4ea2892c294"
integrity sha512-9aMsPRGDWCd3bGjUIKG/ZOJPKsiztlxl/Q3C1XDswO6eNX/Jtwu4M+jb6YDH9hRSUflQWX0XKAfWzgy5Wk54JQ==
esbuild-windows-64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.31.tgz#5d4b0ea686c9b60036303b3415c472f2761bdafc"
integrity sha512-v/2ye5zBqpmCzi3bLCagStbNQlnOsY7WtMrD2Q0xZxeSIXONxji15KYtVee5o7nw4lXWbQSS1BL8G6BBMvtq4A==
esbuild-windows-arm64@0.13.15:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.15.tgz#482173070810df22a752c686509c370c3be3b3c3"
integrity sha512-zzvyCVVpbwQQATaf3IG8mu1IwGEiDxKkYUdA4FpoCHi1KtPa13jeScYDjlW0Qh+ebWzpKfR2ZwvqAQkSWNcKjA==
esbuild@^0.13.12, esbuild@^0.13.2:
esbuild-windows-arm64@0.14.31:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.31.tgz#4f3b9fc34c4a33abbd0171df6cbb657ccbdbfc67"
integrity sha512-RXeU42FJoG1sriNHg73h4S+5B7L/gw+8T7U9u8IWqSSEbY6fZvBh4uofugiU1szUDqqP00GHwZ09WgYe3lGZiw==
esbuild@^0.13.12:
version "0.13.15"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.15.tgz#db56a88166ee373f87dbb2d8798ff449e0450cdf"
integrity sha512-raCxt02HBKv8RJxE8vkTSCXGIyKHdEdGfUmiYb8wnabnaEmHzyW7DCHb5tEN0xU8ryqg5xw54mcwnYkC4x3AIw==
@ -618,6 +718,32 @@ esbuild@^0.13.12, esbuild@^0.13.2:
esbuild-windows-64 "0.13.15"
esbuild-windows-arm64 "0.13.15"
esbuild@^0.14.27:
version "0.14.31"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.31.tgz#f7d0db114edc615f66d84972ee9fbd2b267f4029"
integrity sha512-QA0fUM13+JZzcvg1bdrhi7wo8Lr5IRHA9ypNn2znqxGqb66dSK6pAh01TjyBOhzZGazPQJZ1K26VrCAQJ715qA==
optionalDependencies:
esbuild-android-64 "0.14.31"
esbuild-android-arm64 "0.14.31"
esbuild-darwin-64 "0.14.31"
esbuild-darwin-arm64 "0.14.31"
esbuild-freebsd-64 "0.14.31"
esbuild-freebsd-arm64 "0.14.31"
esbuild-linux-32 "0.14.31"
esbuild-linux-64 "0.14.31"
esbuild-linux-arm "0.14.31"
esbuild-linux-arm64 "0.14.31"
esbuild-linux-mips64le "0.14.31"
esbuild-linux-ppc64le "0.14.31"
esbuild-linux-riscv64 "0.14.31"
esbuild-linux-s390x "0.14.31"
esbuild-netbsd-64 "0.14.31"
esbuild-openbsd-64 "0.14.31"
esbuild-sunos-64 "0.14.31"
esbuild-windows-32 "0.14.31"
esbuild-windows-64 "0.14.31"
esbuild-windows-arm64 "0.14.31"
escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@ -966,10 +1092,10 @@ inherits@2:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-core-module@^2.2.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491"
integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==
is-core-module@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
dependencies:
has "^1.0.3"
@ -1113,10 +1239,10 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.1.28:
version "3.1.28"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.28.tgz#3c01bac14cb6c5680569014cc65a2f26424c6bd4"
integrity sha512-gSu9VZ2HtmoKYe/lmyPFES5nknFrHa+/DT9muUFWFMi6Jh9E1I7bkvlQ8xxf1Kos9pi9o8lBnIOkatMhKX/YUw==
nanoid@^3.3.3:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
natural-compare@^1.4.0:
version "1.4.0"
@ -1193,20 +1319,20 @@ path-key@^3.1.0:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
picocolors@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.2.3:
version "2.3.0"
@ -1232,14 +1358,14 @@ postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.3.8:
version "8.3.9"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31"
integrity sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw==
postcss@^8.4.13:
version "8.4.13"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575"
integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==
dependencies:
nanoid "^3.1.28"
picocolors "^0.2.1"
source-map-js "^0.6.2"
nanoid "^3.3.3"
picocolors "^1.0.0"
source-map-js "^1.0.2"
prelude-ls@^1.2.1:
version "1.2.1"
@ -1296,13 +1422,14 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
resolve@^1.22.0:
version "1.22.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
dependencies:
is-core-module "^2.2.0"
path-parse "^1.0.6"
is-core-module "^2.8.1"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4:
version "1.0.4"
@ -1316,10 +1443,10 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
rollup@^2.57.0:
version "2.58.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.58.0.tgz#a643983365e7bf7f5b7c62a8331b983b7c4c67fb"
integrity sha512-NOXpusKnaRpbS7ZVSzcEXqxcLDOagN6iFS8p45RkoiMqPHDLwJm758UF05KlMoCRbLBTZsPOIa887gZJ1AiXvw==
rollup@^2.59.0:
version "2.70.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.1.tgz#824b1f1f879ea396db30b0fc3ae8d2fead93523e"
integrity sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==
optionalDependencies:
fsevents "~2.3.2"
@ -1373,10 +1500,10 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
source-map-js@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map@~0.6.1:
version "0.6.1"
@ -1423,6 +1550,11 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
table@^6.0.9:
version "6.7.1"
resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2"
@ -1526,15 +1658,15 @@ v8-compile-cache@^2.0.3:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
vite@^2.6.14:
version "2.6.14"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.14.tgz#35c09a15e4df823410819a2a239ab11efb186271"
integrity sha512-2HA9xGyi+EhY2MXo0+A2dRsqsAG3eFNEVIo12olkWhOmc8LfiM+eMdrXf+Ruje9gdXgvSqjLI9freec1RUM5EA==
vite@^2.9.8:
version "2.9.8"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.8.tgz#2c2cb0790beb0fbe4b8c0995b80fe691a91c2545"
integrity sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==
dependencies:
esbuild "^0.13.2"
postcss "^8.3.8"
resolve "^1.20.0"
rollup "^2.57.0"
esbuild "^0.14.27"
postcss "^8.4.13"
resolve "^1.22.0"
rollup "^2.59.0"
optionalDependencies:
fsevents "~2.3.2"