import Adw from 'gi://Adw'; import Gtk from 'gi://Gtk'; import Gio from 'gi://Gio'; import { ExtensionPreferences, gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; export default class SingBoxStatusPreferences extends ExtensionPreferences { fillPreferencesWindow(window) { const settings = this.getSettings(); const page = new Adw.PreferencesPage({ title: _('General'), icon_name: 'network-vpn-symbolic', }); window.add(page); // --- Connection ------------------------------------------------ const connGroup = new Adw.PreferencesGroup({ title: _('Clash API'), description: _('Matches experimental_clash_api in your sing-box config.'), }); page.add(connGroup); connGroup.add(this._entryRow(settings, 'api-url', _('API URL'), _('e.g. http://127.0.0.1:9090'))); connGroup.add(this._passwordRow(settings, 'api-secret', _('Secret'), _('Bearer token; leave empty if none.'))); connGroup.add(this._entryRow(settings, 'dashboard-url', _('Dashboard URL'), _('Opened by “Open dashboard”.'))); // --- Display --------------------------------------------------- const dispGroup = new Adw.PreferencesGroup({ title: _('Display') }); page.add(dispGroup); dispGroup.add(this._switchRow(settings, 'show-outbound', _('Show active outbound'), _('Show the current outbound name in the top bar.'))); dispGroup.add(this._switchRow(settings, 'show-speed', _('Show speed'), _('Show upload/download speed in the top bar.'))); dispGroup.add(this._entryRow(settings, 'selector-group', _('Panel selector group'), _('Group shown in the panel; a URLTest/Fallback name is also accepted. Empty = first switchable (Selector) group, otherwise the first group.'))); const intervalRow = new Adw.SpinRow({ title: _('Refresh interval'), subtitle: _('Seconds between Clash API polls.'), adjustment: new Gtk.Adjustment({ lower: 1, upper: 30, step_increment: 1 }), }); settings.bind('refresh-interval', intervalRow, 'value', Gio.SettingsBindFlags.DEFAULT); dispGroup.add(intervalRow); // --- Connectivity --------------------------------------------- const connCheckGroup = new Adw.PreferencesGroup({ title: _('Connectivity check'), description: _('Actively probe the active outbound to tell “up but no internet” from “up and reachable”. Adds connectivity-based status-dot states (four in total).'), }); page.add(connCheckGroup); const checkRow = this._switchRow(settings, 'check-connectivity', _('Probe active outbound'), _('Periodically sends a background request through the current outbound to the Probe URL below (an external site by default). Off = no background traffic.')); connCheckGroup.add(checkRow); const connIntervalRow = new Adw.SpinRow({ title: _('Probe interval'), subtitle: _('Seconds between outbound probes.'), adjustment: new Gtk.Adjustment({ lower: 5, upper: 600, step_increment: 5 }), }); settings.bind('connectivity-interval', connIntervalRow, 'value', Gio.SettingsBindFlags.DEFAULT); settings.bind('check-connectivity', connIntervalRow, 'sensitive', Gio.SettingsBindFlags.GET); connCheckGroup.add(connIntervalRow); const connUrlRow = this._entryRow(settings, 'connectivity-url', _('Probe / test URL'), _('Used by both the connectivity check and the manual Test latency action.')); connCheckGroup.add(connUrlRow); } _entryRow(settings, key, title, subtitle) { const row = new Adw.EntryRow({ title }); // Adw.EntryRow has no subtitle; surface the hint as a tooltip instead. if (subtitle) row.set_tooltip_text(subtitle); row.text = settings.get_string(key); row.connect('changed', () => settings.set_string(key, row.text)); settings.connect(`changed::${key}`, () => { const v = settings.get_string(key); if (row.text !== v) row.text = v; }); return row; } _passwordRow(settings, key, title, subtitle) { const row = new Adw.PasswordEntryRow({ title }); if (subtitle) row.set_tooltip_text(subtitle); row.text = settings.get_string(key); row.connect('changed', () => settings.set_string(key, row.text)); settings.connect(`changed::${key}`, () => { const v = settings.get_string(key); if (row.text !== v) row.text = v; }); return row; } _switchRow(settings, key, title, subtitle) { const row = new Adw.SwitchRow({ title, subtitle }); settings.bind(key, row, 'active', Gio.SettingsBindFlags.DEFAULT); return row; } }