Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions App/frontend/desktop/src/state/model-workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,15 @@ export function upsertModelConnection(
const previousPresetIds = existingConnection
? existingConnection.modelEntries.map((entry) => entry.presetId)
: [];
const removedPresetIds = previousPresetIds.filter((id) => !nextPresetIds.includes(id));
const assignment = next.modelAssignments[mode];
assignment.agent.candidates = replaceIds(assignment.agent.candidates, previousPresetIds, nextAgentPresetIds);
if (!assignment.agent.candidates.length) assignment.agent.candidates = [...nextAgentPresetIds];
if (!assignment.agent.default || previousPresetIds.includes(assignment.agent.default)) {
assignment.agent.default = nextPresetIds.find((id) => presetHasCapability(next, id, "agent")) ?? assignment.agent.default;
}
clearAssignmentReferences(next.modelAssignments.byok, removedPresetIds);
clearAssignmentReferences(next.modelAssignments.account, removedPresetIds);
refreshEffectiveCandidates(next);
return { workspace: createModelWorkspace(next), error: null };
}
Expand Down
58 changes: 58 additions & 0 deletions App/frontend/desktop/src/state/tests/model-workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,64 @@ describe("canonical model workspace adapter", () => {
expect(saved.providers[0]!.models.map((model) => model.model)).not.toContain("embedding-delete");
});

it("真实 Backend catalog:编辑连接删除已分配模型时清理双空间引用", async () => {
const file = catalogFixture();
const empty = await readModelConfigCatalog(file);
const created = await writeModelConfigCatalog(file, {
configRevision: empty.configRevision,
providers: [{
provider: "openai",
endpoints: [{
endpointId: "embedding",
apiBase: "https://api.openai.com/v1",
protocol: "openai-embeddings",
apiKey: "sk-delete-model"
}],
models: [
{ endpointId: "embedding", model: "embedding-delete", source: "byok", capabilities: ["embedding"] },
{ endpointId: "embedding", model: "embedding-keep", source: "byok", capabilities: ["embedding"] }
]
}],
modelAssignments: structuredClone(emptyAssignments)
});
const removedPreset = created.providers[0]!.models.find((model) => model.model === "embedding-delete")!;
const assignedInput = modelConfigInput(createModelWorkspace(created));
assignedInput.modelAssignments.byok.embedding = removedPreset.presetId;
assignedInput.modelAssignments.account.embedding = removedPreset.presetId;
const base = await writeModelConfigCatalog(file, assignedInput);
const workspace = createModelWorkspace(base);
const connection = workspace.spaces.byok.connections.find((item) => item.endpointId === "embedding")!;
const keptPreset = connection.modelEntries.find((model) => model.model === "embedding-keep")!;

const edited = upsertModelConnection(workspace, "byok", {
id: connection.id,
provider: connection.provider,
endpoint: connection.endpoint,
protocol: connection.protocol,
apiKeyMasked: connection.apiKeyMasked,
models: [keptPreset.model],
modelEntries: [{
presetId: keptPreset.presetId,
model: keptPreset.model,
capability: keptPreset.capability
}]
});

expect(edited.error).toBeNull();
expect(edited.workspace.catalog.modelAssignments.byok.embedding).toBeNull();
expect(edited.workspace.catalog.modelAssignments.account.embedding).toBeNull();

await expect(persistModelCatalogMutation(modelConfigInput(edited.workspace), {
read: () => readModelConfigCatalog(file),
write: (input) => writeModelConfigCatalog(file, input)
}, base)).resolves.toMatchObject({
modelAssignments: {
byok: { embedding: null },
account: { embedding: null }
}
});
});

it("真实 Backend catalog:从账号空间删除共享 DashScope 配置并清理双空间引用", async () => {
const file = catalogFixture();
const empty = await readModelConfigCatalog(file);
Expand Down
8 changes: 8 additions & 0 deletions App/frontend/desktop/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -3894,6 +3894,10 @@ code {
margin-bottom: 20px;
}

body.memmy-platform-windows .memory-panel__header {
padding-top: var(--codex-toolbar-height);
}

.memory-panel__header--single-line {
align-items: flex-start;
flex-wrap: wrap;
Expand Down Expand Up @@ -5055,6 +5059,10 @@ code {
-webkit-app-region: no-drag;
}

body.memmy-platform-windows .memory-drawer {
padding-top: var(--codex-toolbar-height);
}

.memory-drawer,
.memory-drawer * {
-webkit-app-region: no-drag;
Expand Down
12 changes: 12 additions & 0 deletions App/frontend/desktop/src/theme/tests/style-alignment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,18 @@ describe("prototype style alignment", () => {
expect(globalCss).not.toContain(".memory-drawer__copy-id");
});

it("keeps the Windows memory refresh action below the native title-bar overlay", () => {
const windowsMemoryHeaderRule = globalCss.match(/body\.memmy-platform-windows \.memory-panel__header\s*\{[^}]*\}/)?.[0] ?? "";

expect(windowsMemoryHeaderRule).toContain("padding-top: var(--codex-toolbar-height);");
});

it("keeps the Windows memory drawer close action below the native title-bar overlay", () => {
const windowsMemoryDrawerRule = globalCss.match(/body\.memmy-platform-windows \.memory-drawer\s*\{[^}]*\}/)?.[0] ?? "";

expect(windowsMemoryDrawerRule).toContain("padding-top: var(--codex-toolbar-height);");
});

it("copies mascot assets into the App source tree instead of referencing the prototype folder", () => {
const assetNames = [
"memmy-rice.png",
Expand Down