Skip to content

Commit b4dcaad

Browse files
committed
Merge branch 'pr-138-head' into release-integration-20260320
# Conflicts: # docs/features.md
2 parents 49d3ae5 + 646a68d commit b4dcaad

File tree

6 files changed

+404
-63
lines changed

6 files changed

+404
-63
lines changed

docs/features.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ User-facing capability map for `codex-multi-auth`.
5555
| Quick switch and search hotkeys | Faster navigation in the dashboard |
5656
| Account action hotkeys | Per-account set, refresh, toggle, and delete shortcuts |
5757
| In-dashboard settings hub | Runtime and display tuning without editing files directly |
58+
| Experimental settings hotkeys | Keyboard shortcuts for sync preview, backup export, and refresh-guard tuning |
5859
| Browser-first OAuth with manual fallback | `codex auth login` stays browser-first, while `--manual`, `--no-browser`, and `CODEX_AUTH_NO_BROWSER=1` keep login usable in browser-restricted shells |
5960

6061
Manual/non-TTY login accepts the full callback URL on stdin, so automation and host-managed shells can complete auth without relying on a local browser handoff.

docs/reference/settings.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@ Experimental currently hosts:
7474
- named local pool backup export with filename prompt
7575
- refresh guard controls (`proactiveRefreshGuardian`, `proactiveRefreshIntervalMs`)
7676

77+
Experimental TUI shortcuts:
78+
79+
- `1` sync preview
80+
- `2` named backup export
81+
- `3` toggle refresh guard
82+
- `[` or `-` decrease refresh interval
83+
- `]` or `+` increase refresh interval
84+
- `S` save and return
85+
- `Q` back
86+
- sync review also supports `A` apply
87+
7788
Sync behavior:
7889

7990
- preview is always shown before apply

lib/codex-manager/settings-hub.ts

Lines changed: 74 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import type { PluginConfig } from "../types.js";
2828
import { ANSI } from "../ui/ansi.js";
2929
import { UI_COPY } from "../ui/copy.js";
3030
import { getUiRuntimeOptions, setUiRuntimeOptions } from "../ui/runtime.js";
31-
import { type MenuItem, select } from "../ui/select.js";
31+
import { type MenuItem, select, type SelectOptions } from "../ui/select.js";
3232
import { getUnifiedSettingsPath } from "../unified-settings.js";
3333
import { sleep } from "../utils.js";
3434

@@ -281,6 +281,42 @@ type ExperimentalSettingsAction =
281281
| { type: "save" }
282282
| { type: "back" };
283283

284+
function getExperimentalSelectOptions(
285+
ui: ReturnType<typeof getUiRuntimeOptions>,
286+
help: string,
287+
onInput?: SelectOptions<ExperimentalSettingsAction>["onInput"],
288+
): SelectOptions<ExperimentalSettingsAction> {
289+
return {
290+
message: UI_COPY.settings.experimentalTitle,
291+
subtitle: UI_COPY.settings.experimentalSubtitle,
292+
help,
293+
clearScreen: true,
294+
theme: ui.theme,
295+
selectedEmphasis: "minimal",
296+
onInput,
297+
};
298+
}
299+
300+
function mapExperimentalMenuHotkey(
301+
raw: string,
302+
): ExperimentalSettingsAction | undefined {
303+
if (raw === "1") return { type: "sync" };
304+
if (raw === "2") return { type: "backup" };
305+
if (raw === "3") return { type: "toggle-refresh-guardian" };
306+
if (raw === "[" || raw === "-") return { type: "decrease-refresh-interval" };
307+
if (raw === "]" || raw === "+") return { type: "increase-refresh-interval" };
308+
const lower = raw.toLowerCase();
309+
if (lower === "q") return { type: "back" };
310+
if (lower === "s") return { type: "save" };
311+
return undefined;
312+
}
313+
314+
function mapExperimentalStatusHotkey(
315+
raw: string,
316+
): ExperimentalSettingsAction | undefined {
317+
return raw.toLowerCase() === "q" ? { type: "back" } : undefined;
318+
}
319+
284320
const BACKEND_TOGGLE_OPTIONS: BackendToggleSettingOption[] = [
285321
{
286322
key: "liveAccountSync",
@@ -1279,6 +1315,8 @@ const __testOnly = {
12791315
cloneDashboardSettings,
12801316
withQueuedRetry: withQueuedRetryForTests,
12811317
loadExperimentalSyncTarget,
1318+
mapExperimentalMenuHotkey,
1319+
mapExperimentalStatusHotkey,
12821320
promptExperimentalSettings,
12831321
persistDashboardSettingsSelection: persistDashboardSettingsSelectionForTests,
12841322
persistBackendConfigSelection: persistBackendConfigSelectionForTests,
@@ -2573,16 +2611,11 @@ async function promptExperimentalSettings(
25732611
color: "red",
25742612
},
25752613
],
2576-
{
2577-
message: UI_COPY.settings.experimentalTitle,
2578-
subtitle: UI_COPY.settings.experimentalSubtitle,
2579-
help: UI_COPY.settings.experimentalHelpMenu,
2580-
clearScreen: true,
2581-
theme: ui.theme,
2582-
selectedEmphasis: "minimal",
2583-
onInput: (raw) =>
2584-
raw.toLowerCase() === "q" ? { type: "back" } : undefined,
2585-
},
2614+
getExperimentalSelectOptions(
2615+
ui,
2616+
UI_COPY.settings.experimentalHelpMenu,
2617+
mapExperimentalMenuHotkey,
2618+
),
25862619
);
25872620
if (!action || action.type === "back") return null;
25882621
if (action.type === "save") return draft;
@@ -2647,14 +2680,11 @@ async function promptExperimentalSettings(
26472680
color: "red",
26482681
},
26492682
],
2650-
{
2651-
message: UI_COPY.settings.experimentalTitle,
2652-
subtitle: UI_COPY.settings.experimentalSubtitle,
2653-
help: UI_COPY.settings.experimentalHelpStatus,
2654-
clearScreen: true,
2655-
theme: ui.theme,
2656-
selectedEmphasis: "minimal",
2657-
},
2683+
getExperimentalSelectOptions(
2684+
ui,
2685+
UI_COPY.settings.experimentalHelpStatus,
2686+
mapExperimentalStatusHotkey,
2687+
),
26582688
);
26592689
} catch (error) {
26602690
const message =
@@ -2674,14 +2704,11 @@ async function promptExperimentalSettings(
26742704
color: "red",
26752705
},
26762706
],
2677-
{
2678-
message: UI_COPY.settings.experimentalTitle,
2679-
subtitle: UI_COPY.settings.experimentalSubtitle,
2680-
help: UI_COPY.settings.experimentalHelpStatus,
2681-
clearScreen: true,
2682-
theme: ui.theme,
2683-
selectedEmphasis: "minimal",
2684-
},
2707+
getExperimentalSelectOptions(
2708+
ui,
2709+
UI_COPY.settings.experimentalHelpStatus,
2710+
mapExperimentalStatusHotkey,
2711+
),
26852712
);
26862713
}
26872714
} finally {
@@ -2708,14 +2735,11 @@ async function promptExperimentalSettings(
27082735
color: "red",
27092736
},
27102737
],
2711-
{
2712-
message: UI_COPY.settings.experimentalTitle,
2713-
subtitle: UI_COPY.settings.experimentalSubtitle,
2714-
help: UI_COPY.settings.experimentalHelpStatus,
2715-
clearScreen: true,
2716-
theme: ui.theme,
2717-
selectedEmphasis: "minimal",
2718-
},
2738+
getExperimentalSelectOptions(
2739+
ui,
2740+
UI_COPY.settings.experimentalHelpStatus,
2741+
mapExperimentalStatusHotkey,
2742+
),
27192743
);
27202744
continue;
27212745
}
@@ -2747,14 +2771,11 @@ async function promptExperimentalSettings(
27472771
color: "red",
27482772
},
27492773
],
2750-
{
2751-
message: UI_COPY.settings.experimentalTitle,
2752-
subtitle: UI_COPY.settings.experimentalSubtitle,
2753-
help: UI_COPY.settings.experimentalHelpStatus,
2754-
clearScreen: true,
2755-
theme: ui.theme,
2756-
selectedEmphasis: "minimal",
2757-
},
2774+
getExperimentalSelectOptions(
2775+
ui,
2776+
UI_COPY.settings.experimentalHelpStatus,
2777+
mapExperimentalStatusHotkey,
2778+
),
27582779
);
27592780
continue;
27602781
}
@@ -2793,20 +2814,16 @@ async function promptExperimentalSettings(
27932814
color: "red",
27942815
},
27952816
],
2796-
{
2797-
message: UI_COPY.settings.experimentalTitle,
2798-
subtitle: UI_COPY.settings.experimentalSubtitle,
2799-
help: UI_COPY.settings.experimentalHelpStatus,
2800-
clearScreen: true,
2801-
theme: ui.theme,
2802-
selectedEmphasis: "minimal",
2803-
onInput: (raw) => {
2817+
getExperimentalSelectOptions(
2818+
ui,
2819+
UI_COPY.settings.experimentalHelpPreview,
2820+
(raw) => {
28042821
const lower = raw.toLowerCase();
28052822
if (lower === "q") return { type: "back" };
28062823
if (lower === "a") return { type: "apply" };
28072824
return undefined;
28082825
},
2809-
},
2826+
),
28102827
);
28112828
if (!review || review.type === "back") continue;
28122829

@@ -2837,14 +2854,11 @@ async function promptExperimentalSettings(
28372854
},
28382855
{ label: UI_COPY.settings.back, value: { type: "back" }, color: "red" },
28392856
],
2840-
{
2841-
message: UI_COPY.settings.experimentalTitle,
2842-
subtitle: UI_COPY.settings.experimentalSubtitle,
2843-
help: UI_COPY.settings.experimentalHelpStatus,
2844-
clearScreen: true,
2845-
theme: ui.theme,
2846-
selectedEmphasis: "minimal",
2847-
},
2857+
getExperimentalSelectOptions(
2858+
ui,
2859+
UI_COPY.settings.experimentalHelpStatus,
2860+
mapExperimentalStatusHotkey,
2861+
),
28482862
);
28492863
}
28502864
}

lib/ui/copy.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ export const UI_COPY = {
9494
experimental: "Experimental",
9595
experimentalTitle: "Experimental",
9696
experimentalSubtitle: "Preview sync and backup actions before they become stable",
97-
experimentalHelpMenu: "Enter Select | Q Back",
98-
experimentalHelpPreview: "A Apply | Q Back",
97+
experimentalHelpMenu:
98+
"Enter Select | 1 Sync | 2 Backup | 3 Guard | [ - Down | ] + Up | S Save | Q Back",
99+
experimentalHelpPreview: "Enter Select | A Apply | Q Back",
99100
experimentalHelpStatus: "Enter Select | Q Back",
100101
experimentalSync: "Sync Accounts to oc-chatgpt-multi-auth",
101102
experimentalApplySync: "Apply Sync",

0 commit comments

Comments
 (0)