Add button to copy all process logs (vibe-kanban) (#1429)
* Done! I've implemented the "Copy all process logs" button for the `ViewProcessesDialog`. Here's a summary of the changes: **Files modified:** 1. **`ProcessLogsViewer.tsx`** - Added `onLogsChange` callback prop and exported the `LogEntry` type 2. **`ProcessesTab.tsx`** - Added `onLogsChange` prop that passes through to ProcessLogsViewer, and clears logs when no process is selected 3. **`ViewProcessesDialog.tsx`** - Added the copy button with: - State management for logs and copied feedback - Copy button in the dialog header (right-aligned) - Tooltip showing "Copy logs" / "Copied!" - Button disabled when no logs are available - Uses `Copy`/`Check` icons from lucide-react 4. **i18n files** (en, es, ko, ja) - Added translation keys: - `copyLogs`: "Copy logs" - `logsCopied`: "Copied!" **Behavior:** - The copy button appears in the dialog header next to the title - When clicked, it copies all log content (joined by newlines) to clipboard - Shows a checkmark icon for 2 seconds after successful copy - Button is disabled when viewing the process list (no logs selected) * Cleanup script changes for task attempt 2060681f-e4c3-46a7-aca3-3e9705b53248 * Fixed! I wrapped the `Tooltip` component in `TooltipProvider` as required by shadcn/ui. The error should now be resolved - the copy button should work properly. * Done! I've made the following changes: 1. Removed the `Tooltip`, `TooltipProvider`, `TooltipContent`, and `TooltipTrigger` components 2. Added `mr-8` margin to position the button left of the dialog's native close button 3. Added `text-green-500` to the `Check` icon for green success feedback The button should now be properly positioned and show a green checkmark when logs are copied. * Done! Changed `text-green-500` to `text-success` so the check icon uses the theme's success color instead of a hardcoded green. * Done! I've fixed the vertical alignment by: 1. Moving the button outside the `DialogHeader` and positioning it absolutely 2. Using `absolute right-12 top-4` to place it exactly to the left of the close button (which is at `right-4 top-4`) 3. Using `h-4 w-4` to match the close button's icon size 4. Added `rounded-sm opacity-70 hover:opacity-100` to match the close button's styling The copy button should now be perfectly aligned with the close button. * Done! I've moved the copy button to `ProcessesTab` next to the "Back to list" button. The changes: 1. **Removed** the absolute-positioned icon button from `ViewProcessesDialog` 2. **Added** a text button "Copy logs" / "Copied!" in `ProcessesTab` next to "Back to list" 3. **Moved** the i18n keys from `viewProcessesDialog` to `processes` section 4. The button uses the same styling as the "Back to list" button for consistency 5. Shows green "Copied!" text for 2 seconds after successful copy * Done! I've made the following changes to address the review comment: 1. **`ProcessLogsViewer.tsx`**: Removed the `export` keyword from `LogEntry` type - it's now internal to the file 2. **`ProcessesTab.tsx`**: Changed import from `LogEntry` to `PatchType` from `shared/types`, and updated the state and callback types accordingly The type check passes successfully. * Done! I've addressed the review comment by: 1. **Lifted `useLogStream` to `ProcessesTab`** - Now `ProcessesTab` calls `useLogStream(selectedProcess?.id ?? '')` directly, giving it direct access to the logs for the copy button 2. **Removed the hacky `onLogsChange` callback** - No more `useEffect` to propagate logs up via callback 3. **Using `ProcessLogsViewerContent` directly** - `ProcessesTab` now imports and uses `ProcessLogsViewerContent` with the logs from `useLogStream` 4. **Kept `ProcessLogsViewer` as a simpler component** - The default export still exists for other potential consumers, but it's now a simple wrapper without the callback prop The data flow is now cleaner: - `ProcessesTab` owns the log data via `useLogStream` - `ProcessLogsViewerContent` is a pure presentation component - No callback prop threading needed * Done! I've added proper translations for the i18n keys: - **Spanish (es):** "Copiar registros" / "¡Copiado!" - **Korean (ko):** "로그 복사" / "복사됨!" - **Japanese (ja):** "ログをコピー" / "コピーしました!"
This commit is contained in:
committed by
GitHub
parent
7989168e7a
commit
9bfaa6dde5
@@ -12,7 +12,8 @@ import {
|
||||
import { executionProcessesApi } from '@/lib/api.ts';
|
||||
import { ProfileVariantBadge } from '@/components/common/ProfileVariantBadge.tsx';
|
||||
import { useExecutionProcesses } from '@/hooks/useExecutionProcesses';
|
||||
import ProcessLogsViewer from './ProcessLogsViewer';
|
||||
import { useLogStream } from '@/hooks/useLogStream';
|
||||
import { ProcessLogsViewerContent } from './ProcessLogsViewer';
|
||||
import type { ExecutionProcessStatus, ExecutionProcess } from 'shared/types';
|
||||
|
||||
import { useProcessSelection } from '@/contexts/ProcessSelectionContext';
|
||||
@@ -36,12 +37,33 @@ function ProcessesTab({ attemptId }: ProcessesTabProps) {
|
||||
const [localProcessDetails, setLocalProcessDetails] = useState<
|
||||
Record<string, ExecutionProcess>
|
||||
>({});
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const selectedProcess = selectedProcessId
|
||||
? localProcessDetails[selectedProcessId] ||
|
||||
executionProcessesById[selectedProcessId]
|
||||
: null;
|
||||
|
||||
const { logs, error: logsError } = useLogStream(selectedProcess?.id ?? '');
|
||||
|
||||
useEffect(() => {
|
||||
setLocalProcessDetails({});
|
||||
setLoadingProcessId(null);
|
||||
}, [attemptId]);
|
||||
|
||||
const handleCopyLogs = useCallback(async () => {
|
||||
if (logs.length === 0) return;
|
||||
|
||||
const text = logs.map((entry) => entry.content).join('\n');
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (err) {
|
||||
console.warn('Copy to clipboard failed:', err);
|
||||
}
|
||||
}, [logs]);
|
||||
|
||||
const getStatusIcon = (status: ExecutionProcessStatus) => {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
@@ -126,10 +148,6 @@ function ProcessesTab({ attemptId }: ProcessesTabProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const selectedProcess = selectedProcessId
|
||||
? localProcessDetails[selectedProcessId] ||
|
||||
executionProcessesById[selectedProcessId]
|
||||
: null;
|
||||
const { isProcessGreyed } = useRetryUi();
|
||||
|
||||
if (!attemptId) {
|
||||
@@ -261,17 +279,32 @@ function ProcessesTab({ attemptId }: ProcessesTabProps) {
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('processes.detailsTitle')}
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setSelectedProcessId(null)}
|
||||
className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted/50 rounded-md border border-border transition-colors"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
{t('processes.backToList')}
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleCopyLogs}
|
||||
disabled={logs.length === 0}
|
||||
className={`flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md border border-border transition-colors ${
|
||||
copied
|
||||
? 'text-success'
|
||||
: logs.length === 0
|
||||
? 'text-muted-foreground opacity-50 cursor-not-allowed'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/50'
|
||||
}`}
|
||||
>
|
||||
{copied ? t('processes.logsCopied') : t('processes.copyLogs')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedProcessId(null)}
|
||||
className="flex items-center gap-2 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted/50 rounded-md border border-border transition-colors"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
{t('processes.backToList')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
{selectedProcess ? (
|
||||
<ProcessLogsViewer processId={selectedProcess.id} />
|
||||
<ProcessLogsViewerContent logs={logs} error={logsError} />
|
||||
) : loadingProcessId === selectedProcessId ? (
|
||||
<div className="text-center text-muted-foreground">
|
||||
<p>{t('processes.loadingDetails')}</p>
|
||||
|
||||
@@ -126,7 +126,9 @@
|
||||
"detailsTitle": "Process Details",
|
||||
"backToList": "Back to list",
|
||||
"loadingDetails": "Loading process details...",
|
||||
"errorLoadingDetails": "Failed to load process details. Please try again."
|
||||
"errorLoadingDetails": "Failed to load process details. Please try again.",
|
||||
"copyLogs": "Copy logs",
|
||||
"logsCopied": "Copied!"
|
||||
},
|
||||
"taskHeader": {
|
||||
"editTask": "Edit task",
|
||||
|
||||
@@ -304,7 +304,9 @@
|
||||
"processId": "Process ID: {{id}}",
|
||||
"reconnecting": "Reconnecting...",
|
||||
"selectAttempt": "Select an attempt to view execution processes.",
|
||||
"started": "Started: {{date}}"
|
||||
"started": "Started: {{date}}",
|
||||
"copyLogs": "Copiar registros",
|
||||
"logsCopied": "¡Copiado!"
|
||||
},
|
||||
"rebase": {
|
||||
"common": {
|
||||
|
||||
@@ -304,7 +304,9 @@
|
||||
"processId": "Process ID: {{id}}",
|
||||
"reconnecting": "Reconnecting...",
|
||||
"selectAttempt": "Select an attempt to view execution processes.",
|
||||
"started": "Started: {{date}}"
|
||||
"started": "Started: {{date}}",
|
||||
"copyLogs": "ログをコピー",
|
||||
"logsCopied": "コピーしました!"
|
||||
},
|
||||
"rebase": {
|
||||
"common": {
|
||||
|
||||
@@ -304,7 +304,9 @@
|
||||
"processId": "Process ID: {{id}}",
|
||||
"reconnecting": "Reconnecting...",
|
||||
"selectAttempt": "Select an attempt to view execution processes.",
|
||||
"started": "Started: {{date}}"
|
||||
"started": "Started: {{date}}",
|
||||
"copyLogs": "로그 복사",
|
||||
"logsCopied": "복사됨!"
|
||||
},
|
||||
"rebase": {
|
||||
"common": {
|
||||
|
||||
Reference in New Issue
Block a user