├── .eslintrc.json
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .prettierrc.mjs
├── LICENSE
├── README.md
├── components.json
├── docker-compose.yml
├── next.config.mjs
├── package.json
├── postcss.config.js
├── public
├── apple.svg
├── finder-icon.png
├── icon-mac.png
├── solanis-border-shadow.png
├── solanis-border-shadow.svg
├── solanis-transparent.png
├── solanis-transparent.svg
├── solanis.jpeg
├── solanis.png
└── trashempty.png
├── react-graph-vis.d.ts
├── src-tauri
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── build.rs
├── icons
│ ├── Square107x107Logo.png
│ ├── Square142x142Logo.png
│ ├── Square150x150Logo.png
│ ├── Square284x284Logo.png
│ ├── Square30x30Logo.png
│ ├── Square310x310Logo.png
│ ├── Square44x44Logo.png
│ ├── Square71x71Logo.png
│ ├── Square89x89Logo.png
│ ├── StoreLogo.png
│ ├── apple.svg
│ ├── finder-icon.png
│ ├── icon.icns
│ ├── icon.ico
│ ├── icon.png
│ ├── solanis-border-shadow.png
│ ├── solanis-border-shadow.svg
│ ├── solanis-transparent.png
│ ├── solanis-transparent.svg
│ ├── solanis.jpeg
│ ├── solanis.png
│ └── trashempty.png
├── src
│ └── main.rs
└── tauri.conf.json
├── src
├── app
│ ├── (lp-pages)
│ │ ├── login
│ │ │ └── page.tsx
│ │ └── register
│ │ │ └── page.tsx
│ ├── HelveticaNeue.woff2
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ ├── page.tsx
│ └── solanized
│ │ └── [username]
│ │ ├── annotations
│ │ └── page.tsx
│ │ ├── dashboard
│ │ └── page.tsx
│ │ ├── layout.tsx
│ │ ├── markdown-editor
│ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── settings
│ │ └── page.tsx
├── components
│ ├── Annotation.tsx
│ ├── AnnotationList.tsx
│ ├── Command.tsx
│ ├── Dashboard.tsx
│ ├── DynamicIcon.tsx
│ ├── Editor.tsx
│ ├── Footer.tsx
│ ├── Header.tsx
│ ├── ModalCreateSummary.tsx
│ ├── Sidebar.tsx
│ ├── SolanisAvatar.tsx
│ ├── UserItem.tsx
│ └── ui
│ │ ├── animated-shiny-text.tsx
│ │ ├── badge.tsx
│ │ ├── bento-grid.tsx
│ │ ├── breadcrumb.tsx
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ ├── command.tsx
│ │ ├── container-scroll-animation.tsx
│ │ ├── dialog.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── enchanced-separator.tsx
│ │ ├── gradient-heading.tsx
│ │ ├── gradient-text.tsx
│ │ ├── input-icon.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── link-preview.tsx
│ │ ├── resizable.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── skeleton.tsx
│ │ ├── sparkles-text.tsx
│ │ ├── staggered-fade.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── tooltip.tsx
│ │ ├── typewriter-effect.tsx
│ │ └── word-fade-in.tsx
├── contexts
│ ├── editor-provider.tsx
│ └── theme-provider.tsx
├── hooks
│ └── useAnthropic.ts
├── lib
│ └── utils.ts
└── types
│ └── Annotation.ts
├── tailwind.config.ts
├── tsconfig.json
└── yarn.lock
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next", "next/core-web-vitals", "eslint:recommended"],
3 | "globals": {
4 | "React": "readonly"
5 | },
6 | "plugins": ["prettier"],
7 | "rules": {
8 | "no-unused-vars": "warn",
9 | "prettier/prettier": "warn"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | .env
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | package.json
4 | src-tauri
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "semi": true,
4 | "singleQuote": false,
5 | "trailingComma": "all"
6 | }
7 |
--------------------------------------------------------------------------------
/.prettierrc.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import("prettier").Config} */
2 | const config = {
3 | tabWidth: 2,
4 | semi: true,
5 | singleQuote: false,
6 | trailingComma: "all",
7 | };
8 |
9 | export default config;
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Solanis Team (caiodutra08 and gustavorteuber)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Solanis - Create markdowns efficiently.
2 |
3 | Solanis is a cutting-edge AI generator designed to create markdowns efficiently. Powered by Solanis AI v1.0, this tool leverages advanced natural language processing capabilities to generate high-quality, well-structured markdown documents effortlessly.
4 |
5 | We use the most simple structure to save every information to be as light and as fast as possible.
6 |
7 | Solanis by right now is in Alpha and if you wan't to help us, you can use Git or any version control of your choice.
8 |
9 | ### Authors
10 |
11 |
16 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "src/app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 |
3 | services:
4 | postgres:
5 | image: postgres
6 | container_name: solanis-pg
7 | restart: always
8 | environment:
9 | POSTGRES_PASSWORD: postgres
10 | POSTGRES_USER: postgres
11 | POSTGRES_DB: solanis
12 | ports:
13 | - "5432:5432"
14 | volumes:
15 | - db:/var/lib/postgresql/data
16 | volumes:
17 | db:
18 | driver: local
19 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | env: {
4 | ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
5 | },
6 | rewrites: async () => {
7 | return [
8 | {
9 | source: "/anthropic/:path*",
10 | destination: "https://api.anthropic.com/:path*",
11 | },
12 | ];
13 | },
14 | images: {
15 | domains: ["api.microlink.io"],
16 | },
17 | transpilePackages: ["lucide-react"],
18 | };
19 |
20 | export default nextConfig;
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bucetisian",
3 | "version": "0.1.0",
4 | "type": "module",
5 | "private": true,
6 | "scripts": {
7 | "dev": "next dev",
8 | "build": "next build",
9 | "start": "next start",
10 | "lint": "next lint",
11 | "prettier": "prettier --write ."
12 | },
13 | "dependencies": {
14 | "@anthropic-ai/sdk": "^0.24.0",
15 | "@icons-pack/react-simple-icons": "^9.6.0",
16 | "@radix-ui/react-dialog": "^1.1.1",
17 | "@radix-ui/react-dropdown-menu": "^2.1.1",
18 | "@radix-ui/react-hover-card": "^1.1.1",
19 | "@radix-ui/react-label": "^2.1.0",
20 | "@radix-ui/react-select": "^2.1.1",
21 | "@radix-ui/react-separator": "^1.1.0",
22 | "@radix-ui/react-slot": "^1.1.0",
23 | "@radix-ui/react-switch": "^1.1.0",
24 | "@radix-ui/react-tabs": "^1.1.0",
25 | "@radix-ui/react-tooltip": "^1.1.1",
26 | "@tanstack/react-table": "^8.19.2",
27 | "class-variance-authority": "^0.7.0",
28 | "clsx": "^2.1.1",
29 | "cmdk": "^1.0.0",
30 | "easymde": "^2.18.0",
31 | "framer-motion": "^11.3.2",
32 | "gridstack": "^10.3.0",
33 | "jotai": "^2.8.4",
34 | "loadash": "^1.0.0",
35 | "lucide-react": "^0.396.0",
36 | "mini-svg-data-uri": "^1.4.4",
37 | "next": "14.2.4",
38 | "next-themes": "^0.3.0",
39 | "qss": "^3.0.0",
40 | "react": "^18",
41 | "react-dom": "^18",
42 | "react-draggable": "^4.4.6",
43 | "react-graph-vis": "^1.0.7",
44 | "react-resizable-panels": "^2.0.19",
45 | "react-simplemde-editor": "^5.2.0",
46 | "tailwind-merge": "^2.4.0",
47 | "tailwindcss-animate": "^1.0.7",
48 | "uuid": "^10.0.0"
49 | },
50 | "devDependencies": {
51 | "@types/node": "^20",
52 | "@types/react": "^18.3.3",
53 | "@types/react-dom": "^18",
54 | "@types/uuid": "^10.0.0",
55 | "@types/vis": "^4.21.27",
56 | "eslint": "^8",
57 | "eslint-config-next": "14.2.4",
58 | "postcss": "^8",
59 | "prettier": "^3.3.2",
60 | "tailwindcss": "^3.4.1",
61 | "typescript": "^5"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/public/apple.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/finder-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/public/finder-icon.png
--------------------------------------------------------------------------------
/public/icon-mac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/public/icon-mac.png
--------------------------------------------------------------------------------
/public/solanis-border-shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/public/solanis-border-shadow.png
--------------------------------------------------------------------------------
/public/solanis-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/public/solanis-transparent.png
--------------------------------------------------------------------------------
/public/solanis-transparent.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/solanis.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/public/solanis.jpeg
--------------------------------------------------------------------------------
/public/solanis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/public/solanis.png
--------------------------------------------------------------------------------
/public/trashempty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/public/trashempty.png
--------------------------------------------------------------------------------
/react-graph-vis.d.ts:
--------------------------------------------------------------------------------
1 | declare module "react-graph-vis" {
2 | import { Network, NetworkEvents, Options, Node, Edge, DataSet } from "vis";
3 | import { Component } from "react";
4 |
5 | export { Network, NetworkEvents, Options, Node, Edge, DataSet } from "vis";
6 |
7 | export interface graphEvents {
8 | [event: NetworkEvents]: (params?: any) => void;
9 | }
10 |
11 | //Doesn't appear that this module supports passing in a vis.DataSet directly. Once it does graph can just use the Data object from vis.
12 | export interface graphData {
13 | nodes: Node[];
14 | edges: Edge[];
15 | }
16 |
17 | export interface NetworkGraphProps {
18 | graph: graphData;
19 | options?: Options;
20 | events?: graphEvents;
21 | getNetwork?: (network: Network) => void;
22 | identifier?: string;
23 | style?: React.CSSProperties;
24 | getNodes?: (nodes: DataSet) => void;
25 | getEdges?: (edges: DataSet) => void;
26 | }
27 |
28 | export interface NetworkGraphState {
29 | identifier: string;
30 | }
31 |
32 | export default class NetworkGraph extends Component<
33 | NetworkGraphProps,
34 | NetworkGraphState
35 | > {
36 | render();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
--------------------------------------------------------------------------------
/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "app"
3 | version = "0.1.0"
4 | description = "A Tauri App"
5 | authors = ["you"]
6 | license = ""
7 | repository = ""
8 | default-run = "app"
9 | edition = "2021"
10 | rust-version = "1.60"
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [build-dependencies]
15 | tauri-build = { version = "1.5.2", features = [] }
16 |
17 | [dependencies]
18 | serde_json = "1.0"
19 | serde = { version = "1.0", features = ["derive"] }
20 | tauri = { version = "1.6.5", features = [] }
21 |
22 | [features]
23 | # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
24 | # If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
25 | # DO NOT REMOVE!!
26 | custom-protocol = [ "tauri/custom-protocol" ]
27 |
--------------------------------------------------------------------------------
/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/src-tauri/icons/Square107x107Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square107x107Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square142x142Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square142x142Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square150x150Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square150x150Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square284x284Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square284x284Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square30x30Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square30x30Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square310x310Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square310x310Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square44x44Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square44x44Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square71x71Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square71x71Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/Square89x89Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/Square89x89Logo.png
--------------------------------------------------------------------------------
/src-tauri/icons/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/StoreLogo.png
--------------------------------------------------------------------------------
/src-tauri/icons/apple.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src-tauri/icons/finder-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/finder-icon.png
--------------------------------------------------------------------------------
/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/src-tauri/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/icon.png
--------------------------------------------------------------------------------
/src-tauri/icons/solanis-border-shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/solanis-border-shadow.png
--------------------------------------------------------------------------------
/src-tauri/icons/solanis-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/solanis-transparent.png
--------------------------------------------------------------------------------
/src-tauri/icons/solanis.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/solanis.jpeg
--------------------------------------------------------------------------------
/src-tauri/icons/solanis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/solanis.png
--------------------------------------------------------------------------------
/src-tauri/icons/trashempty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src-tauri/icons/trashempty.png
--------------------------------------------------------------------------------
/src-tauri/src/main.rs:
--------------------------------------------------------------------------------
1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!!
2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3 |
4 | fn main() {
5 | tauri::Builder::default()
6 | .run(tauri::generate_context!())
7 | .expect("error while running tauri application");
8 | }
9 |
--------------------------------------------------------------------------------
/src-tauri/tauri.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "beforeBuildCommand": "yarn build",
4 | "beforeDevCommand": "yarn dev",
5 | "devPath": "http://localhost:3000",
6 | "distDir": "../out"
7 | },
8 | "package": {
9 | "productName": "Solanis",
10 | "version": "0.1.0"
11 | },
12 | "tauri": {
13 | "allowlist": {
14 | "all": false
15 | },
16 | "bundle": {
17 | "active": true,
18 | "category": "DeveloperTool",
19 | "copyright": "",
20 | "deb": {
21 | "depends": []
22 | },
23 | "externalBin": [],
24 | "icon": [
25 | "icons/32x32.png",
26 | "icons/128x128.png",
27 | "icons/128x128@2x.png",
28 | "icons/icon.icns",
29 | "icons/icon.ico"
30 | ],
31 | "identifier": "com.seudominio.solanis",
32 | "longDescription": "",
33 | "macOS": {
34 | "entitlements": null,
35 | "exceptionDomain": "",
36 | "frameworks": [],
37 | "providerShortName": null,
38 | "signingIdentity": null
39 | },
40 | "resources": [],
41 | "shortDescription": "",
42 | "targets": "all",
43 | "windows": {
44 | "certificateThumbprint": null,
45 | "digestAlgorithm": "sha256",
46 | "timestampUrl": ""
47 | }
48 | },
49 | "security": {
50 | "csp": null
51 | },
52 | "updater": {
53 | "active": false
54 | },
55 | "windows": [
56 | {
57 | "fullscreen": false,
58 | "height": 600,
59 | "resizable": true,
60 | "title": "Solanis",
61 | "width": 800
62 | }
63 | ]
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/app/(lp-pages)/login/page.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button";
2 | import { GradientHeading } from "@/components/ui/gradient-heading";
3 | import { Input } from "@/components/ui/input";
4 | import Image from "next/image";
5 | import Link from "next/link";
6 |
7 | export default function LoginPage() {
8 | return (
9 |
10 |
11 |
12 |
19 |
20 | solanis
21 |
22 |
23 |
24 |
25 |
26 | Entrar!
27 |
28 |
29 | Não tem uma conta?{" "}
30 |
34 | Registre-se
35 |
36 |
37 |
38 |
39 |
51 |
52 | Entrar
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Olá! Por favor faça login em sua conta.
61 |
62 |
63 | Você sabia que com a solanis você pode criar resumos acadêmicos
64 | diretamente com IA?
65 |
66 |
67 |
70 |
71 |
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/src/app/(lp-pages)/register/page.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button";
2 | import { GradientHeading } from "@/components/ui/gradient-heading";
3 | import { Input } from "@/components/ui/input";
4 | import Image from "next/image";
5 | import Link from "next/link";
6 |
7 | export default function CreateAccountPage() {
8 | return (
9 |
10 |
11 |
12 |
19 |
20 | solanis
21 |
22 |
23 |
24 |
25 |
26 | Registrar!
27 |
28 |
29 | Já tem uma conta?{" "}
30 |
31 | Faça o Login
32 |
33 |
34 |
35 |
45 |
46 |
47 |
48 |
49 |
50 | Olá! Por favor insira seus dados para criar sua conta.
51 |
52 |
53 | Você sabia que com o solanis você pode criar e compartilhar suas
54 | anotações de forma simples e rápida?
55 |
56 |
57 |
60 |
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/src/app/HelveticaNeue.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src/app/HelveticaNeue.woff2
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/solanisdev/solanis/088732e962ffba59bd12087758bc05c32057936a/src/app/favicon.ico
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | /* THEME 1 https://zippystarter.com/tools/shadcn-ui-theme-generator */
7 | /* :root {
8 | --background: 250 100% 100%;
9 | --foreground: 250 5% 10%;
10 | --card: 250 50% 100%;
11 | --card-foreground: 250 5% 15%;
12 | --popover: 250 100% 100%;
13 | --popover-foreground: 250 100% 10%;
14 | --primary: 250 100% 72.7%;
15 | --primary-foreground: 0 0% 0%;
16 | --secondary: 250 30% 90%;
17 | --secondary-foreground: 0 0% 0%;
18 | --muted: 212 30% 95%;
19 | --muted-foreground: 250 5% 40%;
20 | --accent: 212 30% 90%;
21 | --accent-foreground: 250 5% 15%;
22 | --destructive: 0 100% 50%;
23 | --destructive-foreground: 250 5% 100%;
24 | --border: 250 30% 82%;
25 | --input: 250 30% 50%;
26 | --ring: 250 100% 72.7%;
27 | --radius: 0.75rem;
28 | }
29 |
30 | .dark {
31 | --background: 250 50% 10%;
32 | --foreground: 250 5% 100%;
33 | --card: 250 50% 10%;
34 | --card-foreground: 250 5% 100%;
35 | --popover: 250 50% 5%;
36 | --popover-foreground: 250 5% 100%;
37 | --primary: 250 100% 72.7%;
38 | --primary-foreground: 0 0% 0%;
39 | --secondary: 250 30% 20%;
40 | --secondary-foreground: 0 0% 100%;
41 | --muted: 212 30% 25%;
42 | --muted-foreground: 250 5% 65%;
43 | --accent: 212 30% 25%;
44 | --accent-foreground: 250 5% 95%;
45 | --destructive: 0 100% 50%;
46 | --destructive-foreground: 250 5% 100%;
47 | --border: 250 30% 50%;
48 | --input: 250 30% 50%;
49 | --ring: 250 100% 72.7%;
50 | --radius: 0.75rem;
51 | } */
52 |
53 | /* THEME 2 UNNACTIVE https://ui.jln.dev/*/
54 | /* @layer base {
55 | :root {
56 | --background: 250 67% 99%;
57 | --foreground: 250 64% 2%;
58 | --muted: 250 5% 87%;
59 | --muted-foreground: 250 7% 28%;
60 | --popover: 250 67% 99%;
61 | --popover-foreground: 250 64% 2%;
62 | --card: 250 67% 99%;
63 | --card-foreground: 250 64% 2%;
64 | --border: 250 6% 89%;
65 | --input: 250 6% 89%;
66 | --primary: 250 100% 73%;
67 | --primary-foreground: 0 0% 0%;
68 | --secondary: 250 12% 80%;
69 | --secondary-foreground: 250 12% 20%;
70 | --accent: 250 22% 73%;
71 | --accent-foreground: 250 22% 13%;
72 | --destructive: 21 97% 24%;
73 | --destructive-foreground: 21 97% 84%;
74 | --ring: 250 100% 73%;
75 | --radius: 0.5rem;
76 | }
77 |
78 | .dark {
79 | --background: 250 7% 6%;
80 | --foreground: 250 10% 98%;
81 | --muted: 250 5% 13%;
82 | --muted-foreground: 250 7% 72%;
83 | --popover: 250 7% 6%;
84 | --popover-foreground: 250 10% 98%;
85 | --card: 250 7% 6%;
86 | --card-foreground: 250 10% 98%;
87 | --border: 250 6% 10%;
88 | --input: 250 6% 10%;
89 | --primary: 250 100% 73%;
90 | --primary-foreground: 0 0% 0%;
91 | --secondary: 250 11% 20%;
92 | --secondary-foreground: 250 11% 80%;
93 | --accent: 250 19% 26%;
94 | --accent-foreground: 250 19% 86%;
95 | --destructive: 21 97% 56%;
96 | --destructive-foreground: 0 0% 0%;
97 | --ring: 250 100% 73%;
98 | }
99 | } */
100 |
101 | @layer base {
102 | :root {
103 | --background: 44 57% 98%;
104 | --foreground: 44 70% 4%;
105 | --muted: 44 8% 95%;
106 | --muted-foreground: 44 6% 36%;
107 | --popover: 44 57% 98%;
108 | --popover-foreground: 44 70% 4%;
109 | --card: 44 57% 98%;
110 | --card-foreground: 44 70% 4%;
111 | --border: 44 8% 88%;
112 | --input: 44 8% 88%;
113 | --primary: 44 100% 77%;
114 | --primary-foreground: 44 100% 17%;
115 | --secondary: 44 19% 87%;
116 | --secondary-foreground: 44 19% 27%;
117 | --accent: 44 27% 78%;
118 | --accent-foreground: 44 27% 18%;
119 | --destructive: 2 83% 24%;
120 | --destructive-foreground: 2 83% 84%;
121 | --ring: 44 100% 77%;
122 | --radius: 0.5rem;
123 | }
124 |
125 | .dark {
126 | --background: 44 35% 1%;
127 | --foreground: 44 39% 98%;
128 | --muted: 44 8% 5%;
129 | --muted-foreground: 44 6% 64%;
130 | --popover: 44 35% 1%;
131 | --popover-foreground: 44 39% 98%;
132 | --card: 44 35% 1%;
133 | --card-foreground: 44 39% 98%;
134 | --border: 44 8% 13%;
135 | --input: 44 8% 13%;
136 | --primary: 44 100% 77%;
137 | --primary-foreground: 44 100% 17%;
138 | --secondary: 44 8% 19%;
139 | --secondary-foreground: 44 8% 79%;
140 | --accent: 44 15% 29%;
141 | --accent-foreground: 44 15% 89%;
142 | --destructive: 2 83% 55%;
143 | --destructive-foreground: 0 0% 100%;
144 | --ring: 44 100% 77%;
145 | }
146 | }
147 |
148 | }
149 |
150 | @layer base {
151 | * {
152 | @apply border-border;
153 | }
154 | body {
155 | @apply bg-background text-foreground;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import { Inter } from "next/font/google";
3 | import localFont from "next/font/local";
4 | import "./globals.css";
5 |
6 | import { ThemeProvider } from "@/contexts/theme-provider";
7 | import { TooltipProvider } from "@/components/ui/tooltip";
8 | import { EditorProvider } from "@/contexts/editor-provider";
9 | const inter = Inter({ subsets: ["latin"] });
10 | const helvetica_neue = localFont({
11 | src: "./HelveticaNeue.woff2",
12 | display: "swap",
13 | variable: "--font-helvetica-neue",
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: "Solanis - Escrita inteligente",
18 | description: "Solanis é um app de escrita e resumo de alto desempenho.",
19 | };
20 |
21 | export default function RootLayout({
22 | children,
23 | }: Readonly<{
24 | children: React.ReactNode;
25 | }>) {
26 | return (
27 |
28 |
31 |
32 |
33 | {children}
34 |
35 |
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import SolanisAvatar from "@/components/SolanisAvatar";
4 | import { Button } from "@/components/ui/button";
5 | import Link from "next/link";
6 | import { TypewriterEffectSmooth } from "@/components/ui/typewriter-effect";
7 | import { ContainerScroll } from "@/components/ui/container-scroll-animation";
8 | import Image from "next/image";
9 | import { GradientHeading } from "@/components/ui/gradient-heading";
10 | import { BentoGrid, BentoGridItem } from "@/components/ui/bento-grid";
11 | import {
12 | BarChart,
13 | Brain,
14 | Flower2,
15 | Palette,
16 | Rocket,
17 | Smile,
18 | Trophy,
19 | } from "lucide-react";
20 | import { cn } from "@/lib/utils";
21 | import { GradientText } from "@/components/ui/gradient-text";
22 | import { Skeleton } from "@/components/ui/skeleton";
23 | import { WordFadeIn } from "@/components/ui/word-fade-in";
24 | import SparklesText from "@/components/ui/sparkles-text";
25 | import { StaggeredFade } from "@/components/ui/staggered-fade";
26 | import { LinkPreview } from "@/components/ui/link-preview";
27 | import Footer from "@/components/Footer";
28 |
29 | export default function Home() {
30 | const bentoItems = [
31 | {
32 | title: "Rapidez e Eficiência",
33 | description: (
34 |
35 | Crie markdowns impecáveis em um piscar de olhos.
36 |
37 | ),
38 | icon: ,
39 | header: ,
40 | },
41 | {
42 | title: "IA Avançada",
43 | description: (
44 |
45 | Tecnologia inteligente que entende suas necessidades.
46 |
47 | ),
48 | icon: ,
49 | className: "col-span-2",
50 | header: ,
51 | },
52 | {
53 | title: "Recursos Completos",
54 | description: (
55 |
56 | Tudo o que você precisa para criar markdowns incríveis.
57 |
58 | ),
59 | icon: ,
60 | className: "col-span-3",
61 | header: ,
62 | },
63 | {
64 | title: "Fácil de usar",
65 | description: (
66 |
67 | Interface intuitiva e amigável para todos os usuários.
68 |
69 | ),
70 | icon: ,
71 | header: ,
72 | },
73 | {
74 | title: "Aumente sua Produtividade",
75 | description: (
76 |
77 | Crie markdowns de alta qualidade em menos tempo.
78 |
79 | ),
80 | icon: ,
81 | header: ,
82 | },
83 | {
84 | title: "A Ferramenta Definitiva",
85 | description: (
86 |
87 | Solanis é a ferramenta perfeita para todos os seus projetos de
88 | escrita.
89 |
90 | ),
91 | icon: ,
92 | header: ,
93 | },
94 | ];
95 |
96 | const username = "gustavorteuber";
97 |
98 | return (
99 |
100 |
101 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | Solanis é um gerador de markdown de última geração, projetado
132 | para criar markdowns com eficiência.
133 | Alimentado pela{" "}
134 |
138 |
142 |
143 | esta ferramenta utiliza recursos avançados de processamento de
144 | linguagem natural para gerar documentos markdown de alta
145 | qualidade e bem estruturados, sem esforço.
146 |
147 |
148 |
149 |
Solanize-se agora
150 |
151 |
152 |
155 | Desbloqueie o poder do
156 |
161 | Solanis
162 |
163 |
164 | }
165 | >
166 |
174 |
175 |
176 |
177 |
178 | {bentoItems.map((item, index) => (
179 | p:text-lg]", item.className)}
183 | />
184 | ))}
185 |
186 |
187 |
188 |
189 |
190 |
191 | );
192 | }
193 |
--------------------------------------------------------------------------------
/src/app/solanized/[username]/annotations/page.tsx:
--------------------------------------------------------------------------------
1 | import AnnotationList from "@/components/AnnotationList";
2 | import { GradientHeading } from "@/components/ui/gradient-heading";
3 | import React from "react";
4 |
5 | type Props = {};
6 |
7 | export default function AnnotationsPage({}: Props) {
8 | return (
9 |
10 |
11 | Anotações
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/solanized/[username]/dashboard/page.tsx:
--------------------------------------------------------------------------------
1 | import Dashboard from "@/components/Dashboard";
2 |
3 | interface Props {}
4 | export default function page({}: Props) {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/solanized/[username]/layout.tsx:
--------------------------------------------------------------------------------
1 | import Sidebar from "@/components/Sidebar";
2 | import SolanisAvatar from "@/components/SolanisAvatar";
3 | import {
4 | ResizableHandle,
5 | ResizablePanel,
6 | ResizablePanelGroup,
7 | } from "@/components/ui/resizable";
8 | import Header from "@/components/Header";
9 | import React from "react";
10 |
11 | interface LayoutProps {
12 | children: React.ReactNode;
13 | params: { username: string };
14 | }
15 |
16 | export default function Layout({ children, params }: LayoutProps) {
17 | return (
18 | <>
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | {children}
32 |
33 |
34 |
35 | >
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/src/app/solanized/[username]/markdown-editor/page.tsx:
--------------------------------------------------------------------------------
1 | import Editor from "@/components/Editor";
2 | import React from "react";
3 |
4 | type Props = {};
5 |
6 | export default function MarkdownEditorPage({}: Props) {
7 | return ;
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/solanized/[username]/page.tsx:
--------------------------------------------------------------------------------
1 | import Dashboard from "@/components/Dashboard";
2 |
3 | interface Props {}
4 | export default function page({}: Props) {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/solanized/[username]/settings/page.tsx:
--------------------------------------------------------------------------------
1 | import { GradientHeading } from "@/components/ui/gradient-heading";
2 | import { Input } from "@/components/ui/input";
3 | import { Separator } from "@/components/ui/separator";
4 | import { Switch } from "@/components/ui/switch";
5 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
6 | import {
7 | Tooltip,
8 | TooltipContent,
9 | TooltipTrigger,
10 | } from "@/components/ui/tooltip";
11 |
12 | type Props = {};
13 |
14 | export default function SettingsPage({}: Props) {
15 | return (
16 |
17 |
18 |
19 | Configurações da conta
20 |
21 |
22 |
23 |
24 | Perfil
25 | Notificações
26 |
27 |
28 |
29 |
30 |
Customize seu perfil
31 |
32 | Customize seu perfil, atualizando seu nome, sua foto e seu url
33 | para compartilhar sua anotação.
34 |
35 |
36 |
37 |
38 |
39 |
43 |
44 | GR
45 |
46 |
47 | Trocar de foto
48 |
49 |
50 |
51 |
52 |
53 | solanized/
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
Notificações de perfil
65 |
66 | Receba notificações quando algo nas suas anotações acontecer.
67 |
68 |
69 |
70 |
71 |
72 |
73 |
Lembretes
74 |
75 | Novidades sobre novas atualizações do Solanis.
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Menções
83 |
84 | Receber notificações para comentários em suas anotações e
85 | respostas aos seus comentários.
86 |
87 |
88 |
89 |
90 |
91 |
92 |
Amigos
93 |
94 | Notificações sobre atividades de usuários que você é amigo.
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
Notificações Push
104 |
105 | Receba notificações push diretamente no seu dispositivo.
106 |
107 |
108 |
109 |
110 |
111 |
112 |
Mensagens diretas
113 |
114 | Receba notificações push quando receber uma mensagem direta.
115 |
116 |
117 |
118 |
119 |
120 |
121 |
Sugestões de melhorias
122 |
123 | Receba notificações push com sugestões de melhorias IA para
124 | seus documentos markdown.
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | );
134 | }
135 |
--------------------------------------------------------------------------------
/src/components/Annotation.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { Suspense } from "react";
4 | import { Button } from "@/components/ui/button";
5 | import { LoaderCircle, PencilLine } from "lucide-react";
6 | import {
7 | Tooltip,
8 | TooltipContent,
9 | TooltipTrigger,
10 | } from "@/components/ui/tooltip";
11 | import { Badge } from "@/components/ui/badge";
12 | import DynamicIcon from "@/components/DynamicIcon";
13 | import { AnnotationT } from "@/types/Annotation";
14 |
15 | export default function Annotation({
16 | icon,
17 | title,
18 | description,
19 | tags,
20 | lastUpdate,
21 | }: AnnotationT) {
22 | const renderTags = () => {
23 | return tags.map((tag, index) => (
24 |
25 | {tag}
26 |
27 | ));
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 | }
36 | >
37 |
42 |
43 |
44 |
{title}
45 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {tags.length} Tags
56 |
57 |
58 |
59 | {renderTags()}
60 |
61 |
62 |
63 |
{lastUpdate}
64 |
65 |
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/AnnotationList.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import Annotation from "@/components/Annotation";
5 | import dynamicIconImports from "lucide-react/dynamicIconImports";
6 | import { Plus } from "lucide-react";
7 | import { AnnotationT } from "@/types/Annotation";
8 |
9 | type Props = {};
10 |
11 | export default function AnnotationList({}: Props) {
12 | const [annotations, setAnnotations] = React.useState([]);
13 | const icon = "box" as keyof typeof dynamicIconImports;
14 |
15 | const ButtonCreateAnnotation = () => {
16 | return (
17 |
18 |
19 |
Criar nova anotação
20 |
21 | );
22 | };
23 |
24 | return (
25 | <>
26 |
27 | {Array.from({ length: 3 }).map((_, index) => (
28 |
36 | ))}
37 |
38 |
39 | >
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/Command.tsx:
--------------------------------------------------------------------------------
1 | import { Command, CommandInput } from "@/components/ui/command";
2 |
3 | export function CommandDemo() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/Dashboard.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useEffect, useRef, useState } from "react";
4 | import {
5 | GridItemHTMLElement,
6 | GridStack,
7 | GridStackNode,
8 | GridStackWidget,
9 | } from "gridstack";
10 | import "gridstack/dist/gridstack.min.css";
11 | import { v4 as uuidv4 } from "uuid";
12 | import {
13 | DropdownMenu,
14 | DropdownMenuTrigger,
15 | DropdownMenuContent,
16 | DropdownMenuItem,
17 | DropdownMenuLabel,
18 | DropdownMenuSeparator,
19 | DropdownMenuGroup,
20 | DropdownMenuSub,
21 | DropdownMenuSubTrigger,
22 | DropdownMenuPortal,
23 | DropdownMenuSubContent,
24 | } from "@/components/ui/dropdown-menu";
25 | import { Button } from "@/components/ui/button";
26 | import {
27 | BarChart,
28 | FileStack,
29 | Kanban,
30 | LineChart,
31 | LucideX,
32 | PieChart,
33 | PlusIcon,
34 | Sparkles,
35 | } from "lucide-react";
36 | import { GradientHeading } from "@/components/ui/gradient-heading";
37 | import { cn } from "@/lib/utils";
38 |
39 | interface Props {}
40 |
41 | type Widget = {
42 | id: number | string;
43 | type: string;
44 | h: number;
45 | w: number;
46 | x?: number;
47 | y?: number;
48 | static?: boolean;
49 | removed?: boolean;
50 | };
51 |
52 | //TODO: Implement the rest of the widgets
53 | //TODO: Switch to components for each widget to make it easier to add new ones and calling api's
54 | //TODO: Add a way to save the dashboard
55 |
56 | let grid: GridStack;
57 |
58 | export default function Dashboard({}: Props) {
59 | const name = "Gustavo";
60 | const [widgets, setWidgets] = useState([]);
61 |
62 | const widgetsRef = useRef(new Map());
63 |
64 | const getWidgetsMap = () => {
65 | return widgetsRef.current;
66 | };
67 |
68 | const createWidget = (type: string) => {
69 | //todo: calculate the best position to add the widget and h w
70 | return {
71 | id: uuidv4(),
72 | type,
73 | h: 2,
74 | w: 2,
75 | static: false,
76 | };
77 | };
78 |
79 | const addWidget = (type: string) => {
80 | const newWidget = createWidget(type);
81 |
82 | setWidgets([...widgets, newWidget]);
83 |
84 | setTimeout(() => {
85 | grid.addWidget(getWidgetsMap().get(newWidget.id));
86 | }, 5);
87 | };
88 |
89 | const removeWidget = (id: string) => {
90 | const el = document.querySelector(
91 | `.grid-stack-item[gs-id="${id}"]`,
92 | ) as GridItemHTMLElement;
93 |
94 | //set removed
95 | setWidgets((prev) =>
96 | prev.map((widg) => {
97 | if (widg.id === id) {
98 | return { ...widg, removed: true };
99 | }
100 | return widg;
101 | }),
102 | );
103 |
104 | if (el) {
105 | grid.removeWidget(el);
106 | widgetsRef.current.delete(id);
107 | }
108 | };
109 |
110 | const Widget = (widget: Widget) => {
111 | return (
112 |
113 | {widget.type}
114 | removeWidget(widget.id as string)}
118 | >
119 |
120 |
121 |
122 | );
123 | };
124 |
125 | useEffect(() => {
126 | grid = GridStack.init({ margin: 6, row: 8, styleInHead: true });
127 |
128 | grid.on("dragstop", function (_event: Event, el: GridItemHTMLElement) {
129 | if (!el.gridstackNode) {
130 | throw new Error(`Error resizing element: ${el}`);
131 | }
132 |
133 | let node: GridStackNode = el.gridstackNode;
134 | setWidgets((prev) =>
135 | prev.map((widg) => {
136 | if (widg.id === node.id) {
137 | return { ...widg, x: node.x, y: node.y };
138 | }
139 | return widg;
140 | }),
141 | );
142 | });
143 |
144 | grid.on("resizestop", function (event: Event, el: GridItemHTMLElement) {
145 | if (!el.gridstackNode) {
146 | throw new Error(`Error resizing element: ${el}`);
147 | }
148 |
149 | let node: GridStackNode = el.gridstackNode;
150 | setWidgets((prev) =>
151 | prev.map((widg) => {
152 | if (widg.id === node.id) {
153 | const newWidg = { ...widg, w: node.w ?? 0, h: node.h ?? 0 };
154 | return newWidg;
155 | }
156 | return widg;
157 | }),
158 | );
159 | });
160 | }, []);
161 |
162 | useEffect(() => {
163 | const saved = grid.save(true) as GridStackWidget[];
164 | // setWidgets(saved.map((widg: any) => ({ ...widg, id: widg.id })));
165 | console.log("salvou");
166 | }, [widgets]);
167 |
168 | return (
169 | <>
170 |
171 |
172 | Bem vindo Gustavo , ao seu
173 | Dashboard!
174 |
175 |
176 |
177 |
185 |
186 |
187 |
188 |
189 | Widgets
190 |
191 |
192 |
193 | Gráficos
194 |
195 |
196 | addWidget("graphPie")}
199 | >
200 | Gráfico Pizza
201 |
202 | addWidget("graphLine")}
205 | >
206 | Gráfico Linha
207 |
208 | addWidget("graphBar")}
211 | >
212 | Gráfico Barra
213 |
214 |
215 |
216 |
217 | addWidget("recentNotes")}
220 | >
221 | Notas recentes
222 |
223 | addWidget("productivity")}
226 | >
227 | Produtividade
228 |
229 | addWidget("neuralAI")}
232 | >
233 | Neural IA
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 | {widgets.length > 0 ? (
242 | widgets.map((widg) => (
243 |
{
252 | const map = getWidgetsMap();
253 | if (node) map.set(widg.id, node);
254 | else map.delete(widg);
255 | }}
256 | >
257 |
258 |
259 |
260 |
261 | ))
262 | ) : (
263 |
264 |
265 | Adicione widgets
266 |
267 |
268 | )}
269 |
270 |
271 | >
272 | );
273 | }
274 |
--------------------------------------------------------------------------------
/src/components/DynamicIcon.tsx:
--------------------------------------------------------------------------------
1 | import dynamic from "next/dynamic";
2 | import { LucideProps } from "lucide-react";
3 | import dynamicIconImports from "lucide-react/dynamicIconImports";
4 |
5 | interface IconProps extends LucideProps {
6 | name: keyof typeof dynamicIconImports;
7 | }
8 |
9 | const DynamicIcon = ({ name, ...props }: IconProps) => {
10 | const LucideIcon = dynamic(dynamicIconImports[name]);
11 |
12 | return ;
13 | };
14 |
15 | export default DynamicIcon;
16 |
--------------------------------------------------------------------------------
/src/components/Editor.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { EditorContext } from "@/contexts/editor-provider";
4 | import useAnthropic from "@/hooks/useAnthropic";
5 | import { useCallback, useContext, useEffect, useMemo } from "react";
6 | import "easymde/dist/easymde.min.css";
7 | import SimpleMDE from "react-simplemde-editor";
8 | import { Input } from "./ui/input";
9 | import { ChevronRight, Sparkles } from "lucide-react";
10 | import { Button } from "./ui/button";
11 |
12 | const mdeOptions = { minHeight: "100%" };
13 |
14 | export default function Editor({ isDemo = false }: { isDemo?: boolean }) {
15 | const { titleRef, onChange, markdown, setMarkdown } =
16 | useContext(EditorContext);
17 | const { anthropicStream } = useAnthropic();
18 |
19 | const memoMdeOptions = useMemo(() => mdeOptions, []);
20 |
21 | const callStream = async () => {
22 | const title = titleRef?.current?.value || "Untitled";
23 | anthropicStream(title).on("text", (text: string) => {
24 | setMarkdown((prev: string) => prev + text);
25 | });
26 | };
27 |
28 | useEffect(() => {
29 | if (titleRef?.current) {
30 | titleRef.current.value = "";
31 | }
32 | }, []);
33 |
34 | return (
35 | <>
36 |
37 | Solanis IA v1.0
38 |
39 |
40 |
46 |
52 | {} : callStream}
60 | >
61 |
62 | Gerar Markdown com Solanis IA
63 |
64 |
65 |
71 | >
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/src/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import { LinkedinIcon } from "lucide-react";
2 | import Link from "next/link";
3 | import { SiGithub, SiX } from "@icons-pack/react-simple-icons"
4 |
5 | export default function Footer() {
6 | return (
7 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/Header.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { CommandDemo } from "./Command";
4 | import { Button } from "./ui/button";
5 | import * as React from "react";
6 | import { Moon, Sun } from "lucide-react";
7 | import { useTheme } from "next-themes";
8 | import UserItem from "@/components/UserItem";
9 |
10 | import {
11 | DropdownMenu,
12 | DropdownMenuContent,
13 | DropdownMenuItem,
14 | DropdownMenuTrigger,
15 | } from "@/components/ui/dropdown-menu";
16 |
17 | export default function Header() {
18 | const { setTheme } = useTheme();
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Toggle theme
31 |
32 |
33 |
34 | setTheme("light")}>
35 | Light
36 |
37 | setTheme("dark")}>
38 | Dark
39 |
40 | setTheme("system")}>
41 | System
42 |
43 |
44 |
45 |
46 |
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/ModalCreateSummary.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React from "react";
4 | import {
5 | Dialog,
6 | DialogContent,
7 | DialogDescription,
8 | DialogTitle,
9 | } from "@/components/ui/dialog";
10 | import { InputIcon } from "@/components/ui/input-icon";
11 | import { PencilLine, Sparkles } from "lucide-react";
12 | import { Button } from "@/components/ui/button";
13 |
14 | type ModalCreateSummaryProps = {
15 | open: boolean;
16 | setOpen: (open: boolean) => void;
17 | };
18 |
19 | export default function ModalCreateSummary({
20 | open,
21 | setOpen,
22 | }: ModalCreateSummaryProps) {
23 | return (
24 |
25 |
26 |
27 | Crie um resumo
28 |
29 | Crie um resumo para um tópico de seu interesse
30 |
31 |
32 |
33 |
34 | {
38 | //TODO: Create summary
39 | }}
40 | >
41 | Criar Resumo
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/Sidebar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | Command,
5 | CommandGroup,
6 | CommandItem,
7 | CommandList,
8 | CommandSeparator,
9 | } from "@/components/ui/command";
10 | import {
11 | LayoutDashboard,
12 | Settings,
13 | User,
14 | MessageSquareMore,
15 | MessageSquareDiff,
16 | FileText,
17 | Edit,
18 | Book,
19 | } from "lucide-react";
20 | import Link from "next/link";
21 | import React, { useCallback, useState } from "react";
22 | import { usePathname } from "next/navigation";
23 | import Graph from "react-graph-vis";
24 | import { cn } from "@/lib/utils";
25 | import ModalCreateSummary from "@/components/ModalCreateSummary";
26 |
27 | type MenuItem = {
28 | text: string;
29 | icon: string;
30 | link: string;
31 | };
32 |
33 | type MenuGroup = {
34 | group: string;
35 | items: MenuItem[];
36 | };
37 |
38 | const menuList: MenuGroup[] = [
39 | {
40 | group: "Menu Geral",
41 | items: [{ text: "Dashboard", icon: "LayoutDashboard", link: "/dashboard" }],
42 | },
43 | {
44 | group: "Ferramentas",
45 | items: [
46 | { text: "Criar Resumo", icon: "FileText", link: "/create-summary" },
47 | { text: "Editor de Markdown", icon: "Edit", link: "/markdown-editor" },
48 | { text: "Anotações", icon: "Book", link: "/annotations" },
49 | ],
50 | },
51 | {
52 | group: "Configurações",
53 | items: [
54 | { text: "Configurações", icon: "Settings", link: "/settings" },
55 | // { text: "Gerenciar Usuários", icon: "User", link: "/users" },
56 | ],
57 | },
58 | ];
59 |
60 | function renderIcon(iconName: string) {
61 | switch (iconName) {
62 | case "LayoutDashboard":
63 | return ;
64 | case "Profile":
65 | return ;
66 | case "MessageSquareDiff":
67 | return ;
68 | case "Settings":
69 | return ;
70 | case "User":
71 | return ;
72 | case "FileText":
73 | return ;
74 | case "Edit":
75 | return ;
76 | case "Book":
77 | return ;
78 | default:
79 | return null;
80 | }
81 | }
82 |
83 | export const graphOptions = {
84 | layout: {
85 | hierarchical: false,
86 | },
87 | autoResize: true,
88 | edges: {
89 | color: "#000000",
90 | },
91 | nodes: {
92 | color: {
93 | background: "#ffc800",
94 | border: "#ffc800",
95 | },
96 | shape: "box",
97 | },
98 | height: "300px",
99 | width: "100%",
100 | };
101 |
102 | export const graphData = {
103 | nodes: [
104 | { id: 1, label: "Otimizando Seus Estudos com o App Solanis" },
105 | { id: 2, label: "1. Criação de Resumos Eficazes" },
106 | { id: 3, label: "2. Revisão Ativa e Eficaz" },
107 | { id: 4, label: "3. Planejamento e Organização" },
108 | { id: 5, label: "4. Aprendizagem Colaborativa" },
109 | { id: 6, label: "5. Personalização e Flexibilidade" },
110 | ],
111 | edges: [
112 | { from: 1, to: 2 },
113 | { from: 1, to: 3 },
114 | { from: 1, to: 4 },
115 | { from: 1, to: 5 },
116 | { from: 1, to: 6 },
117 | ],
118 | };
119 |
120 | type SidebarProps = {
121 | params?: { username: string };
122 | };
123 |
124 | export default function Sidebar({ params }: SidebarProps) {
125 | const [graphEvents] = useState({});
126 | const username = params?.username;
127 |
128 | const pathname = useCallback(() => {
129 | const pathName = usePathname();
130 | if (params && params.username) {
131 | return pathName.replace(`/solanized/${params.username}`, "");
132 | }
133 | return pathName;
134 | }, []);
135 |
136 | const [open, setOpen] = React.useState(false);
137 |
138 | return (
139 | <>
140 |
141 |
142 | {menuList.map((menu, key) => (
143 |
144 | {menu.items.map((option, optionKey) =>
145 | option.link === "/create-summary" ? (
146 | {
148 | setOpen(true);
149 | }}
150 | className="w-full"
151 | key={optionKey}
152 | >
153 |
161 | {renderIcon(option.icon)}
162 | {option.text}
163 |
164 |
165 | ) : (
166 |
170 |
178 | {renderIcon(option.icon)}
179 | {option.text}
180 |
181 |
182 | ),
183 | )}
184 |
185 | ))}
186 |
187 |
188 |
197 |
198 |
199 | >
200 | );
201 | }
202 |
--------------------------------------------------------------------------------
/src/components/SolanisAvatar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import Image from "next/image";
4 | import SolanisTransparent from "../../public/solanis-transparent.png";
5 | import { GradientHeading } from "@/components/ui/gradient-heading";
6 |
7 | interface SolanisAvatarProps {
8 | hasBorder?: boolean;
9 | size?: "SM" | "MD" | "LG";
10 | }
11 |
12 | export default function SolanisAvatar({
13 | hasBorder = true,
14 | size = "MD",
15 | }: SolanisAvatarProps) {
16 | const sizeClass = {
17 | SM: "min-h-10 min-w-10 h-12 w-12",
18 | MD: "min-h-12 min-w-12 h-14 w-14",
19 | LG: "min-h-14 min-w-14 h-16 w-16",
20 | };
21 |
22 | return (
23 |
26 |
32 |
33 | solanis
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/UserItem.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { GradientHeading } from "./ui/gradient-heading";
4 | import { GradientText } from "./ui/gradient-text";
5 |
6 | export default function UserItem() {
7 | return (
8 |
9 |
10 |
11 | GR
12 |
13 |
14 |
15 |
16 | gustavorteuber
17 |
18 |
19 | gustavorteuber@dev.com
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/ui/animated-shiny-text.tsx:
--------------------------------------------------------------------------------
1 | import type { CSSProperties, FC, ReactNode } from "react";
2 | import { cn } from "@/lib/utils";
3 |
4 | interface AnimatedShinyTextProps {
5 | children: ReactNode;
6 | className?: string;
7 | shimmerWidth?: number;
8 | }
9 |
10 | const AnimatedShinyText: FC = ({
11 | children,
12 | className,
13 | shimmerWidth = 100,
14 | }) => {
15 | return (
16 |
34 | {children}
35 |
36 | );
37 | };
38 |
39 | export default AnimatedShinyText;
40 |
--------------------------------------------------------------------------------
/src/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 |
4 | import { cn } from "@/lib/utils";
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13 | secondary:
14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15 | destructive:
16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17 | outline: "text-foreground",
18 | },
19 | },
20 | defaultVariants: {
21 | variant: "default",
22 | },
23 | },
24 | );
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | );
34 | }
35 |
36 | export { Badge, badgeVariants };
37 |
--------------------------------------------------------------------------------
/src/components/ui/bento-grid.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 |
3 | export const BentoGrid = ({
4 | className,
5 | children,
6 | }: {
7 | className?: string;
8 | children?: React.ReactNode;
9 | }) => {
10 | return (
11 |
17 | {children}
18 |
19 | );
20 | };
21 |
22 | export const BentoGridItem = ({
23 | className,
24 | title,
25 | description,
26 | header,
27 | icon,
28 | }: {
29 | className?: string;
30 | title?: string | React.ReactNode;
31 | description?: string | React.ReactNode;
32 | header?: React.ReactNode;
33 | icon?: React.ReactNode;
34 | }) => {
35 | return (
36 |
42 | {header}
43 |
44 | {icon}
45 |
46 | {title}
47 |
48 |
49 | {description}
50 |
51 |
52 |
53 | );
54 | };
55 |
--------------------------------------------------------------------------------
/src/components/ui/breadcrumb.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { ChevronRight, MoreHorizontal } from "lucide-react";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const Breadcrumb = React.forwardRef<
8 | HTMLElement,
9 | React.ComponentPropsWithoutRef<"nav"> & {
10 | separator?: React.ReactNode;
11 | }
12 | >(({ ...props }, ref) => );
13 | Breadcrumb.displayName = "Breadcrumb";
14 |
15 | const BreadcrumbList = React.forwardRef<
16 | HTMLOListElement,
17 | React.ComponentPropsWithoutRef<"ol">
18 | >(({ className, ...props }, ref) => (
19 |
27 | ));
28 | BreadcrumbList.displayName = "BreadcrumbList";
29 |
30 | const BreadcrumbItem = React.forwardRef<
31 | HTMLLIElement,
32 | React.ComponentPropsWithoutRef<"li">
33 | >(({ className, ...props }, ref) => (
34 |
39 | ));
40 | BreadcrumbItem.displayName = "BreadcrumbItem";
41 |
42 | const BreadcrumbLink = React.forwardRef<
43 | HTMLAnchorElement,
44 | React.ComponentPropsWithoutRef<"a"> & {
45 | asChild?: boolean;
46 | }
47 | >(({ asChild, className, ...props }, ref) => {
48 | const Comp = asChild ? Slot : "a";
49 |
50 | return (
51 |
56 | );
57 | });
58 | BreadcrumbLink.displayName = "BreadcrumbLink";
59 |
60 | const BreadcrumbPage = React.forwardRef<
61 | HTMLSpanElement,
62 | React.ComponentPropsWithoutRef<"span">
63 | >(({ className, ...props }, ref) => (
64 |
72 | ));
73 | BreadcrumbPage.displayName = "BreadcrumbPage";
74 |
75 | const BreadcrumbSeparator = ({
76 | children,
77 | className,
78 | ...props
79 | }: React.ComponentProps<"li">) => (
80 | svg]:size-3.5", className)}
84 | {...props}
85 | >
86 | {children ?? }
87 |
88 | );
89 | BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
90 |
91 | const BreadcrumbEllipsis = ({
92 | className,
93 | ...props
94 | }: React.ComponentProps<"span">) => (
95 |
101 |
102 | More
103 |
104 | );
105 | BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
106 |
107 | export {
108 | Breadcrumb,
109 | BreadcrumbList,
110 | BreadcrumbItem,
111 | BreadcrumbLink,
112 | BreadcrumbPage,
113 | BreadcrumbSeparator,
114 | BreadcrumbEllipsis,
115 | };
116 |
--------------------------------------------------------------------------------
/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 | import { cn } from "@/lib/utils";
5 |
6 | const buttonVariants = cva(
7 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
8 | {
9 | variants: {
10 | variant: {
11 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
12 | destructive:
13 | "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14 | outline:
15 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16 | secondary:
17 | "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18 | ghost: "hover:bg-accent hover:text-accent-foreground",
19 | link: "text-primary underline-offset-4 hover:underline",
20 | expandIcon:
21 | "group relative text-primary-foreground bg-primary hover:bg-primary/90",
22 | ringHover:
23 | "bg-primary text-primary-foreground transition-all duration-300 hover:bg-primary/90 hover:ring-2 hover:ring-primary/90 hover:ring-offset-2",
24 | shine:
25 | "text-primary-foreground animate-shine bg-gradient-to-r from-primary via-primary/75 to-primary bg-[length:400%_100%] ",
26 | gooeyRight:
27 | "text-primary-foreground relative bg-primary z-0 overflow-hidden transition-all duration-500 before:absolute before:inset-0 before:-z-10 before:translate-x-[150%] before:translate-y-[150%] before:scale-[2.5] before:rounded-[100%] before:bg-gradient-to-r from-zinc-400 before:transition-transform before:duration-1000 hover:before:translate-x-[0%] hover:before:translate-y-[0%] ",
28 | gooeyLeft:
29 | "text-primary-foreground relative bg-primary z-0 overflow-hidden transition-all duration-500 after:absolute after:inset-0 after:-z-10 after:translate-x-[-150%] after:translate-y-[150%] after:scale-[2.5] after:rounded-[100%] after:bg-gradient-to-l from-zinc-400 after:transition-transform after:duration-1000 hover:after:translate-x-[0%] hover:after:translate-y-[0%] ",
30 | linkHover1:
31 | "relative after:absolute after:bg-primary after:bottom-2 after:h-[1px] after:w-2/3 after:origin-bottom-left after:scale-x-100 hover:after:origin-bottom-right hover:after:scale-x-0 after:transition-transform after:ease-in-out after:duration-300",
32 | linkHover2:
33 | "relative after:absolute after:bg-primary after:bottom-2 after:h-[1px] after:w-2/3 after:origin-bottom-right after:scale-x-0 hover:after:origin-bottom-left hover:after:scale-x-100 after:transition-transform after:ease-in-out after:duration-300",
34 | expandIconAndShine:
35 | "group text-primary-foreground animate-shine bg-gradient-to-r from-primary via-primary/75 to-primary bg-[length:400%_100%]",
36 | },
37 | size: {
38 | default: "h-10 px-4 py-2",
39 | sm: "h-9 rounded-md px-3",
40 | lg: "h-11 rounded-md px-8",
41 | icon: "h-10 w-10",
42 | },
43 | },
44 | defaultVariants: {
45 | variant: "default",
46 | size: "default",
47 | },
48 | },
49 | );
50 |
51 | interface IconProps {
52 | Icon: React.ElementType;
53 | iconPlacement: "left" | "right";
54 | iconStrokeWidth?: number;
55 | iconColor?: string;
56 | }
57 |
58 | interface IconRefProps {
59 | Icon?: never;
60 | iconPlacement?: undefined;
61 | iconStrokeWidth?: undefined;
62 | iconColor?: undefined;
63 | }
64 |
65 | export interface ButtonProps
66 | extends React.ButtonHTMLAttributes,
67 | VariantProps {
68 | asChild?: boolean;
69 | }
70 |
71 | export type ButtonIconProps = IconProps | IconRefProps;
72 |
73 | const Button = React.forwardRef<
74 | HTMLButtonElement,
75 | ButtonProps & ButtonIconProps
76 | >(
77 | (
78 | {
79 | className,
80 | variant,
81 | size,
82 | asChild = false,
83 | Icon,
84 | iconPlacement,
85 | iconStrokeWidth = 1,
86 | iconColor,
87 | ...props
88 | },
89 | ref,
90 | ) => {
91 | const Comp = asChild ? Slot : "button";
92 | return (
93 |
98 | {Icon && iconPlacement === "left" && (
99 |
100 |
101 |
102 | )}
103 | {props.children}
104 | {Icon && iconPlacement === "right" && (
105 |
106 |
107 |
108 | )}
109 |
110 | );
111 | },
112 | );
113 |
114 | Button.displayName = "Button";
115 |
116 | export { Button, buttonVariants };
117 |
--------------------------------------------------------------------------------
/src/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | const Card = React.forwardRef<
6 | HTMLDivElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
17 | ));
18 | Card.displayName = "Card";
19 |
20 | const CardHeader = React.forwardRef<
21 | HTMLDivElement,
22 | React.HTMLAttributes
23 | >(({ className, ...props }, ref) => (
24 |
29 | ));
30 | CardHeader.displayName = "CardHeader";
31 |
32 | const CardTitle = React.forwardRef<
33 | HTMLParagraphElement,
34 | React.HTMLAttributes
35 | >(({ className, ...props }, ref) => (
36 |
44 | ));
45 | CardTitle.displayName = "CardTitle";
46 |
47 | const CardDescription = React.forwardRef<
48 | HTMLParagraphElement,
49 | React.HTMLAttributes
50 | >(({ className, ...props }, ref) => (
51 |
56 | ));
57 | CardDescription.displayName = "CardDescription";
58 |
59 | const CardContent = React.forwardRef<
60 | HTMLDivElement,
61 | React.HTMLAttributes
62 | >(({ className, ...props }, ref) => (
63 |
64 | ));
65 | CardContent.displayName = "CardContent";
66 |
67 | const CardFooter = React.forwardRef<
68 | HTMLDivElement,
69 | React.HTMLAttributes
70 | >(({ className, ...props }, ref) => (
71 |
76 | ));
77 | CardFooter.displayName = "CardFooter";
78 |
79 | export {
80 | Card,
81 | CardHeader,
82 | CardFooter,
83 | CardTitle,
84 | CardDescription,
85 | CardContent,
86 | };
87 |
--------------------------------------------------------------------------------
/src/components/ui/command.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { type DialogProps } from "@radix-ui/react-dialog";
5 | import { Command as CommandPrimitive } from "cmdk";
6 | import { Search } from "lucide-react";
7 |
8 | import { cn } from "@/lib/utils";
9 | import { Dialog, DialogContent } from "@/components/ui/dialog";
10 |
11 | const Command = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
23 | ));
24 | Command.displayName = CommandPrimitive.displayName;
25 |
26 | interface CommandDialogProps extends DialogProps {}
27 |
28 | const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
29 | return (
30 |
31 |
32 |
33 | {children}
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | const CommandInput = React.forwardRef<
41 | React.ElementRef,
42 | React.ComponentPropsWithoutRef
43 | >(({ className, ...props }, ref) => (
44 |
45 |
46 |
54 |
55 | ));
56 |
57 | CommandInput.displayName = CommandPrimitive.Input.displayName;
58 |
59 | const CommandList = React.forwardRef<
60 | React.ElementRef,
61 | React.ComponentPropsWithoutRef
62 | >(({ className, ...props }, ref) => (
63 |
68 | ));
69 |
70 | CommandList.displayName = CommandPrimitive.List.displayName;
71 |
72 | const CommandEmpty = React.forwardRef<
73 | React.ElementRef,
74 | React.ComponentPropsWithoutRef
75 | >((props, ref) => (
76 |
81 | ));
82 |
83 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
84 |
85 | const CommandGroup = React.forwardRef<
86 | React.ElementRef,
87 | React.ComponentPropsWithoutRef
88 | >(({ className, ...props }, ref) => (
89 |
97 | ));
98 |
99 | CommandGroup.displayName = CommandPrimitive.Group.displayName;
100 |
101 | const CommandSeparator = React.forwardRef<
102 | React.ElementRef,
103 | React.ComponentPropsWithoutRef
104 | >(({ className, ...props }, ref) => (
105 |
110 | ));
111 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
112 |
113 | const CommandItem = React.forwardRef<
114 | React.ElementRef,
115 | React.ComponentPropsWithoutRef
116 | >(({ className, ...props }, ref) => (
117 |
125 | ));
126 |
127 | CommandItem.displayName = CommandPrimitive.Item.displayName;
128 |
129 | const CommandShortcut = ({
130 | className,
131 | ...props
132 | }: React.HTMLAttributes) => {
133 | return (
134 |
141 | );
142 | };
143 | CommandShortcut.displayName = "CommandShortcut";
144 |
145 | export {
146 | Command,
147 | CommandDialog,
148 | CommandInput,
149 | CommandList,
150 | CommandEmpty,
151 | CommandGroup,
152 | CommandItem,
153 | CommandShortcut,
154 | CommandSeparator,
155 | };
156 |
--------------------------------------------------------------------------------
/src/components/ui/container-scroll-animation.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useRef } from "react";
3 | import { useScroll, useTransform, motion, MotionValue } from "framer-motion";
4 |
5 | export const ContainerScroll = ({
6 | titleComponent,
7 | children,
8 | }: {
9 | titleComponent: string | React.ReactNode;
10 | children: React.ReactNode;
11 | }) => {
12 | const containerRef = useRef(null);
13 | const { scrollYProgress } = useScroll({
14 | target: containerRef,
15 | });
16 | const [isMobile, setIsMobile] = React.useState(false);
17 |
18 | React.useEffect(() => {
19 | const checkMobile = () => {
20 | setIsMobile(window.innerWidth <= 768);
21 | };
22 | checkMobile();
23 | window.addEventListener("resize", checkMobile);
24 | return () => {
25 | window.removeEventListener("resize", checkMobile);
26 | };
27 | }, []);
28 |
29 | const scaleDimensions = () => {
30 | return isMobile ? [0.7, 0.9] : [1.05, 1];
31 | };
32 |
33 | const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
34 | const scale = useTransform(scrollYProgress, [0, 1], scaleDimensions());
35 | const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);
36 |
37 | return (
38 |
42 |
48 |
49 |
50 | {children}
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export const Header = ({ translate, titleComponent }: any) => {
58 | return (
59 |
65 | {titleComponent}
66 |
67 | );
68 | };
69 |
70 | export const Card = ({
71 | rotate,
72 | scale,
73 | children,
74 | }: {
75 | rotate: MotionValue;
76 | scale: MotionValue;
77 | translate: MotionValue;
78 | children: React.ReactNode;
79 | }) => {
80 | return (
81 |
90 |
91 | {children}
92 |
93 |
94 | );
95 | };
96 |
--------------------------------------------------------------------------------
/src/components/ui/dialog.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as DialogPrimitive from "@radix-ui/react-dialog";
5 | import { X } from "lucide-react";
6 |
7 | import { cn } from "@/lib/utils";
8 |
9 | const Dialog = DialogPrimitive.Root;
10 |
11 | const DialogTrigger = DialogPrimitive.Trigger;
12 |
13 | const DialogPortal = DialogPrimitive.Portal;
14 |
15 | const DialogClose = DialogPrimitive.Close;
16 |
17 | const DialogOverlay = React.forwardRef<
18 | React.ElementRef,
19 | React.ComponentPropsWithoutRef
20 | >(({ className, ...props }, ref) => (
21 |
29 | ));
30 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
31 |
32 | const DialogContent = React.forwardRef<
33 | React.ElementRef,
34 | React.ComponentPropsWithoutRef
35 | >(({ className, children, ...props }, ref) => (
36 |
37 |
38 |
46 | {children}
47 |
48 |
49 | Close
50 |
51 |
52 |
53 | ));
54 | DialogContent.displayName = DialogPrimitive.Content.displayName;
55 |
56 | const DialogHeader = ({
57 | className,
58 | ...props
59 | }: React.HTMLAttributes) => (
60 |
67 | );
68 | DialogHeader.displayName = "DialogHeader";
69 |
70 | const DialogFooter = ({
71 | className,
72 | ...props
73 | }: React.HTMLAttributes) => (
74 |
81 | );
82 | DialogFooter.displayName = "DialogFooter";
83 |
84 | const DialogTitle = React.forwardRef<
85 | React.ElementRef,
86 | React.ComponentPropsWithoutRef
87 | >(({ className, ...props }, ref) => (
88 |
96 | ));
97 | DialogTitle.displayName = DialogPrimitive.Title.displayName;
98 |
99 | const DialogDescription = React.forwardRef<
100 | React.ElementRef,
101 | React.ComponentPropsWithoutRef
102 | >(({ className, ...props }, ref) => (
103 |
108 | ));
109 | DialogDescription.displayName = DialogPrimitive.Description.displayName;
110 |
111 | export {
112 | Dialog,
113 | DialogPortal,
114 | DialogOverlay,
115 | DialogClose,
116 | DialogTrigger,
117 | DialogContent,
118 | DialogHeader,
119 | DialogFooter,
120 | DialogTitle,
121 | DialogDescription,
122 | };
123 |
--------------------------------------------------------------------------------
/src/components/ui/dropdown-menu.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
5 | import { Check, ChevronRight, Circle } from "lucide-react";
6 |
7 | import { cn } from "@/lib/utils";
8 |
9 | const DropdownMenu = DropdownMenuPrimitive.Root;
10 |
11 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
12 |
13 | const DropdownMenuGroup = DropdownMenuPrimitive.Group;
14 |
15 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
16 |
17 | const DropdownMenuSub = DropdownMenuPrimitive.Sub;
18 |
19 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
20 |
21 | const DropdownMenuSubTrigger = React.forwardRef<
22 | React.ElementRef,
23 | React.ComponentPropsWithoutRef & {
24 | inset?: boolean;
25 | }
26 | >(({ className, inset, children, ...props }, ref) => (
27 |
36 | {children}
37 |
38 |
39 | ));
40 | DropdownMenuSubTrigger.displayName =
41 | DropdownMenuPrimitive.SubTrigger.displayName;
42 |
43 | const DropdownMenuSubContent = React.forwardRef<
44 | React.ElementRef,
45 | React.ComponentPropsWithoutRef
46 | >(({ className, ...props }, ref) => (
47 |
55 | ));
56 | DropdownMenuSubContent.displayName =
57 | DropdownMenuPrimitive.SubContent.displayName;
58 |
59 | const DropdownMenuContent = React.forwardRef<
60 | React.ElementRef,
61 | React.ComponentPropsWithoutRef
62 | >(({ className, sideOffset = 4, ...props }, ref) => (
63 |
64 |
73 |
74 | ));
75 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
76 |
77 | const DropdownMenuItem = React.forwardRef<
78 | React.ElementRef,
79 | React.ComponentPropsWithoutRef & {
80 | inset?: boolean;
81 | }
82 | >(({ className, inset, ...props }, ref) => (
83 |
92 | ));
93 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
94 |
95 | const DropdownMenuCheckboxItem = React.forwardRef<
96 | React.ElementRef,
97 | React.ComponentPropsWithoutRef
98 | >(({ className, children, checked, ...props }, ref) => (
99 |
108 |
109 |
110 |
111 |
112 |
113 | {children}
114 |
115 | ));
116 | DropdownMenuCheckboxItem.displayName =
117 | DropdownMenuPrimitive.CheckboxItem.displayName;
118 |
119 | const DropdownMenuRadioItem = React.forwardRef<
120 | React.ElementRef,
121 | React.ComponentPropsWithoutRef
122 | >(({ className, children, ...props }, ref) => (
123 |
131 |
132 |
133 |
134 |
135 |
136 | {children}
137 |
138 | ));
139 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
140 |
141 | const DropdownMenuLabel = React.forwardRef<
142 | React.ElementRef,
143 | React.ComponentPropsWithoutRef & {
144 | inset?: boolean;
145 | }
146 | >(({ className, inset, ...props }, ref) => (
147 |
156 | ));
157 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
158 |
159 | const DropdownMenuSeparator = React.forwardRef<
160 | React.ElementRef,
161 | React.ComponentPropsWithoutRef
162 | >(({ className, ...props }, ref) => (
163 |
168 | ));
169 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
170 |
171 | const DropdownMenuShortcut = ({
172 | className,
173 | ...props
174 | }: React.HTMLAttributes) => {
175 | return (
176 |
180 | );
181 | };
182 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
183 |
184 | export {
185 | DropdownMenu,
186 | DropdownMenuTrigger,
187 | DropdownMenuContent,
188 | DropdownMenuItem,
189 | DropdownMenuCheckboxItem,
190 | DropdownMenuRadioItem,
191 | DropdownMenuLabel,
192 | DropdownMenuSeparator,
193 | DropdownMenuShortcut,
194 | DropdownMenuGroup,
195 | DropdownMenuPortal,
196 | DropdownMenuSub,
197 | DropdownMenuSubContent,
198 | DropdownMenuSubTrigger,
199 | DropdownMenuRadioGroup,
200 | };
201 |
--------------------------------------------------------------------------------
/src/components/ui/enchanced-separator.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 |
3 | type SeparatorProps = {
4 | /**
5 | * @default ""
6 | */
7 | label?: React.ReactNode;
8 | /**
9 | * @default false
10 | */
11 | gradient?: boolean;
12 | className?: string;
13 | };
14 | //======================================
15 | export const EnchancedSeparator = ({
16 | label,
17 | gradient = false,
18 | className = "",
19 | }: SeparatorProps) => {
20 | if (label) {
21 | return (
22 |
23 |
32 |
33 | {label}
34 |
35 |
44 |
45 | );
46 | }
47 | return (
48 |
57 | );
58 | };
59 |
--------------------------------------------------------------------------------
/src/components/ui/gradient-heading.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const headingVariants = cva(
8 | "tracking-tight pb-3 bg-clip-text text-transparent",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "bg-gradient-to-t from-neutral-700 to-neutral-800 dark:from-stone-200 dark:to-neutral-200",
14 | pink: "bg-gradient-to-t from-accent to-accent/90 dark:from-stone-200 dark:to-neutral-200",
15 | light: "bg-gradient-to-t from-neutral-200 to-neutral-300",
16 | solanis: "bg-gradient-to-t from-primary to-[#8c74ff]/60",
17 | secondary:
18 | "bg-gradient-to-t from-primary-foreground to-muted-foreground",
19 | },
20 | size: {
21 | default: "text-2xl sm:text-3xl lg:text-4xl",
22 | xxs: "text-base sm:text-lg lg:text-lg",
23 | xs: "text-lg sm:text-xl lg:text-2xl",
24 | sm: "text-xl sm:text-2xl lg:text-3xl",
25 | md: "text-2xl sm:text-3xl lg:text-4xl",
26 | lg: "text-3xl sm:text-4xl lg:text-5xl",
27 | xl: "text-4xl sm:text-5xl lg:text-6xl",
28 | xxl: "text-5xl sm:text-6xl lg:text-[6rem]",
29 | xxxl: "text-5xl sm:text-6xl lg:text-[8rem]",
30 | },
31 | weight: {
32 | default: "font-bold",
33 | thin: "font-thin",
34 | base: "font-base",
35 | semi: "font-semibold",
36 | bold: "font-bold",
37 | black: "font-black",
38 | },
39 | },
40 | defaultVariants: {
41 | variant: "default",
42 | size: "default",
43 | weight: "default",
44 | },
45 | },
46 | );
47 |
48 | export interface HeadingProps extends VariantProps {
49 | asChild?: boolean;
50 | children: React.ReactNode;
51 | className?: string;
52 | }
53 |
54 | const GradientHeading = React.forwardRef(
55 | ({ asChild, variant, weight, size, className, children, ...props }, ref) => {
56 | const Comp = asChild ? Slot : "h3"; // default to 'h3' if not a child
57 | return (
58 |
59 |
60 | {children}
61 |
62 |
63 | );
64 | },
65 | );
66 |
67 | GradientHeading.displayName = "GradientHeading";
68 |
69 | // Manually define the variant types
70 | export type Variant = "default" | "pink" | "light" | "secondary";
71 | export type Size =
72 | | "default"
73 | | "xxs"
74 | | "xs"
75 | | "sm"
76 | | "md"
77 | | "lg"
78 | | "xl"
79 | | "xxl"
80 | | "xxxl";
81 | export type Weight = "default" | "thin" | "base" | "semi" | "bold" | "black";
82 |
83 | export { GradientHeading, headingVariants };
84 |
--------------------------------------------------------------------------------
/src/components/ui/gradient-text.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 |
4 | import { cn } from "@/lib/utils";
5 |
6 | const textVariants = cva("tracking-tight bg-clip-text text-transparent", {
7 | variants: {
8 | variant: {
9 | default:
10 | "bg-gradient-to-t from-neutral-700 to-neutral-800 dark:from-stone-200 dark:to-neutral-200",
11 | pink: "bg-gradient-to-t from-accent to-accent/90 dark:from-stone-200 dark:to-neutral-200",
12 | light: "bg-gradient-to-t from-neutral-200 to-neutral-300",
13 | secondary: "bg-gradient-to-t from-primary-foreground to-muted-foreground",
14 | },
15 | size: {
16 | default: "text-base",
17 | xs: "text-xs",
18 | sm: "text-sm",
19 | md: "text-md",
20 | lg: "text-lg",
21 | xl: "text-xl",
22 | },
23 | weight: {
24 | default: "font-medium",
25 | thin: "font-thin",
26 | semi: "font-semibold",
27 | bold: "font-bold",
28 | black: "font-black",
29 | },
30 | },
31 | defaultVariants: {
32 | variant: "default",
33 | size: "default",
34 | weight: "default",
35 | },
36 | });
37 |
38 | export interface GradientTextProps extends VariantProps {
39 | children: React.ReactNode;
40 | className?: string;
41 | }
42 |
43 | const GradientText = React.forwardRef(
44 | ({ variant, weight, size, className, children, ...props }, ref) => {
45 | return (
46 |
51 | {children}
52 |
53 | );
54 | },
55 | );
56 |
57 | GradientText.displayName = "GradientText";
58 |
59 | export type Variant = "default" | "pink" | "light" | "secondary";
60 | export type Size = "default" | "sm" | "md" | "lg" | "xl";
61 | export type Weight = "default" | "thin" | "base" | "semi" | "bold" | "black";
62 |
63 | export { GradientText, textVariants };
64 |
--------------------------------------------------------------------------------
/src/components/ui/input-icon.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 | import { LucideIcon } from "lucide-react";
5 |
6 | export interface InputProps
7 | extends React.InputHTMLAttributes {
8 | startIcon?: LucideIcon;
9 | endIcon?: LucideIcon;
10 | }
11 |
12 | const InputIcon = React.forwardRef(
13 | ({ className, type, startIcon, endIcon, ...props }, ref) => {
14 | const StartIcon = startIcon;
15 | const EndIcon = endIcon;
16 |
17 | return (
18 |
19 |
20 | {StartIcon && (
21 |
22 |
23 |
24 | )}
25 |
36 | {EndIcon && (
37 |
38 |
39 |
40 | )}
41 |
42 |
43 | );
44 | },
45 | );
46 | InputIcon.displayName = "InputIcon";
47 |
48 | export { InputIcon };
49 |
--------------------------------------------------------------------------------
/src/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | export interface InputProps
6 | extends React.InputHTMLAttributes {}
7 |
8 | const Input = React.forwardRef(
9 | ({ className, type, ...props }, ref) => {
10 | return (
11 |
20 | );
21 | },
22 | );
23 | Input.displayName = "Input";
24 |
25 | export { Input };
26 |
--------------------------------------------------------------------------------
/src/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as LabelPrimitive from "@radix-ui/react-label"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const labelVariants = cva(
10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11 | )
12 |
13 | const Label = React.forwardRef<
14 | React.ElementRef,
15 | React.ComponentPropsWithoutRef &
16 | VariantProps
17 | >(({ className, ...props }, ref) => (
18 |
23 | ))
24 | Label.displayName = LabelPrimitive.Root.displayName
25 |
26 | export { Label }
27 |
--------------------------------------------------------------------------------
/src/components/ui/link-preview.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
3 | import Image from "next/image";
4 | import { encode } from "qss";
5 | import React from "react";
6 | import {
7 | AnimatePresence,
8 | motion,
9 | useMotionValue,
10 | useSpring,
11 | } from "framer-motion";
12 | import Link from "next/link";
13 | import { cn } from "@/lib/utils";
14 |
15 | type LinkPreviewProps = {
16 | children: React.ReactNode;
17 | url: string;
18 | className?: string;
19 | width?: number;
20 | height?: number;
21 | quality?: number;
22 | layout?: string;
23 | } & (
24 | | { isStatic: true; imageSrc: string }
25 | | { isStatic?: false; imageSrc?: never }
26 | );
27 |
28 | export const LinkPreview = ({
29 | children,
30 | url,
31 | className,
32 | width = 200,
33 | height = 125,
34 | quality = 50,
35 | layout = "fixed",
36 | isStatic = false,
37 | imageSrc = "",
38 | }: LinkPreviewProps) => {
39 | let src;
40 | if (!isStatic) {
41 | const params = encode({
42 | url,
43 | screenshot: true,
44 | meta: false,
45 | embed: "screenshot.url",
46 | colorScheme: "dark",
47 | "viewport.isMobile": true,
48 | "viewport.deviceScaleFactor": 1,
49 | "viewport.width": width * 3,
50 | "viewport.height": height * 3,
51 | });
52 | src = `https://api.microlink.io/?${params}`;
53 | } else {
54 | src = imageSrc;
55 | }
56 |
57 | const [isOpen, setOpen] = React.useState(false);
58 |
59 | const [isMounted, setIsMounted] = React.useState(false);
60 |
61 | React.useEffect(() => {
62 | setIsMounted(true);
63 | }, []);
64 |
65 | const springConfig = { stiffness: 100, damping: 15 };
66 | const x = useMotionValue(0);
67 |
68 | const translateX = useSpring(x, springConfig);
69 |
70 | const handleMouseMove = (event: any) => {
71 | const targetRect = event.target.getBoundingClientRect();
72 | const eventOffsetX = event.clientX - targetRect.left;
73 | const offsetFromCenter = (eventOffsetX - targetRect.width / 2) / 2;
74 | x.set(offsetFromCenter);
75 | };
76 |
77 | return (
78 | <>
79 | {isMounted ? (
80 |
81 |
90 |
91 | ) : null}
92 |
93 | {
97 | setOpen(open);
98 | }}
99 | >
100 |
105 | {children}
106 |
107 |
108 |
114 |
115 | {isOpen && (
116 |
134 |
139 |
149 |
150 |
151 | )}
152 |
153 |
154 |
155 | >
156 | );
157 | };
158 |
--------------------------------------------------------------------------------
/src/components/ui/resizable.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { GripVertical } from "lucide-react";
4 | import * as ResizablePrimitive from "react-resizable-panels";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | const ResizablePanelGroup = ({
9 | className,
10 | ...props
11 | }: React.ComponentProps) => (
12 |
19 | );
20 |
21 | const ResizablePanel = ResizablePrimitive.Panel;
22 |
23 | const ResizableHandle = ({
24 | withHandle,
25 | className,
26 | ...props
27 | }: React.ComponentProps & {
28 | withHandle?: boolean;
29 | }) => (
30 | div]:rotate-90",
33 | className,
34 | )}
35 | {...props}
36 | >
37 | {withHandle && (
38 |
39 |
40 |
41 | )}
42 |
43 | );
44 |
45 | export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
46 |
--------------------------------------------------------------------------------
/src/components/ui/select.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as SelectPrimitive from "@radix-ui/react-select";
5 | import { Check, ChevronDown, ChevronUp } from "lucide-react";
6 |
7 | import { cn } from "@/lib/utils";
8 |
9 | const Select = SelectPrimitive.Root;
10 |
11 | const SelectGroup = SelectPrimitive.Group;
12 |
13 | const SelectValue = SelectPrimitive.Value;
14 |
15 | const SelectTrigger = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, children, ...props }, ref) => (
19 | span]:line-clamp-1",
23 | className,
24 | )}
25 | {...props}
26 | >
27 | {children}
28 |
29 |
30 |
31 |
32 | ));
33 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
34 |
35 | const SelectScrollUpButton = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 |
48 |
49 | ));
50 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
51 |
52 | const SelectScrollDownButton = React.forwardRef<
53 | React.ElementRef,
54 | React.ComponentPropsWithoutRef
55 | >(({ className, ...props }, ref) => (
56 |
64 |
65 |
66 | ));
67 | SelectScrollDownButton.displayName =
68 | SelectPrimitive.ScrollDownButton.displayName;
69 |
70 | const SelectContent = React.forwardRef<
71 | React.ElementRef,
72 | React.ComponentPropsWithoutRef
73 | >(({ className, children, position = "popper", ...props }, ref) => (
74 |
75 |
86 |
87 |
94 | {children}
95 |
96 |
97 |
98 |
99 | ));
100 | SelectContent.displayName = SelectPrimitive.Content.displayName;
101 |
102 | const SelectLabel = React.forwardRef<
103 | React.ElementRef,
104 | React.ComponentPropsWithoutRef
105 | >(({ className, ...props }, ref) => (
106 |
111 | ));
112 | SelectLabel.displayName = SelectPrimitive.Label.displayName;
113 |
114 | const SelectItem = React.forwardRef<
115 | React.ElementRef,
116 | React.ComponentPropsWithoutRef
117 | >(({ className, children, ...props }, ref) => (
118 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | {children}
133 |
134 | ));
135 | SelectItem.displayName = SelectPrimitive.Item.displayName;
136 |
137 | const SelectSeparator = React.forwardRef<
138 | React.ElementRef,
139 | React.ComponentPropsWithoutRef
140 | >(({ className, ...props }, ref) => (
141 |
146 | ));
147 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
148 |
149 | export {
150 | Select,
151 | SelectGroup,
152 | SelectValue,
153 | SelectTrigger,
154 | SelectContent,
155 | SelectLabel,
156 | SelectItem,
157 | SelectSeparator,
158 | SelectScrollUpButton,
159 | SelectScrollDownButton,
160 | };
161 |
--------------------------------------------------------------------------------
/src/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref,
15 | ) => (
16 |
27 | ),
28 | );
29 | Separator.displayName = SeparatorPrimitive.Root.displayName;
30 |
31 | export { Separator };
32 |
--------------------------------------------------------------------------------
/src/components/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils"
2 |
3 | function Skeleton({
4 | className,
5 | ...props
6 | }: React.HTMLAttributes) {
7 | return (
8 |
12 | )
13 | }
14 |
15 | export { Skeleton }
16 |
--------------------------------------------------------------------------------
/src/components/ui/sparkles-text.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { motion } from 'framer-motion'
4 | import type { CSSProperties, ReactElement } from 'react'
5 | import { useEffect, useState } from 'react'
6 | import { cn } from '@/lib/utils'
7 |
8 | interface Sparkle {
9 | id: string
10 | x: string
11 | y: string
12 | color: string
13 | delay: number
14 | scale: number
15 | lifespan: number
16 | }
17 |
18 | interface SparklesTextProps {
19 | /**
20 | * @default
21 | * @type ReactElement
22 | * @description
23 | * The component to be rendered as the text
24 | */
25 | as?: ReactElement
26 |
27 | /**
28 | * @default ""
29 | * @type string
30 | * @description
31 | * The className of the text
32 | */
33 | className?: string
34 |
35 | /**
36 | * @required
37 | * @type string
38 | * @description
39 | * The text to be displayed
40 | */
41 | text: string
42 |
43 | /**
44 | * @default 10
45 | * @type number
46 | * @description
47 | * The count of sparkles
48 | */
49 | sparklesCount?: number
50 |
51 | /**
52 | * @default "{first: '#A07CFE', second: '#FE8FB5'}"
53 | * @type string
54 | * @description
55 | * The colors of the sparkles
56 | */
57 | colors?: {
58 | first: string
59 | second: string
60 | }
61 | }
62 |
63 | const SparklesText: React.FC = ({
64 | text,
65 | colors = { first: '#A07CFE', second: '#FE8FB5' },
66 | className,
67 | sparklesCount = 10,
68 | ...props
69 | }) => {
70 | const [sparkles, setSparkles] = useState([])
71 |
72 | useEffect(() => {
73 | const generateStar = (): Sparkle => {
74 | const starX = `${Math.random() * 100}%`
75 | const starY = `${Math.random() * 100}%`
76 | const color = Math.random() > 0.5 ? colors.first : colors.second
77 | const delay = Math.random() * 2
78 | const scale = Math.random() * 1 + 0.3
79 | const lifespan = Math.random() * 10 + 5
80 | const id = `${starX}-${starY}-${Date.now()}`
81 | return { id, x: starX, y: starY, color, delay, scale, lifespan }
82 | }
83 |
84 | const initializeStars = () => {
85 | const newSparkles = Array.from({ length: sparklesCount }, generateStar)
86 | setSparkles(newSparkles)
87 | }
88 |
89 | const updateStars = () => {
90 | setSparkles(currentSparkles =>
91 | currentSparkles.map((star) => {
92 | if (star.lifespan <= 0)
93 | return generateStar()
94 | else return { ...star, lifespan: star.lifespan - 0.1 }
95 | }),
96 | )
97 | }
98 |
99 | initializeStars()
100 | const interval = setInterval(updateStars, 100)
101 |
102 | return () => clearInterval(interval)
103 | }, [colors.first, colors.second])
104 |
105 | return (
106 |
116 |
117 | {sparkles.map(sparkle => (
118 |
119 | ))}
120 |
121 | {text}
122 |
123 |
124 |
125 | )
126 | }
127 |
128 | const Sparkle: React.FC = ({ id, x, y, color, delay, scale }) => {
129 | return (
130 |
144 |
148 |
149 | )
150 | }
151 |
152 | export default SparklesText
153 |
--------------------------------------------------------------------------------
/src/components/ui/staggered-fade.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { cn } from "@/lib/utils";
4 | import { motion, useInView } from "framer-motion";
5 | import * as React from "react";
6 |
7 | type TextStaggeredFadeProps = {
8 | text: string;
9 | className?: string;
10 | };
11 |
12 | export const StaggeredFade: React.FC = ({
13 | text,
14 | className = "",
15 | }) => {
16 | const variants = {
17 | hidden: { opacity: 0 },
18 | show: (i: number) => ({
19 | y: 0,
20 | opacity: 1,
21 | transition: { delay: i * 0.07 },
22 | }),
23 | };
24 |
25 | const letters = text.split("");
26 | const ref = React.useRef(null);
27 | const isInView = useInView(ref, { once: true });
28 |
29 | return (
30 |
41 | {letters.map((word, i) => (
42 |
43 | {word}
44 |
45 | ))}
46 |
47 | );
48 | };
49 |
--------------------------------------------------------------------------------
/src/components/ui/switch.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SwitchPrimitives from "@radix-ui/react-switch"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Switch = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
25 |
26 | ))
27 | Switch.displayName = SwitchPrimitives.Root.displayName
28 |
29 | export { Switch }
30 |
--------------------------------------------------------------------------------
/src/components/ui/table.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | const Table = React.forwardRef<
6 | HTMLTableElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
16 | ));
17 | Table.displayName = "Table";
18 |
19 | const TableHeader = React.forwardRef<
20 | HTMLTableSectionElement,
21 | React.HTMLAttributes
22 | >(({ className, ...props }, ref) => (
23 |
24 | ));
25 | TableHeader.displayName = "TableHeader";
26 |
27 | const TableBody = React.forwardRef<
28 | HTMLTableSectionElement,
29 | React.HTMLAttributes
30 | >(({ className, ...props }, ref) => (
31 |
36 | ));
37 | TableBody.displayName = "TableBody";
38 |
39 | const TableFooter = React.forwardRef<
40 | HTMLTableSectionElement,
41 | React.HTMLAttributes
42 | >(({ className, ...props }, ref) => (
43 | tr]:last:border-b-0",
47 | className,
48 | )}
49 | {...props}
50 | />
51 | ));
52 | TableFooter.displayName = "TableFooter";
53 |
54 | const TableRow = React.forwardRef<
55 | HTMLTableRowElement,
56 | React.HTMLAttributes
57 | >(({ className, ...props }, ref) => (
58 |
66 | ));
67 | TableRow.displayName = "TableRow";
68 |
69 | const TableHead = React.forwardRef<
70 | HTMLTableCellElement,
71 | React.ThHTMLAttributes
72 | >(({ className, ...props }, ref) => (
73 |
81 | ));
82 | TableHead.displayName = "TableHead";
83 |
84 | const TableCell = React.forwardRef<
85 | HTMLTableCellElement,
86 | React.TdHTMLAttributes
87 | >(({ className, ...props }, ref) => (
88 |
93 | ));
94 | TableCell.displayName = "TableCell";
95 |
96 | const TableCaption = React.forwardRef<
97 | HTMLTableCaptionElement,
98 | React.HTMLAttributes
99 | >(({ className, ...props }, ref) => (
100 |
105 | ));
106 | TableCaption.displayName = "TableCaption";
107 |
108 | export {
109 | Table,
110 | TableHeader,
111 | TableBody,
112 | TableFooter,
113 | TableHead,
114 | TableRow,
115 | TableCell,
116 | TableCaption,
117 | };
118 |
--------------------------------------------------------------------------------
/src/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TabsPrimitive from "@radix-ui/react-tabs"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Tabs = TabsPrimitive.Root
9 |
10 | const TabsList = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ))
23 | TabsList.displayName = TabsPrimitive.List.displayName
24 |
25 | const TabsTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef
28 | >(({ className, ...props }, ref) => (
29 |
37 | ))
38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
39 |
40 | const TabsContent = React.forwardRef<
41 | React.ElementRef,
42 | React.ComponentPropsWithoutRef
43 | >(({ className, ...props }, ref) => (
44 |
52 | ))
53 | TabsContent.displayName = TabsPrimitive.Content.displayName
54 |
55 | export { Tabs, TabsList, TabsTrigger, TabsContent }
56 |
--------------------------------------------------------------------------------
/src/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | const TooltipProvider = TooltipPrimitive.Provider;
9 |
10 | const Tooltip = TooltipPrimitive.Root;
11 |
12 | const TooltipTrigger = TooltipPrimitive.Trigger;
13 |
14 | const TooltipArrow = React.forwardRef<
15 | React.ElementRef,
16 | React.ComponentPropsWithoutRef
17 | >(({ className, ...props }, ref) => (
18 |
26 | ));
27 |
28 | const TooltipContent = React.forwardRef<
29 | React.ElementRef,
30 | React.ComponentPropsWithoutRef
31 | >(({ className, sideOffset = 4, ...props }, ref) => (
32 |
41 | ));
42 | TooltipContent.displayName = TooltipPrimitive.Content.displayName;
43 |
44 | export {
45 | Tooltip,
46 | TooltipTrigger,
47 | TooltipContent,
48 | TooltipProvider,
49 | TooltipArrow,
50 | };
51 |
--------------------------------------------------------------------------------
/src/components/ui/typewriter-effect.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { cn } from "@/lib/utils";
4 | import { motion, stagger, useAnimate, useInView } from "framer-motion";
5 | import { useEffect } from "react";
6 |
7 | export const TypewriterEffect = ({
8 | words,
9 | className,
10 | cursorClassName,
11 | }: {
12 | words: {
13 | text: string;
14 | className?: string;
15 | }[];
16 | className?: string;
17 | cursorClassName?: string;
18 | }) => {
19 | // split text inside of words into array of characters
20 | const wordsArray = words.map((word) => {
21 | return {
22 | ...word,
23 | text: word.text.split(""),
24 | };
25 | });
26 |
27 | const [scope, animate] = useAnimate();
28 | const isInView = useInView(scope);
29 | useEffect(() => {
30 | if (isInView) {
31 | animate(
32 | "span",
33 | {
34 | display: "inline-block",
35 | opacity: 1,
36 | width: "fit-content",
37 | },
38 | {
39 | duration: 0.3,
40 | delay: stagger(0.1),
41 | ease: "easeInOut",
42 | },
43 | );
44 | }
45 | }, [isInView]);
46 |
47 | const renderWords = () => {
48 | return (
49 |
50 | {wordsArray.map((word, idx) => {
51 | return (
52 |
53 | {word.text.map((char, index) => (
54 |
62 | {char}
63 |
64 | ))}
65 |
66 |
67 | );
68 | })}
69 |
70 | );
71 | };
72 | return (
73 |
79 | {renderWords()}
80 |
97 |
98 | );
99 | };
100 |
101 | export const TypewriterEffectSmooth = ({
102 | words,
103 | className,
104 | cursorClassName,
105 | }: {
106 | words: {
107 | text: string;
108 | className?: string;
109 | }[];
110 | className?: string;
111 | cursorClassName?: string;
112 | }) => {
113 | const wordsArray = words.map((word) => {
114 | return {
115 | ...word,
116 | text: word.text.split(""),
117 | };
118 | });
119 |
120 | const renderWords = () => {
121 | return (
122 |
123 | {wordsArray.map((word, idx) => {
124 | return (
125 |
126 | {word.text.map((char, index) => (
127 |
131 | {char}
132 |
133 | ))}
134 |
135 |
136 | );
137 | })}
138 |
139 | );
140 | };
141 |
142 | return (
143 |
144 |
158 |
164 | {renderWords()}{" "}
165 |
{" "}
166 |
167 |
184 |
185 | );
186 | };
187 |
--------------------------------------------------------------------------------
/src/components/ui/word-fade-in.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import type { Variants } from 'framer-motion'
4 | import { motion } from 'framer-motion'
5 | import { cn } from '@/lib/utils'
6 |
7 | interface WordFadeInProps {
8 | words: string
9 | className?: string
10 | delay?: number
11 | variants?: Variants
12 | }
13 |
14 | export function WordFadeIn({
15 | words,
16 | delay = 0.15,
17 | variants = {
18 | hidden: { opacity: 0 },
19 | visible: (i: any) => ({
20 | y: 0,
21 | opacity: 1,
22 | transition: { delay: i * delay },
23 | }),
24 | },
25 | className,
26 | }: WordFadeInProps) {
27 | const _words = words.split(' ')
28 |
29 | return (
30 |
39 | {_words.map((word, i) => (
40 |
41 | {word}
42 | {' '}
43 |
44 | ))}
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/src/contexts/editor-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Dispatch, RefObject, createContext, useRef, useState } from "react";
4 | import { EditorChange } from "codemirror";
5 |
6 | interface EditorContextProps {
7 | markdown: string;
8 | setMarkdown: Dispatch>;
9 | titleRef: RefObject | null;
10 | onChange: (v: string, _chobj?: EditorChange) => void;
11 | onBlur: () => void;
12 | }
13 |
14 | interface EditorProviderProps {
15 | children: React.ReactNode;
16 | }
17 |
18 | const EditorContext = createContext(
19 | {} as EditorContextProps,
20 | );
21 |
22 | const EditorProvider = ({ children }: EditorProviderProps) => {
23 | const [markdown, setMarkdown] = useState("");
24 | const [lastTitleSaved, setLastTitleSaved] = useState("Untitled");
25 | const titleRef = useRef(null);
26 |
27 | const onChange = (v: string, _chobj?: EditorChange) => {
28 | setMarkdown(v);
29 | };
30 |
31 | const onBlur = () => {
32 | if (titleRef.current) {
33 | if (!titleRef.current.value) titleRef.current.value = lastTitleSaved;
34 |
35 | setLastTitleSaved(titleRef.current.value);
36 | }
37 | };
38 |
39 | return (
40 |
43 | {children}
44 |
45 | );
46 | };
47 |
48 | export { EditorContext, EditorProvider };
49 |
--------------------------------------------------------------------------------
/src/contexts/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { ThemeProvider as NextThemesProvider } from "next-themes";
5 | import { type ThemeProviderProps } from "next-themes/dist/types";
6 |
7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
8 | return {children} ;
9 | }
10 |
--------------------------------------------------------------------------------
/src/hooks/useAnthropic.ts:
--------------------------------------------------------------------------------
1 | import Anthropic from "@anthropic-ai/sdk";
2 |
3 | const anthropicPrompts = {
4 | briefing:
5 | "You are tasked with creating a comprehensive briefing on a given subject. Your goal is to provide an informative and well-structured overview of the topic, supported by credible sources.\n\nThe subject of the briefing is:\n\n{{SUBJECT}}\n \n\nFollow these steps to create your briefing:\n\n1. Research: Gather information from reputable sources such as academic journals, respected news outlets, official websites, and expert opinions. Aim to find a diverse range of perspectives on the subject.\n\n2. Analyze: Critically evaluate the information you've gathered. Look for consensus among experts, identify any controversies or debates, and note any significant recent developments.\n\n3. Organize: Structure your briefing logically. Typically, this should include:\n a. An introduction that provides context and outlines the scope of the briefing\n b. Main body sections that cover key aspects of the subject\n c. A conclusion that summarizes the main points and, if appropriate, looks at future implications or unanswered questions\n\n4. Write: Compose your briefing in clear, concise language. Avoid jargon unless it's necessary, and explain any technical terms you use.\n\n5. Cite sources: For each piece of information you include, note the source. Use a consistent citation format throughout your briefing.\n\n6. Review and refine: After completing your draft, review it for clarity, coherence, and accuracy. Make any necessary revisions.\n\nPresent your final briefing as a Markdown document.\n\n For example:\n\n#[Title here] [Your introduction here] ##[Section title here]: [Content from the section here] >[Source details here] ###[Conclusion title]: [Your conclusion here] \n\nRemember to maintain objectivity throughout your briefing. Your goal is to inform, not to argue for a particular position. If the subject is controversial, present multiple viewpoints fairly.",
6 | };
7 |
8 | export default function useAnthropic() {
9 | const domain = window?.location?.origin || "";
10 |
11 | const anthropic = new Anthropic({
12 | apiKey: process.env.ANTHROPIC_API_KEY,
13 | baseURL: domain + "/anthropic/",
14 | });
15 |
16 | const anthropicStream = (subject: string) =>
17 | anthropic.messages.stream({
18 | model: "claude-3-5-sonnet-20240620",
19 | max_tokens: 1024,
20 | temperature: 0,
21 | messages: [
22 | {
23 | role: "user",
24 | content: [
25 | {
26 | type: "text",
27 | text: anthropicPrompts["briefing"].replace(
28 | "{{SUBJECT}}",
29 | subject,
30 | ),
31 | },
32 | ],
33 | },
34 | ],
35 | stream: true,
36 | });
37 | return {
38 | anthropic,
39 | anthropicStream,
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/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 |
8 | export function ellipsize(str: string, maxLength: number) {
9 | if (str.length <= maxLength) return str;
10 | return str.slice(0, maxLength) + "...";
11 | }
12 |
--------------------------------------------------------------------------------
/src/types/Annotation.ts:
--------------------------------------------------------------------------------
1 | import dynamicIconImports from "lucide-react/dynamicIconImports";
2 |
3 | export type AnnotationT = {
4 | icon: keyof typeof dynamicIconImports;
5 | title: string;
6 | description: string;
7 | tags: string[];
8 | lastUpdate: string;
9 | };
10 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss";
2 | import plugin from "tailwindcss";
3 |
4 | const config = {
5 | darkMode: ["class"],
6 | content: [
7 | "./pages/**/*.{ts,tsx}",
8 | "./components/**/*.{ts,tsx}",
9 | "./app/**/*.{ts,tsx}",
10 | "./src/**/*.{ts,tsx}",
11 | ],
12 | prefix: "",
13 | theme: {
14 | container: {
15 | center: true,
16 | padding: "2rem",
17 | screens: {
18 | "2xl": "1400px",
19 | },
20 | },
21 | extend: {
22 | colors: {
23 | border: "hsl(var(--border))",
24 | input: "hsl(var(--input))",
25 | ring: "hsl(var(--ring))",
26 | background: "hsl(var(--background))",
27 | foreground: "hsl(var(--foreground))",
28 | primary: {
29 | DEFAULT: "hsl(var(--primary))",
30 | foreground: "hsl(var(--primary-foreground))",
31 | },
32 | secondary: {
33 | DEFAULT: "hsl(var(--secondary))",
34 | foreground: "hsl(var(--secondary-foreground))",
35 | },
36 | destructive: {
37 | DEFAULT: "hsl(var(--destructive))",
38 | foreground: "hsl(var(--destructive-foreground))",
39 | },
40 | muted: {
41 | DEFAULT: "hsl(var(--muted))",
42 | foreground: "hsl(var(--muted-foreground))",
43 | },
44 | accent: {
45 | DEFAULT: "hsl(var(--accent))",
46 | foreground: "hsl(var(--accent-foreground))",
47 | },
48 | popover: {
49 | DEFAULT: "hsl(var(--popover))",
50 | foreground: "hsl(var(--popover-foreground))",
51 | },
52 | card: {
53 | DEFAULT: "hsl(var(--card))",
54 | foreground: "hsl(var(--card-foreground))",
55 | },
56 | },
57 | borderRadius: {
58 | lg: "var(--radius)",
59 | md: "calc(var(--radius) - 2px)",
60 | sm: "calc(var(--radius) - 4px)",
61 | },
62 | keyframes: {
63 | "accordion-down": {
64 | from: { height: "0" },
65 | to: { height: "var(--radix-accordion-content-height)" },
66 | },
67 | "accordion-up": {
68 | from: { height: "var(--radix-accordion-content-height)" },
69 | to: { height: "0" },
70 | },
71 | shine: {
72 | from: { backgroundPosition: "200% 0" },
73 | to: { backgroundPosition: "-200% 0" },
74 | },
75 | shimmer: {
76 | "0%, 90%, 100%": {
77 | "background-position": "calc(-100% - var(--shimmer-width)) 0",
78 | },
79 | "30%, 60%": {
80 | "background-position": "calc(100% + var(--shimmer-width)) 0",
81 | },
82 | },
83 | },
84 | animation: {
85 | "accordion-down": "accordion-down 0.2s ease-out",
86 | "accordion-up": "accordion-up 0.2s ease-out",
87 | shine: "shine 8s ease-in-out infinite",
88 | shimmer: "shimmer 8s infinite",
89 | },
90 | fontFamily: {
91 | helv: ["var(--font-helvetica-neue)"],
92 | },
93 | textShadow: {
94 | sm: "0 0 14px rgb(0 0 0 / 20%)",
95 | DEFAULT: "0 0 18px rgb(0 0 0 / 20%)",
96 | lg: "0 0 22px rgb(0 0 0 / 20%)",
97 | },
98 | },
99 | },
100 | plugins: [
101 | require("tailwindcss-animate"),
102 | plugin(function ({ matchUtilities, theme }: any) {
103 | matchUtilities(
104 | {
105 | "text-shadow": (value: any) => ({
106 | textShadow: value,
107 | }),
108 | },
109 | { values: theme("textShadow") },
110 | );
111 | } as any),
112 | ],
113 | } satisfies Config;
114 |
115 | export default config;
116 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------