initiate ziya

This commit is contained in:
rizary 2025-06-21 14:05:50 +07:00
commit 13166b7a19
Signed by untrusted user who does not match committer: rizary
GPG key ID: 2CE8D69D02F1CEB5
34 changed files with 27043 additions and 0 deletions

124
.config/.eslintrc.json Normal file
View file

@ -0,0 +1,124 @@
// .eslintrc.json
{
"root": true,
"env": {
"browser": true,
"es6": true,
"node": true
},
"globals": {
"MAIN_WINDOW_VITE_DEV_SERVER_URL": "readonly",
"MAIN_WINDOW_VITE_NAME": "readonly"
},
// 'positive' dirs in .vscode/settings.json
// "ignorePatterns" .eslintignore
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/electron",
"plugin:import/typescript",
"plugin:vue/vue3-recommended",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"extraFileExtensions": [
".vue"
]
},
"plugins": [
"@typescript-eslint"
],
"settings": {
"import/resolver": {
"typescript": { // REF www.npmjs.com/package/eslint-import-resolver-typescript#configuration
}
}
},
"overrides": [
{
"files": [
"*.vue"
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"sourceType": "module"
},
"rules": {
"vue/html-closing-bracket-spacing": [
"error",
{
"selfClosingTag": "never"
}
]
}
},
{
"files": [
"src/*.d.ts"
],
"rules": {
"no-unused-vars": "off"
}
}
],
"rules": {
"semi": [
"warn",
"never",
{
"beforeStatementContinuationChars": "always"
}
],
"no-tabs": "error",
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"max-statements-per-line": [
"error",
{
"max": 1
}
],
"space-before-function-paren": [
"error",
{
"anonymous": "always",
"named": "never",
"asyncArrow": "always"
}
],
// tolerate but do not enforce comma-dangle
"comma-dangle": "off",
// up to 3 blank lines is semantics for me
"no-multiple-empty-lines": [
"warn",
{
"max": 3,
"maxBOF": 1,
"maxEOF": 1
}
],
// not having to worry about danling commas is a blessing (either way)
"@typescript-eslint/comma-dangle": "off",
// handier for testing
"import/no-named-as-default-member": "off",
// writing the type can be clarifying at times, thus permit
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
}
]
}
}

32
.config/eslint.mjs Normal file
View file

@ -0,0 +1,32 @@
import withNuxt from "../.nuxt/eslint.config.mjs";
export default withNuxt([{
files: ["**/*.vue", "**/*.js", "**/*.ts", "**/*.mjs"],
rules: {
"camelcase": ["error", { properties: "never", ignoreDestructuring: true }],
"no-console": ["error", { allow: ["info", "warn"] }],
"sort-imports": ["error", { ignoreDeclarationSort: true }],
"@stylistic/indent": ["error", 2, { SwitchCase: 1 }],
"@stylistic/linebreak-style": ["error", process.platform === "win32" ? "windows" : "unix"],
"@stylistic/quotes": ["error", "double"],
"@stylistic/semi": ["error", "always"],
"@stylistic/no-extra-semi": "error",
"@stylistic/comma-dangle": ["error", "never"],
"@stylistic/space-before-function-paren": ["error", "always"],
"@stylistic/multiline-ternary": ["error", "never"],
"@stylistic/member-delimiter-style": ["error", { multiline: { delimiter: "semi" }, singleline: { delimiter: "comma" } }],
"@stylistic/arrow-spacing": ["error", { before: true, after: true }],
"@stylistic/brace-style": ["error", "stroustrup", { allowSingleLine: true }],
"@stylistic/no-multi-spaces": "error",
"@stylistic/space-before-blocks": "error",
"@stylistic/no-trailing-spaces": "error",
"nuxt/prefer-import-meta": "error",
"vue/first-attribute-linebreak": ["error", { singleline: "ignore", multiline: "ignore" }],
"vue/max-attributes-per-line": ["error", { singleline: 100 }],
"vue/singleline-html-element-content-newline": ["off"],
"vue/no-multiple-template-root": ["off"],
"vue/html-closing-bracket-spacing": ["error", { selfClosingTag: "always" }],
"vue/html-indent": ["error", 2],
"vue/multiline-html-element-content-newline": ["error", { ignores: [] }]
}
}]);

96
.config/forge.ts Normal file
View file

@ -0,0 +1,96 @@
import { MakerDeb } from "@electron-forge/maker-deb";
import { MakerDMG } from "@electron-forge/maker-dmg";
import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { MakerZIP } from "@electron-forge/maker-zip";
import { AutoUnpackNativesPlugin } from "@electron-forge/plugin-auto-unpack-natives";
import { FusesPlugin } from "@electron-forge/plugin-fuses";
import { VitePlugin } from "@electron-forge/plugin-vite";
import { PublisherGithub } from "@electron-forge/publisher-github";
import type { ForgeConfig } from "@electron-forge/shared-types";
import { FuseV1Options, FuseVersion } from "@electron/fuses";
import setLanguages from "electron-packager-languages";
import packageJSON from "../package.json";
export default {
packagerConfig: {
name: packageJSON.name,
appBundleId: "com.bismillahdao.ziya",
appCategoryType: "public.app-category.utilities",
appCopyright: `Copyright (C) ${new Date().getFullYear()} ${packageJSON.author.name}`,
icon: "public/favicon",
asar: {
unpack: "**/node_modules/{sharp,@img}/**/*"
},
osxSign: {},
ignore: [
/^\/(?!node_modules|package\.json|.vite)/
],
afterCopy: [setLanguages(["en", "en-US", "en-GB"])]
},
rebuildConfig: {
onlyModules: ["sharp"],
force: true
},
makers: [
new MakerZIP({}),
// Windows
new MakerSquirrel({
usePackageJson: true,
iconUrl: "https://raw.githubusercontent.com/rizilab/ziya/main/public/favicon.ico",
setupIcon: "public/favicon.ico"
}),
// macOS
new MakerDMG({
overwrite: true,
format: "ULFO",
icon: "public/favicon.icns"
}),
// Linux
new MakerDeb({
options: {
categories: ["Utility"],
icon: "public/favicon.png"
}
})
],
plugins: [
new VitePlugin({
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
// If you are familiar with Vite configuration, it will look really familiar.
build: [
{
entry: "electron/main.ts",
config: ".config/vite.forge.ts",
target: "main"
},
{
entry: "electron/preload.ts",
config: ".config/vite.forge.ts",
target: "preload"
}
],
renderer: [] // Nuxt app is generated no need to specify renderer
}),
// Fuses are used to enable/disable various Electron functionality
// at package time, before code signing the application
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: false,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true
}),
new AutoUnpackNativesPlugin({})
],
publishers: [
new PublisherGithub({
repository: {
owner: "Rizary",
name: packageJSON.name
},
prerelease: true
})
]
} satisfies ForgeConfig;

76
.config/nuxt.ts Normal file
View file

@ -0,0 +1,76 @@
import { APP } from "../app/utils/app";
export default defineNuxtConfig({
modules: [
"@nuxt/ui",
"@nuxt/eslint",
"@pinia/nuxt"
],
ssr: false,
devtools: { enabled: true },
app: {
baseURL: "./",
cdnURL: "./",
head: {
title: APP.name,
meta: [
{ "http-equiv": "content-security-policy", "content": "script-src 'self' 'unsafe-inline'" }
]
}
},
css: [
"~/assets/css/ui.tailwind.css",
"~/assets/scss/app.scss"
],
router: {
options: {
hashMode: true
}
},
colorMode: {
preference: "dark",
fallback: "dark",
storageKey: "nuxt-color-mode"
},
ui: {
colorMode: true,
fonts: false
},
future: { compatibilityVersion: 4 },
features: {
inlineStyles: false
},
experimental: {
typedPages: true,
payloadExtraction: false,
renderJsonPayloads: false
},
compatibilityDate: "2025-05-26",
vite: {
css: {
preprocessorOptions: {
scss: {
api: "modern-compiler",
silenceDeprecations: ["mixed-decls", "color-functions", "import", "global-builtin"]
}
}
}
},
postcss: {
plugins: {
"@tailwindcss/postcss": {}
}
},
eslint: {
config: {
autoInit: false,
stylistic: true
}
},
icon: {
provider: "iconify",
clientBundle: {
scan: true
}
}
});

16
.config/stylelint.json Normal file
View file

@ -0,0 +1,16 @@
{
"extends": "stylelint-config-standard-scss",
"rules": {
"length-zero-no-unit": true,
"rule-empty-line-before": ["always-multi-line", { "except": ["first-nested"] }],
"color-function-notation": ["modern", { "ignore": ["with-var-inside"] }],
"scss/double-slash-comment-empty-line-before": "never"
},
"ignoreFiles": [
"../node_modules/**/*",
"../.nuxt/**/*",
"../dist/**/*",
"../.output/**/*",
"../public/**/*"
]
}

33
.config/vite.forge.ts Normal file
View file

@ -0,0 +1,33 @@
import { cp, mkdir } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { type Plugin, defineConfig } from "vite";
const copyNuxtOutput: Plugin = {
name: "copy-nuxt-output",
async closeBundle () {
const outputDir = fileURLToPath(new URL("../.output/public", import.meta.url));
const targetDir = fileURLToPath(new URL("../.vite/renderer", import.meta.url));
await mkdir(targetDir, { recursive: true });
await cp(outputDir, targetDir, { recursive: true, force: true });
}
};
export default defineConfig({
publicDir: false,
plugins: [copyNuxtOutput],
build: {
emptyOutDir: false,
lib: {
entry: "electron/main.ts",
formats: ["cjs"]
},
rollupOptions: {
output: {
entryFileNames: "[name].cjs"
},
external: [
"electron",
]
}
}
});

24
.editorconfig Normal file
View file

@ -0,0 +1,24 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
max_line_length = 200
// NOTE: has auto-save impact! always.
trim_trailing_whitespace = true
insert_final_newline = true
[*.{js,ts,vue,css,scss,sass,html}]
insert_final_newline = true
[*.{txt, md}]
max_line_length = off
trim_trailing_whitespace = false
insert_final_newline = false
[*.{yml, json},.prettierrc]
indent_style = space
indent_size = 2

5
.eslintignore Normal file
View file

@ -0,0 +1,5 @@
node_modules/*
dist/*
# all hidden files, too!
.*/*

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto eol=lf

94
.gitignore vendored Normal file
View file

@ -0,0 +1,94 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
.DS_Store
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# Webpack
.webpack/
# Vite
.vite/
# Electron-Forge
out/
.cursor/

3
.npmrc Normal file
View file

@ -0,0 +1,3 @@
shamefully-hoist=true
strict-peer-dependencies=false
node-linker=hoisted

16
.prettierrc Normal file
View file

@ -0,0 +1,16 @@
{
"semi": false,
"useTabs": false,
"singleQuote": true,
"plugins": [
"prettier-plugin-vue"
],
"overrides": [
{
"files": "*.vue",
"options": {
"parser": "vue"
}
}
]
}

9
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"recommendations": [
"syler.sass-indented",
"vue.volar",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"vitest.explorer"
]
}

59
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,59 @@
{
"eslint.validate": [
"javascript",
"typescript",
"vue"
],
"eslint.useFlatConfig": true,
"eslint.options": {
"extensions": [
".js",
".ts",
".mts",
".vue"
],
"overrideConfigFile": ".config/eslint.mjs"
},
"eslint.workingDirectories": [
"."
],
"eslint.probe": [
"javascript",
"typescript",
"html",
"vue"
],
"search.exclude": {
"**/node_modules": true,
"**/.vite": true,
"**/dist": true,
"**/build": true
},
"[javascript]": {
"editor.defaultFormatter": "vscode.typescript-language-features",
"javascript.preferences.quoteStyle": "single"
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features",
"typescript.preferences.quoteStyle": "single"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[sass]": {
"editor.defaultFormatter": "syler.sass-indented",
"editor.insertSpaces": true,
"editor.tabSize": 2
},
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features",
"editor.insertSpaces": true,
"editor.tabSize": 2
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features",
"editor.insertSpaces": true,
"editor.tabSize": 2
},
}

196
README.md Normal file
View file

@ -0,0 +1,196 @@
# Ziya Token Monitor
A modern Electron-based desktop application for monitoring Solana token creation, CEX findings, and developer balance source graphs. Built with React, Redux, and TypeScript.
## Architecture
This project follows a modular architecture with three main packages:
### 📦 Packages
- **`@ziya/shared`** - Shared types, utilities, and domain models
- **`@ziya/frontend`** - React frontend with Redux state management
- **`@ziya/backend`** - Electron main process with Redis integration
### 🏗️ Tech Stack
- **Frontend**: React 18, Redux Toolkit, TypeScript, Styled Components, Vite
- **Backend**: Electron, Node.js, TypeScript, Redis (ioredis)
- **Shared**: TypeScript, Winston (logging)
- **Development**: Yarn Workspaces, ESLint, Prettier
## Features
- 📊 **Real-time Dashboard** - Monitor token activity at a glance
- 🪙 **Token Management** - Track discovered tokens and their metadata
- 🕸️ **Graph Visualization** - Visualize connection graphs for developer relationships
- 📝 **Event Streaming** - Real-time events from the Rust backend via Redis
- 🌙 **Dark/Light Theme** - Modern UI with theme switching
- 🔔 **Notifications** - Real-time notifications for important events
## Prerequisites
- Node.js 16+
- Yarn (recommended)
- Redis server running on localhost:6379
- Rust backend (`muhafidh`) running and publishing events
## Installation
1. **Clone and install dependencies**:
```bash
cd ziya
yarn install
```
2. **Build shared module**:
```bash
yarn workspace @ziya/shared build
```
## Development
### Start Development Server
```bash
# Start both frontend and backend in development mode
yarn dev
```
This will:
- Start the React frontend on `http://localhost:5173`
- Start the Electron backend in development mode
- Enable hot reload for both frontend and backend
### Individual Package Commands
```bash
# Frontend only
yarn workspace @ziya/frontend start
# Backend only
yarn workspace @ziya/backend dev
# Shared module
yarn workspace @ziya/shared dev
```
## Production Build
```bash
# Build all packages
yarn build
# Package the Electron app
yarn package
```
## Event Integration
The application listens for these Redis events from the `muhafidh` Rust backend:
### `token_cex_updated`
```json
{
"mint": "string",
"name": "string",
"uri": "string",
"dev_name": "string",
"cex_name": "string",
"cex_address": "string",
"cex_updated_at": "string",
"node_count": "number",
"edge_count": "number",
"graph": {
"nodes": [...],
"edges": [...]
}
}
```
### `max_depth_reached`
```json
{
"mint": "string",
"name": "string",
"uri": "string",
"bonding_curve": "string",
"updated_at": "string",
"node_count": "number",
"edge_count": "number",
"graph": {
"nodes": [...],
"edges": [...]
}
}
```
## Configuration
### Redis Configuration
Set environment variables:
```bash
REDIS_HOST=localhost
REDIS_PORT=6379
```
### Electron Configuration
The app uses secure defaults:
- Context isolation enabled
- Node integration disabled
- Preload script for secure IPC
## Project Structure
```
ziya/
├── packages/
│ ├── shared/ # Shared types and utilities
│ │ ├── src/
│ │ │ ├── types/ # TypeScript interfaces
│ │ │ └── utils/ # Shared utilities
│ │ └── package.json
│ ├── frontend/ # React frontend
│ │ ├── src/
│ │ │ ├── components/ # React components
│ │ │ ├── pages/ # Page components
│ │ │ ├── store/ # Redux store and slices
│ │ │ ├── services/ # Frontend services
│ │ │ └── styles/ # Styled components
│ │ └── package.json
│ └── backend/ # Electron backend
│ ├── src/
│ │ ├── services/ # Backend services
│ │ ├── main.ts # Electron main process
│ │ └── preload.ts # Preload script
│ └── package.json
├── package.json # Root workspace config
└── README.md
```
## Scripts
- `yarn dev` - Start development servers
- `yarn build` - Build all packages
- `yarn start` - Start production app
- `yarn package` - Package Electron app
- `yarn clean` - Clean all build artifacts
- `yarn lint` - Run linters
- `yarn test` - Run tests
## Security
- Electron app uses context isolation and disables node integration
- IPC communication uses secure preload scripts
- Redis connections use proper error handling and reconnection logic
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run tests and linting
5. Submit a pull request
## License
MIT License - see LICENSE file for details.

42
app/app.vue Normal file
View file

@ -0,0 +1,42 @@
<template>
<div class="min-h-screen bg-gray-50 flex items-center justify-center">
<div class="max-w-md w-full bg-white rounded-lg shadow-lg p-8">
<div class="text-center">
<h1 class="text-3xl font-bold text-gray-900 mb-4">
Hello World! 👋
</h1>
<p class="text-gray-600 mb-6">
Welcome to Ziya - Your Vue + Nuxt + Electron App
</p>
<div class="space-y-4">
<UButton
@click="count++"
color="primary"
size="lg"
class="w-full"
>
Click me! ({{ count }})
</UButton>
<p class="text-sm text-gray-500">
Built with Vue {{ vueVersion }}, Nuxt {{ nuxtVersion }}
</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { version as vueVersion } from 'vue/package.json'
const count = ref(0)
const nuxtVersion = '3.17.5' // From your package.json
// Page metadata
useHead({
title: 'Ziya - Hello World',
meta: [
{ name: 'description', content: 'One Stop Shop for your trading needs' }
]
})
</script>

View file

@ -0,0 +1,43 @@
/* stylelint-disable scss/at-rule-no-unknown, custom-property-empty-line-before */
@import "tailwindcss";
@import "@nuxt/ui";
@custom-variant dark (&:where(.dark, .dark *));
@custom-variant light (&:where(.light, .light *));
@custom-variant hover (&:hover);
@theme static {
--color-green-50: #ebfdf6;
--color-green-100: #d4f8eb;
--color-green-200: #a7f2d9;
--color-green-300: #6ae6c2;
--color-green-400: #00d1a6;
--color-green-500: #00ba91;
--color-green-600: #009775;
--color-green-700: #1B886D;
--color-green-800: #005f4a;
--color-green-900: #164c3d;
--color-green-950: #052c21;
--color-slate-50: #f9fafb;
--color-slate-100: #f3f4f6;
--color-slate-200: #e2e5e9;
--color-slate-300: #cdd5e1;
--color-slate-400: #92a1b7;
--color-slate-500: #63738a;
--color-slate-600: #44546b;
--color-slate-700: #354151;
--color-slate-800: #222830;
--color-slate-900: #15191e;
--color-slate-950: #06090E;
}
:root {
--ui-primary: var(--ui-color-primary-700);
--ui-secondary: var(--ui-color-secondary-600);
}
.dark {
--ui-primary: var(--ui-color-primary-400);
--ui-secondary: var(--ui-color-secondary-300);
}

View file

@ -0,0 +1,29 @@
:root {
scrollbar-width: auto;
scroll-behavior: unset;
}
*::-webkit-scrollbar {
width: 8px;
}
*::-webkit-scrollbar-track {
background-color: var(--ui-bg-muted);
}
*::-webkit-scrollbar-thumb {
height: 56px;
border-radius: 8px;
border: 0 solid transparent;
background-clip: content-box;
background-color: var(--ui-primary);
}
*::-webkit-scrollbar-thumb:hover {
background-color: var(--ui-primary);
}
::selection {
background: var(--ui-primary);
color: var(--ui-bg-muted);
}

View file

@ -0,0 +1,83 @@
.slide-enter-active,
.slide-leave-active,
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
transition: all 0.2s;
}
.slide-enter-from,
.slide-leave-to {
opacity: 0;
transform: translate(0, -10px);
}
.slide-right-enter-from,
.slide-right-leave-to {
opacity: 0;
transform: translate(10px, 0);
}
.slide-left-enter-from,
.slide-left-leave-to {
opacity: 0;
transform: translate(-10px, 0);
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.bounce-enter-active {
animation: bounce-in 0.5s ease;
}
@keyframes bounce-in {
0% { transform: scale(0);}
50% { transform: scale(1.25);}
100% {transform: scale(1);}
}
.list-move,
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}
.list-leave-active {
position: absolute;
width: 0%;
left: 50%;
top: 100%;
transform: translateY(100%);
}
@each $size in (200, 1000) {
.expand-#{$size} {
&-enter-active,
&-leave-active {
transition: max-height 0.3s ease-in-out, opacity 0.2s ease;
max-height: #{$size}px;
overflow: hidden;
}
&-enter-from,
&-leave-to {
max-height: 0;
opacity: 0;
}
}
}

2
app/assets/scss/app.scss Normal file
View file

@ -0,0 +1,2 @@
@import "main";
@import "transitions";

13
app/layouts/default.vue Normal file
View file

@ -0,0 +1,13 @@
<template>
<div class="min-h-screen">
<slot />
</div>
</template>
<script setup lang="ts">
// Default layout for the app
</script>
<style>
/* Global styles can go here */
</style>

67
app/pages/index.vue Normal file
View file

@ -0,0 +1,67 @@
<template>
<div class="container mx-auto px-4 py-8">
<div class="text-center">
<h1 class="text-4xl font-bold text-gray-900 mb-6">
🚀 Ziya Dashboard
</h1>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-4xl mx-auto">
<!-- Trading Card -->
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
<div class="text-blue-500 text-3xl mb-4">📈</div>
<h3 class="text-xl font-semibold mb-2">Trading</h3>
<p class="text-gray-600">Access your trading dashboard and monitor your portfolio</p>
</div>
<!-- Analytics Card -->
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
<div class="text-green-500 text-3xl mb-4">📊</div>
<h3 class="text-xl font-semibold mb-2">Analytics</h3>
<p class="text-gray-600">View detailed analytics and market insights</p>
</div>
<!-- Settings Card -->
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
<div class="text-purple-500 text-3xl mb-4"></div>
<h3 class="text-xl font-semibold mb-2">Settings</h3>
<p class="text-gray-600">Configure your preferences and account settings</p>
</div>
</div>
<div class="mt-8">
<UButton
@click="showWelcome = !showWelcome"
color="primary"
size="lg"
>
{{ showWelcome ? 'Hide' : 'Show' }} Welcome Message
</UButton>
<div v-if="showWelcome" class="mt-4 p-4 bg-blue-50 rounded-lg">
<p class="text-blue-800">
Welcome to your new Electron app! This is running on Nuxt {{ nuxtVersion }} with Vue {{ vueVersion }}.
</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { version as vueVersion } from 'vue/package.json'
const showWelcome = ref(false)
const nuxtVersion = '3.17.5'
// Page metadata
definePageMeta({
title: 'Dashboard'
})
useHead({
title: 'Ziya - Dashboard',
meta: [
{ name: 'description', content: 'Ziya trading dashboard - One stop shop for your trading needs' }
]
})
</script>

4
app/utils/app.ts Normal file
View file

@ -0,0 +1,4 @@
export const APP = {
name: "ziya",
repository: "https://github.com/rizilab/ziya"
};

1
app/utils/electron.ts Normal file
View file

@ -0,0 +1 @@
export const useElectron = () => window.electron;

73
electron/main.ts Normal file
View file

@ -0,0 +1,73 @@
import { BrowserWindow, app, shell } from "electron";
import started from 'electron-squirrel-startup';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (started) {
app.quit();
}
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
minHeight: 800,
minWidth: 1080,
maxHeight: 1080,
maxWidth: 1920,
height: 1024,
width: 1280,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.cjs'),
},
});
mainWindow.setMenuBarVisibility(false);
mainWindow.webContents.on("will-navigate", function (event, reqUrl) {
const requestedHost = new URL(reqUrl).host;
const currentHost = new URL(mainWindow.webContents.getURL()).host;
if (requestedHost && requestedHost != currentHost) {
event.preventDefault();
shell.openExternal(reqUrl);
}
});
const isDev = process.env.NODE_ENV === "development";
// and load the index.html of the app.
if (isDev) {
mainWindow.setIcon(fileURLToPath(new URL("../../public/favicon.ico", import.meta.url)));
mainWindow.loadURL("http://localhost:3000");
mainWindow.webContents.openDevTools();
}
else {
mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
}
};
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.

9
electron/preload.ts Normal file
View file

@ -0,0 +1,9 @@
import { contextBridge } from "electron";
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
export const handlers = {
};
contextBridge.exposeInMainWorld("electron", handlers);

10072
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

71
package.json Normal file
View file

@ -0,0 +1,71 @@
{
"name": "Ziya",
"productName": "Ziya",
"version": "1.0.0",
"description": "One Stop Shop for your trading needs",
"type": "module",
"main": ".vite/build/main.cjs",
"scripts": {
"start": "electron-forge start",
"dev": "concurrently \"pnpm run dev:nuxt\" \"pnpm run dev:electron\"",
"dev:nuxt": "nuxt dev --config-file .config/nuxt.config.ts",
"dev:electron": "cross-env NODE_ENV=development electron-forge start",
"build": "nuxt generate --config-file .config/nuxt.config.ts && electron-forge make",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish",
"lint": "eslint --ext .ts,.tsx,.js,.vue --ignore-path .gitignore .",
"format": "prettier --write ."
},
"keywords": [],
"author": "rizary",
"license": "MIT",
"devDependencies": {
"@electron-forge/cli": "^7.8.1",
"@electron-forge/maker-deb": "^7.8.1",
"@electron-forge/maker-dmg": "^7.8.1",
"@electron-forge/maker-rpm": "^7.8.1",
"@electron-forge/maker-squirrel": "^7.8.1",
"@electron-forge/maker-zip": "^7.8.1",
"@electron-forge/plugin-auto-unpack-natives": "^7.8.1",
"@electron-forge/plugin-fuses": "^7.8.1",
"@electron-forge/plugin-vite": "^7.8.1",
"@electron-forge/publisher-github": "^7.8.1",
"@electron/fuses": "^1.8.0",
"@nuxt/eslint": "^1.4.1",
"@nuxt/ui": "^3.1.3",
"@pinia/nuxt": "^0.11.1",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/node": "^24.0.3",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"changelogen": "^0.6.1",
"concurrently": "^9.1.2",
"cross-env": "^7.0.3",
"electron": "36.5.0",
"electron-packager-languages": "^0.6.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-import": "^2.32.0",
"nuxt": "^3.17.5",
"pinia": "^3.0.3",
"prettier": "3.5.3",
"sass": "^1.89.2",
"stylelint": "^16.21.0",
"stylelint-config-standard-scss": "^15.0.1",
"ts-node": "^10.9.2",
"typescript": "~4.5.4",
"vite": "^5.4.19",
"vite-plugin-electron": "^0.29.0",
"vite-plugin-electron-renderer": "^0.14.6",
"vitest": "^3.2.4",
"vue-tsc": "^2.2.10"
},
"dependencies": {
"electron-squirrel-startup": "^1.0.1"
},
"config": {
"forge": ".config/forge.ts"
},
"packageManager": "pnpm@10.12.1+sha512.f0dda8580f0ee9481c5c79a1d927b9164f2c478e90992ad268bbb2465a736984391d6333d2c327913578b2804af33474ca554ba29c04a8b13060a717675ae3ac"
}

15723
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

12
pnpm-workspace.yaml Normal file
View file

@ -0,0 +1,12 @@
nodeLinker: hoisted
packages:
- .
ignoredBuiltDependencies:
- '@parcel/watcher'
- esbuild
onlyBuiltDependencies:
- electron
- electron-winstaller

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

3
tsconfig.json Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}

11
types/electron.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
import type { handlers } from "./../electron/preload";
type ElectronAPI = typeof handlers;
declare global {
interface Window {
electron: ElectronAPI;
}
}
export { };

1
types/forge.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="@electron-forge/plugin-vite/forge-vite-env" />