opencode-account-manager 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/tui/Dashboard.d.ts.map +1 -1
- package/dist/tui/Dashboard.js +172 -156
- package/dist/tui/Dashboard.js.map +1 -1
- package/dist/tui/components/ActionPalette.d.ts +16 -0
- package/dist/tui/components/ActionPalette.d.ts.map +1 -0
- package/dist/tui/components/ActionPalette.js +106 -0
- package/dist/tui/components/ActionPalette.js.map +1 -0
- package/dist/tui/components/index.d.ts +1 -0
- package/dist/tui/components/index.d.ts.map +1 -1
- package/dist/tui/components/index.js +3 -1
- package/dist/tui/components/index.js.map +1 -1
- package/docs/ROADMAP.md +19 -1
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/tui/Dashboard.tsx +205 -173
- package/src/tui/components/ActionPalette.tsx +120 -0
- package/src/tui/components/index.ts +1 -0
|
@@ -11,4 +11,5 @@ export { PasswordInput } from "./PasswordInput";
|
|
|
11
11
|
export { FileBrowser } from "./FileBrowser";
|
|
12
12
|
export { ExportModal } from "./ExportModal";
|
|
13
13
|
export { ImportModal } from "./ImportModal";
|
|
14
|
+
export { ActionPalette, PaletteAction } from "./ActionPalette";
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tui/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tui/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ImportModal = exports.ExportModal = exports.FileBrowser = exports.PasswordInput = exports.SectionBox = exports.McpServerList = exports.ProviderList = exports.MenuBar = exports.AccountList = exports.AccountRow = exports.StatsRow = exports.StatCard = exports.StatusBadge = exports.Header = exports.Box = void 0;
|
|
3
|
+
exports.ActionPalette = exports.ImportModal = exports.ExportModal = exports.FileBrowser = exports.PasswordInput = exports.SectionBox = exports.McpServerList = exports.ProviderList = exports.MenuBar = exports.AccountList = exports.AccountRow = exports.StatsRow = exports.StatCard = exports.StatusBadge = exports.Header = exports.Box = void 0;
|
|
4
4
|
var Box_1 = require("./Box");
|
|
5
5
|
Object.defineProperty(exports, "Box", { enumerable: true, get: function () { return Box_1.Box; } });
|
|
6
6
|
var Header_1 = require("./Header");
|
|
@@ -29,4 +29,6 @@ var ExportModal_1 = require("./ExportModal");
|
|
|
29
29
|
Object.defineProperty(exports, "ExportModal", { enumerable: true, get: function () { return ExportModal_1.ExportModal; } });
|
|
30
30
|
var ImportModal_1 = require("./ImportModal");
|
|
31
31
|
Object.defineProperty(exports, "ImportModal", { enumerable: true, get: function () { return ImportModal_1.ImportModal; } });
|
|
32
|
+
var ActionPalette_1 = require("./ActionPalette");
|
|
33
|
+
Object.defineProperty(exports, "ActionPalette", { enumerable: true, get: function () { return ActionPalette_1.ActionPalette; } });
|
|
32
34
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tui/components/index.ts"],"names":[],"mappings":";;;AAAA,6BAA4B;AAAnB,0FAAA,GAAG,OAAA;AACZ,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AACf,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,uCAAgD;AAAvC,oGAAA,QAAQ,OAAA;AAAE,oGAAA,QAAQ,OAAA;AAC3B,6CAAwD;AAA/C,yGAAA,UAAU,OAAA;AAAE,0GAAA,WAAW,OAAA;AAChC,+BAA6C;AAApC,+FAAA,OAAO,OAAA;AAChB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tui/components/index.ts"],"names":[],"mappings":";;;AAAA,6BAA4B;AAAnB,0FAAA,GAAG,OAAA;AACZ,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AACf,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,uCAAgD;AAAvC,oGAAA,QAAQ,OAAA;AAAE,oGAAA,QAAQ,OAAA;AAC3B,6CAAwD;AAA/C,yGAAA,UAAU,OAAA;AAAE,0GAAA,WAAW,OAAA;AAChC,+BAA6C;AAApC,+FAAA,OAAO,OAAA;AAChB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,iDAA+D;AAAtD,8GAAA,aAAa,OAAA"}
|
package/docs/ROADMAP.md
CHANGED
|
@@ -48,12 +48,30 @@
|
|
|
48
48
|
- [x] **Tab Navigation** - Fixed Tab key to cycle through sections
|
|
49
49
|
- [x] **UI Indicator** - Show `[1] Providers [2] Accounts [3] MCP` in header
|
|
50
50
|
|
|
51
|
-
### v0.4.4 - Arrow Key Navigation
|
|
51
|
+
### v0.4.4 - Arrow Key Navigation
|
|
52
52
|
- [x] **←→ Arrow Keys** - Switch between sections (Providers ↔ Accounts ↔ MCP)
|
|
53
53
|
- [x] **↑↓ Arrow Keys** - Navigate account list, auto-enters select mode
|
|
54
54
|
- [x] **Space Key** - Toggle selection in select mode
|
|
55
55
|
- [x] **Updated Help Text** - Show arrow key hints in UI
|
|
56
56
|
|
|
57
|
+
### v0.5.0 - OpenCode-style UX (Current)
|
|
58
|
+
- [x] **Action Palette** - Press P to open command palette (like OpenCode Ctrl+P)
|
|
59
|
+
- [x] **Unified Navigation** - ↑↓ to navigate everything, Enter to expand/select
|
|
60
|
+
- [x] **Simplified Controls** - No more complex keyboard shortcuts to remember
|
|
61
|
+
- [x] **Inline Selection** - Space to toggle, selection count shown in help bar
|
|
62
|
+
- [x] **Removed MenuBar** - Clean minimal interface
|
|
63
|
+
|
|
64
|
+
**New Controls:**
|
|
65
|
+
| Key | Action |
|
|
66
|
+
|-----|--------|
|
|
67
|
+
| ↑↓ | Navigate sections and accounts |
|
|
68
|
+
| Enter | Expand section / Toggle account selection |
|
|
69
|
+
| Space | Toggle account selection |
|
|
70
|
+
| P | Open Action Palette |
|
|
71
|
+
| Q | Quit |
|
|
72
|
+
| R | Refresh |
|
|
73
|
+
| Esc | Clear selection |
|
|
74
|
+
|
|
57
75
|
---
|
|
58
76
|
|
|
59
77
|
## Future Ideas (Backlog)
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
package/src/tui/Dashboard.tsx
CHANGED
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
Header,
|
|
5
5
|
StatsRow,
|
|
6
6
|
AccountList,
|
|
7
|
-
MenuBar,
|
|
8
|
-
MenuAction,
|
|
9
7
|
ProviderList,
|
|
10
8
|
McpServerList,
|
|
11
9
|
SectionBox,
|
|
12
10
|
ExportModal,
|
|
13
11
|
ImportModal,
|
|
12
|
+
ActionPalette,
|
|
13
|
+
PaletteAction,
|
|
14
14
|
} from "./components";
|
|
15
15
|
import {
|
|
16
16
|
readPluginAccountsFile,
|
|
@@ -32,8 +32,12 @@ interface DashboardProps {
|
|
|
32
32
|
pluginPath?: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
type
|
|
36
|
-
|
|
35
|
+
type ModalType = "none" | "export" | "import" | "export-selected" | "palette";
|
|
36
|
+
|
|
37
|
+
// Navigation items
|
|
38
|
+
type NavItem =
|
|
39
|
+
| { type: "section"; id: "providers" | "accounts" | "mcp"; label: string }
|
|
40
|
+
| { type: "account"; index: number; email: string };
|
|
37
41
|
|
|
38
42
|
function safeReadPluginFile(pluginPath: string): PluginAccountsFile {
|
|
39
43
|
try {
|
|
@@ -55,10 +59,9 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
55
59
|
const [summary, setSummary] = useState({ total: 0, available: 0, limited: 0 });
|
|
56
60
|
const [message, setMessage] = useState<string | null>(null);
|
|
57
61
|
|
|
58
|
-
// UI state
|
|
59
|
-
const [
|
|
60
|
-
const [
|
|
61
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
62
|
+
// UI state - simplified
|
|
63
|
+
const [expandedSection, setExpandedSection] = useState<"providers" | "accounts" | "mcp">("accounts");
|
|
64
|
+
const [navIndex, setNavIndex] = useState(0); // Current navigation index
|
|
62
65
|
const [checkedEmails, setCheckedEmails] = useState<Set<string>>(new Set());
|
|
63
66
|
|
|
64
67
|
// Modal state
|
|
@@ -79,7 +82,6 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
79
82
|
setAccounts(file.accounts);
|
|
80
83
|
setSummary(summarizeAccounts(file.accounts));
|
|
81
84
|
setCheckedEmails(new Set());
|
|
82
|
-
setSelectedIndex(0);
|
|
83
85
|
};
|
|
84
86
|
|
|
85
87
|
const refresh = () => {
|
|
@@ -93,89 +95,175 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
93
95
|
loadAccounts();
|
|
94
96
|
}, []);
|
|
95
97
|
|
|
96
|
-
//
|
|
98
|
+
// Build navigation items list
|
|
99
|
+
const buildNavItems = (): NavItem[] => {
|
|
100
|
+
const items: NavItem[] = [
|
|
101
|
+
{ type: "section", id: "providers", label: "PROVIDERS" },
|
|
102
|
+
{ type: "section", id: "accounts", label: "ACCOUNTS" },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
// If accounts section is expanded, add account items
|
|
106
|
+
if (expandedSection === "accounts") {
|
|
107
|
+
accounts.forEach((acc, index) => {
|
|
108
|
+
items.push({ type: "account", index, email: acc.email });
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
items.push({ type: "section", id: "mcp", label: "MCP SERVERS" });
|
|
113
|
+
|
|
114
|
+
return items;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const navItems = buildNavItems();
|
|
118
|
+
|
|
119
|
+
// Palette actions
|
|
120
|
+
const paletteActions: PaletteAction[] = [
|
|
121
|
+
{ id: "refresh", label: "Refresh", shortcut: "R" },
|
|
122
|
+
{ id: "export", label: "Export All Accounts", shortcut: "E" },
|
|
123
|
+
{ id: "import", label: "Import from File", shortcut: "I" },
|
|
124
|
+
{ id: "import-am", label: "Import from Antigravity Manager", shortcut: "A" },
|
|
125
|
+
...(checkedEmails.size > 0 ? [
|
|
126
|
+
{ id: "export-selected", label: `Export Selected (${checkedEmails.size})`, shortcut: "X" },
|
|
127
|
+
{ id: "enable-selected", label: `Enable Selected (${checkedEmails.size})` },
|
|
128
|
+
{ id: "disable-selected", label: `Disable Selected (${checkedEmails.size})` },
|
|
129
|
+
{ id: "delete-selected", label: `Delete Selected (${checkedEmails.size})`, shortcut: "Del" },
|
|
130
|
+
{ id: "clear-selection", label: "Clear Selection", shortcut: "N" },
|
|
131
|
+
] : []),
|
|
132
|
+
{ id: "select-all", label: "Select All Accounts", shortcut: "Ctrl+A" },
|
|
133
|
+
{ id: "quit", label: "Quit", shortcut: "Q" },
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
// Keyboard navigation
|
|
97
137
|
useInput((input, key) => {
|
|
138
|
+
// Handle palette separately
|
|
139
|
+
if (activeModal === "palette") return;
|
|
98
140
|
if (activeModal !== "none") return;
|
|
99
141
|
|
|
100
|
-
//
|
|
101
|
-
if (key.
|
|
102
|
-
|
|
103
|
-
if (prev === "providers") return "accounts";
|
|
104
|
-
if (prev === "accounts") return "mcp";
|
|
105
|
-
return "providers";
|
|
106
|
-
});
|
|
107
|
-
if (!key.rightArrow || activeSection !== "accounts") {
|
|
108
|
-
setSelectMode(false);
|
|
109
|
-
}
|
|
142
|
+
// Open palette with Ctrl+P or P
|
|
143
|
+
if ((key.ctrl && input === "p") || input === "p" || input === "P") {
|
|
144
|
+
setActiveModal("palette");
|
|
110
145
|
return;
|
|
111
146
|
}
|
|
112
147
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (prev === "accounts") return "providers";
|
|
117
|
-
return "mcp";
|
|
118
|
-
});
|
|
119
|
-
if (activeSection !== "accounts") {
|
|
120
|
-
setSelectMode(false);
|
|
121
|
-
}
|
|
148
|
+
// Quick shortcuts
|
|
149
|
+
if (input === "q" || input === "Q") {
|
|
150
|
+
exit();
|
|
122
151
|
return;
|
|
123
152
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (input === "1") {
|
|
127
|
-
setActiveSection("providers");
|
|
128
|
-
setSelectMode(false);
|
|
153
|
+
if (input === "r" || input === "R") {
|
|
154
|
+
refresh();
|
|
129
155
|
return;
|
|
130
156
|
}
|
|
131
|
-
|
|
132
|
-
|
|
157
|
+
|
|
158
|
+
// Navigate up
|
|
159
|
+
if (key.upArrow) {
|
|
160
|
+
setNavIndex(prev => Math.max(0, prev - 1));
|
|
133
161
|
return;
|
|
134
162
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
163
|
+
|
|
164
|
+
// Navigate down
|
|
165
|
+
if (key.downArrow) {
|
|
166
|
+
setNavIndex(prev => Math.min(navItems.length - 1, prev + 1));
|
|
138
167
|
return;
|
|
139
168
|
}
|
|
140
169
|
|
|
141
|
-
//
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
170
|
+
// Enter to expand/collapse section or toggle account
|
|
171
|
+
if (key.return) {
|
|
172
|
+
const currentItem = navItems[navIndex];
|
|
173
|
+
if (currentItem?.type === "section") {
|
|
174
|
+
setExpandedSection(currentItem.id);
|
|
175
|
+
// Reset nav index to stay on section header
|
|
176
|
+
} else if (currentItem?.type === "account") {
|
|
177
|
+
// Toggle selection
|
|
178
|
+
const email = currentItem.email;
|
|
179
|
+
setCheckedEmails(prev => {
|
|
180
|
+
const next = new Set(prev);
|
|
181
|
+
if (next.has(email)) {
|
|
182
|
+
next.delete(email);
|
|
183
|
+
} else {
|
|
184
|
+
next.add(email);
|
|
185
|
+
}
|
|
186
|
+
return next;
|
|
187
|
+
});
|
|
151
188
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Space to toggle selection
|
|
193
|
+
if (input === " ") {
|
|
194
|
+
const currentItem = navItems[navIndex];
|
|
195
|
+
if (currentItem?.type === "account") {
|
|
196
|
+
const email = currentItem.email;
|
|
197
|
+
setCheckedEmails(prev => {
|
|
198
|
+
const next = new Set(prev);
|
|
199
|
+
if (next.has(email)) {
|
|
200
|
+
next.delete(email);
|
|
201
|
+
} else {
|
|
202
|
+
next.add(email);
|
|
203
|
+
}
|
|
204
|
+
return next;
|
|
205
|
+
});
|
|
160
206
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
} else {
|
|
170
|
-
next.add(email);
|
|
171
|
-
}
|
|
172
|
-
return next;
|
|
173
|
-
});
|
|
174
|
-
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Escape to clear selection
|
|
211
|
+
if (key.escape) {
|
|
212
|
+
if (checkedEmails.size > 0) {
|
|
213
|
+
setCheckedEmails(new Set());
|
|
214
|
+
showMessage("Selection cleared", 1500);
|
|
175
215
|
}
|
|
216
|
+
return;
|
|
176
217
|
}
|
|
177
218
|
});
|
|
178
219
|
|
|
220
|
+
// Handle palette action
|
|
221
|
+
const handlePaletteAction = (actionId: string) => {
|
|
222
|
+
setActiveModal("none");
|
|
223
|
+
|
|
224
|
+
switch (actionId) {
|
|
225
|
+
case "refresh":
|
|
226
|
+
refresh();
|
|
227
|
+
break;
|
|
228
|
+
case "export":
|
|
229
|
+
setActiveModal("export");
|
|
230
|
+
break;
|
|
231
|
+
case "import":
|
|
232
|
+
setActiveModal("import");
|
|
233
|
+
break;
|
|
234
|
+
case "import-am":
|
|
235
|
+
handleImportAM();
|
|
236
|
+
break;
|
|
237
|
+
case "export-selected":
|
|
238
|
+
if (checkedEmails.size > 0) {
|
|
239
|
+
setActiveModal("export-selected");
|
|
240
|
+
} else {
|
|
241
|
+
showMessage("No accounts selected", 2000);
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
case "enable-selected":
|
|
245
|
+
handleEnableSelected();
|
|
246
|
+
break;
|
|
247
|
+
case "disable-selected":
|
|
248
|
+
handleDisableSelected();
|
|
249
|
+
break;
|
|
250
|
+
case "delete-selected":
|
|
251
|
+
handleDeleteSelected();
|
|
252
|
+
break;
|
|
253
|
+
case "select-all":
|
|
254
|
+
setCheckedEmails(new Set(accounts.map(a => a.email)));
|
|
255
|
+
showMessage(`Selected ${accounts.length} accounts`, 2000);
|
|
256
|
+
break;
|
|
257
|
+
case "clear-selection":
|
|
258
|
+
setCheckedEmails(new Set());
|
|
259
|
+
showMessage("Selection cleared", 1500);
|
|
260
|
+
break;
|
|
261
|
+
case "quit":
|
|
262
|
+
exit();
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
179
267
|
// Export completion handler
|
|
180
268
|
const handleExportComplete = (filePath: string) => {
|
|
181
269
|
setActiveModal("none");
|
|
@@ -184,7 +272,6 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
184
272
|
|
|
185
273
|
// Import completion handler
|
|
186
274
|
const handleImportComplete = (importedAccounts: Account[], newCount: number, overwrittenCount: number) => {
|
|
187
|
-
// Merge imported accounts with existing (overwrite mode)
|
|
188
275
|
const file = safeReadPluginFile(resolvedPath);
|
|
189
276
|
const merged = mergeAccounts(file, importedAccounts, "merge");
|
|
190
277
|
writePluginAccountsFile(pluginPath, merged);
|
|
@@ -214,7 +301,7 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
214
301
|
|
|
215
302
|
const added = merged.accounts.length - existingFile.accounts.length;
|
|
216
303
|
showMessage(
|
|
217
|
-
`Imported from AM: ${result.accounts.length} found, ${added} new
|
|
304
|
+
`Imported from AM: ${result.accounts.length} found, ${added} new`,
|
|
218
305
|
5000
|
|
219
306
|
);
|
|
220
307
|
|
|
@@ -241,7 +328,7 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
241
328
|
writePluginAccountsFile(pluginPath, file);
|
|
242
329
|
showMessage(`Enabled ${count} accounts`, 3000);
|
|
243
330
|
loadAccounts();
|
|
244
|
-
|
|
331
|
+
setCheckedEmails(new Set());
|
|
245
332
|
};
|
|
246
333
|
|
|
247
334
|
const handleDisableSelected = () => {
|
|
@@ -264,7 +351,7 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
264
351
|
writePluginAccountsFile(pluginPath, file);
|
|
265
352
|
showMessage(`Disabled ${count} accounts`, 3000);
|
|
266
353
|
loadAccounts();
|
|
267
|
-
|
|
354
|
+
setCheckedEmails(new Set());
|
|
268
355
|
};
|
|
269
356
|
|
|
270
357
|
const handleDeleteSelected = () => {
|
|
@@ -282,75 +369,13 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
282
369
|
writePluginAccountsFile(pluginPath, file);
|
|
283
370
|
showMessage(`Deleted ${deletedCount} accounts`, 3000);
|
|
284
371
|
loadAccounts();
|
|
285
|
-
setSelectMode(false);
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const handleSelectAll = () => {
|
|
289
|
-
setCheckedEmails(new Set(accounts.map(a => a.email)));
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const handleSelectNone = () => {
|
|
293
372
|
setCheckedEmails(new Set());
|
|
294
373
|
};
|
|
295
374
|
|
|
296
|
-
const handleAction = (action: MenuAction) => {
|
|
297
|
-
// Don't handle actions when modal is open
|
|
298
|
-
if (activeModal !== "none") return;
|
|
299
|
-
|
|
300
|
-
switch (action) {
|
|
301
|
-
case "refresh":
|
|
302
|
-
refresh();
|
|
303
|
-
break;
|
|
304
|
-
case "export":
|
|
305
|
-
setActiveModal("export");
|
|
306
|
-
break;
|
|
307
|
-
case "import-file":
|
|
308
|
-
setActiveModal("import");
|
|
309
|
-
break;
|
|
310
|
-
case "import-am":
|
|
311
|
-
handleImportAM();
|
|
312
|
-
break;
|
|
313
|
-
case "toggle-select-mode":
|
|
314
|
-
if (activeSection === "accounts") {
|
|
315
|
-
setSelectMode(prev => !prev);
|
|
316
|
-
setCheckedEmails(new Set());
|
|
317
|
-
setSelectedIndex(0);
|
|
318
|
-
} else {
|
|
319
|
-
showMessage("Switch to Accounts section first (Tab)", 2000);
|
|
320
|
-
}
|
|
321
|
-
break;
|
|
322
|
-
case "select-all":
|
|
323
|
-
handleSelectAll();
|
|
324
|
-
break;
|
|
325
|
-
case "select-none":
|
|
326
|
-
handleSelectNone();
|
|
327
|
-
break;
|
|
328
|
-
case "enable-selected":
|
|
329
|
-
handleEnableSelected();
|
|
330
|
-
break;
|
|
331
|
-
case "disable-selected":
|
|
332
|
-
handleDisableSelected();
|
|
333
|
-
break;
|
|
334
|
-
case "delete-selected":
|
|
335
|
-
handleDeleteSelected();
|
|
336
|
-
break;
|
|
337
|
-
case "export-selected":
|
|
338
|
-
if (checkedEmails.size === 0) {
|
|
339
|
-
showMessage("No accounts selected", 2000);
|
|
340
|
-
} else {
|
|
341
|
-
setActiveModal("export-selected");
|
|
342
|
-
}
|
|
343
|
-
break;
|
|
344
|
-
case "quit":
|
|
345
|
-
exit();
|
|
346
|
-
break;
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
|
|
350
375
|
// Calculate stats
|
|
351
376
|
const configSummary = opencodeInfo ? getConfigSummary(opencodeInfo) : null;
|
|
352
377
|
|
|
353
|
-
// Get accounts to export
|
|
378
|
+
// Get accounts to export
|
|
354
379
|
const getAccountsForExport = (): Account[] => {
|
|
355
380
|
if (activeModal === "export-selected") {
|
|
356
381
|
return accounts.filter(acc => checkedEmails.has(acc.email));
|
|
@@ -358,7 +383,15 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
358
383
|
return accounts;
|
|
359
384
|
};
|
|
360
385
|
|
|
361
|
-
//
|
|
386
|
+
// Find current nav item for highlighting
|
|
387
|
+
const currentNavItem = navItems[navIndex];
|
|
388
|
+
const isOnSection = (id: string) => currentNavItem?.type === "section" && currentNavItem.id === id;
|
|
389
|
+
const getAccountNavState = (index: number) => {
|
|
390
|
+
const item = navItems[navIndex];
|
|
391
|
+
return item?.type === "account" && item.index === index;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// Render modals
|
|
362
395
|
if (activeModal === "export" || activeModal === "export-selected") {
|
|
363
396
|
return (
|
|
364
397
|
<Box flexDirection="column" padding={1}>
|
|
@@ -392,59 +425,52 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
392
425
|
stats={[
|
|
393
426
|
{ label: "Providers", value: configSummary?.providers || 0, color: "cyan" },
|
|
394
427
|
{ label: "Models", value: configSummary?.models || 0, color: "yellow" },
|
|
395
|
-
{ label: "MCP
|
|
396
|
-
{ label: "MCP Off", value: configSummary?.mcpDisabled || 0, color: "red" },
|
|
428
|
+
{ label: "MCP", value: configSummary?.mcpEnabled || 0, color: "green" },
|
|
397
429
|
{ label: "Accounts", value: summary.total, color: "white" },
|
|
398
430
|
{ label: "Available", value: summary.available, color: "green" },
|
|
399
431
|
{ label: "Limited", value: summary.limited, color: "yellow" },
|
|
400
432
|
]}
|
|
401
433
|
/>
|
|
402
434
|
|
|
403
|
-
{/*
|
|
435
|
+
{/* Help bar */}
|
|
404
436
|
<Box marginY={1}>
|
|
405
|
-
<Text dimColor
|
|
406
|
-
<Text color=
|
|
407
|
-
|
|
408
|
-
</Text>
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
</Text>
|
|
413
|
-
<Text> </Text>
|
|
414
|
-
<Text color={activeSection === "mcp" ? "cyan" : "gray"} bold={activeSection === "mcp"}>
|
|
415
|
-
[3] MCP
|
|
416
|
-
</Text>
|
|
417
|
-
<Text dimColor> (←→ or Tab to switch, ↑↓ in Accounts)</Text>
|
|
437
|
+
<Text dimColor>↑↓ navigate • Enter expand/select • Space toggle • </Text>
|
|
438
|
+
<Text color="cyan" bold>P</Text>
|
|
439
|
+
<Text dimColor> actions • </Text>
|
|
440
|
+
<Text dimColor>Q quit</Text>
|
|
441
|
+
{checkedEmails.size > 0 && (
|
|
442
|
+
<Text color="yellow"> • {checkedEmails.size} selected</Text>
|
|
443
|
+
)}
|
|
418
444
|
</Box>
|
|
419
445
|
|
|
420
446
|
{/* Providers Section */}
|
|
421
447
|
<SectionBox
|
|
422
448
|
title="PROVIDERS"
|
|
423
|
-
borderColor={
|
|
424
|
-
collapsed={
|
|
449
|
+
borderColor={isOnSection("providers") ? "cyan" : (expandedSection === "providers" ? "white" : "gray")}
|
|
450
|
+
collapsed={expandedSection !== "providers"}
|
|
425
451
|
>
|
|
426
452
|
{opencodeInfo && <ProviderList providers={opencodeInfo.providers} />}
|
|
427
453
|
</SectionBox>
|
|
428
454
|
|
|
429
455
|
{/* Plugin Accounts Section */}
|
|
430
456
|
<SectionBox
|
|
431
|
-
title={`
|
|
432
|
-
borderColor={
|
|
433
|
-
collapsed={
|
|
457
|
+
title={`ACCOUNTS (${opencodeInfo?.plugins[0]?.name || "antigravity-auth"})`}
|
|
458
|
+
borderColor={isOnSection("accounts") || (currentNavItem?.type === "account") ? "cyan" : (expandedSection === "accounts" ? "white" : "gray")}
|
|
459
|
+
collapsed={expandedSection !== "accounts"}
|
|
434
460
|
>
|
|
435
461
|
<AccountList
|
|
436
462
|
accounts={accounts}
|
|
437
|
-
selectedIndex={
|
|
463
|
+
selectedIndex={currentNavItem?.type === "account" ? currentNavItem.index : -1}
|
|
438
464
|
checkedEmails={checkedEmails}
|
|
439
|
-
showCheckbox={
|
|
465
|
+
showCheckbox={true}
|
|
440
466
|
/>
|
|
441
467
|
</SectionBox>
|
|
442
468
|
|
|
443
469
|
{/* MCP Servers Section */}
|
|
444
470
|
<SectionBox
|
|
445
471
|
title="MCP SERVERS"
|
|
446
|
-
borderColor={
|
|
447
|
-
collapsed={
|
|
472
|
+
borderColor={isOnSection("mcp") ? "cyan" : (expandedSection === "mcp" ? "white" : "gray")}
|
|
473
|
+
collapsed={expandedSection !== "mcp"}
|
|
448
474
|
>
|
|
449
475
|
{opencodeInfo && <McpServerList servers={opencodeInfo.mcpServers} />}
|
|
450
476
|
</SectionBox>
|
|
@@ -454,21 +480,27 @@ export function Dashboard({ pluginPath }: DashboardProps) {
|
|
|
454
480
|
<Text dimColor>Config: {opencodeInfo?.configPath || "N/A"}</Text>
|
|
455
481
|
</Box>
|
|
456
482
|
|
|
457
|
-
{/* Menu */}
|
|
458
|
-
<Box marginTop={1}>
|
|
459
|
-
<MenuBar
|
|
460
|
-
onSelect={handleAction}
|
|
461
|
-
selectMode={selectMode}
|
|
462
|
-
selectedCount={checkedEmails.size}
|
|
463
|
-
/>
|
|
464
|
-
</Box>
|
|
465
|
-
|
|
466
483
|
{/* Message */}
|
|
467
484
|
{message && (
|
|
468
485
|
<Box marginTop={1}>
|
|
469
486
|
<Text color="green">→ {message}</Text>
|
|
470
487
|
</Box>
|
|
471
488
|
)}
|
|
489
|
+
|
|
490
|
+
{/* Action Palette overlay */}
|
|
491
|
+
{activeModal === "palette" && (
|
|
492
|
+
<Box
|
|
493
|
+
position="absolute"
|
|
494
|
+
marginTop={3}
|
|
495
|
+
marginLeft={10}
|
|
496
|
+
>
|
|
497
|
+
<ActionPalette
|
|
498
|
+
actions={paletteActions}
|
|
499
|
+
onSelect={handlePaletteAction}
|
|
500
|
+
onClose={() => setActiveModal("none")}
|
|
501
|
+
/>
|
|
502
|
+
</Box>
|
|
503
|
+
)}
|
|
472
504
|
</Box>
|
|
473
505
|
);
|
|
474
506
|
}
|