Compare commits
3 commits
df06be7bc7
...
f75c054e65
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f75c054e65 | ||
|
|
35983681e4 | ||
|
|
e04c67133b |
20
.beads/.gitignore
vendored
Normal file
20
.beads/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# SQLite databases
|
||||||
|
*.db
|
||||||
|
*.db-journal
|
||||||
|
*.db-wal
|
||||||
|
*.db-shm
|
||||||
|
|
||||||
|
# Daemon runtime files
|
||||||
|
daemon.lock
|
||||||
|
daemon.log
|
||||||
|
daemon.pid
|
||||||
|
bd.sock
|
||||||
|
|
||||||
|
# Legacy database files
|
||||||
|
db.sqlite
|
||||||
|
bd.db
|
||||||
|
|
||||||
|
# Keep JSONL exports and config (source of truth for git)
|
||||||
|
!*.jsonl
|
||||||
|
!metadata.json
|
||||||
|
!config.json
|
||||||
56
.beads/config.yaml
Normal file
56
.beads/config.yaml
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Beads Configuration File
|
||||||
|
# This file configures default behavior for all bd commands in this repository
|
||||||
|
# All settings can also be set via environment variables (BD_* prefix)
|
||||||
|
# or overridden with command-line flags
|
||||||
|
|
||||||
|
# Issue prefix for this repository (used by bd init)
|
||||||
|
# If not set, bd init will auto-detect from directory name
|
||||||
|
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
|
||||||
|
# issue-prefix: ""
|
||||||
|
|
||||||
|
# Use no-db mode: load from JSONL, no SQLite, write back after each command
|
||||||
|
# When true, bd will use .beads/issues.jsonl as the source of truth
|
||||||
|
# instead of SQLite database
|
||||||
|
# no-db: false
|
||||||
|
|
||||||
|
# Disable daemon for RPC communication (forces direct database access)
|
||||||
|
# no-daemon: false
|
||||||
|
|
||||||
|
# Disable auto-flush of database to JSONL after mutations
|
||||||
|
# no-auto-flush: false
|
||||||
|
|
||||||
|
# Disable auto-import from JSONL when it's newer than database
|
||||||
|
# no-auto-import: false
|
||||||
|
|
||||||
|
# Enable JSON output by default
|
||||||
|
# json: false
|
||||||
|
|
||||||
|
# Default actor for audit trails (overridden by BD_ACTOR or --actor)
|
||||||
|
# actor: ""
|
||||||
|
|
||||||
|
# Path to database (overridden by BEADS_DB or --db)
|
||||||
|
# db: ""
|
||||||
|
|
||||||
|
# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON)
|
||||||
|
# auto-start-daemon: true
|
||||||
|
|
||||||
|
# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE)
|
||||||
|
# flush-debounce: "5s"
|
||||||
|
|
||||||
|
# Multi-repo configuration (experimental - bd-307)
|
||||||
|
# Allows hydrating from multiple repositories and routing writes to the correct JSONL
|
||||||
|
# repos:
|
||||||
|
# primary: "." # Primary repo (where this database lives)
|
||||||
|
# additional: # Additional repos to hydrate from (read-only)
|
||||||
|
# - ~/beads-planning # Personal planning repo
|
||||||
|
# - ~/work-planning # Work planning repo
|
||||||
|
|
||||||
|
# Integration settings (access with 'bd config get/set')
|
||||||
|
# These are stored in the database, not in this file:
|
||||||
|
# - jira.url
|
||||||
|
# - jira.project
|
||||||
|
# - linear.url
|
||||||
|
# - linear.api-key
|
||||||
|
# - github.org
|
||||||
|
# - github.repo
|
||||||
|
# - sync.branch - Git branch for beads commits (use BEADS_SYNC_BRANCH env var or bd config set)
|
||||||
5
.beads/metadata.json
Normal file
5
.beads/metadata.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"database": "beads.db",
|
||||||
|
"version": "0.23.0",
|
||||||
|
"jsonl_export": "beads.jsonl"
|
||||||
|
}
|
||||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
# Use bd merge for beads JSONL files
|
||||||
|
.beads/beads.jsonl merge=beads
|
||||||
95
DIFF_VIEWER_IMPLEMENTATION.md
Normal file
95
DIFF_VIEWER_IMPLEMENTATION.md
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
# Git Diff Viewer Implementation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
The Git Diff Viewer feature has been successfully implemented for FleetCode's web mode. This feature allows users to view changes in their session's worktree compared to the parent branch.
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### 1. Dependencies
|
||||||
|
- **@pierre/precision-diffs**: A powerful diff rendering library
|
||||||
|
- **shiki**: Syntax highlighting for code diffs
|
||||||
|
|
||||||
|
### 2. Data Structure (types.ts)
|
||||||
|
Added `FileDiff` interface to represent file changes:
|
||||||
|
```typescript
|
||||||
|
export interface FileDiff {
|
||||||
|
path: string;
|
||||||
|
oldPath?: string;
|
||||||
|
status: 'added' | 'modified' | 'deleted' | 'renamed';
|
||||||
|
diff: string;
|
||||||
|
additions: number;
|
||||||
|
deletions: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Backend (server.ts)
|
||||||
|
- Added Socket.IO handler `get-session-diff` that:
|
||||||
|
- Retrieves the session's worktree path and parent branch
|
||||||
|
- Executes `git diff <parent-branch>...HEAD` using simple-git
|
||||||
|
- Parses the diff output into `FileDiff` objects
|
||||||
|
- Emits the parsed data back to the client via `session-diff-data` event
|
||||||
|
|
||||||
|
### 4. Frontend UI (index.html)
|
||||||
|
- Added full-screen diff viewer modal with:
|
||||||
|
- Header with title and close button
|
||||||
|
- Left sidebar for file list
|
||||||
|
- Right panel for displaying diffs
|
||||||
|
- Added "Diff" button to active session items in the sidebar
|
||||||
|
|
||||||
|
### 5. Frontend Styling (styles.css)
|
||||||
|
- Added comprehensive styling for:
|
||||||
|
- Diff viewer modal (full-screen, responsive)
|
||||||
|
- File list sidebar with status badges
|
||||||
|
- Diff display area
|
||||||
|
- File status indicators (added/modified/deleted/renamed)
|
||||||
|
- Addition/deletion statistics
|
||||||
|
- Mobile-responsive layouts
|
||||||
|
|
||||||
|
### 6. Frontend Logic (renderer.ts)
|
||||||
|
- Added diff viewer functionality:
|
||||||
|
- `openDiffViewer(sessionId)`: Opens modal and requests diff data
|
||||||
|
- `renderDiffFileList()`: Renders list of changed files
|
||||||
|
- `renderSelectedDiff()`: Renders the selected file's diff using Pierre.js
|
||||||
|
- Dynamic import of libraries to avoid breaking Electron mode
|
||||||
|
- Event handlers for Socket.IO events (`session-diff-data`, `session-diff-error`)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Start a session and make some changes in your worktree
|
||||||
|
2. Click the "Diff" button next to an active session in the sidebar
|
||||||
|
3. A modal will appear showing all changed files
|
||||||
|
4. Click on any file to view its detailed diff
|
||||||
|
5. Click the × button or outside the modal to close
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
- Libraries (@pierre/precision-diffs and shiki) are loaded dynamically using `import()` to avoid conflicts with Electron's module system
|
||||||
|
- The diff is computed between the parent branch and HEAD of the worktree
|
||||||
|
- TypeScript configuration was updated to use ES2020 modules with bundler resolution for better ESM support
|
||||||
|
- The implementation is fully functional in web mode (server.ts) and does not affect Electron mode (main.ts)
|
||||||
|
|
||||||
|
## Build & Run
|
||||||
|
|
||||||
|
1. **Build the application:**
|
||||||
|
```bash
|
||||||
|
npm run build:all
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Start the server:**
|
||||||
|
```bash
|
||||||
|
npm run server
|
||||||
|
# or for development with hot reload:
|
||||||
|
npm run dev:server
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Access the application:**
|
||||||
|
Open http://localhost:3000 in your browser
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
The implementation has been verified:
|
||||||
|
- ✅ Dependencies installed: `@pierre/precision-diffs` and `shiki`
|
||||||
|
- ✅ Server builds and runs successfully
|
||||||
|
- ✅ Diff handler is present in compiled server.js
|
||||||
|
- ✅ Diff libraries are bundled in renderer.web.js
|
||||||
|
- ✅ All TypeScript compiles without errors
|
||||||
58
flake.lock
58
flake.lock
|
|
@ -1,5 +1,27 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"beads": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1762602968,
|
||||||
|
"narHash": "sha256-3F7sv4mAKBQ4YBSABgp9bendgHopObNivO5NQhJYUek=",
|
||||||
|
"owner": "steveyegge",
|
||||||
|
"repo": "beads",
|
||||||
|
"rev": "367bf077e10695976480bba097e2ae47593a4d82",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "steveyegge",
|
||||||
|
"ref": "v0.23.0",
|
||||||
|
"repo": "beads",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
|
|
@ -18,6 +40,24 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1760038930,
|
"lastModified": 1760038930,
|
||||||
|
|
@ -36,7 +76,8 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"beads": "beads",
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -54,6 +95,21 @@
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,13 @@
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
beads = {
|
||||||
|
url = "github:steveyegge/beads/v0.23.0";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
outputs = { self, nixpkgs, flake-utils, beads }:
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
@ -25,6 +29,9 @@
|
||||||
(python3.withPackages (ps: with ps; [ setuptools ]))
|
(python3.withPackages (ps: with ps; [ setuptools ]))
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
||||||
|
# Beads - AI code review tool
|
||||||
|
beads.packages.${system}.default
|
||||||
|
|
||||||
# Native build dependencies for node-pty and Electron
|
# Native build dependencies for node-pty and Electron
|
||||||
gcc
|
gcc
|
||||||
gnumake
|
gnumake
|
||||||
|
|
|
||||||
31
index.html
31
index.html
|
|
@ -196,6 +196,37 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Diff Viewer Modal -->
|
||||||
|
<div id="diff-viewer-modal" class="modal-overlay hidden">
|
||||||
|
<div class="diff-viewer-modal">
|
||||||
|
<div class="diff-viewer-header">
|
||||||
|
<h2 class="modal-title" id="diff-viewer-title">Git Diff Viewer</h2>
|
||||||
|
<div class="diff-viewer-actions">
|
||||||
|
<button id="merge-to-parent-btn" class="btn-merge" disabled title="Merge worktree branch into parent">
|
||||||
|
Merge to Parent
|
||||||
|
</button>
|
||||||
|
<button id="close-diff-viewer" class="diff-viewer-close" title="Close">×</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="diff-viewer-content">
|
||||||
|
<!-- File List Sidebar -->
|
||||||
|
<div class="diff-file-list" id="diff-file-list">
|
||||||
|
<button id="toggle-diff-sidebar" class="diff-sidebar-toggle">
|
||||||
|
<span class="toggle-expand">>></span>
|
||||||
|
<span class="toggle-collapse"><<</span>
|
||||||
|
</button>
|
||||||
|
<div class="diff-loading">Loading diff...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Diff Display -->
|
||||||
|
<div class="diff-display" id="diff-display">
|
||||||
|
<div class="diff-empty">Select a file to view changes</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Settings Modal -->
|
<!-- Settings Modal -->
|
||||||
<div id="settings-modal" class="modal-overlay hidden">
|
<div id="settings-modal" class="modal-overlay hidden">
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
|
|
|
||||||
591
package-lock.json
generated
591
package-lock.json
generated
|
|
@ -9,6 +9,7 @@
|
||||||
"version": "1.0.1-beta.7",
|
"version": "1.0.1-beta.7",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pierre/precision-diffs": "^0.4.2",
|
||||||
"@types/cors": "^2.8.19",
|
"@types/cors": "^2.8.19",
|
||||||
"@types/express": "^5.0.3",
|
"@types/express": "^5.0.3",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
|
|
@ -16,6 +17,7 @@
|
||||||
"electron-store": "^11.0.0",
|
"electron-store": "^11.0.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.0.0",
|
||||||
|
"shiki": "^3.14.0",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.28.0",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
|
|
@ -1926,6 +1928,33 @@
|
||||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pierre/precision-diffs": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pierre/precision-diffs/-/precision-diffs-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-C6LbruH24BCp4awI47D5iMtVaZZD6GkzqBoDw+Sfu7DB3hC9y/rZr1C2BD7AUzAKwByTfFnh16Zp11ipfPqLKw==",
|
||||||
|
"license": "apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/core": "3.14.0",
|
||||||
|
"@shikijs/transformers": "3.14.0",
|
||||||
|
"diff": "8.0.2",
|
||||||
|
"fast-deep-equal": "3.1.3",
|
||||||
|
"hast-util-to-html": "9.0.5",
|
||||||
|
"shiki": "3.14.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.3.1 || ^19.0.0",
|
||||||
|
"react-dom": "^18.3.1 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@pierre/precision-diffs/node_modules/diff": {
|
||||||
|
"version": "8.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz",
|
||||||
|
"integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
|
|
@ -1937,6 +1966,83 @@
|
||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@shikijs/core": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.14.0",
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2",
|
||||||
|
"@types/hast": "^3.0.4",
|
||||||
|
"hast-util-to-html": "^9.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/engine-javascript": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.14.0",
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2",
|
||||||
|
"oniguruma-to-es": "^4.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/engine-oniguruma": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.14.0",
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/langs": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/themes": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/types": "3.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/transformers": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-i67zQnY9wLMMnKasonVW1L9fKneSLZDj1ePsA4o0AZWU4uUobmJY9baRDa36z+a9/g0aG76/2tybQvm4hrwxIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/core": "3.14.0",
|
||||||
|
"@shikijs/types": "3.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/types": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2",
|
||||||
|
"@types/hast": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@shikijs/vscode-textmate": {
|
||||||
|
"version": "10.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
|
||||||
|
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@sindresorhus/is": {
|
"node_modules/@sindresorhus/is": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
||||||
|
|
@ -2091,6 +2197,15 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/hast": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/http-cache-semantics": {
|
"node_modules/@types/http-cache-semantics": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
|
||||||
|
|
@ -2114,6 +2229,15 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/mdast": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||||
|
|
@ -2200,6 +2324,12 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/unist": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/uuid": {
|
"node_modules/@types/uuid": {
|
||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||||
|
|
@ -2226,6 +2356,12 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ungap/structured-clone": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.11",
|
"version": "0.8.11",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
||||||
|
|
@ -3258,6 +3394,16 @@
|
||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/ccount": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
|
@ -3275,6 +3421,26 @@
|
||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/character-entities-html4": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/character-entities-legacy": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
|
|
@ -3481,6 +3647,16 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/comma-separated-tokens": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
|
|
@ -3934,6 +4110,15 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dequal": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz",
|
||||||
|
|
@ -3952,6 +4137,19 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/devlop": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dequal": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/didyoumean": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
|
|
@ -5496,6 +5694,42 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hast-util-to-html": {
|
||||||
|
"version": "9.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
|
||||||
|
"integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hast": "^3.0.0",
|
||||||
|
"@types/unist": "^3.0.0",
|
||||||
|
"ccount": "^2.0.0",
|
||||||
|
"comma-separated-tokens": "^2.0.0",
|
||||||
|
"hast-util-whitespace": "^3.0.0",
|
||||||
|
"html-void-elements": "^3.0.0",
|
||||||
|
"mdast-util-to-hast": "^13.0.0",
|
||||||
|
"property-information": "^7.0.0",
|
||||||
|
"space-separated-tokens": "^2.0.0",
|
||||||
|
"stringify-entities": "^4.0.0",
|
||||||
|
"zwitch": "^2.0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hast-util-whitespace": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hast": "^3.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hosted-git-info": {
|
"node_modules/hosted-git-info": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
|
||||||
|
|
@ -5522,6 +5756,16 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-void-elements": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/http-cache-semantics": {
|
"node_modules/http-cache-semantics": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
||||||
|
|
@ -6143,6 +6387,27 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mdast-util-to-hast": {
|
||||||
|
"version": "13.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
|
||||||
|
"integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hast": "^3.0.0",
|
||||||
|
"@types/mdast": "^4.0.0",
|
||||||
|
"@ungap/structured-clone": "^1.0.0",
|
||||||
|
"devlop": "^1.0.0",
|
||||||
|
"micromark-util-sanitize-uri": "^2.0.0",
|
||||||
|
"trim-lines": "^3.0.0",
|
||||||
|
"unist-util-position": "^5.0.0",
|
||||||
|
"unist-util-visit": "^5.0.0",
|
||||||
|
"vfile": "^6.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/media-typer": {
|
"node_modules/media-typer": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
|
|
@ -6174,6 +6439,95 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/micromark-util-character": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "GitHub Sponsors",
|
||||||
|
"url": "https://github.com/sponsors/unifiedjs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OpenCollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"micromark-util-symbol": "^2.0.0",
|
||||||
|
"micromark-util-types": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micromark-util-encode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "GitHub Sponsors",
|
||||||
|
"url": "https://github.com/sponsors/unifiedjs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OpenCollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/micromark-util-sanitize-uri": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "GitHub Sponsors",
|
||||||
|
"url": "https://github.com/sponsors/unifiedjs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OpenCollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"micromark-util-character": "^2.0.0",
|
||||||
|
"micromark-util-encode": "^2.0.0",
|
||||||
|
"micromark-util-symbol": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micromark-util-symbol": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "GitHub Sponsors",
|
||||||
|
"url": "https://github.com/sponsors/unifiedjs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OpenCollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/micromark-util-types": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "GitHub Sponsors",
|
||||||
|
"url": "https://github.com/sponsors/unifiedjs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OpenCollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/micromatch": {
|
"node_modules/micromatch": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
|
|
@ -6787,6 +7141,23 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/oniguruma-parser": {
|
||||||
|
"version": "0.12.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz",
|
||||||
|
"integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/oniguruma-to-es": {
|
||||||
|
"version": "4.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz",
|
||||||
|
"integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"oniguruma-parser": "^0.12.1",
|
||||||
|
"regex": "^6.0.1",
|
||||||
|
"regex-recursion": "^6.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ora": {
|
"node_modules/ora": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
|
||||||
|
|
@ -7251,6 +7622,16 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/property-information": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
|
|
@ -7381,6 +7762,29 @@
|
||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react": {
|
||||||
|
"version": "19.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-dom": {
|
||||||
|
"version": "19.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||||
|
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"scheduler": "^0.27.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-binary-file-arch": {
|
"node_modules/read-binary-file-arch": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz",
|
||||||
|
|
@ -7445,6 +7849,30 @@
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regex": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"regex-utilities": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/regex-recursion": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"regex-utilities": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/regex-utilities": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
|
@ -7687,6 +8115,13 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/scheduler": {
|
||||||
|
"version": "0.27.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||||
|
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "6.3.1",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
|
|
@ -7829,6 +8264,22 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/shiki": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@shikijs/core": "3.14.0",
|
||||||
|
"@shikijs/engine-javascript": "3.14.0",
|
||||||
|
"@shikijs/engine-oniguruma": "3.14.0",
|
||||||
|
"@shikijs/langs": "3.14.0",
|
||||||
|
"@shikijs/themes": "3.14.0",
|
||||||
|
"@shikijs/types": "3.14.0",
|
||||||
|
"@shikijs/vscode-textmate": "^10.0.2",
|
||||||
|
"@types/hast": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
|
|
@ -8183,6 +8634,16 @@
|
||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/space-separated-tokens": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sprintf-js": {
|
"node_modules/sprintf-js": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
||||||
|
|
@ -8264,6 +8725,20 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stringify-entities": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"character-entities-html4": "^2.0.0",
|
||||||
|
"character-entities-legacy": "^3.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strip-ansi": {
|
"node_modules/strip-ansi": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
|
@ -8715,6 +9190,16 @@
|
||||||
"tree-kill": "cli.js"
|
"tree-kill": "cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/trim-lines": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/truncate-utf8-bytes": {
|
"node_modules/truncate-utf8-bytes": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
|
||||||
|
|
@ -8897,6 +9382,74 @@
|
||||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unist-util-is": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unist-util-position": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unist-util-stringify-position": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unist-util-visit": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0",
|
||||||
|
"unist-util-is": "^6.0.0",
|
||||||
|
"unist-util-visit-parents": "^6.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unist-util-visit-parents": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0",
|
||||||
|
"unist-util-is": "^6.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/universalify": {
|
"node_modules/universalify": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
|
@ -9016,6 +9569,34 @@
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vfile": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0",
|
||||||
|
"vfile-message": "^4.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vfile-message": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0",
|
||||||
|
"unist-util-stringify-position": "^4.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wcwidth": {
|
"node_modules/wcwidth": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||||
|
|
@ -9226,6 +9807,16 @@
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zwitch": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pierre/precision-diffs": "^0.4.2",
|
||||||
"@types/cors": "^2.8.19",
|
"@types/cors": "^2.8.19",
|
||||||
"@types/express": "^5.0.3",
|
"@types/express": "^5.0.3",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
|
|
@ -51,6 +52,7 @@
|
||||||
"electron-store": "^11.0.0",
|
"electron-store": "^11.0.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.0.0",
|
||||||
|
"shiki": "^3.14.0",
|
||||||
"simple-git": "^3.28.0",
|
"simple-git": "^3.28.0",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
|
|
|
||||||
340
renderer.ts
340
renderer.ts
|
|
@ -1,6 +1,6 @@
|
||||||
import {FitAddon} from "@xterm/addon-fit";
|
import {FitAddon} from "@xterm/addon-fit";
|
||||||
import {Terminal} from "xterm";
|
import {Terminal} from "xterm";
|
||||||
import {PersistedSession, SessionConfig} from "./types";
|
import {PersistedSession, SessionConfig, FileDiff, MergeStatus, MergeResult} from "./types";
|
||||||
import {isClaudeSessionReady} from "./terminal-utils";
|
import {isClaudeSessionReady} from "./terminal-utils";
|
||||||
import {ipcAdapter} from "./ipc-adapter";
|
import {ipcAdapter} from "./ipc-adapter";
|
||||||
|
|
||||||
|
|
@ -340,12 +340,33 @@ function activateSession(sessionId: string) {
|
||||||
function updateSessionState(sessionId: string, isActive: boolean) {
|
function updateSessionState(sessionId: string, isActive: boolean) {
|
||||||
const sidebarItem = document.getElementById(`sidebar-${sessionId}`);
|
const sidebarItem = document.getElementById(`sidebar-${sessionId}`);
|
||||||
const indicator = sidebarItem?.querySelector(".session-indicator");
|
const indicator = sidebarItem?.querySelector(".session-indicator");
|
||||||
|
const buttonContainer = sidebarItem?.querySelector(".flex.items-center.space-x-1");
|
||||||
|
|
||||||
if (indicator) {
|
if (indicator) {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
indicator.classList.add("active");
|
indicator.classList.add("active");
|
||||||
|
|
||||||
|
// Add diff button if it doesn't exist
|
||||||
|
if (buttonContainer && !buttonContainer.querySelector(".view-diff-btn")) {
|
||||||
|
const diffBtn = document.createElement("button");
|
||||||
|
diffBtn.className = "view-diff-btn";
|
||||||
|
diffBtn.setAttribute("data-id", sessionId);
|
||||||
|
diffBtn.setAttribute("title", "View Diff");
|
||||||
|
diffBtn.textContent = "Diff";
|
||||||
|
diffBtn.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openDiffViewer(sessionId);
|
||||||
|
});
|
||||||
|
buttonContainer.insertBefore(diffBtn, buttonContainer.firstChild);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
indicator.classList.remove("active");
|
indicator.classList.remove("active");
|
||||||
|
|
||||||
|
// Remove diff button when session becomes inactive
|
||||||
|
const existingDiffBtn = buttonContainer?.querySelector(".view-diff-btn");
|
||||||
|
if (existingDiffBtn) {
|
||||||
|
existingDiffBtn.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -363,7 +384,10 @@ function addToSidebar(sessionId: string, name: string, hasActivePty: boolean) {
|
||||||
<span class="truncate session-name-text" data-id="${sessionId}">${name}</span>
|
<span class="truncate session-name-text" data-id="${sessionId}">${name}</span>
|
||||||
<input type="text" class="session-name-input hidden" data-id="${sessionId}" value="${name}" />
|
<input type="text" class="session-name-input hidden" data-id="${sessionId}" value="${name}" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center space-x-1">
|
||||||
|
${hasActivePty ? `<button class="view-diff-btn" data-id="${sessionId}" title="View Diff">Diff</button>` : ''}
|
||||||
<button class="session-delete-btn" data-id="${sessionId}" title="Delete session">×</button>
|
<button class="session-delete-btn" data-id="${sessionId}" title="Delete session">×</button>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Click on session name to edit
|
// Click on session name to edit
|
||||||
|
|
@ -391,6 +415,7 @@ function addToSidebar(sessionId: string, name: string, hasActivePty: boolean) {
|
||||||
item.addEventListener("click", (e) => {
|
item.addEventListener("click", (e) => {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (!target.classList.contains("session-delete-btn") &&
|
if (!target.classList.contains("session-delete-btn") &&
|
||||||
|
!target.classList.contains("view-diff-btn") &&
|
||||||
!target.classList.contains("session-name-text") &&
|
!target.classList.contains("session-name-text") &&
|
||||||
!target.classList.contains("session-name-input")) {
|
!target.classList.contains("session-name-input")) {
|
||||||
handleSessionClick(sessionId);
|
handleSessionClick(sessionId);
|
||||||
|
|
@ -403,6 +428,12 @@ function addToSidebar(sessionId: string, name: string, hasActivePty: boolean) {
|
||||||
deleteSession(sessionId);
|
deleteSession(sessionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const viewDiffBtn = item.querySelector(".view-diff-btn");
|
||||||
|
viewDiffBtn?.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openDiffViewer(sessionId);
|
||||||
|
});
|
||||||
|
|
||||||
list.appendChild(item);
|
list.appendChild(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1487,3 +1518,310 @@ document.getElementById("session-list")?.addEventListener("click", (e) => {
|
||||||
closeMobileMenu();
|
closeMobileMenu();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Diff Viewer
|
||||||
|
let currentDiffFiles: FileDiff[] = [];
|
||||||
|
let selectedFileIndex: number = -1;
|
||||||
|
let currentDiffSessionId: string = '';
|
||||||
|
let diffSidebarCollapsed: boolean = false;
|
||||||
|
let pierreLib: any = null;
|
||||||
|
let shikiLib: any = null;
|
||||||
|
let highlighter: any = null;
|
||||||
|
|
||||||
|
async function openDiffViewer(sessionId: string) {
|
||||||
|
const modal = document.getElementById("diff-viewer-modal");
|
||||||
|
const fileList = document.getElementById("diff-file-list");
|
||||||
|
const diffDisplay = document.getElementById("diff-display");
|
||||||
|
const mergeBtn = document.getElementById("merge-to-parent-btn") as HTMLButtonElement;
|
||||||
|
|
||||||
|
if (!modal || !fileList || !diffDisplay) return;
|
||||||
|
|
||||||
|
currentDiffSessionId = sessionId;
|
||||||
|
|
||||||
|
// Auto-collapse sidebar on mobile
|
||||||
|
const isMobile = window.innerWidth < 768;
|
||||||
|
diffSidebarCollapsed = isMobile;
|
||||||
|
if (diffSidebarCollapsed) {
|
||||||
|
fileList.classList.add("collapsed");
|
||||||
|
} else {
|
||||||
|
fileList.classList.remove("collapsed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show modal with loading state
|
||||||
|
modal.classList.remove("hidden");
|
||||||
|
fileList.innerHTML = '<div class="diff-loading">Loading diff...</div>';
|
||||||
|
diffDisplay.innerHTML = '<div class="diff-empty">Loading...</div>';
|
||||||
|
|
||||||
|
if (mergeBtn) {
|
||||||
|
mergeBtn.disabled = true;
|
||||||
|
mergeBtn.textContent = "Merge to Parent";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load libraries dynamically (only in browser)
|
||||||
|
if (!pierreLib) {
|
||||||
|
try {
|
||||||
|
pierreLib = await import("@pierre/precision-diffs" as any);
|
||||||
|
shikiLib = await import("shiki" as any);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load diff libraries:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize highlighter if needed
|
||||||
|
if (!highlighter && shikiLib) {
|
||||||
|
try {
|
||||||
|
highlighter = await shikiLib.createHighlighter({
|
||||||
|
themes: ['github-dark'],
|
||||||
|
langs: ['javascript', 'typescript', 'python', 'java', 'go', 'rust', 'html', 'css', 'json', 'markdown']
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to initialize highlighter:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request diff and merge status from server
|
||||||
|
ipcAdapter.send("get-session-diff", sessionId);
|
||||||
|
ipcAdapter.send("check-merge-status", sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDiffFileList() {
|
||||||
|
const fileList = document.getElementById("diff-file-list");
|
||||||
|
if (!fileList) return;
|
||||||
|
|
||||||
|
if (currentDiffFiles.length === 0) {
|
||||||
|
fileList.innerHTML = '<button id="toggle-diff-sidebar" class="diff-sidebar-toggle"><span class="toggle-expand">>></span><span class="toggle-collapse"><<</span></button><div class="diff-loading">No changes found</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileList.innerHTML = '<button id="toggle-diff-sidebar" class="diff-sidebar-toggle"><span class="toggle-expand">>></span><span class="toggle-collapse"><<</span></button>';
|
||||||
|
|
||||||
|
currentDiffFiles.forEach((file, index) => {
|
||||||
|
const item = document.createElement("div");
|
||||||
|
item.className = `diff-file-item ${index === selectedFileIndex ? 'active' : ''}`;
|
||||||
|
|
||||||
|
// Create file path div and set text content securely
|
||||||
|
const pathDiv = document.createElement("div");
|
||||||
|
pathDiv.className = "diff-file-path";
|
||||||
|
pathDiv.textContent = file.path;
|
||||||
|
|
||||||
|
// Create stats div
|
||||||
|
const statsDiv = document.createElement("div");
|
||||||
|
statsDiv.className = "diff-file-stats";
|
||||||
|
|
||||||
|
// Create status badge
|
||||||
|
const statusSpan = document.createElement("span");
|
||||||
|
statusSpan.className = `diff-file-status ${file.status}`;
|
||||||
|
statusSpan.textContent = file.status;
|
||||||
|
statsDiv.appendChild(statusSpan);
|
||||||
|
|
||||||
|
// Add additions count if present
|
||||||
|
if (file.additions > 0) {
|
||||||
|
const additionsSpan = document.createElement("span");
|
||||||
|
additionsSpan.className = "diff-file-additions";
|
||||||
|
additionsSpan.textContent = `+${file.additions}`;
|
||||||
|
statsDiv.appendChild(additionsSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add deletions count if present
|
||||||
|
if (file.deletions > 0) {
|
||||||
|
const deletionsSpan = document.createElement("span");
|
||||||
|
deletionsSpan.className = "diff-file-deletions";
|
||||||
|
deletionsSpan.textContent = `-${file.deletions}`;
|
||||||
|
statsDiv.appendChild(deletionsSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.appendChild(pathDiv);
|
||||||
|
item.appendChild(statsDiv);
|
||||||
|
|
||||||
|
item.addEventListener("click", () => {
|
||||||
|
selectedFileIndex = index;
|
||||||
|
|
||||||
|
// Auto-collapse sidebar on mobile when selecting a file
|
||||||
|
const isMobile = window.innerWidth < 768;
|
||||||
|
if (isMobile) {
|
||||||
|
const fileListEl = document.getElementById("diff-file-list");
|
||||||
|
if (fileListEl) {
|
||||||
|
diffSidebarCollapsed = true;
|
||||||
|
fileListEl.classList.add("collapsed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDiffFileList();
|
||||||
|
renderSelectedDiff();
|
||||||
|
});
|
||||||
|
|
||||||
|
fileList.appendChild(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-attach toggle button event listener
|
||||||
|
document.getElementById("toggle-diff-sidebar")?.replaceWith(document.getElementById("toggle-diff-sidebar")!.cloneNode(true));
|
||||||
|
document.getElementById("toggle-diff-sidebar")?.addEventListener("click", () => {
|
||||||
|
const fileListEl = document.getElementById("diff-file-list");
|
||||||
|
if (!fileListEl) return;
|
||||||
|
|
||||||
|
diffSidebarCollapsed = !diffSidebarCollapsed;
|
||||||
|
if (diffSidebarCollapsed) {
|
||||||
|
fileListEl.classList.add("collapsed");
|
||||||
|
} else {
|
||||||
|
fileListEl.classList.remove("collapsed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderSelectedDiff() {
|
||||||
|
const diffDisplay = document.getElementById("diff-display");
|
||||||
|
if (!diffDisplay || selectedFileIndex < 0 || selectedFileIndex >= currentDiffFiles.length) {
|
||||||
|
if (diffDisplay) {
|
||||||
|
diffDisplay.innerHTML = '<div class="diff-empty">Select a file to view changes</div>';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = currentDiffFiles[selectedFileIndex];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if file is binary
|
||||||
|
if (file.binary) {
|
||||||
|
diffDisplay.innerHTML = '<div class="diff-empty">Binary file changed</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pierreLib) {
|
||||||
|
diffDisplay.innerHTML = '<div class="diff-empty">Loading diff viewer...</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use Pierre.js FileDiff to render the diff
|
||||||
|
const diffContainer = document.createElement("div");
|
||||||
|
diffContainer.className = "diff-content";
|
||||||
|
|
||||||
|
// Create FileDiff instance
|
||||||
|
const fileDiff = new pierreLib.FileDiff({
|
||||||
|
theme: { dark: 'github-dark', light: 'github-light' },
|
||||||
|
themeType: 'dark',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse the diff to get FileDiffMetadata
|
||||||
|
const parsedPatches = pierreLib.parsePatchFiles(file.diff);
|
||||||
|
|
||||||
|
if (!parsedPatches || parsedPatches.length === 0 || !parsedPatches[0].files || parsedPatches[0].files.length === 0) {
|
||||||
|
diffDisplay.innerHTML = '<div class="diff-empty">Could not parse diff data</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first file's diff metadata from the parsed patch
|
||||||
|
const fileDiffMetadata = parsedPatches[0].files[0];
|
||||||
|
|
||||||
|
// Render using the parsed fileDiff metadata
|
||||||
|
await fileDiff.render({
|
||||||
|
fileDiff: fileDiffMetadata,
|
||||||
|
containerWrapper: diffContainer,
|
||||||
|
});
|
||||||
|
|
||||||
|
diffDisplay.innerHTML = '';
|
||||||
|
diffDisplay.appendChild(diffContainer);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error rendering diff:", error);
|
||||||
|
diffDisplay.innerHTML = `<div class="diff-empty">Error rendering diff: ${error}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Close diff viewer
|
||||||
|
document.getElementById("close-diff-viewer")?.addEventListener("click", () => {
|
||||||
|
const modal = document.getElementById("diff-viewer-modal");
|
||||||
|
modal?.classList.add("hidden");
|
||||||
|
currentDiffFiles = [];
|
||||||
|
selectedFileIndex = -1;
|
||||||
|
currentDiffSessionId = '';
|
||||||
|
diffSidebarCollapsed = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Merge to parent button
|
||||||
|
document.getElementById("merge-to-parent-btn")?.addEventListener("click", () => {
|
||||||
|
if (!currentDiffSessionId) return;
|
||||||
|
|
||||||
|
const mergeBtn = document.getElementById("merge-to-parent-btn") as HTMLButtonElement;
|
||||||
|
if (!mergeBtn || mergeBtn.disabled) return;
|
||||||
|
|
||||||
|
if (!confirm("Merge this worktree branch into the parent branch? This cannot be undone.")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeBtn.disabled = true;
|
||||||
|
mergeBtn.textContent = "Merging...";
|
||||||
|
|
||||||
|
ipcAdapter.send("merge-to-parent", currentDiffSessionId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle diff data from server
|
||||||
|
ipcAdapter.on("session-diff-data", (_event, sessionId: string, fileDiffs: FileDiff[]) => {
|
||||||
|
currentDiffFiles = fileDiffs;
|
||||||
|
selectedFileIndex = fileDiffs.length > 0 ? 0 : -1;
|
||||||
|
renderDiffFileList();
|
||||||
|
renderSelectedDiff();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle diff error from server
|
||||||
|
ipcAdapter.on("session-diff-error", (_event, sessionId: string, error: string) => {
|
||||||
|
const fileList = document.getElementById("diff-file-list");
|
||||||
|
const diffDisplay = document.getElementById("diff-display");
|
||||||
|
|
||||||
|
if (fileList) {
|
||||||
|
fileList.innerHTML = `<div class="diff-loading">Error: ${error}</div>`;
|
||||||
|
}
|
||||||
|
if (diffDisplay) {
|
||||||
|
diffDisplay.innerHTML = `<div class="diff-empty">Failed to load diff</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle merge status from server
|
||||||
|
ipcAdapter.on("merge-status-data", (_event, sessionId: string, status: MergeStatus) => {
|
||||||
|
const mergeBtn = document.getElementById("merge-to-parent-btn") as HTMLButtonElement;
|
||||||
|
if (!mergeBtn) return;
|
||||||
|
|
||||||
|
if (status.hasCommits) {
|
||||||
|
mergeBtn.disabled = false;
|
||||||
|
const commitText = status.commitsAhead === 1 ? '1 commit' : `${status.commitsAhead} commits`;
|
||||||
|
mergeBtn.title = `Merge ${commitText} into parent branch`;
|
||||||
|
if (status.hasUncommittedChanges) {
|
||||||
|
mergeBtn.title += ' (has uncommitted changes)';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergeBtn.disabled = true;
|
||||||
|
mergeBtn.title = "No commits to merge";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle merge status error
|
||||||
|
ipcAdapter.on("merge-status-error", (_event, sessionId: string, error: string) => {
|
||||||
|
console.error("Merge status error:", error);
|
||||||
|
const mergeBtn = document.getElementById("merge-to-parent-btn") as HTMLButtonElement;
|
||||||
|
if (mergeBtn) {
|
||||||
|
mergeBtn.disabled = true;
|
||||||
|
mergeBtn.title = `Error checking merge status: ${error}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle merge result
|
||||||
|
ipcAdapter.on("merge-result", (_event, sessionId: string, result: MergeResult) => {
|
||||||
|
const mergeBtn = document.getElementById("merge-to-parent-btn") as HTMLButtonElement;
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
alert(`✓ ${result.message}`);
|
||||||
|
if (mergeBtn) {
|
||||||
|
mergeBtn.disabled = true;
|
||||||
|
mergeBtn.textContent = "Merged";
|
||||||
|
}
|
||||||
|
// Refresh diff and status
|
||||||
|
ipcAdapter.send("get-session-diff", sessionId);
|
||||||
|
ipcAdapter.send("check-merge-status", sessionId);
|
||||||
|
} else {
|
||||||
|
alert(`✗ Merge failed: ${result.message}`);
|
||||||
|
if (mergeBtn) {
|
||||||
|
mergeBtn.disabled = false;
|
||||||
|
mergeBtn.textContent = "Merge to Parent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
238
server.ts
238
server.ts
|
|
@ -8,7 +8,7 @@ import * as path from "path";
|
||||||
import { simpleGit } from "simple-git";
|
import { simpleGit } from "simple-git";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { PersistedSession, SessionConfig } from "./types";
|
import { PersistedSession, SessionConfig, FileDiff, MergeStatus, MergeResult } from "./types";
|
||||||
import { isTerminalReady } from "./terminal-utils";
|
import { isTerminalReady } from "./terminal-utils";
|
||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
|
|
||||||
|
|
@ -234,6 +234,74 @@ async function removeGitBranch(projectDir: string, branchName: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parsePatchToFileDiffs(patch: string): FileDiff[] {
|
||||||
|
const fileDiffs: FileDiff[] = [];
|
||||||
|
|
||||||
|
// Split patch into individual file chunks
|
||||||
|
// Each chunk starts with "diff --git"
|
||||||
|
const fileChunks = patch.split(/(?=diff --git )/g).filter(chunk => chunk.trim());
|
||||||
|
|
||||||
|
for (const chunk of fileChunks) {
|
||||||
|
const lines = chunk.split('\n');
|
||||||
|
|
||||||
|
// Extract file paths from "diff --git a/... b/..."
|
||||||
|
const diffGitMatch = lines[0].match(/^diff --git a\/(.+?) b\/(.+?)$/);
|
||||||
|
if (!diffGitMatch) continue;
|
||||||
|
|
||||||
|
let oldPath = diffGitMatch[1];
|
||||||
|
let newPath = diffGitMatch[2];
|
||||||
|
|
||||||
|
// Determine status and paths
|
||||||
|
let status: FileDiff['status'] = 'modified';
|
||||||
|
let finalPath = newPath;
|
||||||
|
let finalOldPath: string | undefined;
|
||||||
|
let isBinary = false;
|
||||||
|
|
||||||
|
// Check for file mode changes and status indicators
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('new file mode')) {
|
||||||
|
status = 'added';
|
||||||
|
finalOldPath = undefined;
|
||||||
|
} else if (line.startsWith('deleted file mode')) {
|
||||||
|
status = 'deleted';
|
||||||
|
finalPath = oldPath;
|
||||||
|
finalOldPath = undefined;
|
||||||
|
} else if (line.startsWith('rename from')) {
|
||||||
|
status = 'renamed';
|
||||||
|
finalOldPath = line.substring('rename from '.length).trim();
|
||||||
|
} else if (line.startsWith('rename to')) {
|
||||||
|
finalPath = line.substring('rename to '.length).trim();
|
||||||
|
} else if (line.startsWith('Binary files')) {
|
||||||
|
isBinary = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count additions and deletions
|
||||||
|
let additions = 0;
|
||||||
|
let deletions = 0;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('+') && !line.startsWith('+++')) {
|
||||||
|
additions++;
|
||||||
|
} else if (line.startsWith('-') && !line.startsWith('---')) {
|
||||||
|
deletions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDiffs.push({
|
||||||
|
path: finalPath,
|
||||||
|
oldPath: finalOldPath,
|
||||||
|
status,
|
||||||
|
diff: chunk,
|
||||||
|
additions,
|
||||||
|
deletions,
|
||||||
|
binary: isBinary,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileDiffs;
|
||||||
|
}
|
||||||
|
|
||||||
// Socket.IO handlers
|
// Socket.IO handlers
|
||||||
io.on("connection", (socket) => {
|
io.on("connection", (socket) => {
|
||||||
console.log("Client connected:", socket.id);
|
console.log("Client connected:", socket.id);
|
||||||
|
|
@ -495,6 +563,174 @@ io.on("connection", (socket) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("get-session-diff", async (sessionId: string) => {
|
||||||
|
try {
|
||||||
|
const sessions = getPersistedSessions();
|
||||||
|
const session = sessions.find(s => s.id === sessionId);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
socket.emit("session-diff-error", sessionId, "Session not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const git = simpleGit(session.worktreePath);
|
||||||
|
const parentBranch = session.config.parentBranch;
|
||||||
|
|
||||||
|
console.log(`[${sessionId}] Generating unified diff for session, parent branch: ${parentBranch}`);
|
||||||
|
console.log(`[${sessionId}] Worktree path: ${session.worktreePath}`);
|
||||||
|
|
||||||
|
// Get unified patch for tracked changes (includes staged, unstaged, and committed)
|
||||||
|
const trackedPatch = await git.raw([
|
||||||
|
'diff',
|
||||||
|
parentBranch,
|
||||||
|
'--patch',
|
||||||
|
'--find-renames',
|
||||||
|
'--binary'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Get untracked files
|
||||||
|
const statusSummary = await git.status();
|
||||||
|
|
||||||
|
// Generate patches for untracked files using --no-index
|
||||||
|
const untrackedPatches: string[] = [];
|
||||||
|
for (const untrackedFile of statusSummary.not_added) {
|
||||||
|
try {
|
||||||
|
const untrackedPatch = await git.raw([
|
||||||
|
'diff',
|
||||||
|
'--no-index',
|
||||||
|
'--binary',
|
||||||
|
'/dev/null',
|
||||||
|
untrackedFile
|
||||||
|
]);
|
||||||
|
untrackedPatches.push(untrackedPatch);
|
||||||
|
} catch (error: any) {
|
||||||
|
// git diff --no-index exits with code 1 when there are differences, which is expected
|
||||||
|
if (error.message && typeof error.message === 'string') {
|
||||||
|
untrackedPatches.push(error.message);
|
||||||
|
} else {
|
||||||
|
console.error(`[${sessionId}] Error generating patch for untracked file ${untrackedFile}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine all patches
|
||||||
|
const combinedPatch = [trackedPatch, ...untrackedPatches].filter(p => p).join('\n');
|
||||||
|
|
||||||
|
console.log(`[${sessionId}] Combined patch length: ${combinedPatch.length} chars`);
|
||||||
|
console.log(`[${sessionId}] Untracked files: ${statusSummary.not_added.length}`);
|
||||||
|
|
||||||
|
// Parse the combined patch into FileDiff array
|
||||||
|
const fileDiffs: FileDiff[] = parsePatchToFileDiffs(combinedPatch);
|
||||||
|
|
||||||
|
console.log(`[${sessionId}] Parsed ${fileDiffs.length} file diffs`);
|
||||||
|
socket.emit("session-diff-data", sessionId, fileDiffs);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error getting session diff:", error);
|
||||||
|
socket.emit("session-diff-error", sessionId, error instanceof Error ? error.message : String(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("check-merge-status", async (sessionId: string) => {
|
||||||
|
try {
|
||||||
|
const sessions = getPersistedSessions();
|
||||||
|
const session = sessions.find(s => s.id === sessionId);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
socket.emit("merge-status-error", sessionId, "Session not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const git = simpleGit(session.worktreePath);
|
||||||
|
const parentBranch = session.config.parentBranch;
|
||||||
|
const currentBranch = session.gitBranch || (await git.revparse(['--abbrev-ref', 'HEAD']));
|
||||||
|
|
||||||
|
// Check how many commits ahead of parent
|
||||||
|
const revListOutput = await git.raw([
|
||||||
|
'rev-list',
|
||||||
|
'--count',
|
||||||
|
`${parentBranch}..${currentBranch}`
|
||||||
|
]);
|
||||||
|
const commitsAhead = parseInt(revListOutput.trim(), 10);
|
||||||
|
|
||||||
|
// Check for uncommitted changes
|
||||||
|
const statusSummary = await git.status();
|
||||||
|
const hasUncommittedChanges = !statusSummary.isClean();
|
||||||
|
|
||||||
|
const mergeStatus: MergeStatus = {
|
||||||
|
hasCommits: commitsAhead > 0,
|
||||||
|
commitsAhead,
|
||||||
|
hasUncommittedChanges
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`[${sessionId}] Merge status: ${JSON.stringify(mergeStatus)}`);
|
||||||
|
socket.emit("merge-status-data", sessionId, mergeStatus);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error checking merge status:", error);
|
||||||
|
socket.emit("merge-status-error", sessionId, error instanceof Error ? error.message : String(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("merge-to-parent", async (sessionId: string) => {
|
||||||
|
try {
|
||||||
|
const sessions = getPersistedSessions();
|
||||||
|
const session = sessions.find(s => s.id === sessionId);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
socket.emit("merge-result", sessionId, {
|
||||||
|
success: false,
|
||||||
|
message: "Session not found"
|
||||||
|
} as MergeResult);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectDir = session.config.projectDir;
|
||||||
|
const parentBranch = session.config.parentBranch;
|
||||||
|
const worktreeBranch = session.gitBranch || (await simpleGit(session.worktreePath).revparse(['--abbrev-ref', 'HEAD']));
|
||||||
|
|
||||||
|
console.log(`[${sessionId}] Starting merge: ${worktreeBranch} -> ${parentBranch} in ${projectDir}`);
|
||||||
|
|
||||||
|
const git = simpleGit(projectDir);
|
||||||
|
|
||||||
|
// Checkout parent branch
|
||||||
|
await git.checkout(parentBranch);
|
||||||
|
console.log(`[${sessionId}] Checked out ${parentBranch}`);
|
||||||
|
|
||||||
|
// Attempt merge
|
||||||
|
try {
|
||||||
|
await git.merge([worktreeBranch]);
|
||||||
|
console.log(`[${sessionId}] Merge successful`);
|
||||||
|
|
||||||
|
socket.emit("merge-result", sessionId, {
|
||||||
|
success: true,
|
||||||
|
message: `Successfully merged ${worktreeBranch} into ${parentBranch}`
|
||||||
|
} as MergeResult);
|
||||||
|
} catch (mergeError: any) {
|
||||||
|
// Check if it's a merge conflict
|
||||||
|
const status = await git.status();
|
||||||
|
if (status.conflicted.length > 0) {
|
||||||
|
console.log(`[${sessionId}] Merge conflicts detected: ${status.conflicted.join(', ')}`);
|
||||||
|
|
||||||
|
// Abort the merge
|
||||||
|
await git.raw(['merge', '--abort']);
|
||||||
|
|
||||||
|
socket.emit("merge-result", sessionId, {
|
||||||
|
success: false,
|
||||||
|
conflicts: status.conflicted,
|
||||||
|
message: `Merge conflicts in: ${status.conflicted.join(', ')}`
|
||||||
|
} as MergeResult);
|
||||||
|
} else {
|
||||||
|
throw mergeError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error merging to parent:", error);
|
||||||
|
socket.emit("merge-result", sessionId, {
|
||||||
|
success: false,
|
||||||
|
message: error instanceof Error ? error.message : String(error)
|
||||||
|
} as MergeResult);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
// Clean up socket references for this client
|
// Clean up socket references for this client
|
||||||
// Note: We don't kill the PTYs - they keep running for reconnection
|
// Note: We don't kill the PTYs - they keep running for reconnection
|
||||||
|
|
|
||||||
167
styles.css
167
styles.css
|
|
@ -336,4 +336,171 @@
|
||||||
@apply text-lg;
|
@apply text-lg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Diff Viewer Styles */
|
||||||
|
.diff-viewer-modal {
|
||||||
|
@apply bg-gray-900 rounded-lg shadow-xl flex flex-col;
|
||||||
|
width: 95vw;
|
||||||
|
height: 90vh;
|
||||||
|
max-width: 1400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-viewer-header {
|
||||||
|
@apply flex items-center justify-between p-4 border-b border-gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-viewer-actions {
|
||||||
|
@apply flex items-center gap-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-merge {
|
||||||
|
@apply px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded font-medium transition-colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-merge:disabled {
|
||||||
|
@apply bg-gray-700 text-gray-500 cursor-not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-viewer-close {
|
||||||
|
@apply text-gray-400 hover:text-white text-3xl font-bold w-10 h-10 flex items-center justify-center rounded transition-colors hover:bg-gray-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-viewer-content {
|
||||||
|
@apply flex flex-1 overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-list {
|
||||||
|
@apply w-80 border-r border-gray-700 overflow-y-auto bg-gray-800 transition-all duration-300 relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-list.collapsed {
|
||||||
|
@apply w-12 overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-loading {
|
||||||
|
@apply p-4 text-gray-400 text-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-item {
|
||||||
|
@apply px-4 py-3 border-b border-gray-700 cursor-pointer hover:bg-gray-700 transition-colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-item.active {
|
||||||
|
@apply bg-gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-path {
|
||||||
|
@apply text-sm text-gray-200 mb-1 break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-stats {
|
||||||
|
@apply flex items-center space-x-2 text-xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-status {
|
||||||
|
@apply px-2 py-0.5 rounded text-xs font-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-status.added {
|
||||||
|
@apply bg-green-900 text-green-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-status.modified {
|
||||||
|
@apply bg-blue-900 text-blue-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-status.deleted {
|
||||||
|
@apply bg-red-900 text-red-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-status.renamed {
|
||||||
|
@apply bg-purple-900 text-purple-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-additions {
|
||||||
|
@apply text-green-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-deletions {
|
||||||
|
@apply text-red-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-sidebar-toggle {
|
||||||
|
@apply sticky top-0 left-0 w-full bg-gray-700 hover:bg-gray-600 text-gray-300 py-2 px-3 text-sm font-mono transition-colors z-10 border-b border-gray-600 flex items-center justify-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-list.collapsed .diff-sidebar-toggle {
|
||||||
|
@apply px-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-collapse {
|
||||||
|
@apply inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-list.collapsed .toggle-collapse {
|
||||||
|
@apply hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-expand {
|
||||||
|
@apply hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-list.collapsed .toggle-expand {
|
||||||
|
@apply inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.diff-sidebar-toggle {
|
||||||
|
@apply hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-list.collapsed {
|
||||||
|
@apply w-80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-display {
|
||||||
|
@apply flex-1 overflow-auto bg-gray-900 p-4 relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-empty {
|
||||||
|
@apply text-gray-500 text-center p-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-content {
|
||||||
|
@apply text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pierre.js diff styles */
|
||||||
|
.diff-content .pierre-diff {
|
||||||
|
@apply font-mono text-xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-content .pierre-diff .line-number {
|
||||||
|
@apply text-gray-500 select-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-content .pierre-diff .line-addition {
|
||||||
|
@apply bg-green-900 bg-opacity-30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-content .pierre-diff .line-deletion {
|
||||||
|
@apply bg-red-900 bg-opacity-30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-diff-btn {
|
||||||
|
@apply ml-2 text-xs bg-blue-600 hover:bg-blue-700 text-white px-2 py-1 rounded transition-colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.diff-viewer-modal {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
@apply rounded-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-file-list {
|
||||||
|
@apply w-64;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
types.ts
22
types.ts
|
|
@ -18,3 +18,25 @@ export interface PersistedSession {
|
||||||
mcpConfigPath?: string;
|
mcpConfigPath?: string;
|
||||||
gitBranch?: string;
|
gitBranch?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FileDiff {
|
||||||
|
path: string;
|
||||||
|
oldPath?: string;
|
||||||
|
status: 'added' | 'modified' | 'deleted' | 'renamed';
|
||||||
|
diff: string;
|
||||||
|
additions: number;
|
||||||
|
deletions: number;
|
||||||
|
binary?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MergeStatus {
|
||||||
|
hasCommits: boolean;
|
||||||
|
commitsAhead: number;
|
||||||
|
hasUncommittedChanges: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MergeResult {
|
||||||
|
success: boolean;
|
||||||
|
conflicts?: string[];
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue