commit
2f7ea4a99e
@ -0,0 +1,24 @@ |
|||||||
|
# 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? |
@ -0,0 +1,10 @@ |
|||||||
|
# RBlockNote and NextGraph integration demo |
||||||
|
|
||||||
|
``` |
||||||
|
npm i |
||||||
|
npm run dev |
||||||
|
``` |
||||||
|
|
||||||
|
you need to create a wallet at https://nextgraph.eu |
||||||
|
|
||||||
|
Standalone webapps do not work on Safari (yet). |
@ -0,0 +1,28 @@ |
|||||||
|
import js from '@eslint/js' |
||||||
|
import globals from 'globals' |
||||||
|
import reactHooks from 'eslint-plugin-react-hooks' |
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh' |
||||||
|
import tseslint from 'typescript-eslint' |
||||||
|
|
||||||
|
export default tseslint.config( |
||||||
|
{ ignores: ['dist'] }, |
||||||
|
{ |
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended], |
||||||
|
files: ['**/*.{ts,tsx}'], |
||||||
|
languageOptions: { |
||||||
|
ecmaVersion: 2020, |
||||||
|
globals: globals.browser, |
||||||
|
}, |
||||||
|
plugins: { |
||||||
|
'react-hooks': reactHooks, |
||||||
|
'react-refresh': reactRefresh, |
||||||
|
}, |
||||||
|
rules: { |
||||||
|
...reactHooks.configs.recommended.rules, |
||||||
|
'react-refresh/only-export-components': [ |
||||||
|
'warn', |
||||||
|
{ allowConstantExport: true }, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
) |
@ -0,0 +1,12 @@ |
|||||||
|
<!doctype html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="UTF-8" /> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||||
|
<title>BlockNote + NextGraph</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="app"></div> |
||||||
|
<script type="module" src="/src/main.tsx"></script> |
||||||
|
</body> |
||||||
|
</html> |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,43 @@ |
|||||||
|
{ |
||||||
|
"name": "blocknote", |
||||||
|
"private": true, |
||||||
|
"version": "0.0.0", |
||||||
|
"type": "module", |
||||||
|
"scripts": { |
||||||
|
"dev": "vite", |
||||||
|
"build": "tsc -b && vite build", |
||||||
|
"lint": "eslint .", |
||||||
|
"preview": "vite preview" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@blocknote/core": "^0.31.1", |
||||||
|
"@blocknote/mantine": "^0.31.1", |
||||||
|
"@blocknote/react": "^0.31.1", |
||||||
|
"@heroicons/react": "^2.2.0", |
||||||
|
"lib0": "^0.2.108", |
||||||
|
"nextgraph-react": "^0.1.1-alpha.3", |
||||||
|
"nextgraphweb": "^0.1.1-alpha.4", |
||||||
|
"react": "^19.1.0", |
||||||
|
"react-dom": "^19.1.0", |
||||||
|
"react-router": "^7.6.1", |
||||||
|
"react-router-dom": "^7.6.1", |
||||||
|
"tailwindcss": "^4.1.8", |
||||||
|
"yjs": "^13.6.27" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@eslint/js": "^9.25.0", |
||||||
|
"@types/react": "^19.1.2", |
||||||
|
"@types/react-dom": "^19.1.2", |
||||||
|
"@vitejs/plugin-react": "^4.4.1", |
||||||
|
"autoprefixer": "^10.4.21", |
||||||
|
"eslint": "^9.25.0", |
||||||
|
"eslint-plugin-react-hooks": "^5.2.0", |
||||||
|
"eslint-plugin-react-refresh": "^0.4.19", |
||||||
|
"globals": "^16.0.0", |
||||||
|
"postcss": "^8.5.3", |
||||||
|
"tailwindcss": "^3.4.17", |
||||||
|
"typescript": "~5.8.3", |
||||||
|
"typescript-eslint": "^8.30.1", |
||||||
|
"vite": "^6.3.5" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
export default { |
||||||
|
plugins: { |
||||||
|
tailwindcss: {}, |
||||||
|
autoprefixer: {}, |
||||||
|
}, |
||||||
|
} |
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,53 @@ |
|||||||
|
.fullscreen { |
||||||
|
top: 40px; |
||||||
|
position:relative; |
||||||
|
} |
||||||
|
|
||||||
|
.doc { |
||||||
|
height: 100vh; |
||||||
|
} |
||||||
|
|
||||||
|
.centered { |
||||||
|
/*max-width: 1280px;*/ |
||||||
|
margin: 0 auto; |
||||||
|
padding: 0rem; |
||||||
|
text-align: center; |
||||||
|
width: fit-content; |
||||||
|
} |
||||||
|
|
||||||
|
.contact { |
||||||
|
width: 300px; |
||||||
|
height: 300px; |
||||||
|
background-color: #f6f6f6; |
||||||
|
position: relative; |
||||||
|
overflow-wrap: anywhere; |
||||||
|
} |
||||||
|
|
||||||
|
.name { |
||||||
|
padding: 5px; |
||||||
|
height: 35px; |
||||||
|
overflow: hidden; |
||||||
|
background-color: #e0e0e0d0; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
white-space: nowrap; |
||||||
|
} |
||||||
|
|
||||||
|
.email-logo { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.email { |
||||||
|
padding: 5px; |
||||||
|
overflow-wrap: anywhere; |
||||||
|
} |
||||||
|
|
||||||
|
input { |
||||||
|
margin: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
background-color:rgb(73, 114, 165); |
||||||
|
color:white; |
||||||
|
cursor:pointer; |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
import React, { FunctionComponent } from 'react'; |
||||||
|
import { Header } from './Header'; |
||||||
|
import { Doc } from './Doc'; |
||||||
|
import { Home } from './Home'; |
||||||
|
import { NextGraphAuthMethod } from './reactMethods'; |
||||||
|
import { BrowserRouter, Routes, Route } from "react-router"; |
||||||
|
|
||||||
|
|
||||||
|
import './App.css' |
||||||
|
import "./styles.css"; |
||||||
|
|
||||||
|
const App: FunctionComponent = () => { |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="App"> |
||||||
|
<NextGraphAuthMethod> |
||||||
|
<Header /> |
||||||
|
<BrowserRouter> |
||||||
|
<Routes> |
||||||
|
<Route index element={<Home />} /> |
||||||
|
<Route path=":nuri" element={<Doc />} /> |
||||||
|
</Routes> |
||||||
|
</BrowserRouter> |
||||||
|
</NextGraphAuthMethod> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
export default App |
@ -0,0 +1,51 @@ |
|||||||
|
import "@blocknote/core/fonts/inter.css"; |
||||||
|
import { default as React, FunctionComponent, useMemo } from "react"; |
||||||
|
import { BlockNoteView } from "@blocknote/mantine"; |
||||||
|
import "@blocknote/mantine/style.css"; |
||||||
|
import { useCreateBlockNote } from "@blocknote/react"; |
||||||
|
import * as Y from "yjs"; |
||||||
|
import { NextGraphProvider } from "./provider"; |
||||||
|
import { useNextGraphAuth } from './reactMethods'; |
||||||
|
import { useParams } from "react-router"; |
||||||
|
|
||||||
|
export const Doc: FunctionComponent = () => { |
||||||
|
const { session } = useNextGraphAuth(); |
||||||
|
const params = useParams(); |
||||||
|
|
||||||
|
const [doc, provider] = useMemo(() => { |
||||||
|
const doc = new Y.Doc(); |
||||||
|
const provider = new NextGraphProvider(params.nuri, doc, session); |
||||||
|
return [doc, provider]; |
||||||
|
}, [params, session]); |
||||||
|
|
||||||
|
const editor = useCreateBlockNote({ |
||||||
|
collaboration: { |
||||||
|
// The Yjs Provider responsible for transporting updates:
|
||||||
|
provider, |
||||||
|
// Where to store BlockNote data in the Y.Doc:
|
||||||
|
fragment: doc.getXmlFragment('document-store'), |
||||||
|
// Information (name and color) for this user:
|
||||||
|
user: { |
||||||
|
name: "My Username", |
||||||
|
color: "#ff0000", |
||||||
|
}, |
||||||
|
// When to show user labels on the collaboration cursor. Set by default to
|
||||||
|
// "activity" (show when the cursor moves), but can also be set to "always".
|
||||||
|
showCursorLabels: "activity" |
||||||
|
}, |
||||||
|
}, [params, session]) |
||||||
|
|
||||||
|
// Creates a new editor instance.
|
||||||
|
|
||||||
|
if (!session.sessionId) return <></>; |
||||||
|
|
||||||
|
// Renders the editor instance using a React component.
|
||||||
|
return ( |
||||||
|
<div className="doc"> |
||||||
|
<div className="fullscreen"> |
||||||
|
<BlockNoteView editor={editor} /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
import { FunctionComponent } from "react"; |
||||||
|
import { useNextGraphAuth } from "./reactMethods"; |
||||||
|
|
||||||
|
export const Header: FunctionComponent = () => { |
||||||
|
|
||||||
|
const { session, login, logout } = useNextGraphAuth(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
|
||||||
|
{session.sessionId ? ( |
||||||
|
// If the session is logged in
|
||||||
|
<div className="p-1 text-white text-center fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}> |
||||||
|
You are logged in. |
||||||
|
{/* <span className="font-bold clickable" onClick={logout}> Log out</span> */} |
||||||
|
</div> |
||||||
|
) : ( |
||||||
|
// If the session is not logged in
|
||||||
|
<> |
||||||
|
<h1 className="text-2xl text-center mb-10">Welcome to BlockNote + NextGraph demo</h1> |
||||||
|
<div className="text-center text-xl p-1 text-white fixed top-0 left-0 right-0" style={{zIndex:1000, height:'36px', backgroundColor:'rgb(73, 114, 165)'}}> |
||||||
|
Please <span className="font-bold clickable" onClick={login}> Log in</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="text-center max-w-6xl lg:px-8 mx-auto px-4 text-blue-800"> |
||||||
|
|
||||||
|
<svg onClick={login} |
||||||
|
onKeyUp={login} className="cursor-pointer mt-10 h-16 w-16 mx-auto" data-slot="icon" fill="none" strokeWidth="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 9V5.25A2.25 2.25 0 0 1 10.5 3h6a2.25 2.25 0 0 1 2.25 2.25v13.5A2.25 2.25 0 0 1 16.5 21h-6a2.25 2.25 0 0 1-2.25-2.25V15M12 9l3 3m0 0-3 3m3-3H2.25"></path> |
||||||
|
</svg> |
||||||
|
|
||||||
|
<button |
||||||
|
onClick={login} |
||||||
|
onKeyUp={login} |
||||||
|
className="select-none ml-0 mt-2 mb-10 text-white bg-blue-800 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-500/50 rounded-lg text-base p-2 text-center inline-flex items-center dark:focus:ring-primary-700/55" |
||||||
|
> |
||||||
|
Please Log in |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,26 @@ |
|||||||
|
import { FunctionComponent } from "react"; |
||||||
|
import { useNextGraphAuth } from "./reactMethods"; |
||||||
|
import { DocumentPlusIcon } from '@heroicons/react/24/outline' |
||||||
|
import { useNavigate } from "react-router-dom"; |
||||||
|
|
||||||
|
export const Home: FunctionComponent = () => { |
||||||
|
const { session } = useNextGraphAuth(); |
||||||
|
const navigate = useNavigate(); |
||||||
|
|
||||||
|
const create = () => { |
||||||
|
session.ng.doc_create(session.sessionId, "YXml","post:blocknote","store").then((nuri) => { |
||||||
|
console.log("new doc created",nuri) |
||||||
|
navigate("/"+nuri); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
if (!session.sessionId) return <></>; |
||||||
|
|
||||||
|
return <> |
||||||
|
<div className="centered"> |
||||||
|
<div className="flex flex-wrap justify-center gap-5 mt-10 mb-10"> |
||||||
|
<button onClick={create} className="button"><DocumentPlusIcon className="size-7 inline"/> Create Document</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</>; |
||||||
|
}; |
@ -0,0 +1,36 @@ |
|||||||
|
/* |
||||||
|
* Base64URL-ArrayBuffer |
||||||
|
* https://github.com/herrjemand/Base64URL-ArrayBuffer
|
||||||
|
* |
||||||
|
* Copyright (c) 2017 Yuriy Ackermann <ackermann.yuriy@gmail.com> |
||||||
|
* Copyright (c) 2012 Niklas von Hertzen |
||||||
|
* Licensed under the MIT license. |
||||||
|
*
|
||||||
|
*/ |
||||||
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; |
||||||
|
|
||||||
|
// Use a lookup table to find the index.
|
||||||
|
var lookup = new Uint8Array(256); |
||||||
|
for (var i = 0; i < chars.length; i++) { |
||||||
|
lookup[chars.charCodeAt(i)] = i; |
||||||
|
} |
||||||
|
|
||||||
|
export const encode = function(arraybuffer) { |
||||||
|
var bytes = new Uint8Array(arraybuffer), |
||||||
|
i, len = bytes.length, base64 = ""; |
||||||
|
|
||||||
|
for (i = 0; i < len; i+=3) { |
||||||
|
base64 += chars[bytes[i] >> 2]; |
||||||
|
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; |
||||||
|
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; |
||||||
|
base64 += chars[bytes[i + 2] & 63]; |
||||||
|
} |
||||||
|
|
||||||
|
if ((len % 3) === 2) { |
||||||
|
base64 = base64.substring(0, base64.length - 1); |
||||||
|
} else if (len % 3 === 1) { |
||||||
|
base64 = base64.substring(0, base64.length - 2); |
||||||
|
} |
||||||
|
|
||||||
|
return base64; |
||||||
|
}; |
@ -0,0 +1,3 @@ |
|||||||
|
@tailwind base; |
||||||
|
@tailwind components; |
||||||
|
@tailwind utilities; |
@ -0,0 +1,10 @@ |
|||||||
|
//import { StrictMode } from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client' |
||||||
|
import './index.css' |
||||||
|
import App from './App.tsx' |
||||||
|
|
||||||
|
createRoot(document.getElementById('app')!).render( |
||||||
|
// <StrictMode>
|
||||||
|
<App /> |
||||||
|
// </StrictMode>,
|
||||||
|
) |
@ -0,0 +1,93 @@ |
|||||||
|
import { ObservableV2 } from 'lib0/observable' |
||||||
|
import { encode } from "./base64url"; |
||||||
|
import * as Y from "yjs"; |
||||||
|
|
||||||
|
const digest_to_string = function(digest) { |
||||||
|
let copy = [...digest.Blake3Digest32]; |
||||||
|
copy.reverse(); |
||||||
|
copy.push(0); |
||||||
|
let buffer = Uint8Array.from(copy); |
||||||
|
return encode(buffer.buffer); |
||||||
|
}; |
||||||
|
|
||||||
|
export class NextGraphProvider extends ObservableV2 { |
||||||
|
/** |
||||||
|
* @param {string} nuri |
||||||
|
* @param {Y.Doc} doc |
||||||
|
* @param {Object} session |
||||||
|
**/ |
||||||
|
constructor (nuri, doc, session) { |
||||||
|
super() |
||||||
|
if (!session.ng) return; |
||||||
|
this.nuri = nuri |
||||||
|
this.doc = doc |
||||||
|
this.session = session |
||||||
|
this.heads = []; |
||||||
|
this.shouldConnect = false |
||||||
|
|
||||||
|
/** |
||||||
|
* Listens to Yjs updates and sends them to NextGraph |
||||||
|
* @param {Uint8Array} update |
||||||
|
* @param {any} origin |
||||||
|
*/ |
||||||
|
this._updateHandler = async (update, origin) => { |
||||||
|
if (!origin.local) { |
||||||
|
try { |
||||||
|
await this.session.ng.discrete_update(this.session.sessionId, update, this.heads, "YXml", this.nuri); |
||||||
|
} catch (e){ |
||||||
|
console.log(e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
this.doc.on('update', this._updateHandler) |
||||||
|
this.destroy = this.destroy.bind(this) |
||||||
|
this.doc.on('destroy', this.destroy) |
||||||
|
this.connect() |
||||||
|
} |
||||||
|
|
||||||
|
destroy () { |
||||||
|
this.disconnect() |
||||||
|
//this.awareness.off('update', this._awarenessUpdateHandler)
|
||||||
|
this.doc.off('update', this._updateHandler) |
||||||
|
super.destroy() |
||||||
|
} |
||||||
|
|
||||||
|
disconnect () { |
||||||
|
this.shouldConnect = false |
||||||
|
// TODO unsub
|
||||||
|
} |
||||||
|
|
||||||
|
connect () { |
||||||
|
console.log("connecting to doc",this.nuri.substring(0,53)) |
||||||
|
if (this.shouldConnect) return; |
||||||
|
|
||||||
|
this.shouldConnect = true |
||||||
|
this.session.ng.doc_subscribe(this.nuri.substring(0,53), this.session.sessionId,
|
||||||
|
(response) => { |
||||||
|
//console.log("GOT APP RESPONSE", response);
|
||||||
|
if (response.V0.TabInfo) { |
||||||
|
} else if (response.V0.State) { |
||||||
|
if (response.V0.State.discrete) { |
||||||
|
Y.applyUpdate(this.doc, response.V0.State.discrete.YXml, {local:true}) |
||||||
|
} |
||||||
|
for (const head of response.V0.State.heads) { |
||||||
|
let commitId = digest_to_string(head); |
||||||
|
this.heads.push(commitId); |
||||||
|
} |
||||||
|
} else if (response.V0.Patch) { |
||||||
|
if (response.V0.Patch.discrete) { |
||||||
|
Y.applyUpdate(this.doc, response.V0.Patch.discrete.YXml, {local:true}) |
||||||
|
} |
||||||
|
let i = this.heads.length; |
||||||
|
while (i--) { |
||||||
|
if (response.V0.Patch.commit_info.past.includes(this.heads[i])) { |
||||||
|
this.heads.splice(i, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
this.heads.push(response.V0.Patch.commit_id); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
); // TODO .then keep unsub
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
import { createNextGraphAuthMethod } from "nextgraph-react"; |
||||||
|
|
||||||
|
const methods = createNextGraphAuthMethod(); |
||||||
|
|
||||||
|
export const { NextGraphAuthMethod, useNextGraphAuth } = methods; |
||||||
|
|
@ -0,0 +1,226 @@ |
|||||||
|
/* |
||||||
|
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||||
|
// All rights reserved. |
||||||
|
// Licensed under the Apache License, Version 2.0 |
||||||
|
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||||
|
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||||
|
// at your option. All files in the project carrying such |
||||||
|
// notice may not be copied, modified, or distributed except |
||||||
|
// according to those terms. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
/** To format paths, like Settings > Wallet > Generate Wallet QR */ |
||||||
|
.path { |
||||||
|
font-family: monospace; |
||||||
|
background-color: rgba(73, 114, 165, 0.1); |
||||||
|
} |
||||||
|
/* .splash-loaded { |
||||||
|
display: none; |
||||||
|
} */ |
||||||
|
|
||||||
|
|
||||||
|
.toggle * { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.error-popover h3 { |
||||||
|
text-align: center; |
||||||
|
color: rgb(200 30 30); |
||||||
|
} |
||||||
|
.error-popover > div:first-child { |
||||||
|
background-color: rgb(200 30 30); |
||||||
|
} |
||||||
|
.error-popover > div:first-child > h3 { |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.logo { |
||||||
|
padding: 1.5em; |
||||||
|
will-change: filter; |
||||||
|
transition: 0.75s; |
||||||
|
padding-bottom: 1em; |
||||||
|
} |
||||||
|
|
||||||
|
@keyframes pulse-logo-color { |
||||||
|
0%, |
||||||
|
100% { |
||||||
|
fill: rgb(73, 114, 165); |
||||||
|
stroke: rgb(73, 114, 165); |
||||||
|
} |
||||||
|
50% { |
||||||
|
/* Mid-transition color */ |
||||||
|
stroke: #bbb; |
||||||
|
fill: #bbb; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.logo-pulse path { |
||||||
|
animation: pulse-logo-color 2s infinite; |
||||||
|
animation-timing-function: cubic-bezier(0.65, 0.01, 0.59, 0.83); |
||||||
|
} |
||||||
|
|
||||||
|
.logo-gray path { |
||||||
|
fill: #bbb; |
||||||
|
stroke: #bbb; |
||||||
|
} |
||||||
|
|
||||||
|
.logo-blue path { |
||||||
|
fill: rgb(73, 114, 165); |
||||||
|
stroke: rgb(73, 114, 165); |
||||||
|
} |
||||||
|
|
||||||
|
.jse-absolute-popup-content { |
||||||
|
left: 0 !important; |
||||||
|
} |
||||||
|
|
||||||
|
.container3 { |
||||||
|
margin: 0; |
||||||
|
|
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
justify-content: center; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
|
||||||
|
.container3 aside { |
||||||
|
width: 20rem !important; |
||||||
|
} |
||||||
|
|
||||||
|
div[role="alert"] div { |
||||||
|
display: block; |
||||||
|
} |
||||||
|
|
||||||
|
.spinner-overlay button { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
|
||||||
|
.choice-button { |
||||||
|
min-width: 305px; |
||||||
|
} |
||||||
|
|
||||||
|
.clickable { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.row { |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
} |
||||||
|
|
||||||
|
.deactivated-menu > svg { |
||||||
|
color: rgb(156 163 175) !important; |
||||||
|
} |
||||||
|
|
||||||
|
:root { |
||||||
|
font-family: Inter, Avenir, Helvetica, Arial, sans-serif; |
||||||
|
font-size: 16px; |
||||||
|
line-height: 24px; |
||||||
|
font-weight: 400; |
||||||
|
|
||||||
|
color: #0f0f0f; |
||||||
|
background-color: white; |
||||||
|
|
||||||
|
font-synthesis: none; |
||||||
|
text-rendering: optimizeLegibility; |
||||||
|
-webkit-font-smoothing: antialiased; |
||||||
|
-moz-osx-font-smoothing: grayscale; |
||||||
|
-webkit-text-size-adjust: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
margin: 0; |
||||||
|
display: flex; |
||||||
|
place-items: center; |
||||||
|
min-width: 305px; |
||||||
|
min-height: 100vh; |
||||||
|
} |
||||||
|
|
||||||
|
#app { |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
/* #app { |
||||||
|
/*max-width: 1280px; |
||||||
|
margin: 0 auto; |
||||||
|
padding: 0rem; |
||||||
|
text-align: center; |
||||||
|
} */ |
||||||
|
|
||||||
|
/* .container2 { |
||||||
|
padding-top: 10vh; |
||||||
|
} */ |
||||||
|
|
||||||
|
a { |
||||||
|
font-weight: 500; |
||||||
|
color: #646cff; |
||||||
|
text-decoration: inherit; |
||||||
|
} |
||||||
|
|
||||||
|
a:hover { |
||||||
|
color: #535bf2; |
||||||
|
} |
||||||
|
|
||||||
|
.toast { |
||||||
|
left: 50%; |
||||||
|
transform: translateX(-50%); |
||||||
|
z-index: 49; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
input, |
||||||
|
button { |
||||||
|
border-radius: 8px; |
||||||
|
border: 1px solid transparent; |
||||||
|
padding: 0.6em 1.2em; |
||||||
|
font-size: 1em; |
||||||
|
font-weight: 500; |
||||||
|
font-family: inherit; |
||||||
|
color: #0f0f0f; |
||||||
|
background-color: #ffffff; |
||||||
|
transition: border-color 0.25s; |
||||||
|
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); |
||||||
|
} |
||||||
|
|
||||||
|
button { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
button:hover { |
||||||
|
border-color: #396cd8; |
||||||
|
} |
||||||
|
button:active { |
||||||
|
border-color: #396cd8; |
||||||
|
background-color: #e8e8e8; |
||||||
|
} |
||||||
|
|
||||||
|
/* input, |
||||||
|
button { |
||||||
|
outline: none; |
||||||
|
} */ |
||||||
|
|
||||||
|
button:focus, |
||||||
|
button:focus-visible { |
||||||
|
outline: 4px auto -webkit-focus-ring-color; |
||||||
|
} |
||||||
|
|
||||||
|
/* @media (prefers-color-scheme: dark) { |
||||||
|
:root { |
||||||
|
color: #f6f6f6; |
||||||
|
background-color: #2f2f2f; |
||||||
|
} |
||||||
|
|
||||||
|
a:hover { |
||||||
|
color: #24c8db; |
||||||
|
} |
||||||
|
|
||||||
|
input, |
||||||
|
button { |
||||||
|
color: #ffffff; |
||||||
|
background-color: #0f0f0f98; |
||||||
|
} |
||||||
|
button:active { |
||||||
|
background-color: #0f0f0f69; |
||||||
|
} |
||||||
|
} */ |
@ -0,0 +1 @@ |
|||||||
|
/// <reference types="vite/client" />
|
@ -0,0 +1,12 @@ |
|||||||
|
/** @type {import('tailwindcss').Config} */ |
||||||
|
export default { |
||||||
|
content: [ |
||||||
|
"./index.html", |
||||||
|
"./src/**/*.{js,jsx,ts,tsx}" |
||||||
|
], |
||||||
|
theme: { |
||||||
|
extend: {}, |
||||||
|
}, |
||||||
|
plugins: [], |
||||||
|
} |
||||||
|
|
@ -0,0 +1,26 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", |
||||||
|
"target": "ES2020", |
||||||
|
"useDefineForClassFields": true, |
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"], |
||||||
|
"module": "ESNext", |
||||||
|
"skipLibCheck": true, |
||||||
|
|
||||||
|
/* Bundler mode */ |
||||||
|
"moduleResolution": "bundler", |
||||||
|
"allowImportingTsExtensions": true, |
||||||
|
"moduleDetection": "force", |
||||||
|
"noEmit": true, |
||||||
|
"jsx": "react-jsx", |
||||||
|
|
||||||
|
/* Linting */ |
||||||
|
"strict": true, |
||||||
|
"noUnusedLocals": true, |
||||||
|
"noUnusedParameters": true, |
||||||
|
"erasableSyntaxOnly": true, |
||||||
|
"noFallthroughCasesInSwitch": true, |
||||||
|
"noUncheckedSideEffectImports": true |
||||||
|
}, |
||||||
|
"include": ["src"] |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"files": [], |
||||||
|
"references": [ |
||||||
|
{ "path": "./tsconfig.app.json" }, |
||||||
|
{ "path": "./tsconfig.node.json" } |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", |
||||||
|
"target": "ES2022", |
||||||
|
"lib": ["ES2023"], |
||||||
|
"module": "ESNext", |
||||||
|
"skipLibCheck": true, |
||||||
|
|
||||||
|
/* Bundler mode */ |
||||||
|
"moduleResolution": "bundler", |
||||||
|
"allowImportingTsExtensions": true, |
||||||
|
"verbatimModuleSyntax": true, |
||||||
|
"moduleDetection": "force", |
||||||
|
"noEmit": true, |
||||||
|
|
||||||
|
/* Linting */ |
||||||
|
"strict": true, |
||||||
|
"noUnusedLocals": true, |
||||||
|
"noUnusedParameters": true, |
||||||
|
"erasableSyntaxOnly": true, |
||||||
|
"noFallthroughCasesInSwitch": true, |
||||||
|
"noUncheckedSideEffectImports": true |
||||||
|
}, |
||||||
|
"include": ["vite.config.ts"] |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
import { defineConfig } from 'vite' |
||||||
|
import react from '@vitejs/plugin-react' |
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({ |
||||||
|
plugins: [react()], |
||||||
|
}) |
Loading…
Reference in new issue