mirror of
https://github.com/cline/cline.git
synced 2025-06-03 03:59:07 +00:00
Open the hood (#3949)
* add open disk conversation history button * changeset * change icon due to lack of artistic freedom --------- Co-authored-by: Elephant Lumps <celestial_vault@Elephants-MacBook-Pro.local>
This commit is contained in:
parent
f73172aeae
commit
3a325a6445
5
.changeset/gorgeous-mugs-repair.md
Normal file
5
.changeset/gorgeous-mugs-repair.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Add dev only button to open task conversation history
|
@ -52,6 +52,9 @@ service FileService {
|
||||
|
||||
// Refreshes all rule toggles (Cline, External, and Workflows)
|
||||
rpc refreshRules(EmptyRequest) returns (RefreshedRules);
|
||||
|
||||
// Opens a task's conversation history file on disk
|
||||
rpc openTaskHistory(StringRequest) returns (Empty);
|
||||
}
|
||||
|
||||
// Response for refreshRules operation
|
||||
|
19
src/core/controller/file/openTaskHistory.ts
Normal file
19
src/core/controller/file/openTaskHistory.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Controller } from ".."
|
||||
import { Empty, StringRequest } from "@shared/proto/common"
|
||||
import { openFile as openFileIntegration } from "@integrations/misc/open-file"
|
||||
import { FileMethodHandler } from "./index"
|
||||
import path from "path"
|
||||
/**
|
||||
* Opens a file in the editor
|
||||
* @param controller The controller instance
|
||||
* @param request The request message containing the file path in the 'value' field
|
||||
* @returns Empty response
|
||||
*/
|
||||
export const openTaskHistory: FileMethodHandler = async (controller: Controller, request: StringRequest): Promise<Empty> => {
|
||||
const globalStoragePath = controller.context.globalStorageUri.fsPath
|
||||
const taskHistoryPath = path.join(globalStoragePath, "tasks", request.value, "api_conversation_history.json")
|
||||
if (request.value) {
|
||||
openFileIntegration(taskHistoryPath)
|
||||
}
|
||||
return Empty.create()
|
||||
}
|
@ -26,7 +26,7 @@ import BrowserSessionRow from "@/components/chat/BrowserSessionRow"
|
||||
import ChatRow from "@/components/chat/ChatRow"
|
||||
import ChatTextArea from "@/components/chat/ChatTextArea"
|
||||
import QuotedMessagePreview from "@/components/chat/QuotedMessagePreview"
|
||||
import TaskHeader from "@/components/chat/TaskHeader"
|
||||
import TaskHeader from "@/components/chat/task-header/TaskHeader"
|
||||
import TelemetryBanner from "@/components/common/TelemetryBanner"
|
||||
import { unified } from "unified"
|
||||
import remarkStringify from "remark-stringify"
|
||||
|
@ -5,7 +5,7 @@ import { ClineCheckpointRestore } from "@shared/WebviewMessage"
|
||||
import { CheckpointRestoreRequest } from "@shared/proto/checkpoints"
|
||||
import React, { forwardRef, useRef, useState } from "react"
|
||||
import DynamicTextArea from "react-textarea-autosize"
|
||||
import { highlightText } from "./TaskHeader"
|
||||
import { highlightText } from "./task-header/TaskHeader"
|
||||
|
||||
interface UserMessageProps {
|
||||
text?: string
|
||||
|
@ -12,6 +12,10 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import React, { memo, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useWindowSize } from "react-use"
|
||||
import TaskTimeline from "./TaskTimeline"
|
||||
import DeleteTaskButton from "./buttons/DeleteTaskButton"
|
||||
import CopyTaskButton from "./buttons/CopyTaskButton"
|
||||
import OpenDiskTaskHistoryButton from "./buttons/OpenDiskTaskHistoryButton"
|
||||
|
||||
const { IS_DEV } = process.env
|
||||
|
||||
interface TaskHeaderProps {
|
||||
@ -412,9 +416,12 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
|
||||
</div>
|
||||
{!shouldShowPromptCacheInfo() && (
|
||||
<div className="flex items-center flex-wrap">
|
||||
<CopyButton taskText={task.text} />
|
||||
{IS_DEV === '"true"' && <TaskFolderButton taskId={currentTaskItem?.id} />}
|
||||
<DeleteButton taskSize={formatSize(currentTaskItem?.size)} taskId={currentTaskItem?.id} />
|
||||
{IS_DEV === '"true"' && <OpenDiskTaskHistoryButton taskId={currentTaskItem?.id} />}
|
||||
<CopyTaskButton taskText={task.text} />
|
||||
<DeleteTaskButton
|
||||
taskSize={formatSize(currentTaskItem?.size)}
|
||||
taskId={currentTaskItem?.id}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -468,9 +475,12 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center flex-wrap">
|
||||
<CopyButton taskText={task.text} />
|
||||
{IS_DEV === '"true"' && <TaskFolderButton taskId={currentTaskItem?.id} />}
|
||||
<DeleteButton taskSize={formatSize(currentTaskItem?.size)} taskId={currentTaskItem?.id} />
|
||||
{IS_DEV === '"true"' && <OpenDiskTaskHistoryButton taskId={currentTaskItem?.id} />}
|
||||
<CopyTaskButton taskText={task.text} />
|
||||
<DeleteTaskButton
|
||||
taskSize={formatSize(currentTaskItem?.size)}
|
||||
taskId={currentTaskItem?.id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@ -533,34 +543,6 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{/* {apiProvider === "" && (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "color-mix(in srgb, var(--vscode-badge-background) 50%, transparent)",
|
||||
color: "var(--vscode-badge-foreground)",
|
||||
borderRadius: "0 0 3px 3px",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: "4px 12px 6px 12px",
|
||||
fontSize: "0.9em",
|
||||
marginLeft: "10px",
|
||||
marginRight: "10px",
|
||||
}}>
|
||||
<div style={{ fontWeight: "500" }}>Credits Remaining:</div>
|
||||
<div>
|
||||
{formatPrice(Credits || 0)}
|
||||
{(Credits || 0) < 1 && (
|
||||
<>
|
||||
{" "}
|
||||
<VSCodeLink style={{ fontSize: "0.9em" }} href={getAddCreditsUrl(vscodeUriScheme)}>
|
||||
(get more?)
|
||||
</VSCodeLink>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -643,103 +625,4 @@ export const highlightText = (text?: string, withShadow = true) => {
|
||||
return [text]
|
||||
}
|
||||
|
||||
const CopyButton: React.FC<{
|
||||
taskText?: string
|
||||
}> = ({ taskText }) => {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = () => {
|
||||
if (!taskText) return
|
||||
|
||||
navigator.clipboard.writeText(taskText).then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<HeroTooltip content="Copy Task">
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={handleCopy}
|
||||
style={{ padding: "0px 0px" }}
|
||||
className="p-0"
|
||||
aria-label="Copy Task">
|
||||
<div className="flex items-center gap-[3px] text-[8px] font-bold opacity-60">
|
||||
<i className={`codicon codicon-${copied ? "check" : "copy"}`} />
|
||||
</div>
|
||||
</VSCodeButton>
|
||||
</HeroTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const TaskFolderButton: React.FC<{
|
||||
taskId?: string
|
||||
}> = ({ taskId }) => {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = () => {
|
||||
if (!taskId) return
|
||||
|
||||
navigator.clipboard.writeText(taskId).then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<HeroTooltip content="Copy Task ID">
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={handleCopy}
|
||||
style={{ padding: "0px 0px" }}
|
||||
className="p-0"
|
||||
aria-label="Copy Task ID">
|
||||
<div className="flex items-center gap-[3px] text-[8px] font-bold opacity-60">
|
||||
<i className={`codicon codicon-${copied ? "check" : "folder"}`} />
|
||||
</div>
|
||||
</VSCodeButton>
|
||||
</HeroTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const DeleteButton: React.FC<{
|
||||
taskSize: string
|
||||
taskId?: string
|
||||
}> = ({ taskSize, taskId }) => (
|
||||
<HeroTooltip content="Delete Task & Checkpoints">
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={() => taskId && TaskServiceClient.deleteTasksWithIds(StringArrayRequest.create({ value: [taskId] }))}
|
||||
style={{ padding: "0px 0px" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "3px",
|
||||
fontSize: "10px",
|
||||
fontWeight: "bold",
|
||||
opacity: 0.6,
|
||||
}}>
|
||||
<i className={`codicon codicon-trash`} />
|
||||
{taskSize}
|
||||
</div>
|
||||
</VSCodeButton>
|
||||
</HeroTooltip>
|
||||
)
|
||||
|
||||
// const ExportButton = () => (
|
||||
// <VSCodeButton
|
||||
// appearance="icon"
|
||||
// onClick={() => vscode.postMessage({ type: "exportCurrentTask" })}
|
||||
// style={
|
||||
// {
|
||||
// // marginBottom: "-2px",
|
||||
// // marginRight: "-2.5px",
|
||||
// }
|
||||
// }>
|
||||
// <div style={{ fontSize: "10.5px", fontWeight: "bold", opacity: 0.6 }}>EXPORT</div>
|
||||
// </VSCodeButton>
|
||||
// )
|
||||
|
||||
export default memo(TaskHeader)
|
@ -4,7 +4,16 @@ import { ClineMessage } from "@shared/ExtensionMessage"
|
||||
import { combineApiRequests } from "@shared/combineApiRequests"
|
||||
import { combineCommandSequences } from "@shared/combineCommandSequences"
|
||||
import TaskTimelineTooltip from "./TaskTimelineTooltip"
|
||||
import { COLOR_WHITE, COLOR_GRAY, COLOR_DARK_GRAY, COLOR_BEIGE, COLOR_BLUE, COLOR_RED, COLOR_PURPLE, COLOR_GREEN } from "./colors"
|
||||
import {
|
||||
COLOR_WHITE,
|
||||
COLOR_GRAY,
|
||||
COLOR_DARK_GRAY,
|
||||
COLOR_BEIGE,
|
||||
COLOR_BLUE,
|
||||
COLOR_RED,
|
||||
COLOR_PURPLE,
|
||||
COLOR_GREEN,
|
||||
} from "../colors"
|
||||
|
||||
// Timeline dimensions and spacing
|
||||
const TIMELINE_HEIGHT = "18px"
|
@ -1,6 +1,15 @@
|
||||
import React from "react"
|
||||
import { ClineMessage } from "@shared/ExtensionMessage"
|
||||
import { COLOR_WHITE, COLOR_GRAY, COLOR_DARK_GRAY, COLOR_BEIGE, COLOR_BLUE, COLOR_RED, COLOR_PURPLE, COLOR_GREEN } from "./colors"
|
||||
import {
|
||||
COLOR_WHITE,
|
||||
COLOR_GRAY,
|
||||
COLOR_DARK_GRAY,
|
||||
COLOR_BEIGE,
|
||||
COLOR_BLUE,
|
||||
COLOR_RED,
|
||||
COLOR_PURPLE,
|
||||
COLOR_GREEN,
|
||||
} from "../colors"
|
||||
import { Tooltip } from "@heroui/react"
|
||||
|
||||
// Color mapping for different message types
|
@ -0,0 +1,35 @@
|
||||
import HeroTooltip from "@/components/common/HeroTooltip"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import { useState } from "react"
|
||||
|
||||
const CopyTaskButton: React.FC<{
|
||||
taskText?: string
|
||||
}> = ({ taskText }) => {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = () => {
|
||||
if (!taskText) return
|
||||
|
||||
navigator.clipboard.writeText(taskText).then(() => {
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 1500)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<HeroTooltip content="Copy Task">
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={handleCopy}
|
||||
style={{ padding: "0px 0px" }}
|
||||
className="p-0"
|
||||
aria-label="Copy Task">
|
||||
<div className="flex items-center gap-[3px] text-[8px] font-bold opacity-60">
|
||||
<i className={`codicon codicon-${copied ? "check" : "copy"}`} />
|
||||
</div>
|
||||
</VSCodeButton>
|
||||
</HeroTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export default CopyTaskButton
|
@ -0,0 +1,31 @@
|
||||
import HeroTooltip from "@/components/common/HeroTooltip"
|
||||
import { TaskServiceClient } from "@/services/grpc-client"
|
||||
import { StringArrayRequest } from "@shared/proto/common"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
const DeleteTaskButton: React.FC<{
|
||||
taskSize: string
|
||||
taskId?: string
|
||||
}> = ({ taskSize, taskId }) => (
|
||||
<HeroTooltip content="Delete Task & Checkpoints">
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={() => taskId && TaskServiceClient.deleteTasksWithIds(StringArrayRequest.create({ value: [taskId] }))}
|
||||
style={{ padding: "0px 0px" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "3px",
|
||||
fontSize: "10px",
|
||||
fontWeight: "bold",
|
||||
opacity: 0.6,
|
||||
}}>
|
||||
<i className={`codicon codicon-trash`} />
|
||||
{taskSize}
|
||||
</div>
|
||||
</VSCodeButton>
|
||||
</HeroTooltip>
|
||||
)
|
||||
|
||||
export default DeleteTaskButton
|
@ -0,0 +1,33 @@
|
||||
import HeroTooltip from "@/components/common/HeroTooltip"
|
||||
import { FileServiceClient } from "@/services/grpc-client"
|
||||
import { StringRequest } from "@shared/proto/common"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
const OpenDiskTaskHistoryButton: React.FC<{
|
||||
taskId?: string
|
||||
}> = ({ taskId }) => {
|
||||
const handleOpenDiskTaskHistory = () => {
|
||||
if (!taskId) return
|
||||
|
||||
FileServiceClient.openTaskHistory(StringRequest.create({ value: taskId })).catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<HeroTooltip content="Open Disk Task History">
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={handleOpenDiskTaskHistory}
|
||||
style={{ padding: "0px 0px" }}
|
||||
className="p-0"
|
||||
aria-label="Open Disk Task History">
|
||||
<div className="flex items-center gap-[3px] text-[8px] font-bold opacity-60">
|
||||
<i className={`codicon codicon-folder`} />
|
||||
</div>
|
||||
</VSCodeButton>
|
||||
</HeroTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export default OpenDiskTaskHistoryButton
|
Loading…
Reference in New Issue
Block a user