opencode-antigravity-config 1.0.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/renderer.js ADDED
@@ -0,0 +1,316 @@
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const pages = [0, 1, 2, 3, 4, 5].map(i => document.getElementById('page' + i));
3
+ let currentPage = 0;
4
+
5
+ document.getElementById('btnMinimize').addEventListener('click', () => window.api.minimize());
6
+
7
+ // Close confirmation modal
8
+ document.getElementById('btnClose').addEventListener('click', () => {
9
+ showModal(window.t('Close confirm title') || 'Tutup Aplikasi',
10
+ `<p style="margin-bottom:12px;">${window.t('Close confirm text') || 'Apakah Anda yakin ingin keluar dari program?'}</p>
11
+ <div style="display:flex;gap:8px;justify-content:flex-end;">
12
+ <button class="btn secondary" onclick="document.getElementById('modal').classList.remove('visible')">${window.t('Close cancel') || 'Tidak'}</button>
13
+ <button class="btn primary" id="btnConfirmExit" style="background:#E81123;">${window.t('Close exit') || 'Ya, Keluar'}</button>
14
+ </div>`);
15
+ setTimeout(() => {
16
+ const cb = document.getElementById('btnConfirmExit');
17
+ if (cb) cb.addEventListener('click', () => window.api.close());
18
+ }, 50);
19
+ });
20
+
21
+ // Language Toggle
22
+ const btnLang = document.getElementById('btnLang');
23
+ if (btnLang) {
24
+ btnLang.addEventListener('click', () => {
25
+ const newLang = window.currentLang === 'ID' ? 'EN' : 'ID';
26
+ if (typeof window.updateLanguage === 'function') window.updateLanguage(newLang);
27
+ });
28
+ // Initial init
29
+ if (typeof window.updateLanguage === 'function') window.updateLanguage('ID');
30
+ }
31
+
32
+ const modal = document.getElementById('modal');
33
+ const modalTitle = document.getElementById('modalTitle');
34
+ const modalBody = document.getElementById('modalBody');
35
+ function showModal(t, h) { modalTitle.textContent = t; modalBody.innerHTML = h; modal.classList.add('visible'); }
36
+ function hideModal() { modal.classList.remove('visible'); }
37
+ document.getElementById('btnModalClose').addEventListener('click', hideModal);
38
+ modal.addEventListener('click', e => { if (e.target === modal) hideModal(); });
39
+
40
+ function navigateTo(idx) {
41
+ pages.forEach(p => { if (p) p.classList.remove('active'); });
42
+ if (pages[idx]) pages[idx].classList.add('active');
43
+ currentPage = idx;
44
+ for (let i = 0; i < 6; i++) {
45
+ const s = document.getElementById('step' + i);
46
+ if (!s) continue;
47
+ s.classList.remove('active', 'completed');
48
+ if (i < idx) s.classList.add('completed');
49
+ else if (i === idx) s.classList.add('active');
50
+ }
51
+ }
52
+
53
+ function escapeHtml(s) { return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
54
+
55
+ // ── Page 0: Welcome ──
56
+ document.getElementById('btnStart').addEventListener('click', () => { navigateTo(1); });
57
+ (async () => { const m = await window.api.getConfigMeta(); const el = document.getElementById('configMetaText'); if (el && m) el.textContent = `Config v${m.version} • ${m.date} • ${m.fileCount} files`; })();
58
+
59
+ // Agent Model Selection Logic
60
+ const AGENT_GROUPS = {
61
+ heavy: ['sisyphus', 'prometheus', 'metis', 'oracle'],
62
+ standard: ['hephaestus', 'momus', 'atlas'],
63
+ light: ['librarian', 'explore', 'multimodal_looker']
64
+ };
65
+ let agentAdvancedMode = false;
66
+
67
+ // Toggle Advanced / Group mode
68
+ document.getElementById('btnAgentAdvanced').addEventListener('click', () => {
69
+ agentAdvancedMode = !agentAdvancedMode;
70
+ document.getElementById('agentGroupMode').style.display = agentAdvancedMode ? 'none' : '';
71
+ document.getElementById('agentAdvancedMode').style.display = agentAdvancedMode ? '' : 'none';
72
+ document.getElementById('btnAgentAdvanced').textContent = agentAdvancedMode ? '← Simple' : '⚙ Advanced';
73
+ if (!agentAdvancedMode) syncAgentsFromGroups();
74
+ });
75
+
76
+ // When group dropdown changes → update per-agent dropdowns
77
+ function syncAgentsFromGroups() {
78
+ Object.entries(AGENT_GROUPS).forEach(([group, agents]) => {
79
+ const gSel = document.getElementById('selGroup' + group.charAt(0).toUpperCase() + group.slice(1));
80
+ if (!gSel) return;
81
+ agents.forEach(a => {
82
+ const aSel = document.getElementById('selAgent_' + a);
83
+ if (aSel) aSel.value = gSel.value;
84
+ });
85
+ });
86
+ }
87
+ ['selGroupHeavy', 'selGroupStandard', 'selGroupLight'].forEach(id => {
88
+ const el = document.getElementById(id);
89
+ if (el) el.addEventListener('change', () => { if (!agentAdvancedMode) syncAgentsFromGroups(); });
90
+ });
91
+
92
+ // Global Template Presets
93
+ const selGlobalPreset = document.getElementById('selGlobalPreset');
94
+ if (selGlobalPreset) {
95
+ selGlobalPreset.addEventListener('change', (e) => {
96
+ const val = e.target.value;
97
+ if (!val) return;
98
+
99
+ const heavy = document.getElementById('selGroupHeavy');
100
+ const standard = document.getElementById('selGroupStandard');
101
+ const light = document.getElementById('selGroupLight');
102
+ const lookup = {
103
+ 'antigravity_default': ['google/antigravity-gemini-3-1-pro', 'google/antigravity-gemini-3-1-pro', 'google/antigravity-gemini-3-flash'],
104
+ 'max_reasoning': ['google/antigravity-claude-opus-4-6-thinking', 'google/antigravity-gemini-3-deep-think', 'google/antigravity-claude-sonnet-4-6'],
105
+ 'fast_cheap': ['google/antigravity-gemini-3-flash', 'google/antigravity-gemini-3-flash', 'google/antigravity-gemini-3-flash'],
106
+ 'claude_only': ['google/antigravity-claude-opus-4-6-thinking', 'google/antigravity-claude-sonnet-4-6', 'google/antigravity-claude-sonnet-4-6']
107
+ };
108
+
109
+ if (lookup[val] && heavy && standard && light) {
110
+ heavy.value = lookup[val][0];
111
+ standard.value = lookup[val][1];
112
+ light.value = lookup[val][2];
113
+ syncAgentsFromGroups(); // loop updates UI
114
+ }
115
+ });
116
+ }
117
+
118
+ // Gather agent model map (used during install)
119
+ function gatherAgentModels() {
120
+ const map = {};
121
+ const allAgents = ['sisyphus', 'prometheus', 'metis', 'oracle', 'hephaestus', 'momus', 'atlas', 'librarian', 'explore', 'multimodal_looker'];
122
+ if (agentAdvancedMode) {
123
+ allAgents.forEach(a => { const sel = document.getElementById('selAgent_' + a); if (sel) map[a] = sel.value; });
124
+ } else {
125
+ Object.entries(AGENT_GROUPS).forEach(([group, agents]) => {
126
+ const gSel = document.getElementById('selGroup' + group.charAt(0).toUpperCase() + group.slice(1));
127
+ if (!gSel) return;
128
+ agents.forEach(a => { map[a] = gSel.value; });
129
+ });
130
+ }
131
+ return map;
132
+ }
133
+
134
+ document.getElementById('btnVersionCheck').addEventListener('click', async () => {
135
+ const btn = document.getElementById('btnVersionCheck'); btn.textContent = '...'; btn.disabled = true;
136
+ const r = await window.api.checkVersion();
137
+ if (r.noEndpoint) showModal('Version Check', `<p>Current: <strong>v${r.current}</strong></p><p style="color:var(--text-secondary);font-size:11px;margin-top:6px;">No update endpoint configured.</p>`);
138
+ else if (r.upToDate) showModal('Version Check', `<p style="color:var(--accent-green)">✓ Latest: v${r.current}</p>`);
139
+ else showModal('Update Available', `<p>Current: v${r.current}</p><p>Latest: <strong style="color:var(--accent-green)">v${r.latest}</strong></p>`);
140
+ btn.textContent = '🔄 Updates'; btn.disabled = false;
141
+ });
142
+
143
+ document.getElementById('btnUninstall').addEventListener('click', async () => {
144
+ showModal('Uninstall', `<p style="margin-bottom:10px;">Remove all config files from <code>~/.config/opencode/</code>?</p><p style="color:var(--text-secondary);font-size:11px;margin-bottom:12px;">Safety backup created before deletion.</p><div style="display:flex;gap:8px;justify-content:flex-end;"><button class="btn secondary" onclick="document.getElementById('modal').classList.remove('visible')">Cancel</button><button class="btn primary" id="btnConfirmUninstall" style="background:#E81123;">Uninstall</button></div>`);
145
+ setTimeout(() => {
146
+ const cb = document.getElementById('btnConfirmUninstall');
147
+ if (cb) cb.addEventListener('click', async () => {
148
+ cb.textContent = '...'; cb.disabled = true;
149
+ const r = await window.api.uninstallConfig();
150
+ showModal(r.success ? 'Uninstalled' : 'Failed', r.success
151
+ ? `<p style="color:var(--accent-green)">✓ Removed ${r.removed.length} files</p><p style="font-size:11px;color:var(--text-secondary);margin-top:4px;">${r.removed.join(', ')}</p><p style="font-size:11px;color:var(--text-secondary);margin-top:4px;">Backup: ${r.backupDir}</p>`
152
+ : `<p style="color:var(--accent-red)">✗ ${r.message}</p>`);
153
+ });
154
+ }, 50);
155
+ });
156
+
157
+ // ── Page 1: oMo Agent Model ──
158
+ document.getElementById('btnPrev1').addEventListener('click', () => navigateTo(0));
159
+ document.getElementById('btnNext1').addEventListener('click', async () => { navigateTo(2); await runSystemCheck(); });
160
+
161
+ // ── Page 2: System Check ──
162
+ document.getElementById('btnPrev2').addEventListener('click', () => navigateTo(1));
163
+ document.getElementById('btnNext2').addEventListener('click', () => navigateTo(3));
164
+ document.getElementById('btnRecheck').addEventListener('click', () => runSystemCheck());
165
+
166
+ async function runSystemCheck() {
167
+ const pp = document.getElementById('prereqPanel');
168
+ pp.innerHTML = `<p style="color:var(--text-secondary);font-size:11px;text-align:center;margin-top:20px;">${window.t('Checking...') || 'Checking...'}</p>`;
169
+ document.getElementById('btnNext2').disabled = true; document.getElementById('btnNext2').classList.add('disabled');
170
+ const res = await window.api.checkSystem(); pp.innerHTML = '';
171
+ let fail = false;
172
+ res.items.forEach(it => {
173
+ if (it.status === 'fail') fail = true;
174
+ const ic = it.status === 'ok' ? '✓' : it.status === 'warn' ? '⚠' : '✗';
175
+ pp.innerHTML += `<div class="prereq-item"><div class="prereq-info"><div class="label">${it.label}</div><div class="value">${it.value}</div></div><div class="prereq-status status-${it.status}">${ic}</div></div>`;
176
+ });
177
+ if (!fail) { document.getElementById('btnNext2').disabled = false; document.getElementById('btnNext2').classList.remove('disabled'); }
178
+ }
179
+
180
+ // ── Page 3: Config ──
181
+ document.getElementById('btnPrev3').addEventListener('click', () => navigateTo(2));
182
+ document.getElementById('btnNext3').addEventListener('click', () => { navigateTo(4); startInstallation(); });
183
+
184
+ (async () => {
185
+ const k = await window.api.getExistingKeys();
186
+ if (k.supermemoryKey) document.getElementById('txtSupermemoryKey').value = k.supermemoryKey;
187
+ if (k.openSyncKey) document.getElementById('txtOpenSyncKey').value = k.openSyncKey;
188
+ if (k.openSyncUrl) document.getElementById('txtOpenSyncUrl').value = k.openSyncUrl;
189
+ valField('txtSupermemoryKey', 'valSupermemory'); valField('txtOpenSyncKey', 'valOpenSyncKey'); valField('txtOpenSyncUrl', 'valOpenSyncUrl');
190
+ })();
191
+
192
+ function valField(iid, vid) {
193
+ const v = document.getElementById(iid)?.value.trim() || '';
194
+ const el = document.getElementById(vid); if (!el) return;
195
+ if (!v) { el.textContent = window.t('(Optional)') || '(Optional)'; el.className = 'val-indicator val-empty'; }
196
+ else if (v.length < 8 || v.startsWith('__')) { el.textContent = window.t('✗ Invalid') || '✗ Invalid'; el.className = 'val-indicator val-bad'; }
197
+ else { el.textContent = window.t('✓ Set') || '✓ Set'; el.className = 'val-indicator val-ok'; }
198
+ }
199
+ window.reValInputs = () => { valField('txtSupermemoryKey', 'valSupermemory'); valField('txtOpenSyncKey', 'valOpenSyncKey'); valField('txtOpenSyncUrl', 'valOpenSyncUrl'); };
200
+ [['txtSupermemoryKey', 'valSupermemory'], ['txtOpenSyncKey', 'valOpenSyncKey'], ['txtOpenSyncUrl', 'valOpenSyncUrl']].forEach(([i, v]) => document.getElementById(i).addEventListener('input', () => { valField(i, v); updatePluginLogic(); }));
201
+
202
+ function updatePluginLogic() {
203
+ const total = document.querySelectorAll('.plugin-checkbox').length;
204
+ const checked = document.querySelectorAll('.plugin-checkbox:checked').length;
205
+ const elCount = document.getElementById('pluginCount');
206
+ if (elCount) elCount.textContent = `${checked}/${total}`;
207
+
208
+ const smCheck = document.getElementById('chkPluginSupermemory');
209
+ const smInput = document.getElementById('txtSupermemoryKey');
210
+ const smWarn = document.getElementById('warnSupermemory');
211
+ if (smCheck && smInput && smWarn) {
212
+ smInput.disabled = !smCheck.checked;
213
+ if (!smCheck.checked) smWarn.classList.add('hidden');
214
+ else smWarn.classList.toggle('hidden', !!smInput.value.trim());
215
+ }
216
+
217
+ const osCheck = document.getElementById('chkPluginOpenSync');
218
+ const osInputKey = document.getElementById('txtOpenSyncKey');
219
+ const osInputUrl = document.getElementById('txtOpenSyncUrl');
220
+ const osWarn = document.getElementById('warnOpenSync');
221
+ if (osCheck && osInputKey && osInputUrl && osWarn) {
222
+ osInputKey.disabled = !osCheck.checked;
223
+ osInputUrl.disabled = !osCheck.checked;
224
+ if (!osCheck.checked) osWarn.classList.add('hidden');
225
+ else osWarn.classList.toggle('hidden', !!(osInputKey.value.trim() && osInputUrl.value.trim()));
226
+ }
227
+ }
228
+ const pluginGrid = document.getElementById('pluginGrid');
229
+ if (pluginGrid) pluginGrid.addEventListener('change', updatePluginLogic);
230
+ // Initial sync
231
+ setTimeout(updatePluginLogic, 100);
232
+
233
+ // Plugin select all/none
234
+ document.getElementById('btnPluginAll').addEventListener('click', () => { document.querySelectorAll('.plugin-checkbox:not(:disabled)').forEach(c => c.checked = true); updatePluginLogic(); });
235
+ document.getElementById('btnPluginNone').addEventListener('click', () => { document.querySelectorAll('.plugin-checkbox:not(:disabled)').forEach(c => c.checked = false); updatePluginLogic(); });
236
+
237
+ // Preview Final opencode.json
238
+ const btnPreviewConfig = document.getElementById('btnPreviewConfig');
239
+ if (btnPreviewConfig) {
240
+ btnPreviewConfig.addEventListener('click', async () => {
241
+ btnPreviewConfig.textContent = '...';
242
+ const sp = []; document.querySelectorAll('.plugin-checkbox').forEach(c => { if (c.checked) sp.push(c.value); });
243
+ const r = await window.api.previewFinalOpencode(sp);
244
+ btnPreviewConfig.textContent = window.t('👁 Preview opencode.json') || '👁 Preview opencode.json';
245
+ showModal('Preview: ' + r.fileName, `<pre class="modal-code">${escapeHtml(r.content)}</pre>`);
246
+ });
247
+ }
248
+
249
+ // Preview
250
+ document.querySelectorAll('.btn-preview').forEach(b => b.addEventListener('click', async () => {
251
+ const f = b.dataset.file; b.textContent = '...';
252
+ const r = await window.api.previewConfig(f); b.textContent = '👁';
253
+ if (r.error) { showModal('Error', `<p>${r.error}</p>`); return; }
254
+ showModal('Preview: ' + r.fileName, `<pre class="modal-code">${escapeHtml(r.content)}</pre>`);
255
+ }));
256
+
257
+ // Diff
258
+ document.querySelectorAll('.btn-diff').forEach(b => b.addEventListener('click', async () => {
259
+ const f = b.dataset.file; b.textContent = '...';
260
+ const r = await window.api.diffConfig(f); b.textContent = '⇄';
261
+ if (r.error) { showModal('Error', `<p>${r.error}</p>`); return; }
262
+ if (r.isNew) { showModal('Diff: ' + r.fileName, `<p class="diff-new-badge">NEW FILE</p><pre class="modal-code">${escapeHtml(r.newContent)}</pre>`); return; }
263
+ if (!r.hasChanges) { showModal('Diff: ' + r.fileName, `<p style="color:var(--accent-green)">✓ Identical</p>`); return; }
264
+ let h = '<div class="diff-view">';
265
+ r.diffLines.forEach(d => { const c = d.type === 'add' ? 'diff-add' : d.type === 'del' ? 'diff-del' : 'diff-same'; const p = d.type === 'add' ? '+' : d.type === 'del' ? '-' : ' '; h += `<div class="diff-line ${c}"><span class="diff-prefix">${p}</span>${escapeHtml(d.content || '')}</div>`; });
266
+ showModal('Diff: ' + r.fileName, h + '</div>');
267
+ }));
268
+
269
+ // Export / Import
270
+ function gatherSettings() {
271
+ const sp = []; document.querySelectorAll('.plugin-checkbox').forEach(c => { if (c.checked) sp.push(c.value); });
272
+ return { supermemoryKey: document.getElementById('txtSupermemoryKey').value.trim(), openSyncKey: document.getElementById('txtOpenSyncKey').value.trim(), openSyncUrl: document.getElementById('txtOpenSyncUrl').value.trim(), backup: document.getElementById('chkBackup').checked, npmInstall: document.getElementById('chkNpmInstall').checked, selectedPlugins: sp, agentModels: gatherAgentModels() };
273
+ }
274
+ document.getElementById('btnExport').addEventListener('click', async () => { const r = await window.api.exportSettings(gatherSettings()); if (r.saved) showModal('Exported', `<p style="color:var(--accent-green)">✓ Saved to:<br><code style="font-size:11px;">${r.path}</code></p>`); });
275
+ document.getElementById('btnImport').addEventListener('click', async () => {
276
+ const r = await window.api.importSettings(); if (!r.loaded) { if (r.error) showModal('Error', `<p style="color:var(--accent-red)">${r.error}</p>`); return; }
277
+ const s = r.settings;
278
+ if (s.supermemoryKey) document.getElementById('txtSupermemoryKey').value = s.supermemoryKey;
279
+ if (s.openSyncKey) document.getElementById('txtOpenSyncKey').value = s.openSyncKey;
280
+ if (s.openSyncUrl) document.getElementById('txtOpenSyncUrl').value = s.openSyncUrl;
281
+ if (typeof s.backup === 'boolean') document.getElementById('chkBackup').checked = s.backup;
282
+ if (typeof s.npmInstall === 'boolean') document.getElementById('chkNpmInstall').checked = s.npmInstall;
283
+ if (s.selectedFiles) document.querySelectorAll('.file-checkbox').forEach(c => c.checked = s.selectedFiles.includes(c.value));
284
+ valField('txtSupermemoryKey', 'valSupermemory'); valField('txtOpenSyncKey', 'valOpenSyncKey'); valField('txtOpenSyncUrl', 'valOpenSyncUrl');
285
+ // Restore agent models if saved
286
+ if (s.agentModels) {
287
+ const allAgents = ['sisyphus', 'prometheus', 'metis', 'oracle', 'hephaestus', 'momus', 'atlas', 'librarian', 'explore', 'multimodal_looker'];
288
+ allAgents.forEach(a => { const sel = document.getElementById('selAgent_' + a); if (sel && s.agentModels[a]) sel.value = s.agentModels[a]; });
289
+ // Sync group dropdowns from per-agent values
290
+ Object.entries(AGENT_GROUPS).forEach(([group, agents]) => {
291
+ const gSel = document.getElementById('selGroup' + group.charAt(0).toUpperCase() + group.slice(1));
292
+ if (gSel && s.agentModels[agents[0]]) gSel.value = s.agentModels[agents[0]];
293
+ });
294
+ }
295
+ showModal('Imported', `<p style="color:var(--accent-green)">✓ Settings loaded</p>`);
296
+ });
297
+
298
+ // ── Page 4: Install ──
299
+ const logContent = document.getElementById('logContent'), progressBar = document.getElementById('installProgress');
300
+ const installTitle = document.getElementById('installTitle'), installSubtitle = document.getElementById('installSubtitle');
301
+
302
+ function startInstallation() { logContent.innerHTML = ''; progressBar.style.width = '0%'; window.api.startInstall(gatherSettings()); }
303
+ window.api.onInstallProgress(p => { progressBar.style.width = p + '%'; });
304
+ window.api.onInstallLog(l => { const d = document.createElement('div'); d.className = 'log-row log-' + l.type; const px = l.type === 'success' ? ' [OK] ' : l.type === 'error' ? ' [ERR] ' : l.type === 'warn' ? ' [!] ' : ' [i] '; d.textContent = l.message ? px + l.message : ''; logContent.appendChild(d); logContent.scrollTop = logContent.scrollHeight; });
305
+ window.api.onInstallComplete(r => {
306
+ if (r.success) { installTitle.textContent = 'Complete!'; installSubtitle.textContent = 'Semua konfigurasi berhasil di-install'; document.getElementById('txtConfigPath').textContent = r.configPath; }
307
+ else { installTitle.textContent = 'Failed'; installSubtitle.textContent = r.error; }
308
+ document.getElementById('btnNext4').classList.remove('hidden');
309
+ });
310
+
311
+ // ── Page 5: Done ──
312
+ document.getElementById('btnNext4').addEventListener('click', () => navigateTo(5));
313
+ document.getElementById('btnOpenFolder').addEventListener('click', () => window.api.openConfigFolder());
314
+ document.getElementById('btnAuthLogin').addEventListener('click', () => window.api.runAuthLogin());
315
+ document.getElementById('btnFinish').addEventListener('click', () => window.api.close());
316
+ });