gni commited on
Commit ·
15f5e73
1
Parent(s): d4f8d42
feat: Initial release of Redac - PII moderation MVP
Browse files- Consolidated project structure (API + UI).
- Removed redundant CLI tools.
- Updated Docker configuration for dev/prod environments.
- Added MIT License and updated documentation.
- Renamed project to Redac.
- .gitignore +0 -2
- LICENSE +21 -0
- README.md +1 -17
- api/Dockerfile +2 -3
- cli-ts/Dockerfile +0 -16
- cli-ts/index.ts +0 -66
- cli-ts/package-lock.json +0 -560
- cli-ts/package.json +0 -24
- cli-ts/tsconfig.json +0 -14
- cli/Dockerfile +0 -13
- cli/pii_mod.py +0 -57
- docker-compose.yml +0 -25
- ui/index.html +3 -0
- ui/src/App.tsx +116 -92
- ui/src/index.css +14 -36
.gitignore
CHANGED
|
@@ -33,14 +33,12 @@ coverage.xml
|
|
| 33 |
# --- Node.js ---
|
| 34 |
node_modules/
|
| 35 |
ui/node_modules/
|
| 36 |
-
cli-ts/node_modules/
|
| 37 |
npm-debug.log*
|
| 38 |
yarn-debug.log*
|
| 39 |
yarn-error.log*
|
| 40 |
.pnpm-debug.log*
|
| 41 |
.npm
|
| 42 |
ui/dist/
|
| 43 |
-
cli-ts/dist/
|
| 44 |
.vite/
|
| 45 |
|
| 46 |
# --- Docker ---
|
|
|
|
| 33 |
# --- Node.js ---
|
| 34 |
node_modules/
|
| 35 |
ui/node_modules/
|
|
|
|
| 36 |
npm-debug.log*
|
| 37 |
yarn-debug.log*
|
| 38 |
yarn-error.log*
|
| 39 |
.pnpm-debug.log*
|
| 40 |
.npm
|
| 41 |
ui/dist/
|
|
|
|
| 42 |
.vite/
|
| 43 |
|
| 44 |
# --- Docker ---
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Redac_Scan Contributors
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# 🛡️
|
| 2 |
|
| 3 |
A lightweight PII (Personally Identifiable Information) moderation MVP designed to sanitize sensitive data before it reaches LLM APIs.
|
| 4 |
|
|
@@ -10,7 +10,6 @@ A lightweight PII (Personally Identifiable Information) moderation MVP designed
|
|
| 10 |
- **Balanced Anonymization**: Preserves job titles and document structure to keep texts readable.
|
| 11 |
- **Minimal Dashboard**: React-based UI with Risk Assessment visualization.
|
| 12 |
- **Custom Theme UI**: Switch between **Premium**, **Minimal Light**, and **Deep Midnight** modes.
|
| 13 |
-
- **Dual CLI**: Tooling available in both **Python** and **TypeScript**.
|
| 14 |
|
| 15 |
---
|
| 16 |
|
|
@@ -18,9 +17,6 @@ A lightweight PII (Personally Identifiable Information) moderation MVP designed
|
|
| 18 |
|
| 19 |
1. **Core API (`/api`)**: FastAPI server powered by **Microsoft Presidio**.
|
| 20 |
2. **Web Dashboard (`/ui`)**: React + Vite + Tailwind CSS.
|
| 21 |
-
3. **CLI Tools**:
|
| 22 |
-
- **Python (`/cli`)**
|
| 23 |
-
- **TypeScript (`/cli-ts`)**
|
| 24 |
|
| 25 |
---
|
| 26 |
|
|
@@ -33,18 +29,6 @@ docker compose up --build
|
|
| 33 |
- **API**: `http://localhost:8000`
|
| 34 |
- **UI Dashboard**: `http://localhost:5173`
|
| 35 |
|
| 36 |
-
### Usage Examples:
|
| 37 |
-
|
| 38 |
-
**CLI (Python):**
|
| 39 |
-
```bash
|
| 40 |
-
docker compose run cli redact "Hello, my name is John Doe"
|
| 41 |
-
```
|
| 42 |
-
|
| 43 |
-
**CLI (TypeScript):**
|
| 44 |
-
```bash
|
| 45 |
-
docker compose run cli-ts redact "Contact me at 06 12 34 56 78"
|
| 46 |
-
```
|
| 47 |
-
|
| 48 |
---
|
| 49 |
|
| 50 |
## 🧪 Quality Assurance
|
|
|
|
| 1 |
+
# 🛡️ Redac
|
| 2 |
|
| 3 |
A lightweight PII (Personally Identifiable Information) moderation MVP designed to sanitize sensitive data before it reaches LLM APIs.
|
| 4 |
|
|
|
|
| 10 |
- **Balanced Anonymization**: Preserves job titles and document structure to keep texts readable.
|
| 11 |
- **Minimal Dashboard**: React-based UI with Risk Assessment visualization.
|
| 12 |
- **Custom Theme UI**: Switch between **Premium**, **Minimal Light**, and **Deep Midnight** modes.
|
|
|
|
| 13 |
|
| 14 |
---
|
| 15 |
|
|
|
|
| 17 |
|
| 18 |
1. **Core API (`/api`)**: FastAPI server powered by **Microsoft Presidio**.
|
| 19 |
2. **Web Dashboard (`/ui`)**: React + Vite + Tailwind CSS.
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
---
|
| 22 |
|
|
|
|
| 29 |
- **API**: `http://localhost:8000`
|
| 30 |
- **UI Dashboard**: `http://localhost:5173`
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
---
|
| 33 |
|
| 34 |
## 🧪 Quality Assurance
|
api/Dockerfile
CHANGED
|
@@ -13,9 +13,8 @@ RUN apt-get update && apt-get install -y \
|
|
| 13 |
COPY requirements.txt .
|
| 14 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 15 |
|
| 16 |
-
# Copy
|
| 17 |
-
COPY
|
| 18 |
-
COPY setup_models.py .
|
| 19 |
|
| 20 |
EXPOSE 8000
|
| 21 |
|
|
|
|
| 13 |
COPY requirements.txt .
|
| 14 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 15 |
|
| 16 |
+
# Copy all project files
|
| 17 |
+
COPY . .
|
|
|
|
| 18 |
|
| 19 |
EXPOSE 8000
|
| 20 |
|
cli-ts/Dockerfile
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
# CLI TS Dockerfile
|
| 2 |
-
FROM node:25-slim
|
| 3 |
-
|
| 4 |
-
WORKDIR /app
|
| 5 |
-
|
| 6 |
-
COPY package*.json ./
|
| 7 |
-
RUN npm install
|
| 8 |
-
|
| 9 |
-
COPY . .
|
| 10 |
-
RUN npm run build
|
| 11 |
-
|
| 12 |
-
# Set executable permissions
|
| 13 |
-
RUN chmod +x dist/index.js
|
| 14 |
-
|
| 15 |
-
# Remove ENTRYPOINT to allow docker-compose command to run sh
|
| 16 |
-
CMD ["node", "--no-warnings", "dist/index.js"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cli-ts/index.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
| 1 |
-
import { Command } from 'commander';
|
| 2 |
-
import axios from 'axios';
|
| 3 |
-
import chalk from 'chalk';
|
| 4 |
-
import * as fs from 'fs';
|
| 5 |
-
|
| 6 |
-
const program = new Command();
|
| 7 |
-
const API_URL = process.env.API_URL || 'http://localhost:8000';
|
| 8 |
-
|
| 9 |
-
program
|
| 10 |
-
.name('pii-ts')
|
| 11 |
-
.description('TypeScript PII Moderator CLI')
|
| 12 |
-
.version('1.0.0');
|
| 13 |
-
|
| 14 |
-
program
|
| 15 |
-
.command('redact')
|
| 16 |
-
.description('Redact PII from a string')
|
| 17 |
-
.argument('<text>', 'The text to redact')
|
| 18 |
-
.action(async (text: string) => {
|
| 19 |
-
try {
|
| 20 |
-
const response = await axios.post(`${API_URL}/redact`, { text });
|
| 21 |
-
const { original_text, redacted_text, detected_entities } = response.data;
|
| 22 |
-
|
| 23 |
-
console.log(chalk.cyan('\nOriginal:'));
|
| 24 |
-
console.log(original_text);
|
| 25 |
-
|
| 26 |
-
console.log(chalk.green('\nRedacted:'));
|
| 27 |
-
console.log(redacted_text);
|
| 28 |
-
|
| 29 |
-
if (detected_entities && detected_entities.length > 0) {
|
| 30 |
-
console.log(chalk.yellow('\nDetected Entities:'));
|
| 31 |
-
detected_entities.forEach((ent: any) => {
|
| 32 |
-
console.log(`- ${ent.entity_type} (Score: ${ent.score.toFixed(2)})`);
|
| 33 |
-
});
|
| 34 |
-
}
|
| 35 |
-
} catch (error: any) {
|
| 36 |
-
console.error(chalk.red(`Error: ${error.message}`));
|
| 37 |
-
process.exit(1);
|
| 38 |
-
}
|
| 39 |
-
});
|
| 40 |
-
|
| 41 |
-
program
|
| 42 |
-
.command('redact-file')
|
| 43 |
-
.description('Redact PII from a file')
|
| 44 |
-
.argument('<path>', 'Path to the file')
|
| 45 |
-
.action(async (path: string) => {
|
| 46 |
-
try {
|
| 47 |
-
if (!fs.existsSync(path)) {
|
| 48 |
-
console.error(chalk.red(`Error: File not found at ${path}`));
|
| 49 |
-
process.exit(1);
|
| 50 |
-
}
|
| 51 |
-
const content = fs.readFileSync(path, 'utf-8');
|
| 52 |
-
const response = await axios.post(`${API_URL}/redact`, { text: content });
|
| 53 |
-
console.log(response.data.redacted_text);
|
| 54 |
-
} catch (error: any) {
|
| 55 |
-
console.error(chalk.red(`Error: ${error.message}`));
|
| 56 |
-
process.exit(1);
|
| 57 |
-
}
|
| 58 |
-
});
|
| 59 |
-
|
| 60 |
-
// Handle empty args without error
|
| 61 |
-
if (process.argv.length <= 2) {
|
| 62 |
-
program.outputHelp();
|
| 63 |
-
process.exit(0);
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
program.parse(process.argv);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cli-ts/package-lock.json
DELETED
|
@@ -1,560 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "cli-ts",
|
| 3 |
-
"version": "1.0.0",
|
| 4 |
-
"lockfileVersion": 3,
|
| 5 |
-
"requires": true,
|
| 6 |
-
"packages": {
|
| 7 |
-
"": {
|
| 8 |
-
"name": "cli-ts",
|
| 9 |
-
"version": "1.0.0",
|
| 10 |
-
"license": "ISC",
|
| 11 |
-
"dependencies": {
|
| 12 |
-
"axios": "^1.13.6",
|
| 13 |
-
"chalk": "^5.6.2",
|
| 14 |
-
"commander": "^14.0.3"
|
| 15 |
-
},
|
| 16 |
-
"devDependencies": {
|
| 17 |
-
"@types/axios": "^0.9.36",
|
| 18 |
-
"@types/commander": "^2.12.0",
|
| 19 |
-
"@types/node": "^25.5.0",
|
| 20 |
-
"ts-node": "^10.9.2",
|
| 21 |
-
"typescript": "^5.9.3"
|
| 22 |
-
}
|
| 23 |
-
},
|
| 24 |
-
"node_modules/@cspotcode/source-map-support": {
|
| 25 |
-
"version": "0.8.1",
|
| 26 |
-
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
| 27 |
-
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
| 28 |
-
"dev": true,
|
| 29 |
-
"license": "MIT",
|
| 30 |
-
"dependencies": {
|
| 31 |
-
"@jridgewell/trace-mapping": "0.3.9"
|
| 32 |
-
},
|
| 33 |
-
"engines": {
|
| 34 |
-
"node": ">=12"
|
| 35 |
-
}
|
| 36 |
-
},
|
| 37 |
-
"node_modules/@jridgewell/resolve-uri": {
|
| 38 |
-
"version": "3.1.2",
|
| 39 |
-
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
| 40 |
-
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
| 41 |
-
"dev": true,
|
| 42 |
-
"license": "MIT",
|
| 43 |
-
"engines": {
|
| 44 |
-
"node": ">=6.0.0"
|
| 45 |
-
}
|
| 46 |
-
},
|
| 47 |
-
"node_modules/@jridgewell/sourcemap-codec": {
|
| 48 |
-
"version": "1.5.5",
|
| 49 |
-
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
| 50 |
-
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
| 51 |
-
"dev": true,
|
| 52 |
-
"license": "MIT"
|
| 53 |
-
},
|
| 54 |
-
"node_modules/@jridgewell/trace-mapping": {
|
| 55 |
-
"version": "0.3.9",
|
| 56 |
-
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
| 57 |
-
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
| 58 |
-
"dev": true,
|
| 59 |
-
"license": "MIT",
|
| 60 |
-
"dependencies": {
|
| 61 |
-
"@jridgewell/resolve-uri": "^3.0.3",
|
| 62 |
-
"@jridgewell/sourcemap-codec": "^1.4.10"
|
| 63 |
-
}
|
| 64 |
-
},
|
| 65 |
-
"node_modules/@tsconfig/node10": {
|
| 66 |
-
"version": "1.0.12",
|
| 67 |
-
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
| 68 |
-
"integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
|
| 69 |
-
"dev": true,
|
| 70 |
-
"license": "MIT"
|
| 71 |
-
},
|
| 72 |
-
"node_modules/@tsconfig/node12": {
|
| 73 |
-
"version": "1.0.11",
|
| 74 |
-
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
| 75 |
-
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
| 76 |
-
"dev": true,
|
| 77 |
-
"license": "MIT"
|
| 78 |
-
},
|
| 79 |
-
"node_modules/@tsconfig/node14": {
|
| 80 |
-
"version": "1.0.3",
|
| 81 |
-
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
| 82 |
-
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
| 83 |
-
"dev": true,
|
| 84 |
-
"license": "MIT"
|
| 85 |
-
},
|
| 86 |
-
"node_modules/@tsconfig/node16": {
|
| 87 |
-
"version": "1.0.4",
|
| 88 |
-
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
| 89 |
-
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
| 90 |
-
"dev": true,
|
| 91 |
-
"license": "MIT"
|
| 92 |
-
},
|
| 93 |
-
"node_modules/@types/axios": {
|
| 94 |
-
"version": "0.9.36",
|
| 95 |
-
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.9.36.tgz",
|
| 96 |
-
"integrity": "sha512-NLOpedx9o+rxo/X5ChbdiX6mS1atE4WHmEEIcR9NLenRVa5HoVjAvjafwU3FPTqnZEstpoqCaW7fagqSoTDNeg==",
|
| 97 |
-
"dev": true,
|
| 98 |
-
"license": "MIT"
|
| 99 |
-
},
|
| 100 |
-
"node_modules/@types/commander": {
|
| 101 |
-
"version": "2.12.0",
|
| 102 |
-
"resolved": "https://registry.npmjs.org/@types/commander/-/commander-2.12.0.tgz",
|
| 103 |
-
"integrity": "sha512-DDmRkovH7jPjnx7HcbSnqKg2JeNANyxNZeUvB0iE+qKBLN+vzN5iSIwt+J2PFSmBuYEut4mgQvI/fTX9YQH/vw==",
|
| 104 |
-
"dev": true,
|
| 105 |
-
"license": "MIT",
|
| 106 |
-
"dependencies": {
|
| 107 |
-
"commander": "*"
|
| 108 |
-
}
|
| 109 |
-
},
|
| 110 |
-
"node_modules/@types/node": {
|
| 111 |
-
"version": "25.5.0",
|
| 112 |
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
|
| 113 |
-
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
|
| 114 |
-
"dev": true,
|
| 115 |
-
"license": "MIT",
|
| 116 |
-
"dependencies": {
|
| 117 |
-
"undici-types": "~7.18.0"
|
| 118 |
-
}
|
| 119 |
-
},
|
| 120 |
-
"node_modules/acorn": {
|
| 121 |
-
"version": "8.16.0",
|
| 122 |
-
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
| 123 |
-
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
| 124 |
-
"dev": true,
|
| 125 |
-
"license": "MIT",
|
| 126 |
-
"bin": {
|
| 127 |
-
"acorn": "bin/acorn"
|
| 128 |
-
},
|
| 129 |
-
"engines": {
|
| 130 |
-
"node": ">=0.4.0"
|
| 131 |
-
}
|
| 132 |
-
},
|
| 133 |
-
"node_modules/acorn-walk": {
|
| 134 |
-
"version": "8.3.5",
|
| 135 |
-
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
|
| 136 |
-
"integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==",
|
| 137 |
-
"dev": true,
|
| 138 |
-
"license": "MIT",
|
| 139 |
-
"dependencies": {
|
| 140 |
-
"acorn": "^8.11.0"
|
| 141 |
-
},
|
| 142 |
-
"engines": {
|
| 143 |
-
"node": ">=0.4.0"
|
| 144 |
-
}
|
| 145 |
-
},
|
| 146 |
-
"node_modules/arg": {
|
| 147 |
-
"version": "4.1.3",
|
| 148 |
-
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
| 149 |
-
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
| 150 |
-
"dev": true,
|
| 151 |
-
"license": "MIT"
|
| 152 |
-
},
|
| 153 |
-
"node_modules/asynckit": {
|
| 154 |
-
"version": "0.4.0",
|
| 155 |
-
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
| 156 |
-
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
| 157 |
-
"license": "MIT"
|
| 158 |
-
},
|
| 159 |
-
"node_modules/axios": {
|
| 160 |
-
"version": "1.13.6",
|
| 161 |
-
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
|
| 162 |
-
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
| 163 |
-
"license": "MIT",
|
| 164 |
-
"dependencies": {
|
| 165 |
-
"follow-redirects": "^1.15.11",
|
| 166 |
-
"form-data": "^4.0.5",
|
| 167 |
-
"proxy-from-env": "^1.1.0"
|
| 168 |
-
}
|
| 169 |
-
},
|
| 170 |
-
"node_modules/call-bind-apply-helpers": {
|
| 171 |
-
"version": "1.0.2",
|
| 172 |
-
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
| 173 |
-
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
| 174 |
-
"license": "MIT",
|
| 175 |
-
"dependencies": {
|
| 176 |
-
"es-errors": "^1.3.0",
|
| 177 |
-
"function-bind": "^1.1.2"
|
| 178 |
-
},
|
| 179 |
-
"engines": {
|
| 180 |
-
"node": ">= 0.4"
|
| 181 |
-
}
|
| 182 |
-
},
|
| 183 |
-
"node_modules/chalk": {
|
| 184 |
-
"version": "5.6.2",
|
| 185 |
-
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
| 186 |
-
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
| 187 |
-
"license": "MIT",
|
| 188 |
-
"engines": {
|
| 189 |
-
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
| 190 |
-
},
|
| 191 |
-
"funding": {
|
| 192 |
-
"url": "https://github.com/chalk/chalk?sponsor=1"
|
| 193 |
-
}
|
| 194 |
-
},
|
| 195 |
-
"node_modules/combined-stream": {
|
| 196 |
-
"version": "1.0.8",
|
| 197 |
-
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
| 198 |
-
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
| 199 |
-
"license": "MIT",
|
| 200 |
-
"dependencies": {
|
| 201 |
-
"delayed-stream": "~1.0.0"
|
| 202 |
-
},
|
| 203 |
-
"engines": {
|
| 204 |
-
"node": ">= 0.8"
|
| 205 |
-
}
|
| 206 |
-
},
|
| 207 |
-
"node_modules/commander": {
|
| 208 |
-
"version": "14.0.3",
|
| 209 |
-
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
|
| 210 |
-
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
|
| 211 |
-
"license": "MIT",
|
| 212 |
-
"engines": {
|
| 213 |
-
"node": ">=20"
|
| 214 |
-
}
|
| 215 |
-
},
|
| 216 |
-
"node_modules/create-require": {
|
| 217 |
-
"version": "1.1.1",
|
| 218 |
-
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
| 219 |
-
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
| 220 |
-
"dev": true,
|
| 221 |
-
"license": "MIT"
|
| 222 |
-
},
|
| 223 |
-
"node_modules/delayed-stream": {
|
| 224 |
-
"version": "1.0.0",
|
| 225 |
-
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
| 226 |
-
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
| 227 |
-
"license": "MIT",
|
| 228 |
-
"engines": {
|
| 229 |
-
"node": ">=0.4.0"
|
| 230 |
-
}
|
| 231 |
-
},
|
| 232 |
-
"node_modules/diff": {
|
| 233 |
-
"version": "4.0.4",
|
| 234 |
-
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
| 235 |
-
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
|
| 236 |
-
"dev": true,
|
| 237 |
-
"license": "BSD-3-Clause",
|
| 238 |
-
"engines": {
|
| 239 |
-
"node": ">=0.3.1"
|
| 240 |
-
}
|
| 241 |
-
},
|
| 242 |
-
"node_modules/dunder-proto": {
|
| 243 |
-
"version": "1.0.1",
|
| 244 |
-
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
| 245 |
-
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
| 246 |
-
"license": "MIT",
|
| 247 |
-
"dependencies": {
|
| 248 |
-
"call-bind-apply-helpers": "^1.0.1",
|
| 249 |
-
"es-errors": "^1.3.0",
|
| 250 |
-
"gopd": "^1.2.0"
|
| 251 |
-
},
|
| 252 |
-
"engines": {
|
| 253 |
-
"node": ">= 0.4"
|
| 254 |
-
}
|
| 255 |
-
},
|
| 256 |
-
"node_modules/es-define-property": {
|
| 257 |
-
"version": "1.0.1",
|
| 258 |
-
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
| 259 |
-
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
| 260 |
-
"license": "MIT",
|
| 261 |
-
"engines": {
|
| 262 |
-
"node": ">= 0.4"
|
| 263 |
-
}
|
| 264 |
-
},
|
| 265 |
-
"node_modules/es-errors": {
|
| 266 |
-
"version": "1.3.0",
|
| 267 |
-
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
| 268 |
-
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
| 269 |
-
"license": "MIT",
|
| 270 |
-
"engines": {
|
| 271 |
-
"node": ">= 0.4"
|
| 272 |
-
}
|
| 273 |
-
},
|
| 274 |
-
"node_modules/es-object-atoms": {
|
| 275 |
-
"version": "1.1.1",
|
| 276 |
-
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
| 277 |
-
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
| 278 |
-
"license": "MIT",
|
| 279 |
-
"dependencies": {
|
| 280 |
-
"es-errors": "^1.3.0"
|
| 281 |
-
},
|
| 282 |
-
"engines": {
|
| 283 |
-
"node": ">= 0.4"
|
| 284 |
-
}
|
| 285 |
-
},
|
| 286 |
-
"node_modules/es-set-tostringtag": {
|
| 287 |
-
"version": "2.1.0",
|
| 288 |
-
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
| 289 |
-
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
| 290 |
-
"license": "MIT",
|
| 291 |
-
"dependencies": {
|
| 292 |
-
"es-errors": "^1.3.0",
|
| 293 |
-
"get-intrinsic": "^1.2.6",
|
| 294 |
-
"has-tostringtag": "^1.0.2",
|
| 295 |
-
"hasown": "^2.0.2"
|
| 296 |
-
},
|
| 297 |
-
"engines": {
|
| 298 |
-
"node": ">= 0.4"
|
| 299 |
-
}
|
| 300 |
-
},
|
| 301 |
-
"node_modules/follow-redirects": {
|
| 302 |
-
"version": "1.15.11",
|
| 303 |
-
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
| 304 |
-
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
| 305 |
-
"funding": [
|
| 306 |
-
{
|
| 307 |
-
"type": "individual",
|
| 308 |
-
"url": "https://github.com/sponsors/RubenVerborgh"
|
| 309 |
-
}
|
| 310 |
-
],
|
| 311 |
-
"license": "MIT",
|
| 312 |
-
"engines": {
|
| 313 |
-
"node": ">=4.0"
|
| 314 |
-
},
|
| 315 |
-
"peerDependenciesMeta": {
|
| 316 |
-
"debug": {
|
| 317 |
-
"optional": true
|
| 318 |
-
}
|
| 319 |
-
}
|
| 320 |
-
},
|
| 321 |
-
"node_modules/form-data": {
|
| 322 |
-
"version": "4.0.5",
|
| 323 |
-
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
| 324 |
-
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
| 325 |
-
"license": "MIT",
|
| 326 |
-
"dependencies": {
|
| 327 |
-
"asynckit": "^0.4.0",
|
| 328 |
-
"combined-stream": "^1.0.8",
|
| 329 |
-
"es-set-tostringtag": "^2.1.0",
|
| 330 |
-
"hasown": "^2.0.2",
|
| 331 |
-
"mime-types": "^2.1.12"
|
| 332 |
-
},
|
| 333 |
-
"engines": {
|
| 334 |
-
"node": ">= 6"
|
| 335 |
-
}
|
| 336 |
-
},
|
| 337 |
-
"node_modules/function-bind": {
|
| 338 |
-
"version": "1.1.2",
|
| 339 |
-
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
| 340 |
-
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
| 341 |
-
"license": "MIT",
|
| 342 |
-
"funding": {
|
| 343 |
-
"url": "https://github.com/sponsors/ljharb"
|
| 344 |
-
}
|
| 345 |
-
},
|
| 346 |
-
"node_modules/get-intrinsic": {
|
| 347 |
-
"version": "1.3.0",
|
| 348 |
-
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
| 349 |
-
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
| 350 |
-
"license": "MIT",
|
| 351 |
-
"dependencies": {
|
| 352 |
-
"call-bind-apply-helpers": "^1.0.2",
|
| 353 |
-
"es-define-property": "^1.0.1",
|
| 354 |
-
"es-errors": "^1.3.0",
|
| 355 |
-
"es-object-atoms": "^1.1.1",
|
| 356 |
-
"function-bind": "^1.1.2",
|
| 357 |
-
"get-proto": "^1.0.1",
|
| 358 |
-
"gopd": "^1.2.0",
|
| 359 |
-
"has-symbols": "^1.1.0",
|
| 360 |
-
"hasown": "^2.0.2",
|
| 361 |
-
"math-intrinsics": "^1.1.0"
|
| 362 |
-
},
|
| 363 |
-
"engines": {
|
| 364 |
-
"node": ">= 0.4"
|
| 365 |
-
},
|
| 366 |
-
"funding": {
|
| 367 |
-
"url": "https://github.com/sponsors/ljharb"
|
| 368 |
-
}
|
| 369 |
-
},
|
| 370 |
-
"node_modules/get-proto": {
|
| 371 |
-
"version": "1.0.1",
|
| 372 |
-
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
| 373 |
-
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
| 374 |
-
"license": "MIT",
|
| 375 |
-
"dependencies": {
|
| 376 |
-
"dunder-proto": "^1.0.1",
|
| 377 |
-
"es-object-atoms": "^1.0.0"
|
| 378 |
-
},
|
| 379 |
-
"engines": {
|
| 380 |
-
"node": ">= 0.4"
|
| 381 |
-
}
|
| 382 |
-
},
|
| 383 |
-
"node_modules/gopd": {
|
| 384 |
-
"version": "1.2.0",
|
| 385 |
-
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
| 386 |
-
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
| 387 |
-
"license": "MIT",
|
| 388 |
-
"engines": {
|
| 389 |
-
"node": ">= 0.4"
|
| 390 |
-
},
|
| 391 |
-
"funding": {
|
| 392 |
-
"url": "https://github.com/sponsors/ljharb"
|
| 393 |
-
}
|
| 394 |
-
},
|
| 395 |
-
"node_modules/has-symbols": {
|
| 396 |
-
"version": "1.1.0",
|
| 397 |
-
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
| 398 |
-
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
| 399 |
-
"license": "MIT",
|
| 400 |
-
"engines": {
|
| 401 |
-
"node": ">= 0.4"
|
| 402 |
-
},
|
| 403 |
-
"funding": {
|
| 404 |
-
"url": "https://github.com/sponsors/ljharb"
|
| 405 |
-
}
|
| 406 |
-
},
|
| 407 |
-
"node_modules/has-tostringtag": {
|
| 408 |
-
"version": "1.0.2",
|
| 409 |
-
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
| 410 |
-
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
| 411 |
-
"license": "MIT",
|
| 412 |
-
"dependencies": {
|
| 413 |
-
"has-symbols": "^1.0.3"
|
| 414 |
-
},
|
| 415 |
-
"engines": {
|
| 416 |
-
"node": ">= 0.4"
|
| 417 |
-
},
|
| 418 |
-
"funding": {
|
| 419 |
-
"url": "https://github.com/sponsors/ljharb"
|
| 420 |
-
}
|
| 421 |
-
},
|
| 422 |
-
"node_modules/hasown": {
|
| 423 |
-
"version": "2.0.2",
|
| 424 |
-
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
| 425 |
-
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
| 426 |
-
"license": "MIT",
|
| 427 |
-
"dependencies": {
|
| 428 |
-
"function-bind": "^1.1.2"
|
| 429 |
-
},
|
| 430 |
-
"engines": {
|
| 431 |
-
"node": ">= 0.4"
|
| 432 |
-
}
|
| 433 |
-
},
|
| 434 |
-
"node_modules/make-error": {
|
| 435 |
-
"version": "1.3.6",
|
| 436 |
-
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
| 437 |
-
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
| 438 |
-
"dev": true,
|
| 439 |
-
"license": "ISC"
|
| 440 |
-
},
|
| 441 |
-
"node_modules/math-intrinsics": {
|
| 442 |
-
"version": "1.1.0",
|
| 443 |
-
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
| 444 |
-
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
| 445 |
-
"license": "MIT",
|
| 446 |
-
"engines": {
|
| 447 |
-
"node": ">= 0.4"
|
| 448 |
-
}
|
| 449 |
-
},
|
| 450 |
-
"node_modules/mime-db": {
|
| 451 |
-
"version": "1.52.0",
|
| 452 |
-
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
| 453 |
-
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
| 454 |
-
"license": "MIT",
|
| 455 |
-
"engines": {
|
| 456 |
-
"node": ">= 0.6"
|
| 457 |
-
}
|
| 458 |
-
},
|
| 459 |
-
"node_modules/mime-types": {
|
| 460 |
-
"version": "2.1.35",
|
| 461 |
-
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
| 462 |
-
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
| 463 |
-
"license": "MIT",
|
| 464 |
-
"dependencies": {
|
| 465 |
-
"mime-db": "1.52.0"
|
| 466 |
-
},
|
| 467 |
-
"engines": {
|
| 468 |
-
"node": ">= 0.6"
|
| 469 |
-
}
|
| 470 |
-
},
|
| 471 |
-
"node_modules/proxy-from-env": {
|
| 472 |
-
"version": "1.1.0",
|
| 473 |
-
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
| 474 |
-
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
| 475 |
-
"license": "MIT"
|
| 476 |
-
},
|
| 477 |
-
"node_modules/ts-node": {
|
| 478 |
-
"version": "10.9.2",
|
| 479 |
-
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
| 480 |
-
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
| 481 |
-
"dev": true,
|
| 482 |
-
"license": "MIT",
|
| 483 |
-
"dependencies": {
|
| 484 |
-
"@cspotcode/source-map-support": "^0.8.0",
|
| 485 |
-
"@tsconfig/node10": "^1.0.7",
|
| 486 |
-
"@tsconfig/node12": "^1.0.7",
|
| 487 |
-
"@tsconfig/node14": "^1.0.0",
|
| 488 |
-
"@tsconfig/node16": "^1.0.2",
|
| 489 |
-
"acorn": "^8.4.1",
|
| 490 |
-
"acorn-walk": "^8.1.1",
|
| 491 |
-
"arg": "^4.1.0",
|
| 492 |
-
"create-require": "^1.1.0",
|
| 493 |
-
"diff": "^4.0.1",
|
| 494 |
-
"make-error": "^1.1.1",
|
| 495 |
-
"v8-compile-cache-lib": "^3.0.1",
|
| 496 |
-
"yn": "3.1.1"
|
| 497 |
-
},
|
| 498 |
-
"bin": {
|
| 499 |
-
"ts-node": "dist/bin.js",
|
| 500 |
-
"ts-node-cwd": "dist/bin-cwd.js",
|
| 501 |
-
"ts-node-esm": "dist/bin-esm.js",
|
| 502 |
-
"ts-node-script": "dist/bin-script.js",
|
| 503 |
-
"ts-node-transpile-only": "dist/bin-transpile.js",
|
| 504 |
-
"ts-script": "dist/bin-script-deprecated.js"
|
| 505 |
-
},
|
| 506 |
-
"peerDependencies": {
|
| 507 |
-
"@swc/core": ">=1.2.50",
|
| 508 |
-
"@swc/wasm": ">=1.2.50",
|
| 509 |
-
"@types/node": "*",
|
| 510 |
-
"typescript": ">=2.7"
|
| 511 |
-
},
|
| 512 |
-
"peerDependenciesMeta": {
|
| 513 |
-
"@swc/core": {
|
| 514 |
-
"optional": true
|
| 515 |
-
},
|
| 516 |
-
"@swc/wasm": {
|
| 517 |
-
"optional": true
|
| 518 |
-
}
|
| 519 |
-
}
|
| 520 |
-
},
|
| 521 |
-
"node_modules/typescript": {
|
| 522 |
-
"version": "5.9.3",
|
| 523 |
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
| 524 |
-
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
| 525 |
-
"dev": true,
|
| 526 |
-
"license": "Apache-2.0",
|
| 527 |
-
"bin": {
|
| 528 |
-
"tsc": "bin/tsc",
|
| 529 |
-
"tsserver": "bin/tsserver"
|
| 530 |
-
},
|
| 531 |
-
"engines": {
|
| 532 |
-
"node": ">=14.17"
|
| 533 |
-
}
|
| 534 |
-
},
|
| 535 |
-
"node_modules/undici-types": {
|
| 536 |
-
"version": "7.18.2",
|
| 537 |
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
| 538 |
-
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
| 539 |
-
"dev": true,
|
| 540 |
-
"license": "MIT"
|
| 541 |
-
},
|
| 542 |
-
"node_modules/v8-compile-cache-lib": {
|
| 543 |
-
"version": "3.0.1",
|
| 544 |
-
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
| 545 |
-
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
| 546 |
-
"dev": true,
|
| 547 |
-
"license": "MIT"
|
| 548 |
-
},
|
| 549 |
-
"node_modules/yn": {
|
| 550 |
-
"version": "3.1.1",
|
| 551 |
-
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
| 552 |
-
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
| 553 |
-
"dev": true,
|
| 554 |
-
"license": "MIT",
|
| 555 |
-
"engines": {
|
| 556 |
-
"node": ">=6"
|
| 557 |
-
}
|
| 558 |
-
}
|
| 559 |
-
}
|
| 560 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cli-ts/package.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "pii-ts-cli",
|
| 3 |
-
"version": "1.0.0",
|
| 4 |
-
"description": "TypeScript PII Moderator CLI",
|
| 5 |
-
"type": "module",
|
| 6 |
-
"bin": {
|
| 7 |
-
"pii-ts": "./dist/index.js"
|
| 8 |
-
},
|
| 9 |
-
"scripts": {
|
| 10 |
-
"build": "tsc",
|
| 11 |
-
"start": "node --no-warnings dist/index.js",
|
| 12 |
-
"dev": "ts-node --no-warnings index.ts"
|
| 13 |
-
},
|
| 14 |
-
"dependencies": {
|
| 15 |
-
"axios": "^1.8.1",
|
| 16 |
-
"chalk": "^5.4.1",
|
| 17 |
-
"commander": "^13.1.0"
|
| 18 |
-
},
|
| 19 |
-
"devDependencies": {
|
| 20 |
-
"@types/node": "^24.12.0",
|
| 21 |
-
"ts-node": "^10.9.2",
|
| 22 |
-
"typescript": "^5.8.2"
|
| 23 |
-
}
|
| 24 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cli-ts/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"compilerOptions": {
|
| 3 |
-
"target": "ESNext",
|
| 4 |
-
"module": "ESNext",
|
| 5 |
-
"moduleResolution": "node",
|
| 6 |
-
"outDir": "./dist",
|
| 7 |
-
"rootDir": "./",
|
| 8 |
-
"strict": true,
|
| 9 |
-
"esModuleInterop": true,
|
| 10 |
-
"skipLibCheck": true,
|
| 11 |
-
"forceConsistentCasingInFileNames": true
|
| 12 |
-
},
|
| 13 |
-
"include": ["index.ts"]
|
| 14 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cli/Dockerfile
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
# CLI Dockerfile
|
| 2 |
-
FROM python:3.12-slim
|
| 3 |
-
|
| 4 |
-
WORKDIR /app
|
| 5 |
-
|
| 6 |
-
# Install dependencies
|
| 7 |
-
RUN pip install --no-cache-dir click requests
|
| 8 |
-
|
| 9 |
-
# Copy CLI script
|
| 10 |
-
COPY pii_mod.py .
|
| 11 |
-
|
| 12 |
-
# Use CMD instead of ENTRYPOINT to allow docker-compose command
|
| 13 |
-
CMD ["python", "pii_mod.py"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cli/pii_mod.py
DELETED
|
@@ -1,57 +0,0 @@
|
|
| 1 |
-
import click
|
| 2 |
-
import requests
|
| 3 |
-
import json
|
| 4 |
-
import sys
|
| 5 |
-
|
| 6 |
-
API_URL = "http://localhost:8000"
|
| 7 |
-
|
| 8 |
-
@click.group()
|
| 9 |
-
def cli():
|
| 10 |
-
"""PII Moderator CLI - Protect your data before sending it to LLMs."""
|
| 11 |
-
pass
|
| 12 |
-
|
| 13 |
-
@cli.command()
|
| 14 |
-
@click.argument('text')
|
| 15 |
-
@click.option('--entities', '-e', multiple=True, help="Specific entities to redact (e.g. -e PERSON -e EMAIL_ADDRESS)")
|
| 16 |
-
def redact(text, entities):
|
| 17 |
-
"""Redact PII from a given string."""
|
| 18 |
-
try:
|
| 19 |
-
payload = {"text": text}
|
| 20 |
-
if entities:
|
| 21 |
-
payload["entities"] = list(entities)
|
| 22 |
-
|
| 23 |
-
response = requests.post(f"{API_URL}/redact", json=payload)
|
| 24 |
-
response.raise_for_status()
|
| 25 |
-
|
| 26 |
-
result = response.json()
|
| 27 |
-
click.echo(click.style("\nOriginal:", fg="cyan"))
|
| 28 |
-
click.echo(result["original_text"])
|
| 29 |
-
click.echo(click.style("\nRedacted:", fg="green"))
|
| 30 |
-
click.echo(result["redacted_text"])
|
| 31 |
-
|
| 32 |
-
if result["detected_entities"]:
|
| 33 |
-
click.echo(click.style("\nDetected Entities:", fg="yellow"))
|
| 34 |
-
for ent in result["detected_entities"]:
|
| 35 |
-
click.echo(f"- {ent['entity_type']} (Score: {ent['score']:.2f})")
|
| 36 |
-
|
| 37 |
-
except Exception as e:
|
| 38 |
-
click.echo(click.style(f"Error: {e}", fg="red"), err=True)
|
| 39 |
-
sys.exit(1)
|
| 40 |
-
|
| 41 |
-
@cli.command()
|
| 42 |
-
@click.argument('file', type=click.File('r'))
|
| 43 |
-
def redact_file(file):
|
| 44 |
-
"""Redact PII from a file."""
|
| 45 |
-
content = file.read()
|
| 46 |
-
try:
|
| 47 |
-
payload = {"text": content}
|
| 48 |
-
response = requests.post(f"{API_URL}/redact", json=payload)
|
| 49 |
-
response.raise_for_status()
|
| 50 |
-
result = response.json()
|
| 51 |
-
click.echo(result["redacted_text"])
|
| 52 |
-
except Exception as e:
|
| 53 |
-
click.echo(click.style(f"Error: {e}", fg="red"), err=True)
|
| 54 |
-
sys.exit(1)
|
| 55 |
-
|
| 56 |
-
if __name__ == "__main__":
|
| 57 |
-
cli()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docker-compose.yml
CHANGED
|
@@ -29,30 +29,5 @@ services:
|
|
| 29 |
- VITE_API_URL=http://localhost:8000
|
| 30 |
command: sh -c "npm install && npm run dev -- --host"
|
| 31 |
|
| 32 |
-
cli:
|
| 33 |
-
build:
|
| 34 |
-
context: ./cli
|
| 35 |
-
dockerfile: Dockerfile
|
| 36 |
-
volumes:
|
| 37 |
-
- ./cli:/app
|
| 38 |
-
environment:
|
| 39 |
-
- API_URL=http://api:8000
|
| 40 |
-
depends_on:
|
| 41 |
-
- api
|
| 42 |
-
|
| 43 |
-
cli-ts:
|
| 44 |
-
build:
|
| 45 |
-
context: ./cli-ts
|
| 46 |
-
dockerfile: Dockerfile
|
| 47 |
-
volumes:
|
| 48 |
-
- ./cli-ts:/app
|
| 49 |
-
- /app/node_modules
|
| 50 |
-
- /app/dist
|
| 51 |
-
environment:
|
| 52 |
-
- API_URL=http://api:8000
|
| 53 |
-
depends_on:
|
| 54 |
-
- api
|
| 55 |
-
command: sh -c "npm run build && node --no-warnings dist/index.js"
|
| 56 |
-
|
| 57 |
volumes:
|
| 58 |
spacy_data: # Ce volume conservera les modèles et les librairies installées
|
|
|
|
| 29 |
- VITE_API_URL=http://localhost:8000
|
| 30 |
command: sh -c "npm install && npm run dev -- --host"
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
volumes:
|
| 33 |
spacy_data: # Ce volume conservera les modèles et les librairies installées
|
ui/index.html
CHANGED
|
@@ -3,6 +3,9 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
|
|
|
|
|
|
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
<title>Redac_Scan</title>
|
| 8 |
</head>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
| 6 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 7 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 8 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:ital,wght@0,400;0,500;1,400&display=swap" rel="stylesheet">
|
| 9 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 10 |
<title>Redac_Scan</title>
|
| 11 |
</head>
|
ui/src/App.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
| 4 |
Shield, Eye, Lock, CheckCircle2, Copy,
|
| 5 |
Database, Languages, Fingerprint, Zap, Activity,
|
| 6 |
Palette, ChevronDown, Check, Radio, Target, Terminal,
|
| 7 |
-
Upload, Trash2
|
| 8 |
} from 'lucide-react';
|
| 9 |
|
| 10 |
interface EntityMeta {
|
|
@@ -25,8 +25,8 @@ interface RedactResponse {
|
|
| 25 |
type Theme = 'premium' | 'light' | 'dark';
|
| 26 |
|
| 27 |
const EXAMPLES = [
|
| 28 |
-
{ id: "
|
| 29 |
-
{ id: "MED-02", label: "
|
| 30 |
];
|
| 31 |
|
| 32 |
function App() {
|
|
@@ -71,56 +71,84 @@ function App() {
|
|
| 71 |
const response = await axios.post(`${API_URL}/redact`, { text, language });
|
| 72 |
setResult(response.data);
|
| 73 |
} catch (err: any) { console.error(err); }
|
| 74 |
-
finally { setTimeout(() => setLoading(false),
|
| 75 |
};
|
| 76 |
|
| 77 |
const themeClasses = {
|
| 78 |
-
premium: {
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
return (
|
| 86 |
-
<div className={`h-screen flex flex-col transition-
|
| 87 |
-
{theme !== 'light' && <div className="absolute inset-0 bg-grid pointer-events-none opacity-20" />}
|
| 88 |
|
| 89 |
{/* HEADER */}
|
| 90 |
-
<header className={`flex-none flex items-center justify-between px-
|
| 91 |
-
<div className="flex items-center gap-
|
| 92 |
-
<div className=
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
<
|
| 96 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
</div>
|
| 98 |
</div>
|
| 99 |
|
| 100 |
-
<div className="flex items-center gap-
|
| 101 |
-
{/* Theme Dropdown */}
|
| 102 |
<div className="relative" ref={themeRef}>
|
| 103 |
-
<button onClick={() => setIsThemeOpen(!isThemeOpen)} className={`flex items-center gap-2 px-
|
| 104 |
-
<Palette className="w-
|
| 105 |
</button>
|
| 106 |
{isThemeOpen && (
|
| 107 |
-
<div className={`absolute right-0 mt-2 w-48 ${theme === '
|
| 108 |
{['premium', 'light', 'dark'].map((t) => (
|
| 109 |
-
<button key={t} onClick={() => { setTheme(t as Theme); setIsThemeOpen(false); }} className={`w-full text-left px-4 py-2.5 text-
|
| 110 |
))}
|
| 111 |
</div>
|
| 112 |
)}
|
| 113 |
</div>
|
| 114 |
-
|
| 115 |
-
{/* Lang Dropdown */}
|
| 116 |
<div className="relative" ref={langRef}>
|
| 117 |
-
<button onClick={() => setIsLangOpen(!isLangOpen)} className={`flex items-center gap-2 px-
|
| 118 |
-
<Languages className="w-
|
| 119 |
</button>
|
| 120 |
{isLangOpen && (
|
| 121 |
-
<div className={`absolute right-0 mt-2 w-48 ${theme === '
|
| 122 |
{['auto', 'en', 'fr'].map((l) => (
|
| 123 |
-
<button key={l} onClick={() => { setLanguage(l); setIsLangOpen(false); }} className={`w-full text-left px-4 py-2.5 text-
|
| 124 |
))}
|
| 125 |
</div>
|
| 126 |
)}
|
|
@@ -128,82 +156,78 @@ function App() {
|
|
| 128 |
</div>
|
| 129 |
</header>
|
| 130 |
|
| 131 |
-
{/*
|
| 132 |
-
<div className={`flex-none flex items-center gap-4 px-6 py-2 bg-black/5 border-b ${theme === 'light' ? 'border-black' : 'border-cyan-500/10'}`}>
|
| 133 |
-
<span className="text-[8px] font-black uppercase tracking-[0.3em] opacity-40">Load_Protocol:</span>
|
| 134 |
-
<div className="flex gap-2">
|
| 135 |
-
{EXAMPLES.map((ex, i) => (
|
| 136 |
-
<button key={i} onClick={() => { setText(ex.text); setLanguage(ex.lang); setResult(null); }} className={`px-3 py-1 border ${theme === 'light' ? 'border-black hover:bg-black hover:text-white' : 'border-cyan-500/20 hover:border-cyan-500'} rounded text-[9px] font-bold transition-all uppercase`}>{ex.label}</button>
|
| 137 |
-
))}
|
| 138 |
-
</div>
|
| 139 |
-
</div>
|
| 140 |
-
|
| 141 |
-
{/* MAIN WORKSPACE - TRIPLE COLUMN HUD */}
|
| 142 |
<main className="flex-grow flex overflow-hidden">
|
| 143 |
|
| 144 |
-
{/*
|
| 145 |
-
<div className={`w-1/
|
| 146 |
-
<div className=
|
| 147 |
-
<div className="flex items-center gap-3
|
| 148 |
-
<div className="flex gap-
|
| 149 |
-
<button onClick={() => fileInputRef.current?.click()} className="p-1.5 hover:
|
| 150 |
-
<button onClick={() => {setText(''); setResult(null);}} className="p-1.5 hover:
|
| 151 |
-
<input type="file" ref={fileInputRef} onChange={(e) => {
|
| 152 |
</div>
|
| 153 |
</div>
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
</button>
|
| 164 |
</div>
|
| 165 |
-
</div>
|
| 166 |
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
|
|
|
| 175 |
</div>
|
| 176 |
</div>
|
| 177 |
|
| 178 |
-
{/*
|
| 179 |
-
<div className=
|
| 180 |
-
<div className=
|
| 181 |
-
<
|
| 182 |
-
<
|
| 183 |
</div>
|
| 184 |
-
|
|
|
|
| 185 |
{!result ? (
|
| 186 |
-
<div className="h-full flex items-center justify-center opacity-
|
| 187 |
-
|
| 188 |
-
|
|
|
|
| 189 |
) : (
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
<
|
| 193 |
-
<span className=
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
<div className={`w-full h-1 ${theme === 'dark' ? 'bg-slate-800' : 'bg-slate-100'} rounded-full overflow-hidden`}><div className="h-full bg-cyan-500 shadow-[0_0_10px_#00f2ff]" style={{ width: `${ent.score}%` }} /></div>
|
| 198 |
-
</div>
|
| 199 |
-
))
|
| 200 |
)}
|
| 201 |
</div>
|
|
|
|
|
|
|
| 202 |
{result && (
|
| 203 |
-
<div className=
|
| 204 |
-
<div className="flex items-center
|
| 205 |
-
|
| 206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
</div>
|
| 208 |
</div>
|
| 209 |
)}
|
|
|
|
| 4 |
Shield, Eye, Lock, CheckCircle2, Copy,
|
| 5 |
Database, Languages, Fingerprint, Zap, Activity,
|
| 6 |
Palette, ChevronDown, Check, Radio, Target, Terminal,
|
| 7 |
+
Upload, Trash2
|
| 8 |
} from 'lucide-react';
|
| 9 |
|
| 10 |
interface EntityMeta {
|
|
|
|
| 25 |
type Theme = 'premium' | 'light' | 'dark';
|
| 26 |
|
| 27 |
const EXAMPLES = [
|
| 28 |
+
{ id: "PRO-01", label: "Procès Verbal", lang: "fr", text: `PROCÈS-VERBAL DE RÉUNION DE CHANTIER - RÉNOVATION COMPLEXE HÔTELIER\n\nDate : 20 Mars 2026\nLieu : 142 Avenue des Champs-Élysées, 75008 Paris.\n\nPRÉSENTS :\n- M. Alexandre de La Rochefoucauld (Directeur de projet, Groupe Immobilier "Lux-Horizon" - SIRET 321 654 987 00054).\n- Mme Valérie Marchand (Architecte, Cabinet "Marchand & Associés").\n- M. Thomas Dubois (Ingénieur sécurité, joignable au 06.45.12.89.33).\n\nORDRE DU JOUR :\nValidation des acomptes sur l'IBAN FR76 3000 1000 2000 3000 4000 500.` },
|
| 29 |
+
{ id: "MED-02", label: "Medical Record", lang: "en", text: `CLINICAL DISCHARGE SUMMARY - PATIENT ID: #XP-99021\n\nPATIENT INFORMATION:\nName: Sarah-Jane Montgomery\nDOB: 12/05/1982\nAddress: 1244 North Oak Street, San Francisco, CA 94102\nSSN : 123-45-6789. Email: sj.montgomery@provider.net.` }
|
| 30 |
];
|
| 31 |
|
| 32 |
function App() {
|
|
|
|
| 71 |
const response = await axios.post(`${API_URL}/redact`, { text, language });
|
| 72 |
setResult(response.data);
|
| 73 |
} catch (err: any) { console.error(err); }
|
| 74 |
+
finally { setTimeout(() => setLoading(false), 400); }
|
| 75 |
};
|
| 76 |
|
| 77 |
const themeClasses = {
|
| 78 |
+
premium: {
|
| 79 |
+
body: 'bg-slate-50',
|
| 80 |
+
header: 'bg-white border-slate-200 text-slate-900',
|
| 81 |
+
panel: 'bg-white border-slate-200',
|
| 82 |
+
panelHeader: 'bg-slate-50 text-slate-500',
|
| 83 |
+
input: 'bg-white text-slate-900 placeholder-slate-300',
|
| 84 |
+
output: 'bg-slate-50 text-slate-800',
|
| 85 |
+
tag: 'bg-blue-600 text-white shadow-sm',
|
| 86 |
+
footer: 'bg-slate-100/50 border-slate-200',
|
| 87 |
+
btn: 'bg-slate-900 hover:bg-black text-white'
|
| 88 |
+
},
|
| 89 |
+
light: {
|
| 90 |
+
body: 'bg-white',
|
| 91 |
+
header: 'bg-white border-black text-black',
|
| 92 |
+
panel: 'bg-white border-black',
|
| 93 |
+
panelHeader: 'bg-white border-b-black text-black',
|
| 94 |
+
input: 'bg-white text-black placeholder-gray-300',
|
| 95 |
+
output: 'bg-white text-black',
|
| 96 |
+
tag: 'bg-black text-white rounded-none border border-white',
|
| 97 |
+
footer: 'bg-white border-black',
|
| 98 |
+
btn: 'bg-black hover:bg-zinc-800 text-white'
|
| 99 |
+
},
|
| 100 |
+
dark: {
|
| 101 |
+
body: 'bg-[#020617]',
|
| 102 |
+
header: 'bg-slate-900/50 border-slate-800 text-white',
|
| 103 |
+
panel: 'bg-slate-900/30 border-slate-800',
|
| 104 |
+
panelHeader: 'bg-slate-900/50 text-slate-400',
|
| 105 |
+
input: 'bg-transparent text-white placeholder-slate-700',
|
| 106 |
+
output: 'bg-black/20 text-blue-400',
|
| 107 |
+
tag: 'bg-blue-500 text-black font-black',
|
| 108 |
+
footer: 'bg-black/40 border-slate-800',
|
| 109 |
+
btn: 'bg-blue-600 hover:bg-blue-500 text-white shadow-blue-500/20'
|
| 110 |
+
}
|
| 111 |
+
}[theme];
|
| 112 |
|
| 113 |
return (
|
| 114 |
+
<div className={`h-screen flex flex-col font-sans transition-colors duration-300 ${themeClasses.body} ${themeClasses.header.split(' ')[2]} overflow-hidden`}>
|
|
|
|
| 115 |
|
| 116 |
{/* HEADER */}
|
| 117 |
+
<header className={`flex-none flex items-center justify-between px-8 py-4 border-b ${themeClasses.header} z-50`}>
|
| 118 |
+
<div className="flex items-center gap-6">
|
| 119 |
+
<div className="flex items-center gap-3">
|
| 120 |
+
<div className={`p-2 rounded-xl ${theme === 'dark' ? 'bg-blue-600' : 'bg-black'} text-white shadow-lg`}>
|
| 121 |
+
<Shield className="w-5 h-5" />
|
| 122 |
+
</div>
|
| 123 |
+
<h1 className="text-xl font-bold tracking-tight">RedacScan</h1>
|
| 124 |
+
</div>
|
| 125 |
+
<div className="hidden sm:flex items-center gap-2 ml-4 opacity-50">
|
| 126 |
+
<div className={`w-2 h-2 rounded-full ${apiStatus === 'online' ? 'bg-emerald-500' : 'bg-rose-500'}`} />
|
| 127 |
+
<span className="text-[10px] font-bold uppercase tracking-widest">{apiStatus}</span>
|
| 128 |
</div>
|
| 129 |
</div>
|
| 130 |
|
| 131 |
+
<div className="flex items-center gap-3">
|
|
|
|
| 132 |
<div className="relative" ref={themeRef}>
|
| 133 |
+
<button onClick={() => setIsThemeOpen(!isThemeOpen)} className={`flex items-center gap-2 px-4 py-2 border rounded-full text-xs font-semibold transition-all ${themeClasses.panel} hover:border-blue-500`}>
|
| 134 |
+
<Palette className="w-4 h-4" /> <span>{theme}</span>
|
| 135 |
</button>
|
| 136 |
{isThemeOpen && (
|
| 137 |
+
<div className={`absolute right-0 mt-2 w-48 p-2 rounded-2xl shadow-2xl z-[100] border ${themeClasses.panel} ${theme === 'dark' ? 'bg-slate-900' : 'bg-white'}`}>
|
| 138 |
{['premium', 'light', 'dark'].map((t) => (
|
| 139 |
+
<button key={t} onClick={() => { setTheme(t as Theme); setIsThemeOpen(false); }} className={`w-full text-left px-4 py-2.5 text-xs font-medium rounded-xl transition-colors ${theme === t ? 'bg-blue-600 text-white' : 'hover:bg-slate-100 dark:hover:bg-slate-800'}`}>{t}</button>
|
| 140 |
))}
|
| 141 |
</div>
|
| 142 |
)}
|
| 143 |
</div>
|
|
|
|
|
|
|
| 144 |
<div className="relative" ref={langRef}>
|
| 145 |
+
<button onClick={() => setIsLangOpen(!isLangOpen)} className={`flex items-center gap-2 px-4 py-2 border rounded-full text-xs font-semibold transition-all ${themeClasses.panel} hover:border-blue-500`}>
|
| 146 |
+
<Languages className="w-4 h-4" /> <span>{language}</span>
|
| 147 |
</button>
|
| 148 |
{isLangOpen && (
|
| 149 |
+
<div className={`absolute right-0 mt-2 w-48 p-2 rounded-2xl shadow-2xl z-[100] border ${themeClasses.panel} ${theme === 'dark' ? 'bg-slate-900' : 'bg-white'}`}>
|
| 150 |
{['auto', 'en', 'fr'].map((l) => (
|
| 151 |
+
<button key={l} onClick={() => { setLanguage(l); setIsLangOpen(false); }} className={`w-full text-left px-4 py-2.5 text-xs font-medium rounded-xl transition-colors ${language === l ? 'bg-blue-600 text-white' : 'hover:bg-slate-100 dark:hover:bg-slate-800'}`}>{l}</button>
|
| 152 |
))}
|
| 153 |
</div>
|
| 154 |
)}
|
|
|
|
| 156 |
</div>
|
| 157 |
</header>
|
| 158 |
|
| 159 |
+
{/* MAIN WORKSPACE */}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
<main className="flex-grow flex overflow-hidden">
|
| 161 |
|
| 162 |
+
{/* PANEL 1: SOURCE */}
|
| 163 |
+
<div className={`w-1/2 flex flex-col border-r ${themeClasses.panel.split(' ')[1]}`}>
|
| 164 |
+
<div className={`flex-none flex items-center justify-between px-8 py-4 border-b ${themeClasses.panelHeader}`}>
|
| 165 |
+
<div className="flex items-center gap-3 text-xs font-bold uppercase tracking-widest"><Database className="w-4 h-4 text-blue-600" /> Source Document</div>
|
| 166 |
+
<div className="flex gap-3">
|
| 167 |
+
<button onClick={() => fileInputRef.current?.click()} className="p-1.5 rounded-lg hover:bg-black/10 transition-colors"><Upload className="w-4 h-4" /></button>
|
| 168 |
+
<button onClick={() => {setText(''); setResult(null);}} className="p-1.5 rounded-lg hover:bg-black/10 transition-colors text-rose-500"><Trash2 className="w-4 h-4" /></button>
|
| 169 |
+
<input type="file" ref={fileInputRef} onChange={(e) => {const f=e.target.files?.[0]; if(f){const r=new FileReader(); r.onload=(ev)=>setText(ev.target?.result as string); r.readAsText(f);}}} className="hidden" />
|
| 170 |
</div>
|
| 171 |
</div>
|
| 172 |
+
|
| 173 |
+
<div className="flex-grow relative overflow-hidden bg-inherit">
|
| 174 |
+
{loading && <div className="loading-progress"><div className="loading-progress-bar" /></div>}
|
| 175 |
+
<textarea
|
| 176 |
+
className={`w-full h-full p-10 bg-transparent border-none outline-none text-[16px] leading-[1.8] resize-none font-sans custom-scrollbar ${themeClasses.input}`}
|
| 177 |
+
placeholder="Enter your document content here..."
|
| 178 |
+
value={text}
|
| 179 |
+
onChange={(e) => setText(e.target.value)}
|
| 180 |
+
/>
|
|
|
|
| 181 |
</div>
|
|
|
|
| 182 |
|
| 183 |
+
<div className={`flex-none p-8 border-t ${themeClasses.footer}`}>
|
| 184 |
+
<div className="flex gap-4 mb-4 overflow-x-auto pb-2 scrollbar-hide">
|
| 185 |
+
{EXAMPLES.map((ex, i) => (
|
| 186 |
+
<button key={i} onClick={() => { setText(ex.text); setLanguage(ex.lang); setResult(null); }} className={`px-4 py-2 border rounded-xl text-xs font-bold transition-all whitespace-nowrap ${theme === 'light' ? 'border-black hover:bg-black hover:text-white' : 'border-slate-200/20 hover:border-slate-400 bg-white/5'}`}>{ex.label}</button>
|
| 187 |
+
))}
|
| 188 |
+
</div>
|
| 189 |
+
<button onClick={handleRedact} disabled={loading || !text.trim()} className={`w-full py-5 rounded-2xl font-bold text-sm tracking-widest uppercase transition-all flex items-center justify-center gap-4 ${loading || !text.trim() ? 'bg-slate-200 text-slate-400 cursor-not-allowed' : `${themeClasses.btn} shadow-xl active:scale-[0.98]`}`}>
|
| 190 |
+
{loading ? 'ANALYZING DOCUMENT...' : <><Zap className="w-4 h-4 fill-white" /> SANITIZE CONTENT</>}
|
| 191 |
+
</button>
|
| 192 |
</div>
|
| 193 |
</div>
|
| 194 |
|
| 195 |
+
{/* PANEL 2: RESULT */}
|
| 196 |
+
<div className={`w-1/2 flex flex-col ${themeClasses.output.split(' ')[0]}`}>
|
| 197 |
+
<div className={`flex-none flex items-center justify-between px-8 py-4 border-b ${themeClasses.panelHeader}`}>
|
| 198 |
+
<div className="flex items-center gap-3 text-xs font-bold uppercase tracking-widest text-blue-600"><CheckCircle2 className="w-4 h-4" /> Secured View</div>
|
| 199 |
+
{result && <button onClick={() => {navigator.clipboard.writeText(result.redacted_text); setCopied(true); setTimeout(()=>setCopied(false), 2000)}} className={`px-4 py-1.5 rounded-full ${themeClasses.btn} text-[10px] font-bold transition-all`}>{copied ? 'COPIED' : 'COPY RESULT'}</button>}
|
| 200 |
</div>
|
| 201 |
+
|
| 202 |
+
<div className={`flex-grow p-10 font-sans text-[16px] leading-[1.8] whitespace-pre-wrap overflow-y-auto custom-scrollbar ${themeClasses.output.split(' ')[1]}`}>
|
| 203 |
{!result ? (
|
| 204 |
+
<div className="h-full flex flex-col items-center justify-center opacity-20">
|
| 205 |
+
<Lock className="w-16 h-16 mb-4 stroke-1" />
|
| 206 |
+
<p className="font-bold tracking-widest uppercase text-xs">Waiting for Sanitization</p>
|
| 207 |
+
</div>
|
| 208 |
) : (
|
| 209 |
+
<div className="animate-in fade-in duration-500">
|
| 210 |
+
{result.redacted_text.split(/(<[^>]+>)/g).map((part, i) => (
|
| 211 |
+
part.startsWith('<') && part.endsWith('>') ? (
|
| 212 |
+
<span key={i} className={`inline-block px-2 py-0.5 mx-1 rounded font-bold text-[12px] uppercase tracking-tighter ${themeClasses.tag}`}>{part}</span>
|
| 213 |
+
) : part
|
| 214 |
+
))}
|
| 215 |
+
</div>
|
|
|
|
|
|
|
|
|
|
| 216 |
)}
|
| 217 |
</div>
|
| 218 |
+
|
| 219 |
+
{/* RISK LIST FOOTER */}
|
| 220 |
{result && (
|
| 221 |
+
<div className={`flex-none h-48 border-t p-6 overflow-y-auto custom-scrollbar ${themeClasses.footer}`}>
|
| 222 |
+
<div className="flex items-center gap-3 mb-4 opacity-60 font-bold text-[10px] uppercase tracking-widest"><Fingerprint className="w-4 h-4" /> Detected Information</div>
|
| 223 |
+
<div className="flex flex-wrap gap-2">
|
| 224 |
+
{result.entities.map((ent, idx) => (
|
| 225 |
+
<div key={idx} className={`px-3 py-2 rounded-xl border flex items-center gap-3 bg-white shadow-sm border-slate-200 dark:bg-slate-900 dark:border-slate-800`}>
|
| 226 |
+
<span className="text-[9px] font-black text-blue-600 uppercase">{ent.type}</span>
|
| 227 |
+
<span className={`text-xs font-bold truncate max-w-[120px] ${theme === 'dark' ? 'text-white' : 'text-black'}`}>"{ent.text}"</span>
|
| 228 |
+
<span className="text-[10px] font-bold text-slate-400">{ent.score}%</span>
|
| 229 |
+
</div>
|
| 230 |
+
))}
|
| 231 |
</div>
|
| 232 |
</div>
|
| 233 |
)}
|
ui/src/index.css
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
@import "tailwindcss";
|
| 2 |
|
| 3 |
@theme {
|
|
|
|
|
|
|
| 4 |
--color-cyber-blue: #00f2ff;
|
| 5 |
--color-cyber-pink: #ff00e5;
|
| 6 |
--color-cyber-dark: #050a15;
|
|
@@ -8,20 +10,19 @@
|
|
| 8 |
|
| 9 |
@layer base {
|
| 10 |
body {
|
| 11 |
-
@apply
|
| 12 |
}
|
| 13 |
}
|
| 14 |
|
| 15 |
-
/* Grille
|
| 16 |
.bg-grid {
|
| 17 |
background-image:
|
| 18 |
linear-gradient(to right, rgba(0, 242, 255, 0.05) 1px, transparent 1px),
|
| 19 |
linear-gradient(to bottom, rgba(0, 242, 255, 0.05) 1px, transparent 1px);
|
| 20 |
background-size: 40px 40px;
|
| 21 |
-
mask-image: radial-gradient(circle at center, black, transparent 80%);
|
| 22 |
}
|
| 23 |
|
| 24 |
-
/*
|
| 25 |
@keyframes scan {
|
| 26 |
0% { transform: translateY(-100%); opacity: 0; }
|
| 27 |
50% { opacity: 1; }
|
|
@@ -30,38 +31,12 @@
|
|
| 30 |
|
| 31 |
.scanner-line {
|
| 32 |
height: 2px;
|
| 33 |
-
background: linear-gradient(90deg, transparent,
|
| 34 |
-
box-shadow: 0 0 15px
|
| 35 |
-
animation: scan
|
| 36 |
-
display: none;
|
| 37 |
}
|
| 38 |
|
| 39 |
-
|
| 40 |
-
display: block;
|
| 41 |
-
}
|
| 42 |
-
|
| 43 |
-
/* Glassmorphism */
|
| 44 |
-
.glass-panel {
|
| 45 |
-
background: rgba(15, 23, 42, 0.6);
|
| 46 |
-
backdrop-filter: blur(12px);
|
| 47 |
-
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
.neon-border {
|
| 51 |
-
position: relative;
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
.neon-border::after {
|
| 55 |
-
content: '';
|
| 56 |
-
position: absolute;
|
| 57 |
-
inset: -1px;
|
| 58 |
-
background: linear-gradient(45deg, var(--color-cyber-blue), transparent, var(--color-cyber-pink));
|
| 59 |
-
z-index: -1;
|
| 60 |
-
border-radius: inherit;
|
| 61 |
-
opacity: 0.3;
|
| 62 |
-
}
|
| 63 |
-
|
| 64 |
-
/* Custom Scrollbar futuriste */
|
| 65 |
::-webkit-scrollbar {
|
| 66 |
width: 4px;
|
| 67 |
}
|
|
@@ -69,7 +44,10 @@
|
|
| 69 |
background: transparent;
|
| 70 |
}
|
| 71 |
::-webkit-scrollbar-thumb {
|
| 72 |
-
background:
|
| 73 |
border-radius: 10px;
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
| 75 |
}
|
|
|
|
| 1 |
@import "tailwindcss";
|
| 2 |
|
| 3 |
@theme {
|
| 4 |
+
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
|
| 5 |
+
--font-mono: 'JetBrains Mono', monospace;
|
| 6 |
--color-cyber-blue: #00f2ff;
|
| 7 |
--color-cyber-pink: #ff00e5;
|
| 8 |
--color-cyber-dark: #050a15;
|
|
|
|
| 10 |
|
| 11 |
@layer base {
|
| 12 |
body {
|
| 13 |
+
@apply antialiased overflow-hidden h-screen;
|
| 14 |
}
|
| 15 |
}
|
| 16 |
|
| 17 |
+
/* Grille Cyber */
|
| 18 |
.bg-grid {
|
| 19 |
background-image:
|
| 20 |
linear-gradient(to right, rgba(0, 242, 255, 0.05) 1px, transparent 1px),
|
| 21 |
linear-gradient(to bottom, rgba(0, 242, 255, 0.05) 1px, transparent 1px);
|
| 22 |
background-size: 40px 40px;
|
|
|
|
| 23 |
}
|
| 24 |
|
| 25 |
+
/* Scanner Laser */
|
| 26 |
@keyframes scan {
|
| 27 |
0% { transform: translateY(-100%); opacity: 0; }
|
| 28 |
50% { opacity: 1; }
|
|
|
|
| 31 |
|
| 32 |
.scanner-line {
|
| 33 |
height: 2px;
|
| 34 |
+
background: linear-gradient(90deg, transparent, #00f2ff, transparent);
|
| 35 |
+
box-shadow: 0 0 15px #00f2ff;
|
| 36 |
+
animation: scan 2.5s linear infinite;
|
|
|
|
| 37 |
}
|
| 38 |
|
| 39 |
+
/* Scrollbar Style */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
::-webkit-scrollbar {
|
| 41 |
width: 4px;
|
| 42 |
}
|
|
|
|
| 44 |
background: transparent;
|
| 45 |
}
|
| 46 |
::-webkit-scrollbar-thumb {
|
| 47 |
+
background: #00f2ff;
|
| 48 |
border-radius: 10px;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.custom-scrollbar-light::-webkit-scrollbar-thumb {
|
| 52 |
+
background: #000000;
|
| 53 |
}
|