init
This commit is contained in:
commit
2f0e0e5c45
11
.eslintrc.cjs
Normal file
11
.eslintrc.cjs
Normal file
@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"],
|
||||
ignorePatterns: ["dist", ".eslintrc.cjs"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["react-refresh"],
|
||||
rules: {
|
||||
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
|
||||
},
|
||||
};
|
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["arcanis.vscode-zipfs", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||
}
|
893
.yarn/releases/yarn-4.0.2.cjs
vendored
Executable file
893
.yarn/releases/yarn-4.0.2.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
20
.yarn/sdks/eslint/bin/eslint.js
vendored
Executable file
20
.yarn/sdks/eslint/bin/eslint.js
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real eslint/bin/eslint.js your application uses
|
||||
module.exports = absRequire(`eslint/bin/eslint.js`);
|
20
.yarn/sdks/eslint/lib/api.js
vendored
Normal file
20
.yarn/sdks/eslint/lib/api.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real eslint your application uses
|
||||
module.exports = absRequire(`eslint`);
|
20
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
Normal file
20
.yarn/sdks/eslint/lib/unsupported-api.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/use-at-your-own-risk
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real eslint/use-at-your-own-risk your application uses
|
||||
module.exports = absRequire(`eslint/use-at-your-own-risk`);
|
14
.yarn/sdks/eslint/package.json
vendored
Normal file
14
.yarn/sdks/eslint/package.json
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "eslint",
|
||||
"version": "8.54.0-sdk",
|
||||
"main": "./lib/api.js",
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"eslint": "./bin/eslint.js"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": "./lib/api.js",
|
||||
"./use-at-your-own-risk": "./lib/unsupported-api.js"
|
||||
}
|
||||
}
|
5
.yarn/sdks/integrations.yml
vendored
Normal file
5
.yarn/sdks/integrations.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# This file is automatically generated by @yarnpkg/sdks.
|
||||
# Manual changes might be lost!
|
||||
|
||||
integrations:
|
||||
- vscode
|
20
.yarn/sdks/prettier/bin/prettier.cjs
vendored
Executable file
20
.yarn/sdks/prettier/bin/prettier.cjs
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier/bin/prettier.cjs
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real prettier/bin/prettier.cjs your application uses
|
||||
module.exports = absRequire(`prettier/bin/prettier.cjs`);
|
20
.yarn/sdks/prettier/index.cjs
vendored
Normal file
20
.yarn/sdks/prettier/index.cjs
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real prettier your application uses
|
||||
module.exports = absRequire(`prettier`);
|
7
.yarn/sdks/prettier/package.json
vendored
Normal file
7
.yarn/sdks/prettier/package.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "prettier",
|
||||
"version": "3.1.0-sdk",
|
||||
"main": "./index.cjs",
|
||||
"type": "commonjs",
|
||||
"bin": "./bin/prettier.cjs"
|
||||
}
|
20
.yarn/sdks/typescript/bin/tsc
vendored
Executable file
20
.yarn/sdks/typescript/bin/tsc
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsc
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/bin/tsc your application uses
|
||||
module.exports = absRequire(`typescript/bin/tsc`);
|
20
.yarn/sdks/typescript/bin/tsserver
vendored
Executable file
20
.yarn/sdks/typescript/bin/tsserver
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsserver
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/bin/tsserver your application uses
|
||||
module.exports = absRequire(`typescript/bin/tsserver`);
|
20
.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
20
.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsc.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsc.js your application uses
|
||||
module.exports = absRequire(`typescript/lib/tsc.js`);
|
252
.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
252
.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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) => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
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 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
|
||||
// before forwarding it to TS, and to add it back on all returned paths.
|
||||
|
||||
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))) {
|
||||
// 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
|
||||
// file instances instead of the real ones.
|
||||
//
|
||||
// We only do this to modules owned by the the dependency tree roots.
|
||||
// This avoids breaking the resolution when jumping inside a vendor
|
||||
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||
// errors on react).
|
||||
//
|
||||
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))
|
||||
) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
str = normalize(str);
|
||||
|
||||
if (str.match(/\.zip\//)) {
|
||||
switch (hostInfo) {
|
||||
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||
// VSCode only adds it automatically for supported schemes,
|
||||
// so we have to do it manually for the `zip` scheme.
|
||||
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||
//
|
||||
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||
//
|
||||
// 2021-10-08: VSCode changed the format in 1.61.
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-04-06: VSCode changed the format in 1.66.
|
||||
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-05-06: VSCode changed the format in 1.68
|
||||
// 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.66`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode <1.68`:
|
||||
{
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
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 `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;
|
||||
}
|
||||
}
|
||||
|
||||
// Force enable 'allowLocalPluginLoads'
|
||||
// TypeScript tries to resolve plugins using a path relative to itself
|
||||
// which doesn't work when using the global cache
|
||||
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||
// 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 () {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
|
||||
// And here is the point where we hijack the VSCode <-> TS communications
|
||||
// by adding ourselves in the middle. We locate everything that looks
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
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 parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
typeof parsedMessage === `object` &&
|
||||
parsedMessage.arguments &&
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
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);
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
hostInfo += ` <1.61`;
|
||||
} else if (minor < 66) {
|
||||
hostInfo += ` <1.66`;
|
||||
} else if (minor < 68) {
|
||||
hostInfo += ` <1.68`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === "string" ? fromEditorPath(value) : value;
|
||||
});
|
||||
|
||||
return originalOnMessage.call(this, 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 tsserver;
|
||||
};
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserver.js your application uses
|
||||
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`));
|
252
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
252
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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) => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
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 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
|
||||
// before forwarding it to TS, and to add it back on all returned paths.
|
||||
|
||||
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))) {
|
||||
// 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
|
||||
// file instances instead of the real ones.
|
||||
//
|
||||
// We only do this to modules owned by the the dependency tree roots.
|
||||
// This avoids breaking the resolution when jumping inside a vendor
|
||||
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||
// errors on react).
|
||||
//
|
||||
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))
|
||||
) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
str = normalize(str);
|
||||
|
||||
if (str.match(/\.zip\//)) {
|
||||
switch (hostInfo) {
|
||||
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||
// VSCode only adds it automatically for supported schemes,
|
||||
// so we have to do it manually for the `zip` scheme.
|
||||
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||
//
|
||||
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||
//
|
||||
// 2021-10-08: VSCode changed the format in 1.61.
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-04-06: VSCode changed the format in 1.66.
|
||||
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-05-06: VSCode changed the format in 1.68
|
||||
// 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.66`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode <1.68`:
|
||||
{
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
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 `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;
|
||||
}
|
||||
}
|
||||
|
||||
// Force enable 'allowLocalPluginLoads'
|
||||
// TypeScript tries to resolve plugins using a path relative to itself
|
||||
// which doesn't work when using the global cache
|
||||
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||
// 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 () {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
|
||||
// And here is the point where we hijack the VSCode <-> TS communications
|
||||
// by adding ourselves in the middle. We locate everything that looks
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
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 parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
typeof parsedMessage === `object` &&
|
||||
parsedMessage.arguments &&
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
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);
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
hostInfo += ` <1.61`;
|
||||
} else if (minor < 66) {
|
||||
hostInfo += ` <1.66`;
|
||||
} else if (minor < 68) {
|
||||
hostInfo += ` <1.68`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === "string" ? fromEditorPath(value) : value;
|
||||
});
|
||||
|
||||
return originalOnMessage.call(this, 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 tsserver;
|
||||
};
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
|
||||
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`));
|
20
.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
20
.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
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);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript your application uses
|
||||
module.exports = absRequire(`typescript`);
|
10
.yarn/sdks/typescript/package.json
vendored
Normal file
10
.yarn/sdks/typescript/package.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "typescript",
|
||||
"version": "5.3.2-sdk",
|
||||
"main": "./lib/typescript.js",
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"tsc": "./bin/tsc",
|
||||
"tsserver": "./bin/tsserver"
|
||||
}
|
||||
}
|
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@ -0,0 +1 @@
|
||||
yarnPath: .yarn/releases/yarn-4.0.2.cjs
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default {
|
||||
// other rules...
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
project: ["./tsconfig.json", "./tsconfig.node.json"],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
17
index.html
Normal file
17
index.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Snort Community</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=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
36
package.json
Normal file
36
package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "snort_community",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 120
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^3.1.0",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
"packageManager": "yarn@4.0.2"
|
||||
}
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
23
public/icons.svg
Normal file
23
public/icons.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<svg id="award" width="62" height="62" viewBox="0 0 62 62" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_2660_40043" x1="31" y1="3.57143" x2="31" y2="58.4286" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5B2CB3"/>
|
||||
<stop offset="1" stop-color="#811EFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_2660_40043" x1="15.5594" y1="24.305" x2="46.433" y2="24.305" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AC88FF"/>
|
||||
<stop offset="1" stop-color="#7234FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="award-02">
|
||||
<rect x="1.85713" y="1.85714" width="58.2857" height="58.2857" rx="29.1429" fill="#AC88FF" fill-opacity="0.2"/>
|
||||
<rect x="1.85713" y="1.85714" width="58.2857" height="58.2857" rx="29.1429" stroke="url(#paint0_linear_2660_40043)" stroke-width="3.42857"/>
|
||||
<path id="Solid" d="M23.2006 52.4983L22.5639 50.9066L23.2006 52.4983L30.9963 49.38L38.7919 52.4983C39.8813 52.934 41.116 52.801 42.0876 52.1432C43.0592 51.4854 43.6412 50.3885 43.6412 49.2151V38.1015C46.467 35.038 48.1957 30.9408 48.1957 26.4427C48.1957 16.9437 40.4952 9.24329 30.9963 9.24329C21.4973 9.24329 13.7968 16.9437 13.7968 26.4427C13.7968 30.9408 15.5255 35.038 18.3513 38.1015V49.2151C18.3513 50.3885 18.9333 51.4854 19.9049 52.1432C20.8765 52.801 22.1112 52.934 23.2006 52.4983ZM27.2967 43.2429L25.4234 43.9922V42.7187C26.0332 42.9275 26.6584 43.1029 27.2967 43.2429ZM34.6958 43.2429C35.3341 43.1029 35.9593 42.9275 36.5691 42.7187V43.9922L34.6958 43.2429Z" fill="url(#paint1_linear_2660_40043)" stroke="#251250" stroke-width="3.42857" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path id="Ellipse 1595" d="M24.2557 14.6002C17.7766 18.3409 15.5567 26.6257 19.2974 33.1049L42.7604 19.5585C39.0196 13.0794 30.7348 10.8595 24.2557 14.6002Z" fill="white" fill-opacity="0.1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
56
src/award.tsx
Normal file
56
src/award.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
export default function AwardIcon({ size }: { size?: number }) {
|
||||
return (
|
||||
<svg width={size} height={size} viewBox="0 0 62 62" fill="none" className="award">
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_2660_40043"
|
||||
x1="31"
|
||||
y1="3.57143"
|
||||
x2="31"
|
||||
y2="58.4286"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#5B2CB3" />
|
||||
<stop offset="1" stop-color="#811EFF" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_2660_40043"
|
||||
x1="15.5594"
|
||||
y1="24.305"
|
||||
x2="46.433"
|
||||
y2="24.305"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#AC88FF" />
|
||||
<stop offset="1" stop-color="#7234FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="award-02">
|
||||
<rect x="1.85713" y="1.85714" width="58.2857" height="58.2857" rx="29.1429" fill="#AC88FF" fill-opacity="0.2" />
|
||||
<rect
|
||||
x="1.85713"
|
||||
y="1.85714"
|
||||
width="58.2857"
|
||||
height="58.2857"
|
||||
rx="29.1429"
|
||||
stroke="url(#paint0_linear_2660_40043)"
|
||||
strokeWidth="3.42857"
|
||||
/>
|
||||
<path
|
||||
id="Solid"
|
||||
d="M23.2006 52.4983L22.5639 50.9066L23.2006 52.4983L30.9963 49.38L38.7919 52.4983C39.8813 52.934 41.116 52.801 42.0876 52.1432C43.0592 51.4854 43.6412 50.3885 43.6412 49.2151V38.1015C46.467 35.038 48.1957 30.9408 48.1957 26.4427C48.1957 16.9437 40.4952 9.24329 30.9963 9.24329C21.4973 9.24329 13.7968 16.9437 13.7968 26.4427C13.7968 30.9408 15.5255 35.038 18.3513 38.1015V49.2151C18.3513 50.3885 18.9333 51.4854 19.9049 52.1432C20.8765 52.801 22.1112 52.934 23.2006 52.4983ZM27.2967 43.2429L25.4234 43.9922V42.7187C26.0332 42.9275 26.6584 43.1029 27.2967 43.2429ZM34.6958 43.2429C35.3341 43.1029 35.9593 42.9275 36.5691 42.7187V43.9922L34.6958 43.2429Z"
|
||||
fill="url(#paint1_linear_2660_40043)"
|
||||
stroke="#251250"
|
||||
strokeWidth="3.42857"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
id="Ellipse 1595"
|
||||
d="M24.2557 14.6002C17.7766 18.3409 15.5567 26.6257 19.2974 33.1049L42.7604 19.5585C39.0196 13.0794 30.7348 10.8595 24.2557 14.6002Z"
|
||||
fill="white"
|
||||
fill-opacity="0.1"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
43
src/index.css
Normal file
43
src/index.css
Normal file
@ -0,0 +1,43 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.text-nostr-fade {
|
||||
background: linear-gradient(90deg, #ac88ff 0.16%, #7234ff 99.84%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.award {
|
||||
fill: rgba(172, 136, 255, 0.2);
|
||||
}
|
||||
|
||||
.bg-nostr-haze {
|
||||
background: linear-gradient(78deg, rgba(140, 91, 251, 0.25) 11.54%, rgba(140, 91, 251, 0.16) 85.45%);
|
||||
backdrop-filter: blur(8.571428298950195px);
|
||||
}
|
||||
|
||||
button.primary {
|
||||
display: flex;
|
||||
padding: 10px 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-radius: 1000px;
|
||||
background: linear-gradient(90deg, #ef9644 0%, #7b41f6 100%);
|
||||
}
|
||||
|
||||
.overflow-wrap {
|
||||
overflow-wrap: break-word;
|
||||
}
|
75
src/layout.tsx
Normal file
75
src/layout.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import AwardIcon from "./award";
|
||||
import Radar from "./radar";
|
||||
|
||||
export function Layout() {
|
||||
return (
|
||||
<div className="flex flex-col gap-6 container mx-auto text-center">
|
||||
<div className="flex items-center rounded-full py-2 px-8 mx-auto gap-6 border border-nostr mt-8 w-fit">
|
||||
<div className="font-semibold">Snort</div>
|
||||
<div>Overview</div>
|
||||
<div>Benefits</div>
|
||||
<div>Current Leaders</div>
|
||||
<div>
|
||||
<button className="primary">Become a Leader</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative h-[800px] flex flex-col items-center justify-center">
|
||||
<Radar />
|
||||
<div className="flex flex-col items-center gap-6 z-10 overflow-wrap">
|
||||
<div className="p-4 rounded-3xl border border-nostr w-fit bg-nostr-haze">
|
||||
<AwardIcon size={64} />
|
||||
</div>
|
||||
<h1 className="text-3xl font-semibold">
|
||||
Grow your local community as a nostr <span className="text-nostr-fade">community leader</span>
|
||||
</h1>
|
||||
<h2 className="text-gray-400 text-lg">
|
||||
Community leaders is a new program that empowers leaders worldwide to build local nostr communities, and
|
||||
earn recognition.
|
||||
</h2>
|
||||
<button className="primary">Become a Leader</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1 rounded-xl bg-zinc-950 p-4 border border-zinc-900 flex flex-col gap-4">
|
||||
<h2 className="text-2xl">Client-wide recognition</h2>
|
||||
<h3>Leaders are recognized across Snort, and hopefully other clients to come</h3>
|
||||
</div>
|
||||
<div className="flex-1 rounded-xl bg-zinc-950 p-4 border border-zinc-900 flex flex-col gap-4">
|
||||
<h2 className="text-2xl">Exclusive discussions</h2>
|
||||
<h3>Join optional calls with other community leaders to share ideas and draw inspiration</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-xl bg-zinc-950 p-4 border border-zinc-900 flex flex-col gap-4">
|
||||
<h2 className="text-2xl">Earn referral income</h2>
|
||||
<h3>Community leaders earn subscription referral revenue for every person who signs up to the PRO plan</h3>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl">Onboard new people, have fun, and get the recognition you deserve</h1>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1 rounded-xl bg-zinc-950 p-4 border border-zinc-900 flex flex-col gap-4">
|
||||
<h2 className="text-2xl">Organize local meetups</h2>
|
||||
<h3>Meetups are a great way to get your local community involved in nostr.</h3>
|
||||
</div>
|
||||
<div className="flex-1 rounded-xl bg-zinc-950 p-4 border border-zinc-900 flex flex-col gap-4">
|
||||
<h2 className="text-2xl">Create educational content</h2>
|
||||
<h3>Video, long form, newsletters, presentations all go a long way!</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1 rounded-xl bg-zinc-950 p-4 border border-zinc-900 flex flex-col gap-4">
|
||||
<h2 className="text-2xl">Stream content</h2>
|
||||
<h3>Reach the younger generations with streamed content and video on demand.</h3>
|
||||
</div>
|
||||
<div className="flex-1 rounded-xl bg-zinc-950 p-4 border border-zinc-900 flex flex-col gap-4">
|
||||
<h2 className="text-2xl">Create guides in your local language</h2>
|
||||
<h3>Love writing? Create guides in your language to help onboard the local community.</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<h1 className="text-3xl">Join the community leader program. It’s easy!</h1>
|
||||
<button className="primary">Become a Leader</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
11
src/main.tsx
Normal file
11
src/main.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
|
||||
import "./index.css";
|
||||
import { Layout } from "./layout";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<Layout />
|
||||
</React.StrictMode>,
|
||||
);
|
108
src/radar.tsx
Normal file
108
src/radar.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
let mouseX = 0;
|
||||
let mouseY = 0;
|
||||
document.addEventListener("mousemove", function (event) {
|
||||
mouseX = event.clientX;
|
||||
mouseY = event.clientY;
|
||||
});
|
||||
|
||||
export default function Radar() {
|
||||
function animate() {
|
||||
const canvas = document.getElementById("radarCanvas") as HTMLCanvasElement | null;
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
const avatars = [
|
||||
{ image: new Image(), angle: -45, dir: -1, speed: 0.1, level: 2, size: 60 },
|
||||
{ image: new Image(), angle: -140, dir: 1, speed: 0.2, level: 3, size: 60 },
|
||||
{ image: new Image(), angle: -150, dir: 1, speed: 0.2, level: 2, size: 60 },
|
||||
// Add more avatars with their starting angles here
|
||||
];
|
||||
|
||||
avatars.forEach((a) => {
|
||||
a.image.src = "https://void.cat/d/U3WxrFBhgyR4TmZzR5xpGE";
|
||||
});
|
||||
function drawRadar() {
|
||||
if (!ctx || !canvas) return;
|
||||
const centerX = canvas.width / 2;
|
||||
const centerY = canvas.height / 2;
|
||||
const rings = 3;
|
||||
const radius = (canvas.width / rings) * 2;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Draw radar circles
|
||||
for (let i = 1; i <= rings; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(centerX, centerY, (radius * i) / (rings + 1), 0, 2 * Math.PI);
|
||||
ctx.strokeStyle = `#5B2CB3${(255 - i * 80).toString(16)}`;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const fmx = mouseX - rect.left;
|
||||
const fmy = mouseY - rect.top;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(fmx, fmy, 10, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw avatars
|
||||
avatars.forEach((avatar) => {
|
||||
const x = centerX + ((radius * avatar.level) / (rings + 1)) * Math.cos((avatar.angle * Math.PI) / 180);
|
||||
const y = centerY + ((radius * avatar.level) / (rings + 1)) * Math.sin((avatar.angle * Math.PI) / 180);
|
||||
const fx = x + rect.left;
|
||||
const fy = y + rect.top;
|
||||
|
||||
const distance = Math.sqrt(Math.pow(mouseX - fx, 2) + Math.pow(mouseY - fy, 2));
|
||||
const scaleFactor = Math.min(2, Math.max(0.5, 2 - distance / canvas.width));
|
||||
const avatarSize = avatar.size * scaleFactor;
|
||||
const avatarRadius = avatarSize / 2;
|
||||
|
||||
if (fmx > 0 && fmy > 0 && fmx < canvas.width && fmy < canvas.height) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(fmx, fmy);
|
||||
ctx.lineTo(x, y);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = "#5B2CB366";
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Clip into a circle
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, avatarRadius, 0, 2 * Math.PI);
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
|
||||
ctx.drawImage(avatar.image, x - avatarRadius, y - avatarRadius, avatarRadius * 2, avatarRadius * 2);
|
||||
|
||||
ctx.restore(); // Restore the context to draw the border
|
||||
|
||||
// Draw border
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, avatarRadius, 0, 2 * Math.PI);
|
||||
ctx.lineWidth = 3;
|
||||
ctx.strokeStyle = "#5B2CB3";
|
||||
ctx.stroke();
|
||||
|
||||
//avatar.angle += (avatar.dir * avatar.speed);
|
||||
if (avatar.angle >= 360) avatar.angle = 0;
|
||||
});
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = "14px";
|
||||
ctx.strokeText(`m:${mouseX},${mouseY}`, 10, 10);
|
||||
requestAnimationFrame(drawRadar);
|
||||
}
|
||||
|
||||
requestAnimationFrame(drawRadar);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
animate();
|
||||
}, []);
|
||||
|
||||
return <canvas id="radarCanvas" className="z-0 absolute aspect-square" width={800} height={800}></canvas>;
|
||||
}
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
12
tailwind.config.js
Normal file
12
tailwind.config.js
Normal file
@ -0,0 +1,12 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{ts,tsx}"],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
nostr: "#5b2cb3",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
10
tsconfig.node.json
Normal file
10
tsconfig.node.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
7
vite.config.ts
Normal file
7
vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
});
|
Loading…
Reference in New Issue
Block a user