Skip to content

Commit 07c4a7d

Browse files
Merge remote-tracking branch 'origin/main' into pr-33752
2 parents 1b42994 + 7171fdd commit 07c4a7d

5 files changed

Lines changed: 86 additions & 13 deletions

File tree

‎apps/desktop/src/window/DesktopWindow.test.ts‎

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ function layerTest(input: {
233233
readonly destroyAll?: Effect.Effect<void>;
234234
readonly onWindowCreate?: Effect.Effect<void>;
235235
readonly openedExternalUrls?: unknown[];
236-
readonly copiedTexts?: string[];
236+
readonly onCopyText?: (text: string) => Effect.Effect<void>;
237237
readonly onPopupTemplate?: (input: ElectronMenu.ElectronMenuTemplateInput) => Effect.Effect<void>;
238238
readonly previewZoomReapplies?: number[];
239239
readonly onReveal?: (window: Electron.BrowserWindow) => void;
@@ -325,10 +325,7 @@ function layerTest(input: {
325325
return true;
326326
}),
327327
openSystemSettings: () => Effect.succeed(true),
328-
copyText: (text) =>
329-
Effect.sync(() => {
330-
input.copiedTexts?.push(text);
331-
}),
328+
copyText: input.onCopyText ?? (() => Effect.void),
332329
} satisfies ElectronShell.ElectronShell["Service"]),
333330
layerElectronTheme,
334331
layerElectronWindow,
@@ -1186,6 +1183,56 @@ describe("DesktopWindow", () => {
11861183
}),
11871184
);
11881185

1186+
it.effect("offers to copy the bare address when right-clicking a mailto link", () =>
1187+
Effect.gen(function* () {
1188+
const fakeWindow = makeFakeBrowserWindow();
1189+
const createCount = yield* Ref.make(0);
1190+
const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none());
1191+
const popup = yield* Deferred.make<ElectronMenu.ElectronMenuTemplateInput>();
1192+
const copied = yield* Deferred.make<string>();
1193+
const layer = layerTest({
1194+
window: fakeWindow.window,
1195+
createCount,
1196+
mainWindow,
1197+
onPopupTemplate: (input) => Deferred.succeed(popup, input),
1198+
onCopyText: (text) => Deferred.succeed(copied, text),
1199+
});
1200+
1201+
yield* Effect.gen(function* () {
1202+
const desktopWindow = yield* DesktopWindow.DesktopWindow;
1203+
yield* desktopWindow.handleBackendReady(new URL("http://127.0.0.1:3773"));
1204+
1205+
const contextMenu = fakeWindow.webContentsListeners.get("context-menu");
1206+
if (!contextMenu) {
1207+
return yield* Effect.die("context-menu listener was not registered");
1208+
}
1209+
contextMenu(
1210+
{ preventDefault: vi.fn() },
1211+
{
1212+
linkURL: "mailto:restaurant%40example.com?subject=Hi",
1213+
misspelledWord: "",
1214+
dictionarySuggestions: [],
1215+
mediaType: "none",
1216+
editFlags: { canCut: false, canCopy: false, canPaste: false, canSelectAll: true },
1217+
},
1218+
);
1219+
1220+
const { template } = yield* Deferred.await(popup);
1221+
const copyEmail = template.find((item) => item.label === "Copy Email Address");
1222+
assert.isDefined(
1223+
copyEmail,
1224+
`no "Copy Email Address" item in ${JSON.stringify(template.map((item) => item.label ?? item.role ?? item.type))}`,
1225+
);
1226+
copyEmail?.click?.(
1227+
{} as Electron.MenuItem,
1228+
fakeWindow.window,
1229+
{} as Electron.KeyboardEvent,
1230+
);
1231+
assert.equal(yield* Deferred.await(copied), "[email protected]");
1232+
}).pipe(Effect.provide(layer));
1233+
}),
1234+
);
1235+
11891236
it.effect(
11901237
"retries opening the real main on activate when a failed post-readiness open left only the splash",
11911238
() =>

‎apps/desktop/src/window/DesktopWindow.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ class ContextMenuImageCopyError extends Schema.TaggedError<ContextMenuImageCopyE
5353
const MACOS_WORKSPACE_TOPBAR_HEIGHT = 52;
5454
const MACOS_WINDOW_BUTTON_RADIUS = 7;
5555

56+
function parseMailtoAddress(rawUrl: string): Option.Option<string> {
57+
let url: URL;
58+
try {
59+
url = new URL(rawUrl);
60+
} catch {
61+
return Option.none();
62+
}
63+
if (url.protocol !== "mailto:" || url.pathname === "") return Option.none();
64+
try {
65+
return Option.some(decodeURIComponent(url.pathname));
66+
} catch {
67+
return Option.some(url.pathname);
68+
}
69+
}
70+
5671
function syncMacosWindowButtons(window: Electron.BrowserWindow): void {
5772
if (window.isDestroyed() || window.isFullScreen()) return;
5873
window.setWindowButtonPosition({
@@ -590,6 +605,19 @@ export const make = Effect.gen(function* () {
590605
);
591606
}
592607

608+
const mailtoAddress = parseMailtoAddress(params.linkURL);
609+
if (Option.isSome(mailtoAddress)) {
610+
menuTemplate.push(
611+
{
612+
label: "Copy Email Address",
613+
click: () => {
614+
void runPromise(electronShell.copyText(mailtoAddress.value));
615+
},
616+
},
617+
{ type: "separator" },
618+
);
619+
}
620+
593621
if (params.mediaType === "image") {
594622
menuTemplate.push({
595623
label: "Copy Image",

‎apps/server/src/provider/AcpRegistryProbe.test.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ describe("ACP Registry probe", () => {
273273
},
274274
);
275275

276-
it("bounds and de-duplicates advertised commands", () => {
276+
it("keeps every distinct advertised command and de-duplicates the rest", () => {
277277
const commands = normalizeAcpRegistryCommands([
278278
{
279279
name: "create_plan",
@@ -285,9 +285,11 @@ describe("ACP Registry probe", () => {
285285
name: `command_${index}`,
286286
description: "",
287287
})),
288+
{ name: "$late-skill", description: "Advertised after 128 commands" },
288289
]);
289290

290-
expect(commands.slashCommands).toHaveLength(128);
291+
expect(commands.slashCommands).toHaveLength(141);
292+
expect(commands.slashCommands.at(-1)).toEqual({ name: "command_139" });
291293
expect(commands.slashCommands[0]).toEqual({
292294
name: "create_plan",
293295
description: "Create a plan",
@@ -296,7 +298,7 @@ describe("ACP Registry probe", () => {
296298
expect(
297299
commands.slashCommands.filter((command) => command.name.toLowerCase() === "create_plan"),
298300
).toHaveLength(1);
299-
expect(commands.skills).toEqual([]);
301+
expect(commands.skills.map((skill) => skill.name)).toEqual(["late-skill"]);
300302
});
301303

302304
it("routes dollar-prefixed ACP commands to the provider skill menu", () => {

‎apps/web/src/components/CustomSnoozeDialog.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function CustomSnoozeDialog() {
8585
if (!open) finish(null);
8686
}}
8787
>
88-
<DialogPopup className="sm:max-w-sm">
88+
<DialogPopup className="sm:max-w-md">
8989
<form
9090
className="flex min-h-0 flex-col"
9191
onSubmit={(event) => {

‎packages/provider-acp-registry/src/server/probe.ts‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ const MAX_MODELS = 256;
4141
const MAX_ID_LENGTH = 128;
4242
const MAX_NAME_LENGTH = 160;
4343
const MAX_DESCRIPTION_LENGTH = 1_024;
44-
const MAX_COMMANDS = 128;
4544
const MAX_COMMAND_LINE_LENGTH = 2_048;
4645
const PROBE_TIMEOUT_SECONDS = 60;
4746
const PROBE_TIMEOUT = `${PROBE_TIMEOUT_SECONDS} seconds`;
@@ -215,7 +214,6 @@ export function normalizeAcpRegistryCommands(
215214
const seen = new Set<string>();
216215
const slashCommands: Array<ServerProviderSlashCommand> = [];
217216
const skills: Array<ServerProviderSkill> = [];
218-
let accepted = 0;
219217
for (const command of commands) {
220218
const name = boundedOpaqueValue(command.name, MAX_ID_LENGTH);
221219
if (name === undefined) continue;
@@ -241,8 +239,6 @@ export function normalizeAcpRegistryCommands(
241239
...(hint ? { input: { hint } } : {}),
242240
});
243241
}
244-
accepted += 1;
245-
if (accepted === MAX_COMMANDS) break;
246242
}
247243
return { slashCommands, skills };
248244
}

0 commit comments

Comments
 (0)