├── test-results └── .last-run.json ├── public ├── robots.txt ├── favicon.ico ├── audio │ └── notify.mp3 ├── images │ ├── screen.png │ ├── YoursIcon.svg │ ├── retrofeed_512_2.svg │ └── logo-noBgColor.svg └── manifest.json ├── babel-plugin-macros.config.cjs ├── .dockerignore ├── .playwright-mcp ├── login-page-blocked.png └── sigma-auth-backup-2025-10-28.json ├── bigblocks.config.json ├── railway.toml ├── postcss.config.js ├── src ├── types │ ├── bops.d.ts │ ├── global.d.ts │ └── index.ts ├── lib │ └── utils.ts ├── utils │ ├── strings.ts │ ├── common.ts │ ├── validation.ts │ ├── sign.js │ ├── storage.ts │ ├── next-polyfill.js │ ├── backupDecryptor.ts │ ├── file.jsx │ └── backupDetector.ts ├── hooks │ ├── useActiveUser.ts │ ├── use-mobile.ts │ ├── index.ts │ └── useOwnedThemes.tsx ├── components │ ├── authForm │ │ ├── SignupPage.tsx │ │ ├── Form.js │ │ ├── Label.ts │ │ ├── Input.ts │ │ ├── SubmitButton.js │ │ ├── Select.js │ │ ├── Layout.tsx │ │ └── BigBlocksLoginPage.tsx.disabled │ ├── ui │ │ ├── skeleton.tsx │ │ ├── label.tsx │ │ ├── textarea.tsx │ │ ├── separator.tsx │ │ ├── input.tsx │ │ ├── switch.tsx │ │ ├── badge.tsx │ │ ├── avatar.tsx │ │ ├── scroll-area.tsx │ │ ├── alert.tsx │ │ ├── Tooltip.tsx │ │ ├── List.jsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── EmojiPicker.tsx │ │ └── dialog.tsx │ ├── dashboard │ │ ├── InvisibleSubmitButton.jsx │ │ ├── ChatArea.tsx │ │ ├── ChannelTextArea.js │ │ ├── List.jsx │ │ ├── Hashtag.jsx │ │ ├── At.jsx │ │ ├── ArrowTooltip.jsx │ │ ├── modals │ │ │ ├── FilePreviewModal.tsx │ │ │ ├── PinChannelModal.tsx │ │ │ └── DirectMessageModal.tsx │ │ ├── SubmitButton.tsx │ │ ├── MessageFiles.tsx │ │ ├── Dashboard.tsx │ │ ├── MessageFile.tsx │ │ ├── FileRenderer.tsx │ │ ├── ProfilePanel.tsx │ │ ├── Sidebar.tsx │ │ ├── UserPanel.tsx │ │ ├── ListItem.jsx │ │ ├── ChannelList.tsx │ │ ├── ServerList.tsx │ │ ├── MemberList.tsx │ │ ├── Messages.tsx │ │ └── Avatar.tsx │ ├── home │ │ ├── HomePage.jsx │ │ └── Layout.jsx │ ├── common │ │ └── ConfirmationModal.tsx │ ├── icons │ │ ├── HandcashIcon.jsx │ │ └── YoursIcon.tsx │ ├── login-form.tsx │ └── ErrorBoundary.tsx ├── twin.d.ts ├── reducers │ ├── sidebarReducer.ts │ ├── profileReducer.ts │ ├── serverReducer.ts │ └── settingsReducer.ts ├── App.tsx ├── config │ └── constants.ts ├── context │ ├── bmap │ │ └── index.tsx │ └── theme.tsx ├── constants │ └── servers.ts ├── styles │ ├── GlobalStyles.tsx │ └── typography.ts ├── store.ts ├── main.tsx ├── design │ └── mixins.ts ├── routes │ ├── index.tsx │ └── lazyComponents.tsx └── api │ ├── channel.ts │ └── fetch.ts ├── .bitcoin └── templates │ └── p2pkh.json ├── tsconfig.node.json ├── .mcp.json ├── Caddyfile ├── index.html ├── components.json ├── tsconfig.json ├── Dockerfile ├── test-oauth-manual.js ├── .vscode └── settings.json ├── playwright.config.ts ├── biome.json ├── tests ├── e2e │ ├── homepage.spec.ts │ ├── styling.spec.ts │ └── unhooked-features.spec.ts └── visual │ ├── README.md │ └── playwright.config.ts ├── README.md ├── LICENSE ├── .gitignore ├── .cursorrules ├── BACKEND_FIX_VALIDATION.md ├── vite.config.ts ├── .claude └── commands │ └── review.md ├── MESSAGE_FORMAT.md ├── API_STRUCTURE.md └── scripts └── validate-build.ts /test-results/.last-run.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "passed", 3 | "failedTests": [] 4 | } 5 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rohenaz/allaboard-bitchat-nitro/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /babel-plugin-macros.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | twin: { 3 | preset: 'emotion', 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /public/audio/notify.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rohenaz/allaboard-bitchat-nitro/HEAD/public/audio/notify.mp3 -------------------------------------------------------------------------------- /public/images/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rohenaz/allaboard-bitchat-nitro/HEAD/public/images/screen.png -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .env 4 | *.md 5 | .gitignore 6 | .vscode 7 | # Ensure build directory is included 8 | !build/ -------------------------------------------------------------------------------- /.playwright-mcp/login-page-blocked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rohenaz/allaboard-bitchat-nitro/HEAD/.playwright-mcp/login-page-blocked.png -------------------------------------------------------------------------------- /bigblocks.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "componentsDir": "./components/bigblocks", 3 | "typescript": true, 4 | "radixThemes": false, 5 | "initialized": true 6 | } 7 | -------------------------------------------------------------------------------- /railway.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "https://railway.com/railway.schema.json" 2 | 3 | [build] 4 | builder = "DOCKERFILE" 5 | 6 | [deploy] 7 | restartPolicyType = "ON_FAILURE" -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | '@tailwindcss/postcss': {}, 5 | }, 6 | }; 7 | export default config; 8 | -------------------------------------------------------------------------------- /src/types/bops.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'bops' { 2 | function from(str: string, encoding: string): Buffer; 3 | function to(buffer: Buffer, encoding: string): string; 4 | export { from, to }; 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/strings.ts: -------------------------------------------------------------------------------- 1 | // Email validation functions moved to utils/validation.ts 2 | // Import from there: import { isValidEmail, validateEmail } from '../utils/validation'; 3 | 4 | export { isValidEmail } from './validation'; 5 | -------------------------------------------------------------------------------- /.bitcoin/templates/p2pkh.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "P2PKH", 3 | "pattern": "OP_DUP OP_HASH160 ([0-9A-Fa-f]{40}) OP_EQUALVERIFY OP_CHECKSIG", 4 | "description": "Pay to Public Key Hash - Standard Bitcoin payment script", 5 | "placeholders": ["pubKeyHash"] 6 | } 7 | -------------------------------------------------------------------------------- /src/hooks/useActiveUser.ts: -------------------------------------------------------------------------------- 1 | import { useSelector } from 'react-redux'; 2 | import type { RootState } from '../store'; 3 | 4 | export const useActiveUser = () => { 5 | const session = useSelector((state: RootState) => state.session); 6 | return session.user; 7 | }; 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /.playwright-mcp/sigma-auth-backup-2025-10-28.json: -------------------------------------------------------------------------------- 1 | kJLMw46zowN9wVnwL3AWVtseATSxDVlq085fKPkECx2XBCFfIWcETfXwLKsgM6f3zxEzMu+BI2jI+I6l9930vhI8dzonkPdTE72jP1uqmktyLol8pD2fQ74wpMzYiTzt+awcqx92L8CmuvtnBHm9XoEGtTh+cytNCnDLZLiqraIlrthUIfEecFWSJ7wOH87mO/BmMbL+GKFyAB8aj6N0nhyG9rzUetJ3hRd1xPEn25ol -------------------------------------------------------------------------------- /src/components/authForm/SignupPage.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from 'react'; 2 | 3 | export const SignupPage: FC = () => { 4 | return ( 5 |
6 |

Sign Up

7 | {/* Add signup form here */} 8 |
9 | ); 10 | }; 11 | 12 | export default SignupPage; 13 | -------------------------------------------------------------------------------- /src/components/authForm/Form.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Form = styled.form` 4 | display: flex; 5 | flex-direction: column; 6 | font-size: 13px; 7 | padding: 12px 0; 8 | width: 400px; 9 | 10 | @media (max-width: 550px) { 11 | width: 100%; 12 | } 13 | `; 14 | 15 | export default Form; 16 | -------------------------------------------------------------------------------- /src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) { 4 | return ( 5 |
10 | ) 11 | } 12 | 13 | export { Skeleton } 14 | -------------------------------------------------------------------------------- /src/components/dashboard/InvisibleSubmitButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styled from 'styled-components'; 4 | 5 | export const Wrapper = styled.button.attrs((_p) => ({ type: 'submit' }))` 6 | display: none; 7 | `; 8 | 9 | const InvisibleSubmitButton = () => { 10 | return ; 11 | }; 12 | 13 | export default InvisibleSubmitButton; 14 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Discord", 3 | "name": "Discord Clone", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/components/dashboard/ChatArea.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react'; 2 | import Messages from './Messages'; 3 | import WriteArea from './WriteArea'; 4 | 5 | const ChatArea: React.FC = () => { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | }; 13 | 14 | export default ChatArea; 15 | -------------------------------------------------------------------------------- /src/components/dashboard/ChannelTextArea.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const ChannelTextArea = styled.input.attrs({ 4 | className: 'border-0 rounded-lg text-[15px] h-11 w-full outline-hidden', 5 | })` 6 | background-color: var(--input); 7 | color: var(--foreground); 8 | 9 | &::placeholder { 10 | color: var(--muted-foreground); 11 | } 12 | `; 13 | 14 | export default ChannelTextArea; 15 | -------------------------------------------------------------------------------- /.mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "next-devtools": { 4 | "command": "bunx", 5 | "args": ["next-devtools-mcp@latest"] 6 | }, 7 | "encore-mcp": { 8 | "command": "encore", 9 | "args": ["mcp", "run", "--app=go-faucet-api-mazi"], 10 | "cwd": "../go-faucet-api" 11 | }, 12 | "playwright": { 13 | "command": "bunx", 14 | "args": ["@playwright/mcp@latest"] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/twin.d.ts: -------------------------------------------------------------------------------- 1 | import 'twin.macro'; 2 | import type { css as cssImport } from '@emotion/react'; 3 | import type { CSSInterpolation } from '@emotion/serialize'; 4 | import type styledImport from '@emotion/styled'; 5 | 6 | declare module 'twin.macro' { 7 | const styled: typeof styledImport; 8 | const css: typeof cssImport; 9 | } 10 | 11 | declare module 'react' { 12 | interface DOMAttributes<_T> { 13 | tw?: string; 14 | css?: CSSInterpolation; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/common.ts: -------------------------------------------------------------------------------- 1 | export enum FetchStatus { 2 | Idle = 'idle', 3 | Loading = 'loading', 4 | Success = 'success', 5 | Error = 'error', 6 | } 7 | 8 | export const defaultAlias = { 9 | '@context': 'https://schema.org', 10 | '@type': 'Person', 11 | alternateName: '', 12 | logo: '', 13 | image: '', 14 | homeLocation: { 15 | '@type': 'Place', 16 | name: '', 17 | }, 18 | description: '', 19 | url: '', 20 | paymail: '', 21 | bitcoinAddress: '', 22 | }; 23 | -------------------------------------------------------------------------------- /Caddyfile: -------------------------------------------------------------------------------- 1 | :{$PORT} 2 | 3 | root * /srv 4 | encode gzip 5 | 6 | # Set proper MIME types for SVG files 7 | @svg { 8 | path *.svg 9 | } 10 | route @svg { 11 | header Content-Type image/svg+xml 12 | file_server 13 | } 14 | 15 | # Handle static assets first 16 | @static { 17 | path /images/* /audio/* /js/* /assets/* 18 | } 19 | route @static { 20 | file_server 21 | } 22 | 23 | # Handle all other routes with SPA fallback 24 | file_server 25 | try_files {path} /index.html 26 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | BitChat Nitro 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/authForm/Label.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | interface LabelProps { 4 | error?: boolean; 5 | } 6 | 7 | const Label = styled.label` 8 | color: ${(p) => (p.error ? 'var(--text-danger)' : 'var(--foreground)')}; 9 | font-weight: 500; 10 | text-transform: uppercase; 11 | font-size: 12px; 12 | padding: 8px 0; 13 | display: flex; 14 | align-items: center; 15 | cursor: pointer; 16 | `; 17 | 18 | export default Label; 19 | -------------------------------------------------------------------------------- /src/components/dashboard/List.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styled from 'styled-components'; 4 | 5 | const Wrapper = styled.div` 6 | display: flex; 7 | flex-direction: ${(p) => (p.horizontal ? 'row' : 'column')}; 8 | gap: ${(p) => p.gap}; 9 | `; 10 | 11 | const List = ({ horizontal, gap, children, ...delegated }) => { 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | }; 18 | 19 | export default List; 20 | -------------------------------------------------------------------------------- /src/reducers/sidebarReducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | interface SidebarState { 4 | isOpen: boolean; 5 | } 6 | 7 | const initialState: SidebarState = { 8 | isOpen: false, 9 | }; 10 | 11 | const sidebarSlice = createSlice({ 12 | name: 'sidebar', 13 | initialState, 14 | reducers: { 15 | toggleSidebar: (state) => { 16 | state.isOpen = !state.isOpen; 17 | }, 18 | }, 19 | }); 20 | 21 | export const { toggleSidebar } = sidebarSlice.actions; 22 | 23 | export default sidebarSlice.reducer; 24 | -------------------------------------------------------------------------------- /src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare global { 4 | interface Window { 5 | location: Location; 6 | } 7 | 8 | interface HTMLFormElement extends Element { 9 | elements: HTMLFormControlsCollection; 10 | requestSubmit(): void; 11 | } 12 | 13 | interface HTMLTextAreaElement extends HTMLElement { 14 | value: string; 15 | form: HTMLFormElement | null; 16 | } 17 | 18 | interface HTMLFormControlsCollection { 19 | namedItem(name: string): HTMLElement | null; 20 | } 21 | } 22 | 23 | export {}; 24 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/index.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "iconLibrary": "lucide", 14 | "aliases": { 15 | "components": "@/components", 16 | "utils": "@/lib/utils", 17 | "ui": "@/components/ui", 18 | "lib": "@/lib", 19 | "hooks": "@/hooks" 20 | }, 21 | "registries": {} 22 | } 23 | -------------------------------------------------------------------------------- /src/utils/validation.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Validates an email string against a comprehensive regex pattern 3 | */ 4 | const validateEmail = (email: string): RegExpMatchArray | null => { 5 | return String(email) 6 | .toLowerCase() 7 | .match( 8 | /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 9 | ); 10 | }; 11 | 12 | /** 13 | * Returns true if the candidate string is a valid email address 14 | */ 15 | export const isValidEmail = (candidate: string): boolean => Boolean(validateEmail(candidate)); 16 | -------------------------------------------------------------------------------- /src/components/dashboard/Hashtag.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { FaHashtag } from 'react-icons/fa'; 4 | import styled from 'styled-components'; 5 | 6 | import { baseIcon, roundedBackground } from '../../design/mixins'; 7 | 8 | export const Wrapper = styled.div` 9 | ${baseIcon}; 10 | color: ${(p) => p.color || 'var(--muted-foreground)'}; 11 | ${(p) => p.bgColor && roundedBackground}; 12 | `; 13 | 14 | const Hashtag = ({ ...delegated }) => { 15 | return ( 16 | 17 | 18 | 19 | ); 20 | }; 21 | 22 | export default Hashtag; 23 | -------------------------------------------------------------------------------- /src/hooks/use-mobile.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const MOBILE_BREAKPOINT = 768; 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState(undefined); 7 | 8 | React.useEffect(() => { 9 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); 10 | const onChange = () => { 11 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 12 | }; 13 | mql.addEventListener('change', onChange); 14 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 15 | return () => mql.removeEventListener('change', onChange); 16 | }, []); 17 | 18 | return !!isMobile; 19 | } 20 | -------------------------------------------------------------------------------- /src/components/dashboard/At.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { FaAt } from 'react-icons/fa'; 4 | import styled from 'styled-components'; 5 | 6 | import { baseIcon, roundedBackground } from '../../design/mixins'; 7 | 8 | export const Wrapper = styled.div` 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | ${baseIcon}; 13 | color: ${(p) => p.color || 'var(--muted-foreground)'}; 14 | ${(p) => p.bgColor && roundedBackground}; 15 | `; 16 | 17 | const At = ({ ...delegated }) => { 18 | return ( 19 | 20 | 21 | 22 | ); 23 | }; 24 | 25 | export default At; 26 | -------------------------------------------------------------------------------- /src/reducers/profileReducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | interface ProfileState { 4 | profile: unknown; // TODO: Add proper profile type 5 | isOpen: boolean; 6 | } 7 | 8 | const initialState: ProfileState = { 9 | profile: {}, 10 | isOpen: false, 11 | }; 12 | 13 | const profileSlice = createSlice({ 14 | name: 'profile', 15 | initialState, 16 | reducers: { 17 | setProfile: (state, action) => { 18 | state.profile = action.payload; 19 | }, 20 | toggleProfile: (state) => { 21 | state.isOpen = !state.isOpen; 22 | }, 23 | }, 24 | }); 25 | 26 | export const { setProfile, toggleProfile } = profileSlice.actions; 27 | 28 | export default profileSlice.reducer; 29 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | 4 | import { cn } from "@/lib/utils" 5 | 6 | function Label({ 7 | className, 8 | ...props 9 | }: React.ComponentProps) { 10 | return ( 11 | 19 | ) 20 | } 21 | 22 | export { Label } 23 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from 'react'; 2 | import { RouterProvider } from 'react-router-dom'; 3 | import ErrorBoundary from './components/ErrorBoundary'; 4 | import router from './routes/index.tsx'; 5 | import { GlobalStyles } from './styles/GlobalStyles'; 6 | 7 | const LoadingSpinner = () => ( 8 |
Loading...
9 | ); 10 | 11 | const App = () => { 12 | return ( 13 | 14 | 15 |
16 | }> 17 | 18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default App; 25 | -------------------------------------------------------------------------------- /src/components/dashboard/ArrowTooltip.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Tooltip from '@mui/material/Tooltip'; 4 | import { styled } from '@mui/material/styles'; 5 | 6 | const Wrapper = styled((p) => ( 7 | 8 | ))` 9 | & .MuiTooltip-tooltip { 10 | background-color: var(--popover); 11 | color: var(--popover-foreground); 12 | font-size: 14px; 13 | padding: 10px; 14 | } 15 | 16 | & .MuiTooltip-arrow { 17 | color: var(--popover); 18 | } 19 | `; 20 | 21 | const ArrowTooltip = ({ children, ...delegated }) => { 22 | return ( 23 | 24 |
{children}
25 |
26 | ); 27 | }; 28 | 29 | export default ArrowTooltip; 30 | -------------------------------------------------------------------------------- /src/utils/sign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get a signing path from a hex number 3 | * 4 | * @param hexString {string} 5 | * @param hardened {boolean} Whether to return a hardened path 6 | * @returns {string} 7 | */ 8 | const getSigningPathFromHex = (hexString, hardened = true) => { 9 | // "m/0/0/1" 10 | let signingPath = 'm'; 11 | const signingHex = hexString.match(/.{1,8}/g); 12 | const maxNumber = 2147483648 - 1; // 0x80000000 13 | if (signingHex) { 14 | for (const hexNumber of signingHex) { 15 | let number = Number(`0x${hexNumber}`); 16 | if (number > maxNumber) number -= maxNumber; 17 | signingPath += `/${number}${hardened ? "'" : ''}`; 18 | } 19 | } 20 | return signingPath; 21 | }; 22 | 23 | export { getSigningPathFromHex }; 24 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |