Skip to content

Commit c04eec3

Browse files
committed
improvement desktop
1 parent b5db795 commit c04eec3

114 files changed

Lines changed: 12489 additions & 1164 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
116 KB
Binary file not shown.

apps/desktop/src/main/browser-agent/cdp.test.ts

Lines changed: 436 additions & 2 deletions
Large diffs are not rendered by default.

apps/desktop/src/main/browser-agent/cdp.ts

Lines changed: 320 additions & 16 deletions
Large diffs are not rendered by default.

apps/desktop/src/main/browser-agent/context-menu.test.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ function page(overrides: Partial<Page> = {}): Page {
4444

4545
function handlers(): Handlers {
4646
return {
47+
addToChat: vi.fn(),
4748
copy: vi.fn(),
4849
paste: vi.fn(),
4950
back: vi.fn(),
@@ -73,7 +74,7 @@ describe('buildAgentContextMenuTemplate', () => {
7374
'Reload',
7475
'Zoom In',
7576
'Zoom Out',
76-
'Reset Zoom (100%)',
77+
'Actual Size (100%)',
7778
])
7879
})
7980

@@ -105,6 +106,22 @@ describe('buildAgentContextMenuTemplate', () => {
105106
expect(labels(readOnly)).not.toContain('Paste')
106107
})
107108

109+
it('puts Add to chat first and preserves the exact nonblank selection', () => {
110+
const handled = handlers()
111+
const template = buildAgentContextMenuTemplate(
112+
params({ selectionText: ' selected\ntext ', linkURL: 'https://example.com/docs' }),
113+
page(),
114+
handled
115+
)
116+
117+
expect(labels(template)[0]).toBe('Add to chat')
118+
item(template, 'Add to chat')?.click?.({} as never, undefined as never, {} as never)
119+
expect(handled.addToChat).toHaveBeenCalledWith(' selected\ntext ')
120+
expect(
121+
labels(buildAgentContextMenuTemplate(params({ selectionText: ' \n ' }), page(), handlers()))
122+
).not.toContain('Add to chat')
123+
})
124+
108125
it('offers link items for http(s) targets only', () => {
109126
const handled = handlers()
110127
const template = buildAgentContextMenuTemplate(
@@ -143,7 +160,7 @@ describe('buildAgentContextMenuTemplate', () => {
143160
// against Chromium's native scale (where this factor would read 110%).
144161
const twoUp = steppedZoomFactor(steppedZoomFactor(BASE_ZOOM_FACTOR, 1), 1)
145162
const stepped = buildAgentContextMenuTemplate(params(), page({ zoomFactor: twoUp }), handlers())
146-
expect(item(stepped, 'Reset Zoom (121%)')?.enabled).toBe(true)
163+
expect(item(stepped, 'Actual Size (121%)')?.enabled).toBe(true)
147164

148165
const atMax = buildAgentContextMenuTemplate(params(), page({ zoomFactor: 3 }), handlers())
149166
expect(item(atMax, 'Zoom In')?.enabled).toBe(false)
@@ -154,7 +171,7 @@ describe('buildAgentContextMenuTemplate', () => {
154171

155172
// Nothing to reset to at 100%.
156173
expect(
157-
item(buildAgentContextMenuTemplate(params(), page(), handlers()), 'Reset Zoom (100%)')
174+
item(buildAgentContextMenuTemplate(params(), page(), handlers()), 'Actual Size (100%)')
158175
?.enabled
159176
).toBe(false)
160177
})
@@ -171,7 +188,7 @@ describe('buildAgentContextMenuTemplate', () => {
171188
handled
172189
)
173190

174-
item(template, 'Reset Zoom (133%)')?.click?.({} as never, undefined as never, {} as never)
191+
item(template, 'Actual Size (133%)')?.click?.({} as never, undefined as never, {} as never)
175192

176193
expect(handled.setZoomFactor).toHaveBeenCalledWith(configuredDefault)
177194
})
@@ -202,6 +219,7 @@ describe('attachAgentContextMenu', () => {
202219
const contents = new WebContentsView().webContents
203220
vi.mocked(contents.navigationHistory.canGoBack).mockReturnValue(true)
204221
attachAgentContextMenu(contents, {
222+
addToChat: vi.fn(),
205223
openTab: vi.fn(),
206224
defaultZoomFactor: () => BASE_ZOOM_FACTOR,
207225
})

apps/desktop/src/main/browser-agent/context-menu.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* terminal's hidden textarea, the roles here act on a real page: `copy` and
1414
* `paste` go to the frame that was clicked.
1515
*/
16+
17+
import { resolveDesktopZoom } from '@sim/desktop-bridge'
1618
import type { ContextMenuParams, MenuItemConstructorOptions, WebContents } from 'electron'
1719
import { clipboard, Menu } from 'electron'
1820

@@ -22,9 +24,9 @@ import { clipboard, Menu } from 'electron'
2224
* Chromium refuses to scale past them, and a rung outside the range would come
2325
* back clamped and leave the menu offering a step that never lands.
2426
*/
25-
const ZOOM_STEP_RATIO = 1.1
2627
const MIN_ZOOM_FACTOR = 0.5
2728
const MAX_ZOOM_FACTOR = 3
29+
const ZOOM_FACTOR_BOUNDS = { min: MIN_ZOOM_FACTOR, max: MAX_ZOOM_FACTOR } as const
2830

2931
/**
3032
* What the panel calls 100%.
@@ -33,14 +35,14 @@ const MAX_ZOOM_FACTOR = 3
3335
* it renders a rung below Chromium's native scale and treats THAT as its
3436
* baseline: the menu reads 100% there, and every other rung is reported
3537
* relative to it. New installs start there; when a user chooses a different
36-
* default, Reset Zoom returns to that configured percentage. The initial 100%
38+
* default, Actual Size returns to that configured percentage. The initial 100%
3739
* is genuinely rendering at ~91% of native.
3840
*
3941
* Defined as one rung below native rather than as a round number so the ladder
4042
* still lands exactly on Chromium's 1.0 (the crispest rasterization, one step
4143
* up from the baseline) instead of straddling it.
4244
*/
43-
export const BASE_ZOOM_FACTOR = 1 / ZOOM_STEP_RATIO
45+
export const BASE_ZOOM_FACTOR = resolveDesktopZoom(1, 'out', 1, ZOOM_FACTOR_BOUNDS)
4446

4547
/**
4648
* A Chromium zoom factor as a percentage of {@link BASE_ZOOM_FACTOR} — what the
@@ -60,9 +62,12 @@ export function zoomPercentOf(factor: number): number {
6062
* the item rather than offer a step that does nothing.
6163
*/
6264
export function steppedZoomFactor(current: number, direction: 1 | -1): number {
63-
const base = Number.isFinite(current) && current > 0 ? current : BASE_ZOOM_FACTOR
64-
const next = direction === 1 ? base * ZOOM_STEP_RATIO : base / ZOOM_STEP_RATIO
65-
return Math.min(MAX_ZOOM_FACTOR, Math.max(MIN_ZOOM_FACTOR, next))
65+
return resolveDesktopZoom(
66+
current,
67+
direction === 1 ? 'in' : 'out',
68+
BASE_ZOOM_FACTOR,
69+
ZOOM_FACTOR_BOUNDS
70+
)
6671
}
6772

6873
/** The parts of a right-click the menu acts on. */
@@ -80,6 +85,7 @@ interface AgentPageContext {
8085
}
8186

8287
interface AgentContextMenuHandlers {
88+
addToChat(text: string): void
8389
copy(): void
8490
paste(): void
8591
back(): void
@@ -91,6 +97,8 @@ interface AgentContextMenuHandlers {
9197
}
9298

9399
export interface AgentContextMenuHost {
100+
/** Attaches selected page text to the chat that owns this browser tab. */
101+
addToChat(text: string): void
94102
/** Opens a link from the page in another tab of the same browser. */
95103
openTab(url: string): void
96104
/** Returns the device's current default page zoom factor. */
@@ -112,6 +120,14 @@ export function buildAgentContextMenuTemplate(
112120
): MenuItemConstructorOptions[] {
113121
const template: MenuItemConstructorOptions[] = []
114122
const linkUrl = /^https?:\/\//i.test(params.linkURL) ? params.linkURL : ''
123+
const selectionText = params.selectionText
124+
125+
if (selectionText.trim()) {
126+
template.push(
127+
{ label: 'Add to chat', click: () => handlers.addToChat(selectionText) },
128+
{ type: 'separator' }
129+
)
130+
}
115131

116132
if (linkUrl) {
117133
template.push(
@@ -121,7 +137,7 @@ export function buildAgentContextMenuTemplate(
121137
)
122138
}
123139

124-
if (params.selectionText.trim()) {
140+
if (selectionText.trim()) {
125141
template.push({ label: 'Copy', click: () => handlers.copy() })
126142
}
127143
if (params.isEditable && params.editFlags.canPaste) {
@@ -153,7 +169,7 @@ export function buildAgentContextMenuTemplate(
153169
click: () => handlers.setZoomFactor(zoomOut),
154170
},
155171
{
156-
label: `Reset Zoom (${zoomPercent}%)`,
172+
label: `Actual Size (${zoomPercent}%)`,
157173
enabled: page.zoomFactor !== page.defaultZoomFactor,
158174
click: () => handlers.setZoomFactor(page.defaultZoomFactor),
159175
}
@@ -174,6 +190,7 @@ export function attachAgentContextMenu(contents: WebContents, host: AgentContext
174190
defaultZoomFactor: host.defaultZoomFactor(),
175191
},
176192
{
193+
addToChat: (text) => host.addToChat(text),
177194
copy: () => contents.copy(),
178195
paste: () => contents.paste(),
179196
back: () => contents.navigationHistory.goBack(),

0 commit comments

Comments
 (0)