Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bca5d4a
MAESTRO: add web UX parity shared types and request-response protocol
chr1syy Mar 17, 2026
c893eac
MAESTRO: add Auto Run CRUD callbacks to CallbackRegistry
chr1syy Mar 17, 2026
fdbb69e
MAESTRO: add Auto Run WS message handlers for web UX parity
chr1syy Mar 17, 2026
52134fd
MAESTRO: wire Auto Run CRUD callbacks in web-server-factory with IPC …
chr1syy Mar 17, 2026
97692cc
MAESTRO: add renderer-side IPC handlers for Auto Run web operations
chr1syy Mar 17, 2026
f4db8e1
MAESTRO: add autorun_docs_changed broadcast for web UX parity
chr1syy Mar 17, 2026
7ba91f8
MAESTRO: add useAutoRun hook and autorun_docs_changed WebSocket handl…
chr1syy Mar 17, 2026
e22381f
MAESTRO: add AutoRunPanel full-screen management view for mobile web
chr1syy Mar 17, 2026
842208a
MAESTRO: add AutoRunDocumentViewer full-screen markdown viewer/editor…
chr1syy Mar 17, 2026
7de4013
MAESTRO: add AutoRunSetupSheet bottom sheet for Auto Run configuratio…
chr1syy Mar 17, 2026
c059970
MAESTRO: integrate Auto Run panel, document viewer, and setup sheet i…
chr1syy Mar 17, 2026
a87dcb5
MAESTRO: add notification_event broadcast with state transition detec…
chr1syy Mar 17, 2026
b86792f
MAESTRO: enhance useNotifications hook with preferences, event handli…
chr1syy Mar 17, 2026
e6064ae
MAESTRO: wire notification events from WebSocket to notification hand…
chr1syy Mar 17, 2026
11b5eeb
MAESTRO: add NotificationSettingsSheet bottom sheet for notification …
chr1syy Mar 17, 2026
d90a2bc
MAESTRO: add notification settings bell button with unread badge to m…
chr1syy Mar 17, 2026
17a8e57
MAESTRO: add settings callbacks to web server callback registry and f…
chr1syy Mar 17, 2026
11fda4d
MAESTRO: add get_settings and set_setting WebSocket message handlers …
chr1syy Mar 17, 2026
49440f8
MAESTRO: add settings_changed broadcast and wire settings callbacks t…
chr1syy Mar 17, 2026
833d848
MAESTRO: add renderer-side IPC handler for remote:setSetting with req…
chr1syy Mar 17, 2026
c6e6feb
MAESTRO: add useSettings hook and settings_changed WebSocket message …
chr1syy Mar 17, 2026
412f5b0
MAESTRO: add SettingsPanel full-screen component for mobile web inter…
chr1syy Mar 17, 2026
63f14ce
MAESTRO: integrate SettingsPanel into mobile App with gear button and…
chr1syy Mar 17, 2026
1121a08
MAESTRO: add agent/group management callbacks, WS handlers, and IPC w…
chr1syy Mar 17, 2026
82cba3d
MAESTRO: add groups_changed broadcast and renderer-side IPC handlers …
chr1syy Mar 17, 2026
7647534
MAESTRO: add useAgentManagement hook and groups_changed WebSocket sup…
chr1syy Mar 17, 2026
fe894da
MAESTRO: add AgentCreationSheet bottom sheet for mobile agent creation
chr1syy Mar 17, 2026
c90ece3
MAESTRO: add git status and diff callbacks, WS handlers, and IPC wiri…
chr1syy Mar 18, 2026
d669a27
MAESTRO: add useGitStatus hook for web git status and diff management
chr1syy Mar 18, 2026
5112ddd
MAESTRO: add GitStatusPanel component for mobile git status display
chr1syy Mar 18, 2026
e749ef6
MAESTRO: add GitDiffViewer component for mobile unified diff display
chr1syy Mar 18, 2026
b3ba176
MAESTRO: add RightDrawer component for mobile unified slide-out drawer
chr1syy Mar 18, 2026
1be0178
MAESTRO: integrate RightDrawer into mobile App with edge-swipe gesture
chr1syy Mar 18, 2026
cf1d8d1
MAESTRO: add Group Chat backend infrastructure for web interface
chr1syy Mar 18, 2026
44239d2
MAESTRO: add useGroupChat hook and WebSocket group chat broadcast sup…
chr1syy Mar 18, 2026
72fc6a4
MAESTRO: add GroupChatPanel component for mobile group chat conversat…
chr1syy Mar 18, 2026
24a0b80
MAESTRO: add GroupChatSetupSheet component for mobile group chat crea…
chr1syy Mar 18, 2026
9b04d2b
MAESTRO: integrate group chat UI into mobile App with header button a…
chr1syy Mar 18, 2026
cbf804b
MAESTRO: refactor QuickActionsMenu into full command palette with sea…
chr1syy Mar 18, 2026
dbacf1a
MAESTRO: add Cmd+K keyboard shortcut to open command palette in mobil…
chr1syy Mar 18, 2026
8771f70
MAESTRO: add context management backend with merge, transfer, and sum…
chr1syy Mar 18, 2026
b0fd459
MAESTRO: add ContextManagementSheet component for mobile context mana…
chr1syy Mar 18, 2026
32ad694
MAESTRO: integrate context management into mobile app with command pa…
chr1syy Mar 18, 2026
820147e
MAESTRO: add Cue automation backend infrastructure with WebSocket ope…
chr1syy Mar 18, 2026
1baf914
MAESTRO: add useCue hook for Cue automation WebSocket integration
chr1syy Mar 18, 2026
43522c1
MAESTRO: add CuePanel mobile component for Cue automation dashboard
chr1syy Mar 18, 2026
c72563b
MAESTRO: integrate CuePanel into mobile App with header button and co…
chr1syy Mar 18, 2026
358e192
MAESTRO: add Usage Dashboard and Achievements backend WebSocket opera…
chr1syy Mar 18, 2026
993bceb
MAESTRO: add UsageDashboardPanel mobile component with token/cost ana…
chr1syy Mar 18, 2026
280e49d
MAESTRO: add AchievementsPanel mobile component with read-only achiev…
chr1syy Mar 18, 2026
8f87f2a
MAESTRO: reorganize mobile header with overflow menu and responsive l…
chr1syy Mar 18, 2026
f21bb00
MAESTRO: fix test failures and circular import for web UX parity buil…
chr1syy Mar 18, 2026
1a696ff
Merge upstream/rc into feat/feat-web-ux-parity
chr1syy Mar 26, 2026
e744dfa
feat(web): improve mobile UX parity with desktop interface
chr1syy Mar 29, 2026
3cda177
style: fix Prettier formatting in 5 files flagged by pre-push hook
chr1syy Mar 29, 2026
471506a
fix(web): address PR review comments for mobile UX parity
chr1syy Mar 29, 2026
85bee58
fix(web): remove dead code and unused props per PR review nitpicks
chr1syy Mar 29, 2026
fac2201
fix(web): replace console.log with webLogger, add missing WS types, f…
pedramamini Mar 29, 2026
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
29 changes: 29 additions & 0 deletions src/__tests__/main/web-server/web-server-factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,35 @@ vi.mock('../../../main/web-server/WebServer', () => {
setRefreshFileTreeCallback = vi.fn();
setRefreshAutoRunDocsCallback = vi.fn();
setConfigureAutoRunCallback = vi.fn();
setGetAutoRunDocsCallback = vi.fn();
setGetAutoRunDocContentCallback = vi.fn();
setSaveAutoRunDocCallback = vi.fn();
setStopAutoRunCallback = vi.fn();
setGetSettingsCallback = vi.fn();
setSetSettingCallback = vi.fn();
setGetGroupsCallback = vi.fn();
setCreateGroupCallback = vi.fn();
setRenameGroupCallback = vi.fn();
setDeleteGroupCallback = vi.fn();
setMoveSessionToGroupCallback = vi.fn();
setCreateSessionCallback = vi.fn();
setDeleteSessionCallback = vi.fn();
setRenameSessionCallback = vi.fn();
setGetGitStatusCallback = vi.fn();
setGetGitDiffCallback = vi.fn();
setGetGroupChatsCallback = vi.fn();
setStartGroupChatCallback = vi.fn();
setGetGroupChatStateCallback = vi.fn();
setStopGroupChatCallback = vi.fn();
setSendGroupChatMessageCallback = vi.fn();
setMergeContextCallback = vi.fn();
setTransferContextCallback = vi.fn();
setSummarizeContextCallback = vi.fn();
setGetCueSubscriptionsCallback = vi.fn();
setToggleCueSubscriptionCallback = vi.fn();
setGetCueActivityCallback = vi.fn();
setGetUsageDashboardCallback = vi.fn();
setGetAchievementsCallback = vi.fn();

constructor(port: number, securityToken?: string) {
this.port = port;
Expand Down
53 changes: 53 additions & 0 deletions src/__tests__/renderer/hooks/useRemoteIntegration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,59 @@ describe('useRemoteIntegration', () => {
}),
sendRemoteNewTabResponse: vi.fn(),
sendRemoteConfigureAutoRunResponse: vi.fn(),
onRemoteGetAutoRunDocs: vi.fn().mockImplementation(() => {
return () => {};
}),
onRemoteGetAutoRunDocContent: vi.fn().mockImplementation(() => {
return () => {};
}),
onRemoteSaveAutoRunDoc: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteSaveAutoRunDocResponse: vi.fn(),
sendRemoteGetAutoRunDocsResponse: vi.fn(),
sendRemoteGetAutoRunDocContentResponse: vi.fn(),
onRemoteStopAutoRun: vi.fn().mockImplementation(() => {
return () => {};
}),
onRemoteSetSetting: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteSetSettingResponse: vi.fn(),
onRemoteCreateSession: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteCreateSessionResponse: vi.fn(),
onRemoteDeleteSession: vi.fn().mockImplementation(() => {
return () => {};
}),
onRemoteRenameSession: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteRenameSessionResponse: vi.fn(),
onRemoteCreateGroup: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteCreateGroupResponse: vi.fn(),
onRemoteRenameGroup: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteRenameGroupResponse: vi.fn(),
onRemoteDeleteGroup: vi.fn().mockImplementation(() => {
return () => {};
}),
onRemoteMoveSessionToGroup: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteMoveSessionToGroupResponse: vi.fn(),
onRemoteGetGitStatus: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteGetGitStatusResponse: vi.fn(),
onRemoteGetGitDiff: vi.fn().mockImplementation(() => {
return () => {};
}),
sendRemoteGetGitDiffResponse: vi.fn(),
};

const mockLive = {
Expand Down
84 changes: 24 additions & 60 deletions src/__tests__/web/hooks/useLongPressMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,15 @@ describe('useLongPressMenu', () => {
vi.restoreAllMocks();
});

it('opens the menu after long press', () => {
it('triggers onOpenCommandPalette after long press', () => {
const onOpenCommandPalette = vi.fn();
const button = document.createElement('button');
button.getBoundingClientRect = vi.fn(() => ({
left: 10,
top: 20,
width: 30,
height: 40,
right: 40,
bottom: 60,
x: 10,
y: 20,
toJSON: () => {},
})) as unknown as () => DOMRect;

const { result } = renderHook(() =>
useLongPressMenu({
inputMode: 'ai',
value: 'hello',
onOpenCommandPalette,
})
);

Expand All @@ -61,28 +52,18 @@ describe('useLongPressMenu', () => {
vi.advanceTimersByTime(500);
});

expect(result.current.isMenuOpen).toBe(true);
expect(result.current.menuAnchor).toEqual({ x: 25, y: 20 });
expect(onOpenCommandPalette).toHaveBeenCalledTimes(1);
});

it('cancels long press on touch move', () => {
const onOpenCommandPalette = vi.fn();
const button = document.createElement('button');
button.getBoundingClientRect = vi.fn(() => ({
left: 0,
top: 0,
width: 10,
height: 10,
right: 10,
bottom: 10,
x: 0,
y: 0,
toJSON: () => {},
})) as unknown as () => DOMRect;

const { result } = renderHook(() =>
useLongPressMenu({
inputMode: 'ai',
value: 'hello',
onOpenCommandPalette,
})
);

Expand All @@ -96,44 +77,18 @@ describe('useLongPressMenu', () => {
vi.advanceTimersByTime(500);
});

expect(result.current.isMenuOpen).toBe(false);
expect(onOpenCommandPalette).not.toHaveBeenCalled();
});

it('handles quick action selection', () => {
const onModeToggle = vi.fn();
const { result } = renderHook(() =>
useLongPressMenu({
inputMode: 'ai',
onModeToggle,
value: 'hello',
})
);

act(() => {
result.current.handleQuickAction('switch_mode');
});

expect(onModeToggle).toHaveBeenCalledWith('terminal');
});

it('closes the menu when requested', () => {
it('does not trigger onOpenCommandPalette when touch ends before duration', () => {
const onOpenCommandPalette = vi.fn();
const button = document.createElement('button');
button.getBoundingClientRect = vi.fn(() => ({
left: 0,
top: 0,
width: 10,
height: 10,
right: 10,
bottom: 10,
x: 0,
y: 0,
toJSON: () => {},
})) as unknown as () => DOMRect;

const { result } = renderHook(() =>
useLongPressMenu({
inputMode: 'ai',
value: 'hello',
onOpenCommandPalette,
})
);

Expand All @@ -143,15 +98,24 @@ describe('useLongPressMenu', () => {

act(() => {
result.current.handleTouchStart(createTouchEvent(button));
result.current.handleTouchEnd(createTouchEvent(button));
vi.advanceTimersByTime(500);
});

expect(result.current.isMenuOpen).toBe(true);
expect(onOpenCommandPalette).not.toHaveBeenCalled();
});

act(() => {
result.current.closeMenu();
});
it('returns expected handler functions', () => {
const { result } = renderHook(() =>
useLongPressMenu({
inputMode: 'ai',
value: 'hello',
})
);

expect(result.current.isMenuOpen).toBe(false);
expect(typeof result.current.handleTouchStart).toBe('function');
expect(typeof result.current.handleTouchEnd).toBe('function');
expect(typeof result.current.handleTouchMove).toBe('function');
expect(result.current.sendButtonRef).toBeDefined();
});
});
12 changes: 9 additions & 3 deletions src/__tests__/web/mobile/AllSessionsView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -299,15 +299,18 @@ describe('AllSessionsView', () => {
});

describe('session card interaction', () => {
it('calls onSelectSession and onClose when card is clicked', async () => {
it('calls onSelectSession and onClose when card is tapped', async () => {
const onSelectSession = vi.fn();
const onClose = vi.fn();
const sessions = [createMockSession({ id: 'session-1', name: 'Click Me' })];

render(<AllSessionsView {...createDefaultProps({ sessions, onSelectSession, onClose })} />);

const sessionCard = screen.getByRole('button', { name: /Click Me/i });
fireEvent.click(sessionCard);
// JSDOM has ontouchstart in window, so the click handler is bypassed.
// Use touch events to simulate a tap (as the source uses handleTouchEnd for selection).
fireEvent.touchStart(sessionCard, { touches: [{ clientX: 0, clientY: 0 }] });
fireEvent.touchEnd(sessionCard);

expect(mockTriggerHaptic).toHaveBeenCalledWith([10]); // HAPTIC_PATTERNS.tap
expect(onSelectSession).toHaveBeenCalledWith('session-1');
Expand Down Expand Up @@ -957,8 +960,11 @@ describe('AllSessionsView', () => {
});

// 3. Select Backend
// JSDOM has ontouchstart in window, so the click handler is bypassed.
// Use touch events to simulate a tap (as the source uses handleTouchEnd for selection).
const backendCard = screen.getByRole('button', { name: /Backend session/i });
fireEvent.click(backendCard);
fireEvent.touchStart(backendCard, { touches: [{ clientX: 0, clientY: 0 }] });
fireEvent.touchEnd(backendCard);

expect(onSelectSession).toHaveBeenCalledWith('s2');
expect(onClose).toHaveBeenCalled();
Expand Down
Loading