diff --git a/.env.example b/.env.example index 7a550fe..2137088 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,14 @@ -# GEMINI_API_KEY: Required for Gemini AI API calls. -# AI Studio automatically injects this at runtime from user secrets. -# Users configure this via the Secrets panel in the AI Studio UI. +# GEMINI_API_KEY: Required for live Gemini AI API calls. +# AI Studio can inject this at runtime from user secrets. GEMINI_API_KEY="MY_GEMINI_API_KEY" +# GEMINI_MODEL: Optional model override for the RKix command endpoint. +# Defaults to gemini-2.5-flash when omitted. +GEMINI_MODEL="gemini-2.5-flash" + +# PORT: Optional local/server port. +PORT="3000" + # APP_URL: The URL where this applet is hosted. -# AI Studio automatically injects this at runtime with the Cloud Run service URL. -# Used for self-referential links, OAuth callbacks, and API endpoints. +# AI Studio can inject this at runtime with the Cloud Run service URL. APP_URL="MY_APP_URL" diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..42bf91f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +thanks_dev: # Replace with a single thanks.dev username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..48d5f81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml new file mode 100644 index 0000000..67be9b0 --- /dev/null +++ b/.github/workflows/jekyll-gh-pages.yml @@ -0,0 +1,51 @@ +# Sample workflow for building and deploying a Jekyll site to GitHub Pages +name: Deploy Jekyll with GitHub Pages dependencies preinstalled + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1 + with: + source: ./ + destination: ./_site + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..460f782 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/webpack.yml b/.github/workflows/webpack.yml new file mode 100644 index 0000000..9626ff6 --- /dev/null +++ b/.github/workflows/webpack.yml @@ -0,0 +1,28 @@ +name: NodeJS with Webpack + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Build + run: | + npm install + npx webpack diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..31e25eb --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +terkix.com \ No newline at end of file diff --git a/README.md b/README.md index e69de29..5945940 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,94 @@ +# RKix Terminal OS + +RKix Terminal OS is an AI-native development workspace built with React, Vite, Express, and the Google Gemini API. It provides a terminal-first interface for managing virtual projects, files, agent workflows, deployments, plugins, contacts, and telemetry dashboards. + +## What this repo contains + +- **React 19 SPA** for the RKix terminal, workspace explorer, dashboard, plugin hub, contacts manager, and telemetry views. +- **Express + Vite server** for local development and production static hosting. +- **Gemini command API** at `/api/gemini/command` for multi-agent workflow generation with an offline simulator fallback in the UI. +- **Safe local persistence** for projects, terminal history, active workspace, and command metrics. + +## Requirements + +- Node.js 22 or newer is recommended. +- npm 10 or newer. +- A Gemini API key when you want live AI responses. Without a configured key, the client falls back to the built-in simulator. + +## Environment setup + +Copy the example environment file and fill in your values: + +```bash +cp .env.example .env +``` + +Supported variables: + +| Variable | Required | Default | Description | +| --- | --- | --- | --- | +| `GEMINI_API_KEY` | Yes for live AI | _none_ | API key used by `@google/genai`. | +| `GEMINI_MODEL` | No | `gemini-2.5-flash` | Model used by the server command endpoint. | +| `PORT` | No | `3000` | Port for the Express/Vite server. | +| `APP_URL` | No | _none_ | Public URL used by hosted environments. | + +## Development + +Install dependencies: + +```bash +npm install +``` + +Run the app locally: + +```bash +npm run dev +``` + +Open `http://localhost:3000`. + +## Quality checks + +Run TypeScript validation: + +```bash +npm run typecheck +``` + +Run the full production build: + +```bash +npm run build +``` + +Start the production build: + +```bash +npm start +``` + +Check server health when the app is running: + +```bash +curl http://localhost:3000/api/health +``` + +## Project structure + +```text +. +├── server.ts # Express server and Gemini command endpoint +├── src/App.tsx # Main RKix UI shell and workspace logic +├── src/components/ # Dashboard, project, plugin, contact, and chart modules +├── src/data/presets.ts # Default virtual projects and sample workspace files +├── src/types.ts # Shared domain types +└── src/utils/storage.ts # Defensive localStorage helpers +``` + +## Notes for contributors + +- Keep generated workspace file paths under `workspace/project/...` so the RKix virtual file explorer can mount them correctly. +- Keep dependencies that are only needed for builds or tooling in `devDependencies`. +- Do not commit `.env` files or real API keys. +- Run `npm run typecheck` and `npm run build` before opening a pull request. diff --git a/index.html b/index.html index 21dfe69..380ace8 100644 --- a/index.html +++ b/index.html @@ -3,11 +3,11 @@ - My Google AI Studio App + + RKix Terminal OS
- diff --git a/metadata.json b/metadata.json index 9285442..8727af9 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,10 @@ { "name": "RKix Terminal OS", - "description": "AI-native terminal-based software development environment with autonomous planner, builder, designer, debugger, and deployer agents.", - "requestFramePermissions": [], - "majorCapabilities": ["MAJOR_CAPABILITY_SERVER_SIDE_GEMINI_API"] + "description": "AI-native terminal-based software development environment with autonomous planner, builder, designer, debugger, deployer, plugin, contact, and telemetry agents.", + "requestFramePermissions": [ + "microphone" + ], + "majorCapabilities": [ + "MAJOR_CAPABILITY_SERVER_SIDE_GEMINI_API" + ] } diff --git a/package-lock.json b/package-lock.json index de6fc61..6d13845 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,29 +1,28 @@ { - "name": "react-example", - "version": "0.0.0", + "name": "rkix-terminal-os", + "version": "1.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "react-example", - "version": "0.0.0", + "name": "rkix-terminal-os", + "version": "1.0.5", "dependencies": { "@google/genai": "^2.4.0", - "@tailwindcss/vite": "^4.1.14", - "@vitejs/plugin-react": "^5.0.4", "d3": "^7.9.0", "dotenv": "^17.2.3", "express": "^4.21.2", "lucide-react": "^0.546.0", "motion": "^12.23.24", "react": "^19.0.1", - "react-dom": "^19.0.1", - "vite": "^6.2.3" + "react-dom": "^19.0.1" }, "devDependencies": { + "@tailwindcss/vite": "^4.1.14", "@types/d3": "^7.4.3", "@types/express": "^4.17.21", "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^5.0.4", "autoprefixer": "^10.4.21", "esbuild": "^0.25.0", "tailwindcss": "^4.1.14", @@ -36,6 +35,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -50,6 +50,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -59,6 +60,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -89,6 +91,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.7", @@ -105,6 +108,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.7", @@ -121,6 +125,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -130,6 +135,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", @@ -143,6 +149,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", @@ -160,6 +167,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -169,6 +177,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -178,6 +187,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -187,6 +197,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -196,6 +207,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", @@ -209,6 +221,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" @@ -224,6 +237,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -239,6 +253,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -254,6 +269,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -268,6 +284,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -286,6 +303,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -302,6 +320,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -318,6 +337,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -334,6 +354,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -350,6 +371,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -366,6 +388,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -382,6 +405,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -398,6 +422,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -414,6 +439,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -430,6 +456,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -446,6 +473,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -462,6 +490,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -478,6 +507,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -494,6 +524,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -510,6 +541,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -526,6 +558,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -542,6 +575,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -558,6 +592,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -574,6 +609,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -590,6 +626,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -606,6 +643,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -622,6 +660,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -638,6 +677,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -654,6 +694,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -670,6 +711,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -686,6 +728,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -702,6 +745,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -739,6 +783,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -749,6 +794,7 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -759,6 +805,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -768,12 +815,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -847,6 +896,7 @@ "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { @@ -856,6 +906,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -869,6 +920,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -882,6 +934,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -895,6 +948,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -908,6 +962,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -921,6 +976,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -934,6 +990,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -947,6 +1004,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -960,6 +1018,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -973,6 +1032,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -986,6 +1046,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -999,6 +1060,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1012,6 +1074,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1025,6 +1088,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1038,6 +1102,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1051,6 +1116,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1064,6 +1130,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1077,6 +1144,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1090,6 +1158,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1103,6 +1172,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1116,6 +1186,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1129,6 +1200,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1142,6 +1214,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1155,6 +1228,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1168,6 +1242,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1178,6 +1253,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", @@ -1193,6 +1269,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 20" @@ -1219,6 +1296,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1235,6 +1313,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1251,6 +1330,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1267,6 +1347,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1283,6 +1364,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1299,6 +1381,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1315,6 +1398,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1331,6 +1415,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1347,6 +1432,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1371,6 +1457,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1392,6 +1479,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1408,6 +1496,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1421,6 +1510,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "dev": true, "license": "MIT", "dependencies": { "@tailwindcss/node": "4.3.0", @@ -1435,6 +1525,7 @@ "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", @@ -1448,6 +1539,7 @@ "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" @@ -1457,6 +1549,7 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", @@ -1467,6 +1560,7 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.2" @@ -1781,6 +1875,7 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, "license": "MIT" }, "node_modules/@types/express": { @@ -1896,6 +1991,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.29.0", @@ -2001,6 +2097,7 @@ "version": "2.10.34", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz", "integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==", + "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -2061,6 +2158,7 @@ "version": "4.28.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2138,6 +2236,7 @@ "version": "1.0.30001797", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==", + "dev": true, "funding": [ { "type": "opencollective", @@ -2188,6 +2287,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, "license": "MIT" }, "node_modules/cookie": { @@ -2676,6 +2776,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -2726,6 +2827,7 @@ "version": "1.5.368", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz", "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==", + "dev": true, "license": "ISC" }, "node_modules/encodeurl": { @@ -2741,6 +2843,7 @@ "version": "5.23.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -2784,6 +2887,7 @@ "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -2825,6 +2929,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2916,6 +3021,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -3060,6 +3166,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3111,6 +3218,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -3195,6 +3303,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/has-symbols": { @@ -3294,6 +3403,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -3303,12 +3413,14 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -3330,6 +3442,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -3363,6 +3476,7 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -3395,6 +3509,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3415,6 +3530,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3435,6 +3551,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3455,6 +3572,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3475,6 +3593,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3495,6 +3614,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3515,6 +3635,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3535,6 +3656,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3555,6 +3677,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3575,6 +3698,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3595,6 +3719,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3618,6 +3743,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -3636,6 +3762,7 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -3761,6 +3888,7 @@ "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, "funding": [ { "type": "github", @@ -3826,6 +3954,7 @@ "version": "2.0.47", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -3887,12 +4016,14 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3905,6 +4036,7 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -4037,6 +4169,7 @@ "version": "0.18.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4061,6 +4194,7 @@ "version": "4.61.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.9" @@ -4143,6 +4277,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -4284,6 +4419,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -4302,12 +4438,14 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, "license": "MIT" }, "node_modules/tapable": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4321,6 +4459,7 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -4352,7 +4491,7 @@ "version": "4.22.4", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "esbuild": "~0.28.0" @@ -4374,6 +4513,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4390,6 +4530,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4406,6 +4547,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4422,6 +4564,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4438,6 +4581,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4454,6 +4598,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4470,6 +4615,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4486,6 +4632,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4502,6 +4649,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4518,6 +4666,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4534,6 +4683,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4550,6 +4700,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4566,6 +4717,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4582,6 +4734,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4598,6 +4751,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4614,6 +4768,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4630,6 +4785,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4646,6 +4802,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4662,6 +4819,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4678,6 +4836,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4694,6 +4853,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4710,6 +4870,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4726,6 +4887,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4742,6 +4904,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4758,6 +4921,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4774,6 +4938,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4787,7 +4952,7 @@ "version": "0.28.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -4871,6 +5036,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, "funding": [ { "type": "opencollective", @@ -4919,6 +5085,7 @@ "version": "6.4.3", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -5023,6 +5190,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, "license": "ISC" } } diff --git a/package.json b/package.json index 9073fbe..a2f907a 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,26 @@ { - "name": "react-example", + "name": "rkix-terminal-os", "private": true, - "version": "0.0.0", + "version": "1.0.5", "type": "module", "scripts": { "dev": "tsx server.ts", "build": "vite build && esbuild server.ts --bundle --platform=node --format=cjs --packages=external --sourcemap --outfile=dist/server.cjs", "start": "node dist/server.cjs", + "preview": "vite preview", "clean": "rm -rf dist server.js", - "lint": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "lint": "npm run typecheck" }, "dependencies": { "@google/genai": "^2.4.0", - "@tailwindcss/vite": "^4.1.14", - "@vitejs/plugin-react": "^5.0.4", "d3": "^7.9.0", "dotenv": "^17.2.3", "express": "^4.21.2", "lucide-react": "^0.546.0", "motion": "^12.23.24", "react": "^19.0.1", - "react-dom": "^19.0.1", - "vite": "^6.2.3" + "react-dom": "^19.0.1" }, "devDependencies": { "@types/d3": "^7.4.3", @@ -32,6 +31,9 @@ "tailwindcss": "^4.1.14", "tsx": "^4.21.0", "typescript": "~5.8.2", - "vite": "^6.2.3" - } + "vite": "^6.2.3", + "@tailwindcss/vite": "^4.1.14", + "@vitejs/plugin-react": "^5.0.4" + }, + "description": "AI-native terminal-based software development workspace with autonomous planner, builder, designer, debugger, and deployment agents." } diff --git a/server.ts b/server.ts index 8d68ed6..23fb3d7 100644 --- a/server.ts +++ b/server.ts @@ -3,20 +3,49 @@ * SPDX-License-Identifier: Apache-2.0 */ +import dotenv from "dotenv"; import express from "express"; import path from "path"; -import { createServer as createViteServer } from "vite"; import { GoogleGenAI, Type } from "@google/genai"; -import dotenv from "dotenv"; +import { createServer as createViteServer } from "vite"; dotenv.config(); +interface WorkspaceFilePayload { + path: string; + name?: string; + content: string; + language?: string; +} + +interface ProjectContextPayload { + name?: string; + description?: string; +} + +interface GeminiCommandRequest { + command?: string; + currentFiles?: WorkspaceFilePayload[]; + projectContext?: ProjectContextPayload; + activeBranch?: string; + thinkingMode?: boolean; +} + const app = express(); -const PORT = 3000; +const PORT = Number.parseInt(process.env.PORT || "3000", 10); +const GEMINI_MODEL = process.env.GEMINI_MODEL || "gemini-2.5-flash"; app.use(express.json({ limit: "50mb" })); -// Lazy initializer for Google GenAI client +app.get("/api/health", (_req, res) => { + res.json({ + ok: true, + service: "RKix Terminal OS", + geminiModel: GEMINI_MODEL, + }); +}); + +// Lazy initializer for Google GenAI client. let aiClient: GoogleGenAI | null = null; function getGenAI(): GoogleGenAI { if (!aiClient) { @@ -24,6 +53,7 @@ function getGenAI(): GoogleGenAI { if (!key || key === "MY_GEMINI_API_KEY") { throw new Error("GEMINI_API_KEY environment variable is not configured. Please add it via the Secrets panel in AI Studio settings."); } + aiClient = new GoogleGenAI({ apiKey: key, httpOptions: { @@ -33,23 +63,46 @@ function getGenAI(): GoogleGenAI { }, }); } + return aiClient; } -// REST route for Terminal command processing with Gemini Agent orchestrating +function normalizeFiles(files: unknown): WorkspaceFilePayload[] { + if (!Array.isArray(files)) { + return []; + } + + return files.filter((file): file is WorkspaceFilePayload => + typeof file?.path === "string" && typeof file?.content === "string" + ); +} + +function parseGeminiJson(text: string): unknown { + const trimmed = text.trim(); + const unfenced = trimmed + .replace(/^```(?:json)?\s*/i, "") + .replace(/\s*```$/i, "") + .trim(); + + return JSON.parse(unfenced); +} + +// REST route for Terminal command processing with Gemini Agent orchestrating. app.post("/api/gemini/command", async (req, res) => { try { - const { command, currentFiles, projectContext, activeBranch, outputMimeType, thinkingMode } = req.body; - - if (!command) { + const { command, currentFiles, projectContext, activeBranch, thinkingMode } = req.body as GeminiCommandRequest; + const cleanCommand = command?.trim(); + + if (!cleanCommand) { return res.status(400).json({ error: "Command is required" }); } const ai = getGenAI(); + const normalizedFiles = normalizeFiles(currentFiles); - // Construct the context representing the current virtual project directory structure and code bases - const filesContext = currentFiles && currentFiles.length > 0 - ? currentFiles.map((f: any) => `\n--- File: ${f.path} ---\nLanguage: ${f.language}\nContent:\n${f.content}`).join("\n\n") + // Construct the context representing the current virtual project directory structure and code bases. + const filesContext = normalizedFiles.length > 0 + ? normalizedFiles.map((file) => `\n--- File: ${file.path} ---\nLanguage: ${file.language || "text"}\nContent:\n${file.content}`).join("\n\n") : "[The workspace is currently empty. No files exist yet.]"; const systemInstruction = `You are the core RKix Terminal OS agent, directing a multi-agent software development terminal. @@ -68,7 +121,7 @@ Your goal is to parse this input and simulate the response of a highly professio - Deploy: Simulates build output, artifact bundling, and cloud service links. - Research: Explains documentation, searches appropriate modules. -You MUST always return a JSON object sticking strictly to the response schema. +You MUST always return a JSON object sticking strictly to the response schema. Provide a realistic developer workflow inside "agentWorkflow" with detailed logs of what different agents did. Provide "workspaceChanges" containing: - filesToCreate: Array of new files. ALWAYS include the fully fleshed out code content (e.g., beautiful Tailwind HTML layouts, React components or style configurations). Avoid placeholders! Provide complete source code! @@ -94,13 +147,13 @@ THINKING_MODE_ACTIVE: ${thinkingMode ? "TRUE" : "FALSE"} ${filesContext} *** DEVELOPER INPUT COMMAND *** -${command} +${cleanCommand} Generate the agent workflow, workspace updates, detailed reasoning (if thinking mode is true), and terminal outputs. Make sure to generate detailed, fully-written file contents without leaving any comment-based placeholders (e.g., do not write "// code goes here", write the real complete code). `; const response = await ai.models.generateContent({ - model: "gemini-3.5-flash", + model: GEMINI_MODEL, contents: userPrompt, config: { systemInstruction, @@ -111,16 +164,16 @@ Generate the agent workflow, workspace updates, detailed reasoning (if thinking commandParsed: { type: Type.OBJECT, properties: { - intent: { + intent: { type: Type.STRING, - description: "Categorize input intent: create_project, create_file, edit_file, delete_file, deploy, git_command, general_query" + description: "Categorize input intent: create_project, create_file, edit_file, delete_file, deploy, git_command, general_query", }, - target: { + target: { type: Type.STRING, - description: "The specific filename, branch name, or folder path targeted, if applicable." - } + description: "The specific filename, branch name, or folder path targeted, if applicable.", + }, }, - required: ["intent", "target"] + required: ["intent", "target"], }, agentWorkflow: { type: Type.ARRAY, @@ -130,10 +183,10 @@ Generate the agent workflow, workspace updates, detailed reasoning (if thinking properties: { agent: { type: Type.STRING, description: "Role name, e.g. Planner, Builder, Designer, Debugger, Deploy, Research" }, action: { type: Type.STRING, description: "Action text e.g. Scaffolding structure, Refining CSS layout" }, - log: { type: Type.STRING, description: "Log details representing thoughts or actual work files processed" } + log: { type: Type.STRING, description: "Log details representing thoughts or actual work files processed" }, }, - required: ["agent", "action", "log"] - } + required: ["agent", "action", "log"], + }, }, workspaceChanges: { type: Type.OBJECT, @@ -146,10 +199,10 @@ Generate the agent workflow, workspace updates, detailed reasoning (if thinking path: { type: Type.STRING, description: "The full path starting with 'workspace/project/'" }, name: { type: Type.STRING }, content: { type: Type.STRING, description: "The COMPLETE, functional file code" }, - language: { type: Type.STRING, description: "e.g. html, css, tsx, js, json, md" } + language: { type: Type.STRING, description: "e.g. html, css, tsx, js, json, md" }, }, - required: ["path", "name", "content", "language"] - } + required: ["path", "name", "content", "language"], + }, }, filesToEdit: { type: Type.ARRAY, @@ -157,34 +210,34 @@ Generate the agent workflow, workspace updates, detailed reasoning (if thinking type: Type.OBJECT, properties: { path: { type: Type.STRING, description: "Path of existing file to update" }, - content: { type: Type.STRING, description: "The updated FULL contents of the file" } + content: { type: Type.STRING, description: "The updated FULL contents of the file" }, }, - required: ["path", "content"] - } + required: ["path", "content"], + }, }, filesToDelete: { type: Type.ARRAY, - items: { type: Type.STRING } - } + items: { type: Type.STRING }, + }, }, - required: ["filesToCreate", "filesToEdit", "filesToDelete"] + required: ["filesToCreate", "filesToEdit", "filesToDelete"], }, - terminalOutput: { - type: Type.STRING, - description: "Simulated text to append to the system command prompt, with color tags or standard console structure." + terminalOutput: { + type: Type.STRING, + description: "Simulated text to append to the system command prompt, with color tags or standard console structure.", }, - explanation: { - type: Type.STRING, - description: "A summary explaining the actions taken in a polite, professional developer voice." + explanation: { + type: Type.STRING, + description: "A summary explaining the actions taken in a polite, professional developer voice.", }, detailedReasoning: { type: Type.STRING, - description: "Comprehensive multi-agent thinking, architectural debate, and planning process when thinkingMode is active (in Vietnamese or English matching user language)." - } + description: "Comprehensive multi-agent thinking, architectural debate, and planning process when thinkingMode is active (in Vietnamese or English matching user language).", + }, }, - required: ["commandParsed", "agentWorkflow", "workspaceChanges", "terminalOutput", "explanation"] - } - } + required: ["commandParsed", "agentWorkflow", "workspaceChanges", "terminalOutput", "explanation"], + }, + }, }); const resultText = response.text; @@ -192,18 +245,15 @@ Generate the agent workflow, workspace updates, detailed reasoning (if thinking throw new Error("Empty response from Gemini API"); } - const commandResult = JSON.parse(resultText.trim()); - return res.json(commandResult); - - } catch (error: any) { + return res.json(parseGeminiJson(resultText)); + } catch (error) { + const message = error instanceof Error ? error.message : "An error occurred while communicating with the AI Core."; console.error("Gemini Command processing failed:", error); - return res.status(500).json({ - error: error.message || "An error occurred while communicating with the AI Core." - }); + return res.status(500).json({ error: message }); } }); -// Configure Vite middleware or Static files hosting +// Configure Vite middleware or static file hosting. async function startServer() { if (process.env.NODE_ENV !== "production") { const vite = await createViteServer({ @@ -214,13 +264,13 @@ async function startServer() { } else { const distPath = path.join(process.cwd(), "dist"); app.use(express.static(distPath)); - app.get("*", (req, res) => { + app.get("*", (_req, res) => { res.sendFile(path.join(distPath, "index.html")); }); } app.listen(PORT, "0.0.0.0", () => { - console.log(`RKix Terminal OS server booted successfully on port ${PORT}`); + console.log(`RKix Terminal OS server booted successfully on port ${PORT} using ${GEMINI_MODEL}`); }); } diff --git a/src/App.tsx b/src/App.tsx index 8e31b67..a0f2235 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -46,6 +46,7 @@ import { MicOff } from "lucide-react"; import { Project, WorkspaceFile, Agent, TerminalLine, Deployment, GitCommit } from "./types"; +import { readJsonStorage, readNumberStorage, readStringStorage, writeStorage } from "./utils/storage"; import { PRESET_PROJECTS } from "./data/presets"; import DashboardOverview from "./components/DashboardOverview"; import ProjectList from "./components/ProjectList"; @@ -76,30 +77,29 @@ export default function App() { const rkixRootRef = useRef(null); // Persistence state loaders - const [projects, setProjects] = useState(() => { - const saved = localStorage.getItem("rkix_projects"); - return saved ? JSON.parse(saved) : PRESET_PROJECTS; - }); + const [projects, setProjects] = useState(() => + readJsonStorage("rkix_projects", PRESET_PROJECTS, (value): value is Project[] => + Array.isArray(value) && value.every((item) => typeof item?.id === "string" && Array.isArray(item?.files)) + ) + ); - const [activeProjectId, setActiveProjectId] = useState(() => { - const saved = localStorage.getItem("rkix_active_project_id"); - if (saved) return saved; - return PRESET_PROJECTS[0]?.id || ""; - }); + const [activeProjectId, setActiveProjectId] = useState(() => + readStringStorage("rkix_active_project_id", PRESET_PROJECTS[0]?.id || "") + ); const [currentSection, setCurrentSection] = useState("terminal"); - const [terminalLines, setTerminalLines] = useState(() => { - const saved = localStorage.getItem("rkix_terminal_lines"); - return saved ? JSON.parse(saved) : INITIAL_LINES; - }); + const [terminalLines, setTerminalLines] = useState(() => + readJsonStorage("rkix_terminal_lines", INITIAL_LINES, (value): value is TerminalLine[] => + Array.isArray(value) && value.every((item) => typeof item?.id === "string" && typeof item?.text === "string") + ) + ); const [agents, setAgents] = useState(DEFAULT_AGENTS); const [commandText, setCommandText] = useState(""); const [isProcessing, setIsProcessing] = useState(false); - const [totalCommandsRun, setTotalCommandsRun] = useState(() => { - const saved = localStorage.getItem("rkix_total_commands"); - return saved ? parseInt(saved, 10) : 0; - }); + const [totalCommandsRun, setTotalCommandsRun] = useState(() => + readNumberStorage("rkix_total_commands", 0) + ); // File explorer states const [selectedFilePath, setSelectedFilePath] = useState(""); @@ -120,7 +120,7 @@ export default function App() { const [isMicActive, setIsMicActive] = useState(false); const [micStream, setMicStream] = useState(null); - const longPressTimerRef = useRef(null); + const longPressTimerRef = useRef | null>(null); const [longPressedTriggered, setLongPressedTriggered] = useState(false); const startLongPressTimer = () => { @@ -400,25 +400,39 @@ export default function App() { }, 1200); }; - const activeProject = projects.find(p => p.id === activeProjectId) || projects[0]; + const activeProject = projects.find(p => p.id === activeProjectId) || projects[0] || PRESET_PROJECTS[0]; + + useEffect(() => { + if (projects.length === 0) { + setProjects(PRESET_PROJECTS); + setActiveProjectId(PRESET_PROJECTS[0]?.id || ""); + return; + } + + if (!projects.some((project) => project.id === activeProjectId)) { + setActiveProjectId(projects[0].id); + } + }, [activeProjectId, projects]); useEffect(() => { - localStorage.setItem("rkix_projects", JSON.stringify(projects)); + writeStorage("rkix_projects", projects); }, [projects]); useEffect(() => { - localStorage.setItem("rkix_active_project_id", activeProjectId); + writeStorage("rkix_active_project_id", activeProjectId); if (activeProject && activeProject.files.length > 0) { - setSelectedFilePath(activeProject.files[0].path); + setSelectedFilePath((current) => + activeProject.files.some((file) => file.path === current) ? current : activeProject.files[0].path + ); } }, [activeProjectId, activeProject]); useEffect(() => { - localStorage.setItem("rkix_terminal_lines", JSON.stringify(terminalLines)); + writeStorage("rkix_terminal_lines", terminalLines); }, [terminalLines]); useEffect(() => { - localStorage.setItem("rkix_total_commands", totalCommandsRun.toString()); + writeStorage("rkix_total_commands", totalCommandsRun.toString()); }, [totalCommandsRun]); // Update Real-time Telemetry Metrics periodically to drive live D3 visualizations diff --git a/src/components/DashboardOverview.tsx b/src/components/DashboardOverview.tsx index 919dce1..b6a3ab2 100644 --- a/src/components/DashboardOverview.tsx +++ b/src/components/DashboardOverview.tsx @@ -72,99 +72,99 @@ export default function DashboardOverview({ ]; return ( -
+
{/* OS Banner */} -
-
+
+
- - RKIX CORE KERNEL ACTIVE + + RKIX CORE
-

RKix Terminal OS

-

- Active Project: {project.name} • Branch: {project.activeBranch} +

RKix Terminal OS

+

+ Project: {project.name}

-
-
- SYS_TIME: {systime.split("T")[1].slice(0, 8)} UTC +
+
+ SYS: {systime.split("T")[1].slice(0, 8)}
- | -
+ +
CPU: {cpuUsage}%
- | -
- RAM: {memoryUsed}GB / 8.00GB + +
+ RAM: {memoryUsed}GB
{/* Grid of indicators */} -
+
{/* Metric 1 */} -
-
- WORKSPACE FILES - +
+
+ FILES +
-
{project.files.length}
-
- Total footprint: {totalLinesOfCode} lines of code +
{project.files.length}
+
+ {totalLinesOfCode} LOC
{/* Metric 2 */} -
-
- GIT REVISIONS - +
+
+ GIT +
-
{project.commitHistory.length}
-
- Active branch: {project.activeBranch} +
{project.commitHistory.length}
+
+ {project.activeBranch}
{/* Metric 3 */} -
-
- LIVE SERVICES - +
+
+ LIVE +
-
- {project.deployments.filter(d => d.status === "live").length} Live +
+ {project.deployments.filter(d => d.status === "live").length}
-
- Configured providers: Vercel, Railway +
+ Services
{/* Metric 4 */} -
-
- RUN COMMANDS - +
+
+ RUNS +
-
{totalCommandsRun}
-
- Orchestrated with AI +
{totalCommandsRun}
+
+ Commands
-
+
{/* Left column: Quick Shell Commands */} -
-
-
- -

Automated Agent Presets

+
+
+
+ +

Presets

-

- RKix OS is optimized for prompt-first workflows. Click any prompt below to pipeline it directly into our intelligent Agent Terminal environment: +

+ Click to run in Agent Terminal:

-
+
{presets.map((preset, index) => ( ))}
{/* Project Details Panel */} -
-
-
- -

Active Workspace Metadata

+
+
+
+ +

Metadata

{/* Right column: Active Agents Health Diagnostic */} -
-
-
- -

Autonomous Developer Matrix

+
+
+
+ +

Agents

-
+
{agents.map((agent) => { const isRunning = agent.status === "running"; return (
-
-
-
- {agent.name} - - {agent.role} +
+
+
+ {agent.name} + + {agent.role.substring(0, 4)}
typeof window !== "undefined" && typeof window.localStorage !== "undefined"; + +export function readJsonStorage( + key: string, + fallback: T, + validator?: (value: unknown) => value is T, +): T { + if (!isBrowserStorageAvailable()) { + return fallback; + } + + try { + const saved = window.localStorage.getItem(key); + if (!saved) { + return fallback; + } + + const parsed = JSON.parse(saved) as unknown; + if (validator && !validator(parsed)) { + window.localStorage.removeItem(key); + return fallback; + } + + return parsed as T; + } catch (error) { + console.warn(`Ignoring corrupted localStorage value for ${key}:`, error); + window.localStorage.removeItem(key); + return fallback; + } +} + +export function readStringStorage(key: string, fallback: string): string { + if (!isBrowserStorageAvailable()) { + return fallback; + } + + try { + return window.localStorage.getItem(key) || fallback; + } catch (error) { + console.warn(`Unable to read localStorage value for ${key}:`, error); + return fallback; + } +} + +export function readNumberStorage(key: string, fallback: number): number { + const rawValue = readStringStorage(key, String(fallback)); + const parsed = Number.parseInt(rawValue, 10); + return Number.isFinite(parsed) ? parsed : fallback; +} + +export function writeStorage(key: string, value: unknown): void { + if (!isBrowserStorageAvailable()) { + return; + } + + try { + const serialized = typeof value === "string" ? value : JSON.stringify(value); + window.localStorage.setItem(key, serialized); + } catch (error) { + console.warn(`Unable to persist localStorage value for ${key}:`, error); + } +} diff --git a/vite.config.ts b/vite.config.ts index fc23e76..9eb85fa 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig(() => { }, server: { // HMR is disabled in AI Studio via DISABLE_HMR env var. - // Do not modify—file watching is disabled to prevent flickering during agent edits. + // Do not modify—file watching is disabled to prevent flickering during agent edits. hmr: process.env.DISABLE_HMR !== 'true', // Disable file watching when DISABLE_HMR is true to save CPU during agent edits. watch: process.env.DISABLE_HMR === 'true' ? null : {},