- string.ts: remove dead rejsonDel function (P3#38) - AGENTS.md: add batchMemoryUsage and executeUnsafe to IPC channels table (P3#41) - connection.ts: add console.warn to silent TLS/SSH catch blocks (P3#44) - i18n: replace hardcoded strings in KeyDetail/TitleBar/Sidebar with t() (P3#37) - composables/useTypeColor.ts: extract shared typeColor, replace in 7 editors (P3#39) - CliView: extract formatRedisResult function to deduplicate (P3#39) - SetEditor/ZsetEditor/CommandLog: watch+ref -> computed (P3#40) - preload/index.d.ts: replace 15+ any types with concrete types (P3#36, P3#42) - store.ts: add ConnectionConfig version field + migrateConnections() (P3#43) - docs: update PROJECT_REVIEW.md - all 54 issues processed
13 KiB
AGENTS.md - JRedisDesktop v2
Project identity
Fork of 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
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:batchMemoryUsage |
invoke | Pipeline MEMORY USAGE multiple keys |
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 |
redis:executeUnsafe |
invoke | Execute arbitrary command (CLI bypass for blocked commands) |
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.tsdefines 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 @rendereralias resolves tosrc/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.jsonreferencestsconfig.node.json(main + preload) andtsconfig.web.json(renderer) - Node target (
tsconfig.node.json) includessrc/main/**/*,src/preload/**/*.ts,electron.vite.config.ts - Web target (
tsconfig.web.json) includessrc/renderer/src/**/*,src/preload/**/*.d.ts - Both use
verbatimModuleSyntax(type-only imports required) andmoduleResolution: "bundler" src/preload/index.d.tsdeclares theElectronAPIinterface and augments globalWindow
Security
contextIsolation: true- renderer has zero Node.js accessnodeIntegration: false- no Node globals in renderersafeStorage- 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:
node node_modules/electron/install.js
If the download is slow (GitHub releases), use a mirror:
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" node node_modules/electron/install.js
package.jsonreferencesscripts/fix-electron.sh(npm run fix:electron) but the script does not exist yet.