Files
JRedisDesktop/AGENTS.md
T
Jokul 3dea59f703 refactor: 组件按功能分组到子目录
components/ 拆分为 5 个子目录:
- layout/       TitleBar, StatusBar, Sidebar, MainArea
- connection/   ConnectionList, ConnectionCard, NewConnectionDialog
- key/          KeyBrowser, KeyList, KeyDetail, DbSelector
- editors/      String/Hash/List/Set/Zset/Stream/ReJson Editor
- views/        StatusView, CliView, SlowLogView, MemoryAnalysis, CommandLog, SettingsView

更新 4 个文件的跨目录引用 (App.vue, Sidebar, MainArea, KeyDetail)
AGENTS.md 架构树同步更新
2026-07-11 21:21:47 +08:00

274 lines
13 KiB
Markdown

# AGENTS.md - JRedisDesktop v2
## Project identity
Fork of [AnotherRedisDesktopManager](https://github.com/qishibo/AnotherRedisDesktopManager). The legacy Vue 2 codebase lives in `legacy/` and is gitignored. Work only in the root project.
## Stack
- **Shell:** Electron 33 (`src/main/index.ts`)
- **Preload:** `src/preload/index.ts` (contextBridge, typed API)
- **Renderer:** Vue 3 + Pinia + Element Plus + Vue Router (`src/renderer/src/`)
- **IPC types:** `src/preload/index.d.ts` (ElectronAPI interface, global Window augmentation)
- **Build:** electron-vite + Vite 6 + TypeScript 5
- **Module system:** `"type": "module"` (ESM) — main outputs `.js`, preload outputs `.mjs`
- **Redis client:** ioredis 5 (main process only)
- **Persistence:** electron-store + safeStorage (credentials)
## Commands
```bash
npm run dev # electron-vite dev (HMR, renderer on :5173)
npm run build # production build -> out/
npm start # preview built app
npm run build:unpack # build + electron-builder --dir
npm run build:win # build + electron-builder --win
npm run build:mac # build + electron-builder --mac
npm run build:linux # build + electron-builder --linux
npm run lint # eslint --fix
```
## Architecture
```
Electron main (src/main/)
├── index.ts # Window creation, app lifecycle
├── ipc-handlers.ts # all IPC channel handlers
├── store.ts # electron-store (connections, settings)
├── credential.ts # safeStorage encrypt/decrypt
├── win-state.ts # window position persistence
├── updater.ts # electron-updater (check/download/install)
└── redis/ # Redis service modules
├── index.ts # Unified export
├── connection.ts # connect, disconnect, isClientConnected
├── keys.ts # scan, delete, rename, ttl, batchDelete
├── string.ts # get, set
├── hash.ts # hscan, hset, hdel
├── list.ts # range, push, set, remove
├── set.ts # members, add, remove
├── zset.ts # range, add, remove
├── stream.ts # info, range, add, trim
├── server.ts # info, select, ping, slowlog
├── pubsub.ts # SUBSCRIBE/UNSUBSCRIBE
├── commandLogger.ts # command execution log
└── format.ts # value decode/format detection
Preload (src/preload/)
├── index.ts # contextBridge.exposeInMainWorld
└── index.d.ts # ElectronAPI type declarations
Renderer (src/renderer/)
├── index.html # Vite entry HTML
└── src/
├── main.ts # Vue 3 bootstrap (Pinia, Element Plus)
├── App.vue # Shell layout (TitleBar + Sidebar + MainArea + StatusBar)
├── env.d.ts # Vite client + *.vue / *.css module declarations
├── stores/
│ ├── app.ts # theme, fontSize, scanCount
│ ├── connection.ts # connections CRUD, connect/disconnect, health check
│ └── key.ts # key list, scan, tree view, selected key data
├── components/
│ ├── layout/ # App shell
│ │ ├── TitleBar.vue # window controls
│ │ ├── StatusBar.vue # connection status
│ │ ├── Sidebar.vue # connection list host, keyboard shortcuts
│ │ └── MainArea.vue # tab host (Status/Keys/CLI/SlowLog/Settings)
│ ├── connection/ # Connection management
│ │ ├── ConnectionList.vue # search, import/export, context menu
│ │ ├── ConnectionCard.vue # single connection card
│ │ └── NewConnectionDialog.vue # create/edit/test connection
│ ├── key/ # Key browsing
│ │ ├── KeyBrowser.vue # split pane (KeyList + KeyDetail)
│ │ ├── KeyList.vue # tree view, SCAN, filter, multi-select
│ │ ├── KeyDetail.vue # type dispatch, TTL, rename, copy
│ │ └── DbSelector.vue # database 0-15 dropdown
│ ├── editors/ # Type-specific value editors
│ │ ├── StringEditor.vue # view/edit with JSON format
│ │ ├── HashEditor.vue # field table CRUD
│ │ ├── ListEditor.vue # paginated elements
│ │ ├── SetEditor.vue # member list
│ │ ├── ZsetEditor.vue # sorted set with scores
│ │ ├── StreamEditor.vue # stream entries
│ │ └── ReJsonEditor.vue # JSON.GET / JSON.SET editing
│ └── views/ # Tab content views
│ ├── StatusView.vue # INFO dashboard with stat cards
│ ├── CliView.vue # interactive terminal
│ ├── SlowLogView.vue # slow log table
│ ├── MemoryAnalysis.vue # MEMORY USAGE scan & sort
│ ├── CommandLog.vue # command execution history
│ └── SettingsView.vue # theme, language, font
├── i18n/
│ ├── index.ts # useI18n composable
│ └── locales/
│ ├── en.ts # English
│ ├── zh-CN.ts # 中文
│ ├── ja.ts # 日本語
│ ├── ko.ts # 한국어
│ └── de.ts # Deutsch
├── composables/
│ └── useKeyboard.ts # keyboard shortcut handler
├── styles/
│ ├── variables.css # CSS custom properties
│ └── global.css # global overrides
├── data/
│ └── commands.ts # Redis command definitions
└── utils/
└── binary.ts # hex ↔ buffer conversion
```
## IPC Channels
| Channel | Type | Purpose |
|---------|------|---------|
| `redis:connect` | invoke | Create Redis connection |
| `redis:disconnect` | invoke | Close connection |
| `redis:execute` | invoke | Execute arbitrary command |
| `redis:getInfo` | invoke | INFO server info |
| `redis:ping` | invoke | Health check ping |
| `redis:isConnected` | invoke | Check connection status |
| `redis:scanKeys` | invoke | SCAN with pattern |
| `redis:getKeyType` | invoke | TYPE of a key |
| `redis:getKeyTTL` | invoke | TTL of a key |
| `redis:deleteKey` | invoke | DEL a single key |
| `redis:existsKey` | invoke | EXISTS check |
| `redis:getString` | invoke | GET string value |
| `redis:setString` | invoke | SET string value |
| `redis:hashScan` | invoke | HSCAN fields |
| `redis:hashSet` | invoke | HSET field |
| `redis:hashDel` | invoke | HDEL field |
| `redis:listRange` | invoke | LRANGE elements |
| `redis:listLength` | invoke | LLEN |
| `redis:listPush` | invoke | RPUSH elements |
| `redis:listSet` | invoke | LSET element |
| `redis:listRemove` | invoke | LREM elements |
| `redis:setMembers` | invoke | SMEMBERS |
| `redis:setSize` | invoke | SCARD |
| `redis:setAdd` | invoke | SADD members |
| `redis:setRemove` | invoke | SREM members |
| `redis:zsetRange` | invoke | ZRANGE with scores |
| `redis:zsetSize` | invoke | ZCARD |
| `redis:zsetAdd` | invoke | ZADD member |
| `redis:zsetRemove` | invoke | ZREM members |
| `redis:streamInfo` | invoke | XINFO STREAM |
| `redis:streamRange` | invoke | XRANGE entries |
| `redis:streamAdd` | invoke | XADD entry |
| `redis:streamTrim` | invoke | XTRIM |
| `redis:streamDel` | invoke | XDEL entries |
| `redis:streamGroups` | invoke | XINFO GROUPS |
| `redis:streamConsumers` | invoke | XINFO CONSUMERS |
| `redis:streamGroupCreate` | invoke | XGROUP CREATE |
| `redis:streamGroupDestroy` | invoke | XGROUP DESTROY |
| `redis:streamAck` | invoke | XACK messages |
| `redis:batchDelete` | invoke | Pipeline DEL multiple keys |
| `redis:renameKey` | invoke | RENAME key |
| `redis:setTTL` | invoke | EXPIRE/PERSIST |
| `redis:selectDb` | invoke | SELECT database |
| `redis:dbSize` | invoke | DBSIZE |
| `redis:slowLogGet` | invoke | SLOWLOG GET |
| `redis:slowLogLen` | invoke | SLOWLOG LEN |
| `redis:memoryUsage` | invoke | MEMORY USAGE |
| `redis:subscribe` | invoke | SUBSCRIBE to channels |
| `redis:unsubscribe` | invoke | UNSUBSCRIBE from channels |
| `redis:subscribeMessage` | send | Push subscribed message to renderer |
| `redis:monitor` | invoke | Start MONITOR stream |
| `redis:monitorStop` | invoke | Stop MONITOR stream |
| `redis:monitorMessage` | send | Push monitored command to renderer |
| `redis:getCommandLog` | invoke | Get command execution log |
| `redis:clearCommandLog` | invoke | Clear command log |
| `redis:decodeValue` | invoke | Decode value by format |
| `redis:detectFormat` | invoke | Auto-detect value format |
| `redis:customFormat` | invoke | Custom external formatter |
| `redis:rejsonGet` | invoke | JSON.GET value |
| `redis:rejsonSet` | invoke | JSON.SET value |
| `storage:getConnections` | invoke | Read connections |
| `storage:saveConnection` | invoke | Save connection |
| `storage:deleteConnection` | invoke | Delete connection |
| `storage:reorderConnections` | invoke | Reorder connections |
| `storage:getSettings` | invoke | Read settings |
| `storage:saveSettings` | invoke | Save settings |
| `credential:encrypt` | invoke | safeStorage encrypt |
| `credential:decrypt` | invoke | safeStorage decrypt |
| `dialog:openFile` | invoke | Open file picker |
| `theme:get` | invoke | Get OS dark mode |
| `theme:set` | send | Set theme source |
| `theme:os-updated` | send | Push OS theme change to renderer |
| `window:minimize` | send | Minimize window |
| `window:maximize` | send | Toggle maximize |
| `window:close` | send | Close window |
| `updater:check` | invoke | Check for updates |
| `updater:download` | invoke | Download update |
| `updater:install` | invoke | Quit and install update |
| `updater:status` | send | Push updater status to renderer |
## Keyboard Shortcuts
| Shortcut | Action | Scope |
|----------|--------|-------|
| `Ctrl+N` | New connection | Global |
| `Ctrl+R` | Refresh keys | Global |
| `F5` | Refresh keys | Global |
| `Delete` | Delete selected key | Global |
| `Escape` | Deselect key | Global |
## Build config
- `electron.vite.config.ts` defines three build targets: main, preload, renderer
- Entry points auto-detected (zero-config): `src/main/index.ts`, `src/preload/index.ts`, `src/renderer/index.html`
- Build output: `out/main/index.js`, `out/preload/index.mjs`, `out/renderer/` (index.html + assets/)
- `package.json` `"main"` points to `./out/main/index.js`
- electron-builder packages into `release/`
- Renderer dev server runs on port 5173
- Main/preload use `externalizeDepsPlugin()` to keep node_modules external
- `@renderer` alias resolves to `src/renderer/src`
### Runtime paths in main process
`src/main/index.ts` uses `__dirname` (resolved to `out/main/` at runtime) to locate siblings:
| Target | Expression |
|--------|-----------|
| Preload script | `join(__dirname, '../preload/index.mjs')` |
| Renderer HTML | `join(__dirname, '../renderer/index.html')` |
| App icon | `join(__dirname, '../../build/icons/icon_256.png')` |
If output directory or file extensions change, update these paths accordingly.
## TypeScript
- Root `tsconfig.json` references `tsconfig.node.json` (main + preload) and `tsconfig.web.json` (renderer)
- Node target (`tsconfig.node.json`) includes `src/main/**/*`, `src/preload/**/*.ts`, `electron.vite.config.ts`
- Web target (`tsconfig.web.json`) includes `src/renderer/src/**/*`, `src/preload/**/*.d.ts`
- Both use `verbatimModuleSyntax` (type-only imports required) and `moduleResolution: "bundler"`
- `src/preload/index.d.ts` declares the `ElectronAPI` interface and augments global `Window`
## Security
- `contextIsolation: true` - renderer has zero Node.js access
- `nodeIntegration: false` - no Node globals in renderer
- `safeStorage` - OS-level encryption for credentials
- All Redis operations go through typed IPC to main process
## Legacy
The `legacy/` directory contains the old Vue 2 + Webpack 4 codebase. It is gitignored and not part of the v2 build. See `legacy/AGENTS.md` for its documentation.
## Known issues
### Electron binary not installed after `npm install`
The `allowScripts` field in `package.json` blocks electron's postinstall script (`node install.js`), so `npm install` only fetches the npm package without downloading the ~100 MB binary. `npm run dev` then fails with `Error: Electron failed to install correctly` or `spawn ... ENOENT`.
**Fix:** Run the install script manually after `npm install`:
```bash
node node_modules/electron/install.js
```
If the download is slow (GitHub releases), use a mirror:
```bash
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" node node_modules/electron/install.js
```
> `package.json` references `scripts/fix-electron.sh` (`npm run fix:electron`) but the script does not exist yet.