Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/main/web-server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface AITabData {
createdAt: number;
state: 'idle' | 'busy';
thinkingStartTime?: number | null;
hasUnread?: boolean;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/main/web-server/web-server-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export function createWebServerFactory(deps: WebServerFactoryDependencies) {
createdAt: tab.createdAt,
state: tab.state || 'idle',
thinkingStartTime: tab.thinkingStartTime || null,
hasUnread: tab.hasUnread ?? false,
})) || [];

return {
Expand Down
1 change: 1 addition & 0 deletions src/renderer/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ interface MaestroAPI {
createdAt: number;
state: 'idle' | 'busy';
thinkingStartTime?: number | null;
hasUnread?: boolean;
}>,
activeTabId: string
) => Promise<void>;
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/hooks/remote/useRemoteIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ export function useRemoteIntegration(deps: UseRemoteIntegrationDeps): UseRemoteI

// Create a hash of tab properties that should trigger a broadcast when changed
const tabsHash = session.aiTabs
.map((t) => `${t.id}:${t.name || ''}:${t.starred}:${t.state}`)
.map((t) => `${t.id}:${t.name || ''}:${t.starred}:${t.state}:${t.hasUnread ?? false}`)
.join('|');

const prev = prevTabsRef.current.get(session.id);
Expand All @@ -867,6 +867,7 @@ export function useRemoteIntegration(deps: UseRemoteIntegrationDeps): UseRemoteI
createdAt: tab.createdAt,
state: tab.state,
thinkingStartTime: tab.thinkingStartTime,
hasUnread: tab.hasUnread,
}));

window.maestro.web.broadcastTabsChange(session.id, tabsForBroadcast, current.activeTabId);
Expand Down
115 changes: 115 additions & 0 deletions src/web/mobile/AutoRunDocumentCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* Shared DocumentCard component for Auto Run document listings.
*
* Used by both AutoRunPanel (full-screen) and AutoRunTabContent (inline tab).
*/

import { useCallback } from 'react';
import { useThemeColors } from '../components/ThemeProvider';
import { triggerHaptic, HAPTIC_PATTERNS } from './constants';
import type { AutoRunDocument } from '../hooks/useAutoRun';

export interface DocumentCardProps {
document: AutoRunDocument;
onTap: (filename: string) => void;
}

export function DocumentCard({ document: doc, onTap }: DocumentCardProps) {
const colors = useThemeColors();
const progress = doc.taskCount > 0 ? Math.round((doc.completedCount / doc.taskCount) * 100) : 0;

const handleTap = useCallback(() => {
triggerHaptic(HAPTIC_PATTERNS.tap);
onTap(doc.filename);
}, [doc.filename, onTap]);

return (
<button
onClick={handleTap}
style={{
display: 'flex',
flexDirection: 'column',
gap: '8px',
padding: '14px 16px',
borderRadius: '12px',
border: `1px solid ${colors.border}`,
backgroundColor: colors.bgSidebar,
color: colors.textMain,
width: '100%',
textAlign: 'left',
cursor: 'pointer',
transition: 'all 0.15s ease',
touchAction: 'manipulation',
WebkitTapHighlightColor: 'transparent',
userSelect: 'none',
WebkitUserSelect: 'none',
}}
aria-label={`${doc.filename}, ${doc.completedCount} of ${doc.taskCount} tasks completed`}
>
{/* Filename */}
<div
style={{
fontSize: '15px',
fontWeight: 600,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '100%',
}}
>
{doc.filename}
</div>

{/* Progress row */}
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '10px',
width: '100%',
}}
>
<span
style={{
fontSize: '12px',
color: colors.textDim,
flexShrink: 0,
}}
>
{doc.completedCount}/{doc.taskCount} tasks
</span>

{/* Mini progress bar */}
<div
style={{
flex: 1,
height: '4px',
backgroundColor: `${colors.textDim}20`,
borderRadius: '2px',
overflow: 'hidden',
}}
>
<div
style={{
width: `${progress}%`,
height: '100%',
backgroundColor: progress === 100 ? colors.success : colors.accent,
borderRadius: '2px',
transition: 'width 0.3s ease-out',
}}
/>
</div>

<span
style={{
fontSize: '11px',
color: colors.textDim,
flexShrink: 0,
}}
>
{progress}%
</span>
</div>
</button>
);
}
115 changes: 3 additions & 112 deletions src/web/mobile/AutoRunPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,120 +8,11 @@

import { useState, useCallback, useEffect } from 'react';
import { useThemeColors } from '../components/ThemeProvider';
import { useAutoRun, type AutoRunDocument } from '../hooks/useAutoRun';
import { useAutoRun } from '../hooks/useAutoRun';
import { DocumentCard } from './AutoRunDocumentCard';
import type { AutoRunState, UseWebSocketReturn } from '../hooks/useWebSocket';
import { triggerHaptic, HAPTIC_PATTERNS } from './constants';

/**
* Document card component for the Auto Run panel
*/
interface DocumentCardProps {
document: AutoRunDocument;
onTap: (filename: string) => void;
}

function DocumentCard({ document, onTap }: DocumentCardProps) {
const colors = useThemeColors();
const progress =
document.taskCount > 0 ? Math.round((document.completedCount / document.taskCount) * 100) : 0;

const handleTap = useCallback(() => {
triggerHaptic(HAPTIC_PATTERNS.tap);
onTap(document.filename);
}, [document.filename, onTap]);

return (
<button
onClick={handleTap}
style={{
display: 'flex',
flexDirection: 'column',
gap: '8px',
padding: '14px 16px',
borderRadius: '12px',
border: `1px solid ${colors.border}`,
backgroundColor: colors.bgSidebar,
color: colors.textMain,
width: '100%',
textAlign: 'left',
cursor: 'pointer',
transition: 'all 0.15s ease',
touchAction: 'manipulation',
WebkitTapHighlightColor: 'transparent',
outline: 'none',
userSelect: 'none',
WebkitUserSelect: 'none',
}}
aria-label={`${document.filename}, ${document.completedCount} of ${document.taskCount} tasks completed`}
>
{/* Filename */}
<div
style={{
fontSize: '15px',
fontWeight: 600,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
width: '100%',
}}
>
{document.filename}
</div>

{/* Progress row */}
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '10px',
width: '100%',
}}
>
<span
style={{
fontSize: '12px',
color: colors.textDim,
flexShrink: 0,
}}
>
{document.completedCount}/{document.taskCount} tasks
</span>

{/* Mini progress bar */}
<div
style={{
flex: 1,
height: '4px',
backgroundColor: `${colors.textDim}20`,
borderRadius: '2px',
overflow: 'hidden',
}}
>
<div
style={{
width: `${progress}%`,
height: '100%',
backgroundColor: progress === 100 ? colors.success : colors.accent,
borderRadius: '2px',
transition: 'width 0.3s ease-out',
}}
/>
</div>

<span
style={{
fontSize: '11px',
color: colors.textDim,
flexShrink: 0,
}}
>
{progress}%
</span>
</div>
</button>
);
}

/**
* Props for AutoRunPanel component
*/
Expand Down Expand Up @@ -520,7 +411,7 @@ export function AutoRunPanel({
borderRadius: '3px',
}}
>
.maestro/auto-run/
.maestro/playbooks/
</code>{' '}
directory to get started
</p>
Expand Down
Loading
Loading