fix(codex-config): close fix-round-1 parity gaps against CcConfig

Isolate the codex-CLI-backed reads (mcp/plugins/marketplaces) from the
file-backed ones in fetchAll via two independent Promise.all groups, so a
missing `codex` binary degrades only the mcp/plugins tabs instead of
leaving the whole panel stuck on skeletons. Generalize EditorModal's
nameless-type detection behind a `namelessTypes` prop (defaulting to
Claude's own ["memory"]) so Codex's config.toml can be created without a
name. Port BackupsModal, disable the plugin/mcp Remove button when no
stable identifier resolved, hide the inert search box on memory/config,
drop a redundant empty state, and reword the delete-dialog copy to be
tool-neutral.

Co-authored-by: Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
This commit is contained in:
2026-09-08 21:34:06 +07:00
parent 8015730adf
commit 56bf5bbf4d
8 changed files with 432 additions and 73 deletions
+12 -3
View File
@@ -30,6 +30,10 @@ export type EditorState<TType extends string = CcArtifactType> =
}
| null;
/** Types that are a single fixed file per scope (e.g. Claude's CLAUDE.md, Codex's
* AGENTS.md / config.toml) - no `name` field, and no artifact name is ever sent. */
const DEFAULT_NAMELESS_TYPES = ["memory"];
interface EditorModalProps<TType extends string = CcArtifactType> {
state: NonNullable<EditorState<TType>>;
fetchContent: (filePath: string) => Promise<string>;
@@ -41,6 +45,9 @@ interface EditorModalProps<TType extends string = CcArtifactType> {
content: string;
project?: string;
}) => Promise<void>;
/** Which `state.type` values are nameless singleton files. Defaults to
* Claude Code's own `["memory"]` so existing callers are unaffected. */
namelessTypes?: readonly string[];
}
export function EditorModal<TType extends string = CcArtifactType>({
@@ -48,9 +55,11 @@ export function EditorModal<TType extends string = CcArtifactType>({
fetchContent,
onClose,
onSave,
namelessTypes = DEFAULT_NAMELESS_TYPES,
}: EditorModalProps<TType>) {
const { t } = useTranslation("ccConfig");
const isCreate = state.mode === "create";
const isNameless = namelessTypes.includes(state.type);
const isAutoMemory = state.type === "auto-memory";
const [content, setContent] = useState<string>(isCreate ? state.template : "");
const [name, setName] = useState<string>("");
@@ -91,7 +100,7 @@ export function EditorModal<TType extends string = CcArtifactType>({
setSaving(true);
setError(null);
try {
if (state.type !== "memory" && state.mode === "create" && !name) {
if (!isNameless && state.mode === "create" && !name) {
setError(t("edit.nameLabel"));
setSaving(false);
return;
@@ -99,7 +108,7 @@ export function EditorModal<TType extends string = CcArtifactType>({
// For auto-memory creates, append a .md extension when the user omits it.
const createName = isAutoMemory && !/\.md$/i.test(name) ? `${name}.md` : name;
const effectiveName =
state.mode === "edit" ? state.name : state.type === "memory" ? undefined : createName;
state.mode === "edit" ? state.name : isNameless ? undefined : createName;
await onSave({
type: state.type,
targetScope: isAutoMemory ? "auto-memory" : targetScope,
@@ -143,7 +152,7 @@ export function EditorModal<TType extends string = CcArtifactType>({
</div>
<div className="overflow-auto p-4 space-y-3">
{isCreate && state.type !== "memory" && (
{isCreate && !isNameless && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<label className="block text-[11px] font-semibold uppercase tracking-wider text-fg-muted mb-1">