Compare commits

...

10 Commits

Author SHA1 Message Date
7cba67e4c1
nip5 handles 2023-08-23 14:14:38 +01:00
c191a7684a
fix CSP for fonts 2023-08-22 22:54:10 +01:00
0e590feab4
formatting 2023-08-22 22:50:26 +01:00
16c54185bb
cleanup 2023-08-22 22:48:37 +01:00
d071736d4c Merge pull request 'Host fonts locally to limit user tracking by Google' (#82) from florian/stream:host-fonts-locally into main
Reviewed-on: Kieran/stream#82
Reviewed-by: Kieran <kieran@noreply.localhost>
2023-08-22 21:12:25 +00:00
f7517d7e1a Merge pull request 'Additional Zap amounts + larger text input for chat' (#83) from TheGrinder/zapstream:main into main
Reviewed-on: Kieran/stream#83
Reviewed-by: Kieran <kieran@noreply.localhost>
2023-08-22 21:09:36 +00:00
TheGrinder
9741eb6a83 change write-message from 32px to 40px to display
two lines of typed text instead of one.
2023-08-21 12:22:22 +02:00
TheGrinder
cb94afdc16 modified css for added zap amounts 2023-08-21 01:40:32 +02:00
TheGrinder
5a24671839 add additional zap amounts 2023-08-21 01:19:05 +02:00
6b328a1fde chore: Host fonts locally to limit tracking by google 2023-08-14 11:06:13 +02:00
45 changed files with 468 additions and 250 deletions

View File

@ -22,4 +22,4 @@ steps:
volumes:
- name: cache
claim:
name: docker-cache
name: docker-cache

View File

@ -1,20 +1,19 @@
module.exports = {
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
root: true,
ignorePatterns: ["build/", "*.test.ts", "*.js"],
env: {
browser: true,
worker: true,
commonjs: true,
node: false,
},
"rules": {
"@typescript-eslint/no-non-null-assertion": "error",
"require-await": "error",
"eqeqeq": "error",
"object-shorthand": "warn",
}
};
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
root: true,
ignorePatterns: ["build/", "*.test.ts", "*.js"],
env: {
browser: true,
worker: true,
commonjs: true,
node: false,
},
rules: {
"@typescript-eslint/no-non-null-assertion": "error",
"require-await": "error",
eqeqeq: "error",
"object-shorthand": "warn",
},
};

View File

@ -1,8 +1,8 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";

View File

@ -1,8 +1,8 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";

View File

@ -1,8 +1,8 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const relPnpApiPath = "../../../.pnp.cjs";

View File

@ -1,8 +1,8 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";

View File

@ -1,29 +1,31 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);
const moduleWrapper = tsserver => {
const moduleWrapper = (tsserver) => {
if (!process.versions.pnp) {
return tsserver;
}
const {isAbsolute} = require(`path`);
const { isAbsolute } = require(`path`);
const pnpApi = require(`pnpapi`);
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
const isPortal = str => str.startsWith("portal:/");
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
const isPortal = (str) => str.startsWith("portal:/");
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
return `${locator.name}@${locator.reference}`;
}));
const dependencyTreeRoots = new Set(
pnpApi.getDependencyTreeRoots().map((locator) => {
return `${locator.name}@${locator.reference}`;
})
);
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
// doesn't understand. This layer makes sure to remove the protocol
@ -31,7 +33,11 @@ const moduleWrapper = tsserver => {
function toEditorPath(str) {
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
if (
isAbsolute(str) &&
!str.match(/^\^?(zip:|\/zip\/)/) &&
(str.match(/\.zip\//) || isVirtual(str))
) {
// We also take the opportunity to turn virtual paths into physical ones;
// this makes it much easier to work with workspaces that list peer
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
@ -45,7 +51,11 @@ const moduleWrapper = tsserver => {
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
if (resolved) {
const locator = pnpApi.findPackageLocator(resolved);
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
if (
locator &&
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) ||
isPortal(locator.reference))
) {
str = resolved;
}
}
@ -73,41 +83,55 @@ const moduleWrapper = tsserver => {
// Before | ^/zip/c:/foo/bar.zip/package.json
// After | ^/zip//c:/foo/bar.zip/package.json
//
case `vscode <1.61`: {
str = `^zip:${str}`;
} break;
case `vscode <1.61`:
{
str = `^zip:${str}`;
}
break;
case `vscode <1.66`: {
str = `^/zip/${str}`;
} break;
case `vscode <1.66`:
{
str = `^/zip/${str}`;
}
break;
case `vscode <1.68`: {
str = `^/zip${str}`;
} break;
case `vscode <1.68`:
{
str = `^/zip${str}`;
}
break;
case `vscode`: {
str = `^/zip/${str}`;
} break;
case `vscode`:
{
str = `^/zip/${str}`;
}
break;
// To make "go to definition" work,
// We have to resolve the actual file system path from virtual path
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
case `coc-nvim`: {
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = resolve(`zipfile:${str}`);
} break;
case `coc-nvim`:
{
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = resolve(`zipfile:${str}`);
}
break;
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
// We have to resolve the actual file system path from virtual path,
// everything else is up to neovim
case `neovim`: {
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = `zipfile://${str}`;
} break;
case `neovim`:
{
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = `zipfile://${str}`;
}
break;
default: {
str = `zip:${str}`;
} break;
default:
{
str = `zip:${str}`;
}
break;
}
} else {
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
@ -119,26 +143,35 @@ const moduleWrapper = tsserver => {
function fromEditorPath(str) {
switch (hostInfo) {
case `coc-nvim`: {
str = str.replace(/\.zip::/, `.zip/`);
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
// So in order to convert it back, we use .* to match all the thing
// before `zipfile:`
return process.platform === `win32`
? str.replace(/^.*zipfile:\//, ``)
: str.replace(/^.*zipfile:/, ``);
} break;
case `coc-nvim`:
{
str = str.replace(/\.zip::/, `.zip/`);
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
// So in order to convert it back, we use .* to match all the thing
// before `zipfile:`
return process.platform === `win32`
? str.replace(/^.*zipfile:\//, ``)
: str.replace(/^.*zipfile:/, ``);
}
break;
case `neovim`: {
str = str.replace(/\.zip::/, `.zip/`);
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
return str.replace(/^zipfile:\/\//, ``);
} break;
case `neovim`:
{
str = str.replace(/\.zip::/, `.zip/`);
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
return str.replace(/^zipfile:\/\//, ``);
}
break;
case `vscode`:
default: {
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
} break;
default:
{
return str.replace(
/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/,
process.platform === `win32` ? `` : `/`
);
}
break;
}
}
@ -150,8 +183,9 @@ const moduleWrapper = tsserver => {
// TypeScript already does local loads and if this code is running the user trusts the workspace
// https://github.com/microsoft/vscode/issues/45856
const ConfiguredProject = tsserver.server.ConfiguredProject;
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } =
ConfiguredProject.prototype;
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
this.projectService.allowLocalPluginLoads = true;
return originalEnablePluginsWithOptions.apply(this, arguments);
};
@ -161,12 +195,13 @@ const moduleWrapper = tsserver => {
// like an absolute path of ours and normalize it.
const Session = tsserver.server.Session;
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
const { onMessage: originalOnMessage, send: originalSend } =
Session.prototype;
let hostInfo = `unknown`;
Object.assign(Session.prototype, {
onMessage(/** @type {string | object} */ message) {
const isStringMessage = typeof message === 'string';
const isStringMessage = typeof message === "string";
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
if (
@ -177,10 +212,12 @@ const moduleWrapper = tsserver => {
) {
hostInfo = parsedMessage.arguments.hostInfo;
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
// The RegExp from https://semver.org/ but without the caret at the start
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
) ?? []).map(Number)
const [, major, minor] = (
process.env.VSCODE_IPC_HOOK.match(
// The RegExp from https://semver.org/ but without the caret at the start
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
) ?? []
).map(Number);
if (major === 1) {
if (minor < 61) {
@ -194,21 +231,31 @@ const moduleWrapper = tsserver => {
}
}
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
return typeof value === 'string' ? fromEditorPath(value) : value;
});
const processedMessageJSON = JSON.stringify(
parsedMessage,
(key, value) => {
return typeof value === "string" ? fromEditorPath(value) : value;
}
);
return originalOnMessage.call(
this,
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
isStringMessage
? processedMessageJSON
: JSON.parse(processedMessageJSON)
);
},
send(/** @type {any} */ msg) {
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
return typeof value === `string` ? toEditorPath(value) : value;
})));
}
return originalSend.call(
this,
JSON.parse(
JSON.stringify(msg, (key, value) => {
return typeof value === `string` ? toEditorPath(value) : value;
})
)
);
},
});
return tsserver;

View File

@ -1,29 +1,31 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = createRequire(absPnpApiPath);
const moduleWrapper = tsserver => {
const moduleWrapper = (tsserver) => {
if (!process.versions.pnp) {
return tsserver;
}
const {isAbsolute} = require(`path`);
const { isAbsolute } = require(`path`);
const pnpApi = require(`pnpapi`);
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
const isPortal = str => str.startsWith("portal:/");
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
const isPortal = (str) => str.startsWith("portal:/");
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
return `${locator.name}@${locator.reference}`;
}));
const dependencyTreeRoots = new Set(
pnpApi.getDependencyTreeRoots().map((locator) => {
return `${locator.name}@${locator.reference}`;
})
);
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
// doesn't understand. This layer makes sure to remove the protocol
@ -31,7 +33,11 @@ const moduleWrapper = tsserver => {
function toEditorPath(str) {
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
if (
isAbsolute(str) &&
!str.match(/^\^?(zip:|\/zip\/)/) &&
(str.match(/\.zip\//) || isVirtual(str))
) {
// We also take the opportunity to turn virtual paths into physical ones;
// this makes it much easier to work with workspaces that list peer
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
@ -45,7 +51,11 @@ const moduleWrapper = tsserver => {
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
if (resolved) {
const locator = pnpApi.findPackageLocator(resolved);
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
if (
locator &&
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) ||
isPortal(locator.reference))
) {
str = resolved;
}
}
@ -73,41 +83,55 @@ const moduleWrapper = tsserver => {
// Before | ^/zip/c:/foo/bar.zip/package.json
// After | ^/zip//c:/foo/bar.zip/package.json
//
case `vscode <1.61`: {
str = `^zip:${str}`;
} break;
case `vscode <1.61`:
{
str = `^zip:${str}`;
}
break;
case `vscode <1.66`: {
str = `^/zip/${str}`;
} break;
case `vscode <1.66`:
{
str = `^/zip/${str}`;
}
break;
case `vscode <1.68`: {
str = `^/zip${str}`;
} break;
case `vscode <1.68`:
{
str = `^/zip${str}`;
}
break;
case `vscode`: {
str = `^/zip/${str}`;
} break;
case `vscode`:
{
str = `^/zip/${str}`;
}
break;
// To make "go to definition" work,
// We have to resolve the actual file system path from virtual path
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
case `coc-nvim`: {
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = resolve(`zipfile:${str}`);
} break;
case `coc-nvim`:
{
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = resolve(`zipfile:${str}`);
}
break;
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
// We have to resolve the actual file system path from virtual path,
// everything else is up to neovim
case `neovim`: {
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = `zipfile://${str}`;
} break;
case `neovim`:
{
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
str = `zipfile://${str}`;
}
break;
default: {
str = `zip:${str}`;
} break;
default:
{
str = `zip:${str}`;
}
break;
}
} else {
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
@ -119,26 +143,35 @@ const moduleWrapper = tsserver => {
function fromEditorPath(str) {
switch (hostInfo) {
case `coc-nvim`: {
str = str.replace(/\.zip::/, `.zip/`);
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
// So in order to convert it back, we use .* to match all the thing
// before `zipfile:`
return process.platform === `win32`
? str.replace(/^.*zipfile:\//, ``)
: str.replace(/^.*zipfile:/, ``);
} break;
case `coc-nvim`:
{
str = str.replace(/\.zip::/, `.zip/`);
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
// So in order to convert it back, we use .* to match all the thing
// before `zipfile:`
return process.platform === `win32`
? str.replace(/^.*zipfile:\//, ``)
: str.replace(/^.*zipfile:/, ``);
}
break;
case `neovim`: {
str = str.replace(/\.zip::/, `.zip/`);
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
return str.replace(/^zipfile:\/\//, ``);
} break;
case `neovim`:
{
str = str.replace(/\.zip::/, `.zip/`);
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
return str.replace(/^zipfile:\/\//, ``);
}
break;
case `vscode`:
default: {
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
} break;
default:
{
return str.replace(
/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/,
process.platform === `win32` ? `` : `/`
);
}
break;
}
}
@ -150,8 +183,9 @@ const moduleWrapper = tsserver => {
// TypeScript already does local loads and if this code is running the user trusts the workspace
// https://github.com/microsoft/vscode/issues/45856
const ConfiguredProject = tsserver.server.ConfiguredProject;
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } =
ConfiguredProject.prototype;
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
this.projectService.allowLocalPluginLoads = true;
return originalEnablePluginsWithOptions.apply(this, arguments);
};
@ -161,12 +195,13 @@ const moduleWrapper = tsserver => {
// like an absolute path of ours and normalize it.
const Session = tsserver.server.Session;
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
const { onMessage: originalOnMessage, send: originalSend } =
Session.prototype;
let hostInfo = `unknown`;
Object.assign(Session.prototype, {
onMessage(/** @type {string | object} */ message) {
const isStringMessage = typeof message === 'string';
const isStringMessage = typeof message === "string";
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
if (
@ -177,10 +212,12 @@ const moduleWrapper = tsserver => {
) {
hostInfo = parsedMessage.arguments.hostInfo;
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
// The RegExp from https://semver.org/ but without the caret at the start
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
) ?? []).map(Number)
const [, major, minor] = (
process.env.VSCODE_IPC_HOOK.match(
// The RegExp from https://semver.org/ but without the caret at the start
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
) ?? []
).map(Number);
if (major === 1) {
if (minor < 61) {
@ -194,21 +231,31 @@ const moduleWrapper = tsserver => {
}
}
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
return typeof value === 'string' ? fromEditorPath(value) : value;
});
const processedMessageJSON = JSON.stringify(
parsedMessage,
(key, value) => {
return typeof value === "string" ? fromEditorPath(value) : value;
}
);
return originalOnMessage.call(
this,
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
isStringMessage
? processedMessageJSON
: JSON.parse(processedMessageJSON)
);
},
send(/** @type {any} */ msg) {
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
return typeof value === `string` ? toEditorPath(value) : value;
})));
}
return originalSend.call(
this,
JSON.parse(
JSON.stringify(msg, (key, value) => {
return typeof value === `string` ? toEditorPath(value) : value;
})
)
);
},
});
return tsserver;

View File

@ -1,8 +1,8 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire} = require(`module`);
const {resolve} = require(`path`);
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const relPnpApiPath = "../../../../.pnp.cjs";

View File

@ -1,2 +1,2 @@
/*
Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src *; img-src * data: blob:; font-src https://fonts.gstatic.com; media-src * blob:; script-src 'self';
Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self';

View File

@ -1,21 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Nostr live streaming" />
<link rel="apple-touch-icon" href="/logo.png" />
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />
<title>zap.stream</title>
</head>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Nostr live streaming" />
<link rel="apple-touch-icon" href="/logo.png" />
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />
<title>zap.stream</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div id="root"></div>
</body>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,3 +1 @@
[
{ "id": "nsfw", "text": "NSFW" }
]
[{ "id": "nsfw", "text": "NSFW" }]

View File

@ -65,7 +65,7 @@ export function ChatMessage({
inView?.isIntersecting ? ev.pubkey : undefined
);
const shouldShowMuteButton =
ev.pubkey !== streamer && ev.pubkey != login?.pubkey;
ev.pubkey !== streamer && ev.pubkey !== login?.pubkey;
const zapTarget = profile?.lud16 ?? profile?.lud06;
const zaps = useMemo(() => {
return reactions
@ -111,8 +111,8 @@ export function ChatMessage({
const pub = login?.publisher();
if (emoji.native) {
reply = await pub?.react(ev, emoji.native || "+1");
} else {
const e = getEmojiById(emoji.id!);
} else if (emoji.id) {
const e = getEmojiById(emoji.id);
if (e) {
reply = await pub?.generic((eb) => {
return eb
@ -120,7 +120,7 @@ export function ChatMessage({
.content(`:${emoji.id}:`)
.tag(["e", ev.id])
.tag(["p", ev.pubkey])
.tag(["emoji", e.at(1)!, e.at(2)!]);
.tag(["emoji", e[1], e[2]]);
});
}
}
@ -141,7 +141,7 @@ export function ChatMessage({
setShowEmojiPicker(!showEmojiPicker);
}
async function muteUser(e: React.MouseEvent) {
function muteUser(e: React.MouseEvent) {
e.stopPropagation();
mute();
}
@ -194,7 +194,7 @@ export function ChatMessage({
<div className="message-reaction-container">
{isCustomEmojiReaction && emoji ? (
<span className="message-reaction">
<EmojiComponent name={emoji.at(1)!} url={emoji.at(2)!} />
<EmojiComponent name={emoji[1]} url={emoji[2]} />
</span>
) : (
<span className="message-reaction">{e}</span>

View File

@ -12,7 +12,9 @@ export function LoggedInFollowButton({
value: string;
}) {
const login = useLogin();
const { tags, content, timestamp } = login!.follows;
if (!login) return;
const { tags, content, timestamp } = login.follows;
const follows = tags.filter((t) => t.at(0) === tag);
const isFollowing = follows.find((t) => t.at(1) === value);

View File

@ -52,7 +52,7 @@
}
.live-chat > .write-message > div:nth-child(1) {
height: 32px;
height: 40px;
flex-grow: 1;
}
@ -325,8 +325,6 @@
text-transform: lowercase;
color: #fff;
font-size: 12px;
font-family: Outfit;
font-style: normal;
font-weight: 500;
line-height: 18px;
}

View File

@ -8,7 +8,7 @@ import {
parseZap,
encodeTLV,
} from "@snort/system";
import { unixNow } from "@snort/shared";
import { unixNow, unwrap } from "@snort/shared";
import { useEffect, useMemo } from "react";
import uniqBy from "lodash.uniqby";
@ -96,7 +96,7 @@ export function LiveChat({
const login = useLogin();
useEffect(() => {
const pubkeys = [
...new Set(feed.zaps.flatMap((a) => [a.pubkey, findTag(a, "p")!])),
...new Set(feed.zaps.flatMap((a) => [a.pubkey, unwrap(findTag(a, "p"))])),
];
System.ProfileLoader.TrackMetadata(pubkeys);
return () => System.ProfileLoader.UntrackMetadata(pubkeys);

View File

@ -9,6 +9,7 @@ import { StreamEditor, StreamEditorProps } from "./stream-editor";
import { useNavigate } from "react-router-dom";
import { eventLink, findTag } from "utils";
import { NostrProviderDialog } from "./nostr-provider-dialog";
import { unwrap } from "@snort/shared";
function NewStream({ ev, onFinish }: StreamEditorProps) {
const providers = useStreamProvider();
@ -19,7 +20,7 @@ function NewStream({ ev, onFinish }: StreamEditorProps) {
if (!currentProvider) {
setCurrentProvider(
ev !== undefined
? providers.find((a) => a.name.toLowerCase() === "manual")!
? unwrap(providers.find((a) => a.name.toLowerCase() === "manual"))
: providers.at(0)
);
}
@ -35,7 +36,11 @@ function NewStream({ ev, onFinish }: StreamEditorProps) {
onFinish={(ex) => {
currentProvider.updateStreamInfo(ex);
if (!ev) {
if (findTag(ex, "content-warning") && __XXX_HOST && __XXX === false) {
if (
findTag(ex, "content-warning") &&
__XXX_HOST &&
__XXX === false
) {
location.href = `${__XXX_HOST}/${eventLink(ex)}`;
} else {
navigate(`/${eventLink(ex)}`, {

View File

@ -6,7 +6,7 @@
.send-zap .amounts {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-columns: repeat(6, 1fr);
justify-content: space-evenly;
gap: 8px;
}

View File

@ -43,10 +43,11 @@ export function SendZaps({
targetName,
onFinish,
}: SendZapsProps) {
const UsdRate = 30_000;
const UsdRate = 28_000;
const satsAmounts = [
100, 1_000, 5_000, 10_000, 50_000, 100_000, 500_000, 1_000_000,
21, 69, 121, 221, 420, 1_000, 2_100, 5_000, 6_666, 10_000, 21_000, 42_000,
69_000, 100_000, 210_000, 500_000, 1_000_000,
];
const usdAmounts = [0.05, 0.5, 2, 5, 10, 50, 100, 200];
const [isFiat, setIsFiat] = useState(false);

View File

@ -1,9 +1,11 @@
import { Menu, MenuItem } from "@szhsin/react-menu";
import * as Dialog from "@radix-ui/react-dialog";
import { unwrap } from "@snort/shared";
import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
import { Icon } from "./icon";
import { useState } from "react";
import { Textarea } from "./textarea";
import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
import { findTag } from "utils";
import AsyncButton from "./async-button";
import { useLogin } from "hooks/login";
@ -18,7 +20,7 @@ export function ShareMenu({ ev }: { ev: NostrEvent }) {
const naddr = encodeTLV(
NostrPrefix.Address,
findTag(ev, "d")!,
unwrap(findTag(ev, "d")),
undefined,
ev.kind,
ev.pubkey

View File

@ -100,7 +100,7 @@ function Card({ canEdit, ev, cards }: CardProps) {
);
function findTagByIdentifier(d: string) {
return tags.find((t) => t.at(1)!.endsWith(`:${d}`));
return tags.find((t) => t[1].endsWith(`:${d}`));
}
const [dropStyle, dropRef] = useDrop(
@ -293,7 +293,7 @@ function EditCard({ card, cards }: EditCardProps) {
async function onCancel() {
const pub = login?.publisher();
if (pub) {
const newTags = tags.filter((t) => !t.at(1)!.endsWith(`:${identifier}`));
const newTags = tags.filter((t) => !t[1].endsWith(`:${identifier}`));
const userCardsEv = await pub.generic((eb) => {
eb.kind(USER_CARDS).content("");
for (const tag of newTags) {
@ -408,7 +408,7 @@ export function StreamCardEditor({ pubkey, tags }: StreamCardEditorProps) {
<>
<div className="stream-cards">
{cards.map((ev) => (
<Card canEdit={isEditing} cards={cards} key={ev.id} ev={ev!} />
<Card canEdit={isEditing} cards={cards} key={ev.id} ev={ev} />
))}
{isEditing && <AddCard cards={cards} />}
</div>
@ -433,7 +433,7 @@ export function ReadOnlyStreamCards({ host }: StreamCardsProps) {
return (
<div className="stream-cards">
{cards.map((ev) => (
<Card cards={cards} key={ev!.id} ev={ev!} />
<Card cards={cards} key={ev.id} ev={ev} />
))}
</div>
);

View File

@ -53,11 +53,11 @@ export function Textarea({ emojis, ...props }: TextareaProps) {
const userDataProvider = async (token: string) => {
const cache = System.ProfileLoader.Cache;
if (cache instanceof UserProfileCache) {
return cache.search(token);
return await cache.search(token);
}
};
const emojiDataProvider = async (token: string) => {
const emojiDataProvider = (token: string) => {
const results = emojis
.map((t) => {
return {

View File

@ -0,0 +1,84 @@
/* latin-ext */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(outfit_400_latin-ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(outfit_400_latin.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(outfit_500_latin-ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(outfit_500_latin.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(outfit_600_latin-ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(outfit_600_latin.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(outfit_700_latin-ext.woff2) format("woff2");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Outfit";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(outfit_700_latin.woff2) format("woff2");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -29,7 +29,7 @@ export function useUserCards(
const subRelated = useMemo(() => {
if (!pubkey) return null;
const splitted = related.map((t) => t.at(1)!.split(":"));
const splitted = related.map((t) => t[1].split(":"));
const authors = splitted
.map((s) => s.at(1))
.filter((s) => s)
@ -58,7 +58,7 @@ export function useUserCards(
const cards = useMemo(() => {
return related
.map((t) => {
const [k, pubkey, identifier] = t.at(1)!.split(":");
const [k, pubkey, identifier] = t[1].split(":");
const kind = Number(k);
return (data ?? []).find(
(e) =>
@ -104,7 +104,7 @@ export function useCards(pubkey: string, leaveOpen = false): TaggedRawEvent[] {
const subRelated = useMemo(() => {
if (!pubkey) return null;
const splitted = related.map((t) => t.at(1)!.split(":"));
const splitted = related.map((t) => t[1].split(":"));
const authors = splitted
.map((s) => s.at(1))
.filter((s) => s)
@ -134,7 +134,7 @@ export function useCards(pubkey: string, leaveOpen = false): TaggedRawEvent[] {
const cards = useMemo(() => {
return related
.map((t) => {
const [k, pubkey, identifier] = t.at(1)!.split(":");
const [k, pubkey, identifier] = t[1].split(":");
const kind = Number(k);
return cardEvents.find(
(e) =>

View File

@ -45,7 +45,7 @@ export function useUserEmojiPacks(pubkey?: string, userEmoji?: Tags) {
const subRelated = useMemo(() => {
if (!pubkey) return null;
const splitted = related.map((t) => t.at(1)!.split(":"));
const splitted = related.map((t) => t[1].split(":"));
const authors = splitted
.map((s) => s.at(1))
.filter((s) => s)

View File

@ -9,6 +9,7 @@ import {
parseZap,
} from "@snort/system";
import { useRequestBuilder } from "@snort/system-react";
import { unwrap } from "@snort/shared";
import { GOAL } from "const";
import { System } from "index";
@ -36,14 +37,15 @@ export function useZaps(goal: NostrEvent, leaveOpen = false) {
);
}
export function useZapGoal(host: string, link: NostrLink, leaveOpen = false) {
export function useZapGoal(host: string, link?: NostrLink, leaveOpen = false) {
const sub = useMemo(() => {
if (!link) return null;
const b = new RequestBuilder(`goals:${host.slice(0, 12)}`);
b.withOptions({ leaveOpen });
b.withFilter()
.kinds([GOAL])
.authors([host])
.tag("a", [`${link.kind}:${link.author!}:${link.id}`]);
.tag("a", [`${link.kind}:${unwrap(link.author)}:${link.id}`]);
return b;
}, [link, leaveOpen]);

View File

@ -1,5 +1,6 @@
import "@szhsin/react-menu/dist/index.css";
import "./index.css";
import "./fonts/outfit/outfit.css";
import React from "react";
import ReactDOM from "react-dom/client";
@ -10,7 +11,7 @@ import { RootPage } from "pages/root";
import { TagPage } from "pages/tag";
import { LayoutPage } from "pages/layout";
import { ProfilePage } from "pages/profile-page";
import { StreamPage } from "pages/stream-page";
import { StreamPageHandler } from "pages/stream-page";
import { ChatPopout } from "pages/chat-popout";
import { LoginStore } from "login";
import { StreamProvidersPage } from "pages/providers";
@ -53,7 +54,7 @@ const router = createBrowserRouter([
},
{
path: "/:id",
element: <StreamPage />,
element: <StreamPageHandler />,
},
{
path: "/providers/:id?",

View File

@ -1,6 +1,6 @@
import { bytesToHex } from "@noble/curves/abstract/utils";
import { schnorr } from "@noble/curves/secp256k1";
import { ExternalStore } from "@snort/shared";
import { ExternalStore, unwrap } from "@snort/shared";
import { EventPublisher, Nip7Signer, PrivateKeySigner } from "@snort/system";
import type { EmojiPack, Tags } from "types";
@ -131,7 +131,7 @@ export function getPublisher(session: LoginSession) {
}
case LoginType.PrivateKey: {
return new EventPublisher(
new PrivateKeySigner(session.privateKey!),
new PrivateKeySigner(unwrap(session.privateKey)),
session.pubkey
);
}

View File

@ -2,12 +2,13 @@ import "./chat-popout.css";
import { LiveChat } from "element/live-chat";
import { useParams } from "react-router-dom";
import { NostrPrefix, encodeTLV, parseNostrLink } from "@snort/system";
import { unwrap } from "@snort/shared";
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
import { findTag } from "utils";
export function ChatPopout() {
const params = useParams();
const link = parseNostrLink(params.id!);
const link = parseNostrLink(unwrap(params.id));
const ev = useCurrentStreamFeed(link, true);
const lnk = parseNostrLink(

View File

@ -10,6 +10,7 @@ import {
encodeTLV,
} from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { unwrap } from "@snort/shared";
import { Profile } from "element/profile";
import { Icon } from "element/icon";
import { SendZapsDialog } from "element/send-zap";
@ -52,7 +53,7 @@ const defaultBanner = "https://void.cat/d/Hn1AdN5UKmceuDkgDW847q.webp";
export function ProfilePage() {
const navigate = useNavigate();
const params = useParams();
const link = parseNostrLink(params.npub!);
const link = parseNostrLink(unwrap(params.npub));
const placeholder = usePlaceholder(link.id);
const profile = useUserProfile(System, link.id);
const zapTarget = profile?.lud16 ?? profile?.lud06;

View File

@ -1,5 +1,6 @@
import "./stream-page.css";
import { parseNostrLink, TaggedRawEvent } from "@snort/system";
import { NostrLink, NostrPrefix, TaggedRawEvent, tryParseNostrLink } from "@snort/system";
import { fetchNip05Pubkey } from "@snort/shared";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import { Helmet } from "react-helmet";
@ -9,6 +10,7 @@ import {
findTag,
getEventFromLocationState,
getHost,
hexToBech32,
} from "utils";
import { Profile, getName } from "element/profile";
import { LiveChat } from "element/live-chat";
@ -31,6 +33,7 @@ import {
isContentWarningAccepted,
} from "element/content-warning";
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
import { useEffect, useState } from "react";
function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
const login = useLogin();
@ -110,14 +113,41 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
);
}
export function StreamPage() {
export function StreamPageHandler() {
const params = useParams();
const location = useLocation();
const evPreload = getEventFromLocationState(location.state);
const link = parseNostrLink(params.id!);
const [link, setLink] = useState<NostrLink>();
useEffect(() => {
if (params.id) {
const parsedLink = tryParseNostrLink(params.id);
if (parsedLink) {
setLink(parsedLink);
} else {
const [handle, domain] = (params.id.includes("@") ? params.id : `${params.id}@zap.stream`).split("@");
fetchNip05Pubkey(handle, domain).then(d => {
if (d) {
setLink({
id: d,
type: NostrPrefix.PublicKey,
encode: () => hexToBech32(NostrPrefix.PublicKey, d)
} as NostrLink);
}
})
}
}
}, [params.id]);
if (link) {
return <StreamPage link={link} evPreload={evPreload} />
}
}
export function StreamPage({ link, evPreload }: { evPreload?: NostrEvent, link: NostrLink }) {
const ev = useCurrentStreamFeed(link, true, evPreload);
const host = getHost(ev);
const goal = useZapGoal(host, link, true);
const goal = useZapGoal(host, createNostrLink(ev), true);
const title = findTag(ev, "title");
const summary = findTag(ev, "summary");

View File

@ -1,5 +1,6 @@
import "./tag.css";
import { useParams } from "react-router-dom";
import { unwrap } from "@snort/shared";
import { VideoTile } from "element/video-tile";
import { FollowTagButton } from "element/follow-button";
@ -12,7 +13,7 @@ export function TagPage() {
<div className="tag-page">
<div className="tag-page-header">
<h1>#{tag}</h1>
<FollowTagButton tag={tag!} />
<FollowTagButton tag={unwrap(tag)} />
</div>
<div className="video-grid">
{live.map((e) => (

View File

@ -58,7 +58,7 @@ export class OwncastProvider implements StreamProvider {
body?: unknown
): Promise<T> {
const rsp = await fetch(`${this.#url}${path}`, {
method: method,
method,
body: body ? JSON.stringify(body) : undefined,
headers: {
"content-type": "application/json",

View File

@ -103,7 +103,7 @@ export class Nip103StreamProvider implements StreamProvider {
.tag(["method", method]);
});
const rsp = await fetch(u, {
method: method,
method,
body: body ? JSON.stringify(body) : undefined,
headers: {
"content-type": "application/json",

View File

@ -109,7 +109,7 @@ export function getHost(ev?: NostrEvent) {
);
}
export async function openFile(): Promise<File | undefined> {
export function openFile(): Promise<File | undefined> {
return new Promise((resolve) => {
const elm = document.createElement("input");
elm.type = "file";

View File

@ -503,7 +503,7 @@ export class WISH extends TypedEventTarget {
},
});
const body = await resp.text();
if (resp.status != 201) {
if (resp.status !== 201) {
throw new Error(`Unexpected status code ${resp.status}: ${body}`);
}
@ -611,7 +611,7 @@ export class WISH extends TypedEventTarget {
throw new Error(`Unexpected status code ${resp.status}: ${body}`);
}
async WithEndpoint(endpoint: string, trickle: boolean) {
WithEndpoint(endpoint: string, trickle: boolean) {
if (endpoint === "") {
throw new Error("Endpoint cannot be empty");
}
@ -637,7 +637,7 @@ export class WISH extends TypedEventTarget {
method: "DELETE",
mode: "cors",
});
if (resp.status != 200) {
if (resp.status !== 200) {
const body = await resp.text();
throw new Error(`Unexpected status code ${resp.status}: ${body}`);
}

View File

@ -68,8 +68,8 @@ const config = {
}),
new webpack.DefinePlugin({
__XXX: process.env["__XXX"] || JSON.stringify(false),
__XXX_HOST: JSON.stringify("https://xxzap.com")
})
__XXX_HOST: JSON.stringify("https://xxzap.com"),
}),
],
module: {
rules: [
@ -89,9 +89,12 @@ const config = {
babelrc: false,
configFile: false,
presets: [
["@babel/preset-env", {
targets: "defaults"
}],
[
"@babel/preset-env",
{
targets: "defaults",
},
],
["@babel/preset-react", { runtime: "automatic" }],
"@babel/preset-typescript",
],
@ -106,7 +109,7 @@ const config = {
],
},
},
require.resolve("ts-loader")
require.resolve("ts-loader"),
],
},
{
@ -148,7 +151,7 @@ const config = {
aliasFields: ["browser"],
extensions: ["...", ".tsx", ".ts", ".jsx", ".js"],
modules: ["...", __dirname, path.resolve(__dirname, "src")],
fallback: { "crypto": false }
fallback: { crypto: false },
},
};