mirror of
https://github.com/cline/cline.git
synced 2026-09-09 06:45:53 +08:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ebc3eb825 | ||
|
|
5953a212f0 | ||
|
|
50496e4fd5 | ||
|
|
acc12df665 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
create clinerules folder if its currently a file and creating new rule
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
updated drag and drop text to say "drop" instead of "drag"
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Minor UX improvement to drag and drop ux
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
add checkpoints after more messages
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Introduce UI library for future UI development
|
||||
@@ -13,7 +13,6 @@ permissions:
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
if: false
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ jobs:
|
||||
run: npm run format
|
||||
|
||||
# Build the extension before running tests
|
||||
- name: Build Tests and Extension
|
||||
run: npm run pretest
|
||||
- name: Build Extension
|
||||
run: npm run compile
|
||||
|
||||
- name: Unit Tests
|
||||
run: npm run test:unit
|
||||
|
||||
Vendored
+1
@@ -12,6 +12,7 @@
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}", "--disable-workspace-trust", "${workspaceFolder}"],
|
||||
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"postDebugTask": "stop",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}"
|
||||
|
||||
@@ -221,7 +221,14 @@ export const createRuleFile = async (isGlobal: boolean, filename: string, cwd: s
|
||||
filePath = path.join(globalClineRulesFilePath, filename)
|
||||
} else {
|
||||
const localClineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules)
|
||||
|
||||
const hasError = await ensureLocalClinerulesDirExists(cwd)
|
||||
if (hasError === true) {
|
||||
return { filePath: null, fileExists: false }
|
||||
}
|
||||
|
||||
await fs.mkdir(localClineRulesFilePath, { recursive: true })
|
||||
|
||||
filePath = path.join(localClineRulesFilePath, filename)
|
||||
}
|
||||
|
||||
|
||||
+45
-73
@@ -927,7 +927,6 @@ export class Task {
|
||||
let responseImages: string[] | undefined
|
||||
if (response === "messageResponse") {
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
responseText = text
|
||||
responseImages = images
|
||||
}
|
||||
@@ -1327,7 +1326,6 @@ export class Task {
|
||||
|
||||
if (userFeedback) {
|
||||
await this.say("user_feedback", userFeedback.text, userFeedback.images)
|
||||
await this.saveCheckpoint()
|
||||
return [
|
||||
true,
|
||||
formatResponse.toolResult(
|
||||
@@ -1765,7 +1763,6 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
this.didRejectTool = true // Prevent further tool uses in this message
|
||||
return false
|
||||
@@ -1774,7 +1771,6 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1849,7 +1845,7 @@ export class Task {
|
||||
if (!accessAllowed) {
|
||||
await this.say("clineignore_error", relPath)
|
||||
pushToolResult(formatResponse.toolError(formatResponse.clineIgnoreError(relPath)))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -1905,7 +1901,6 @@ export class Task {
|
||||
)
|
||||
await this.diffViewProvider.revertChanges()
|
||||
await this.diffViewProvider.reset()
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} else if (content) {
|
||||
@@ -1963,28 +1958,28 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError(block.name, "path"))
|
||||
await this.diffViewProvider.reset()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
if (block.name === "replace_in_file" && !diff) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("replace_in_file", "diff"))
|
||||
await this.diffViewProvider.reset()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
if (block.name === "write_to_file" && !content) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("write_to_file", "content"))
|
||||
await this.diffViewProvider.reset()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
if (block.name === "new_rule" && !content) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("new_rule", "content"))
|
||||
await this.diffViewProvider.reset()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2043,7 +2038,6 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
this.didRejectTool = true
|
||||
didApprove = false
|
||||
@@ -2053,14 +2047,12 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
pushAdditionalToolFeedback(text, images)
|
||||
await this.say("user_feedback", text, images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
|
||||
if (!didApprove) {
|
||||
await this.diffViewProvider.revertChanges()
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2121,7 +2113,7 @@ export class Task {
|
||||
await handleError("writing file", error)
|
||||
await this.diffViewProvider.revertChanges()
|
||||
await this.diffViewProvider.reset()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2150,7 +2142,7 @@ export class Task {
|
||||
if (!relPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("read_file", "path"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2158,7 +2150,7 @@ export class Task {
|
||||
if (!accessAllowed) {
|
||||
await this.say("clineignore_error", relPath)
|
||||
pushToolResult(formatResponse.toolError(formatResponse.clineIgnoreError(relPath)))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2181,7 +2173,6 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "tool")
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
break
|
||||
}
|
||||
@@ -2194,12 +2185,12 @@ export class Task {
|
||||
await this.fileContextTracker.trackFileContext(relPath, "read_tool")
|
||||
|
||||
pushToolResult(content)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("reading file", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2230,7 +2221,7 @@ export class Task {
|
||||
if (!relDirPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("list_files", "path"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2263,18 +2254,17 @@ export class Task {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
pushToolResult(result)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("listing files", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2303,7 +2293,7 @@ export class Task {
|
||||
if (!relDirPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("list_code_definition_names", "path"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2333,18 +2323,17 @@ export class Task {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
pushToolResult(result)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("parsing source code definitions", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2377,13 +2366,13 @@ export class Task {
|
||||
if (!relDirPath) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("search_files", "path"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
if (!regex) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("search_files", "regex"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2415,18 +2404,17 @@ export class Task {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, false)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
telemetryService.captureToolUsage(this.taskId, block.name, false, true)
|
||||
}
|
||||
pushToolResult(results)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("searching files", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2442,7 +2430,6 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "action"))
|
||||
await this.browserSession.closeBrowser()
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -2486,7 +2473,7 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "url"))
|
||||
await this.browserSession.closeBrowser()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2502,7 +2489,6 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "browser_action_launch")
|
||||
const didApprove = await askApproval("browser_action_launch", url)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2528,7 +2514,7 @@ export class Task {
|
||||
await this.sayAndCreateMissingParamError("browser_action", "coordinate"),
|
||||
)
|
||||
await this.browserSession.closeBrowser()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break // can't be within an inner switch
|
||||
}
|
||||
}
|
||||
@@ -2537,7 +2523,7 @@ export class Task {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "text"))
|
||||
await this.browserSession.closeBrowser()
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2586,7 +2572,7 @@ export class Task {
|
||||
browserActionResult.screenshot ? [browserActionResult.screenshot] : [],
|
||||
),
|
||||
)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
case "close":
|
||||
pushToolResult(
|
||||
@@ -2594,7 +2580,7 @@ export class Task {
|
||||
`The browser has been closed. You may now proceed to using other tools.`,
|
||||
),
|
||||
)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2603,7 +2589,7 @@ export class Task {
|
||||
} catch (error) {
|
||||
await this.browserSession.closeBrowser() // if any error occurs, the browser session is terminated
|
||||
await handleError("executing browser action", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2631,7 +2617,7 @@ export class Task {
|
||||
if (!command) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("execute_command", "command"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
if (!requiresApprovalRaw) {
|
||||
@@ -2639,7 +2625,7 @@ export class Task {
|
||||
pushToolResult(
|
||||
await this.sayAndCreateMissingParamError("execute_command", "requires_approval"),
|
||||
)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2655,7 +2641,7 @@ export class Task {
|
||||
pushToolResult(
|
||||
formatResponse.toolError(formatResponse.clineIgnoreError(ignoredFileAttemptedToAccess)),
|
||||
)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -2687,7 +2673,6 @@ export class Task {
|
||||
`${this.shouldAutoApproveTool(block.name) && requiresApprovalPerLLM ? COMMAND_REQ_APP_STRING : ""}`, // ugly hack until we refactor combineCommandSequences
|
||||
)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2723,7 +2708,7 @@ export class Task {
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("executing command", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2753,13 +2738,13 @@ export class Task {
|
||||
if (!server_name) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("use_mcp_tool", "server_name"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
if (!tool_name) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("use_mcp_tool", "tool_name"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
// arguments are optional, but if they are provided they must be valid JSON
|
||||
@@ -2783,7 +2768,7 @@ export class Task {
|
||||
formatResponse.invalidMcpToolArgumentError(server_name, tool_name),
|
||||
),
|
||||
)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2810,7 +2795,6 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "use_mcp_server")
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2862,7 +2846,7 @@ export class Task {
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("executing MCP tool", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2890,13 +2874,13 @@ export class Task {
|
||||
if (!server_name) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("access_mcp_resource", "server_name"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
if (!uri) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("access_mcp_resource", "uri"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2917,7 +2901,6 @@ export class Task {
|
||||
this.removeLastPartialMessageIfExistsWithType("say", "use_mcp_server")
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2937,12 +2920,12 @@ export class Task {
|
||||
.join("\n\n") || "(Empty response)"
|
||||
await this.say("mcp_server_response", resourceResultPretty)
|
||||
pushToolResult(formatResponse.toolResult(resourceResultPretty))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("accessing MCP resource", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2961,7 +2944,7 @@ export class Task {
|
||||
if (!question) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("ask_followup_question", "question"))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -2998,12 +2981,12 @@ export class Task {
|
||||
}
|
||||
|
||||
pushToolResult(formatResponse.toolResult(`<answer>\n${text}\n</answer>`, images))
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("asking question", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3017,7 +3000,6 @@ export class Task {
|
||||
if (!context) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("new_task", "context"))
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -3046,12 +3028,10 @@ export class Task {
|
||||
formatResponse.toolResult(`The user has created a new task with the provided context.`),
|
||||
)
|
||||
}
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("creating new task", error)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3065,7 +3045,6 @@ export class Task {
|
||||
if (!context) {
|
||||
this.consecutiveMistakeCount++
|
||||
pushToolResult(await this.sayAndCreateMissingParamError("condense", "context"))
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
@@ -3108,12 +3087,10 @@ export class Task {
|
||||
await ensureTaskDirectoryExists(this.getContext(), this.taskId),
|
||||
)
|
||||
}
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("condensing context window", error)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3174,7 +3151,6 @@ export class Task {
|
||||
if (text || images?.length) {
|
||||
telemetryService.captureOptionsIgnored(this.taskId, options.length, "plan")
|
||||
await this.say("user_feedback", text ?? "", images)
|
||||
await this.saveCheckpoint()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3321,14 +3297,12 @@ export class Task {
|
||||
// complete command message
|
||||
const didApprove = await askApproval("command", command)
|
||||
if (!didApprove) {
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
const [userRejected, execCommandResult] = await this.executeCommandTool(command!)
|
||||
if (userRejected) {
|
||||
this.didRejectTool = true
|
||||
pushToolResult(execCommandResult)
|
||||
await this.saveCheckpoint()
|
||||
break
|
||||
}
|
||||
// user didn't reject, but the command may have output
|
||||
@@ -3347,7 +3321,6 @@ export class Task {
|
||||
break
|
||||
}
|
||||
await this.say("user_feedback", text ?? "", images)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
const toolResults: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = []
|
||||
if (commandResult) {
|
||||
@@ -3376,7 +3349,7 @@ export class Task {
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("attempting completion", error)
|
||||
await this.saveCheckpoint()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -3478,6 +3451,9 @@ export class Task {
|
||||
|
||||
// Save checkpoint if this is the first API request
|
||||
const isFirstRequest = this.clineMessages.filter((m) => m.say === "api_req_started").length === 0
|
||||
if (isFirstRequest) {
|
||||
await this.say("checkpoint_created") // no hash since we need to wait for CheckpointTracker to be initialized
|
||||
}
|
||||
|
||||
// getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds
|
||||
// for the best UX we show a placeholder api_req_started message with a loading spinner as this happens
|
||||
@@ -3488,10 +3464,6 @@ export class Task {
|
||||
}),
|
||||
)
|
||||
|
||||
if (isFirstRequest) {
|
||||
await this.say("checkpoint_created") // no hash since we need to wait for CheckpointTracker to be initialized
|
||||
}
|
||||
|
||||
// use this opportunity to initialize the checkpoint tracker (can be expensive to initialize in the constructor)
|
||||
// FIXME: right now we're letting users init checkpoints for old tasks, but this could be a problem if opening a task in the wrong workspace
|
||||
// isNewTask &&
|
||||
|
||||
Generated
+65
-4047
File diff suppressed because it is too large
Load Diff
@@ -15,13 +15,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.27.4",
|
||||
"@heroui/react": "^2.8.0-beta.2",
|
||||
"@vscode/webview-ui-toolkit": "^1.4.0",
|
||||
"debounce": "^2.1.1",
|
||||
"dompurify": "^3.2.4",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"firebase": "^11.3.0",
|
||||
"framer-motion": "^12.7.4",
|
||||
"fuse.js": "^7.0.0",
|
||||
"fzf": "^0.5.2",
|
||||
"mermaid": "^11.4.1",
|
||||
@@ -44,7 +42,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@tailwindcss/vite": "^4.0.12",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
@@ -62,7 +60,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"globals": "^15.14.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"tailwindcss": "^4.0.12",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.18.2",
|
||||
"vite": "^6.2.6",
|
||||
|
||||
@@ -6,10 +6,10 @@ import HistoryView from "./components/history/HistoryView"
|
||||
import SettingsView from "./components/settings/SettingsView"
|
||||
import WelcomeView from "./components/welcome/WelcomeView"
|
||||
import AccountView from "./components/account/AccountView"
|
||||
import { useExtensionState } from "./context/ExtensionStateContext"
|
||||
import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext"
|
||||
import { FirebaseAuthProvider } from "./context/FirebaseAuthContext"
|
||||
import { vscode } from "./utils/vscode"
|
||||
import McpView from "./components/mcp/configuration/McpConfigurationView"
|
||||
import { Providers } from "./Providers"
|
||||
|
||||
const AppContent = () => {
|
||||
const { didHydrateState, showWelcome, shouldShowAnnouncement, showMcp, mcpTab } = useExtensionState()
|
||||
@@ -126,9 +126,11 @@ const AppContent = () => {
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<Providers>
|
||||
<AppContent />
|
||||
</Providers>
|
||||
<ExtensionStateContextProvider>
|
||||
<FirebaseAuthProvider>
|
||||
<AppContent />
|
||||
</FirebaseAuthProvider>
|
||||
</ExtensionStateContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { ReactNode } from "react"
|
||||
|
||||
import { ExtensionStateContextProvider } from "./context/ExtensionStateContext"
|
||||
import { FirebaseAuthProvider } from "./context/FirebaseAuthContext"
|
||||
import { HeroUIProvider } from "@heroui/react"
|
||||
|
||||
export function Providers({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<ExtensionStateContextProvider>
|
||||
<FirebaseAuthProvider>
|
||||
<HeroUIProvider>{children}</HeroUIProvider>
|
||||
</FirebaseAuthProvider>
|
||||
</ExtensionStateContextProvider>
|
||||
)
|
||||
}
|
||||
@@ -237,7 +237,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
) => {
|
||||
const { filePaths, chatSettings, apiConfiguration, openRouterModels, platform } = useExtensionState()
|
||||
const [isTextAreaFocused, setIsTextAreaFocused] = useState(false)
|
||||
const [isDraggingOver, setIsDraggingOver] = useState(false)
|
||||
const [gitCommits, setGitCommits] = useState<GitCommit[]>([])
|
||||
|
||||
const [showSlashCommandsMenu, setShowSlashCommandsMenu] = useState(false)
|
||||
@@ -267,10 +266,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
const [menuPosition, setMenuPosition] = useState(0)
|
||||
const [shownTooltipMode, setShownTooltipMode] = useState<ChatSettings["mode"] | null>(null)
|
||||
const [pendingInsertions, setPendingInsertions] = useState<string[]>([])
|
||||
const [showShiftDragTip, setShowShiftDragTip] = useState(false)
|
||||
const shiftHoldTimerRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const [showUnsupportedFileError, setShowUnsupportedFileError] = useState(false)
|
||||
const unsupportedFileTimerRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
const [fileSearchResults, setFileSearchResults] = useState<SearchResult[]>([])
|
||||
const [searchLoading, setSearchLoading] = useState(false)
|
||||
@@ -1021,84 +1016,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
}
|
||||
}, [showModelSelector])
|
||||
|
||||
// Effect for Shift key hold detection
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Shift" && !event.repeat) {
|
||||
// Start timer only if Shift is pressed and not already held down
|
||||
if (shiftHoldTimerRef.current === null) {
|
||||
shiftHoldTimerRef.current = setTimeout(() => {
|
||||
setShowShiftDragTip(true)
|
||||
}, 250) // 250ms delay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === "Shift") {
|
||||
// Clear timer and hide tip when Shift is released
|
||||
if (shiftHoldTimerRef.current !== null) {
|
||||
clearTimeout(shiftHoldTimerRef.current)
|
||||
shiftHoldTimerRef.current = null
|
||||
}
|
||||
setShowShiftDragTip(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Add listeners
|
||||
window.addEventListener("keydown", handleKeyDown)
|
||||
window.addEventListener("keyup", handleKeyUp)
|
||||
|
||||
// Cleanup listeners on component unmount
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown)
|
||||
window.removeEventListener("keyup", handleKeyUp)
|
||||
// Clear any running timer on unmount
|
||||
if (shiftHoldTimerRef.current !== null) {
|
||||
clearTimeout(shiftHoldTimerRef.current)
|
||||
}
|
||||
}
|
||||
}, []) // Empty dependency array ensures this runs only once on mount/unmount
|
||||
|
||||
// Function to show error message for unsupported files for drag and drop
|
||||
const showUnsupportedFileErrorMessage = () => {
|
||||
// Show error message for unsupported files
|
||||
setShowUnsupportedFileError(true)
|
||||
|
||||
// Clear any existing timer
|
||||
if (unsupportedFileTimerRef.current) {
|
||||
clearTimeout(unsupportedFileTimerRef.current)
|
||||
}
|
||||
|
||||
// Set timer to hide error after 3 seconds
|
||||
unsupportedFileTimerRef.current = setTimeout(() => {
|
||||
setShowUnsupportedFileError(false)
|
||||
unsupportedFileTimerRef.current = null
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const handleDragEnter = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setIsDraggingOver(true)
|
||||
|
||||
// Check if files are being dragged
|
||||
if (e.dataTransfer.types.includes("Files")) {
|
||||
// Check if any of the files are not images
|
||||
const items = Array.from(e.dataTransfer.items)
|
||||
const hasNonImageFile = items.some((item) => {
|
||||
if (item.kind === "file") {
|
||||
const type = item.type.split("/")[0]
|
||||
return type !== "image"
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if (hasNonImageFile) {
|
||||
showUnsupportedFileErrorMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the drag over event to allow dropping.
|
||||
* Prevents the default behavior to enable drop.
|
||||
@@ -1107,37 +1024,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
*/
|
||||
const onDragOver = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
// Ensure state remains true if dragging continues over the element
|
||||
if (!isDraggingOver) {
|
||||
setIsDraggingOver(true)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDragLeave = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
// Check if the related target is still within the drop zone; prevents flickering
|
||||
const dropZone = e.currentTarget as HTMLElement
|
||||
if (!dropZone.contains(e.relatedTarget as Node)) {
|
||||
setIsDraggingOver(false)
|
||||
// Don't clear the error message here, let it time out naturally
|
||||
}
|
||||
}
|
||||
|
||||
// Effect to detect when drag operation ends outside the component
|
||||
useEffect(() => {
|
||||
const handleGlobalDragEnd = () => {
|
||||
// This will be triggered when the drag operation ends anywhere
|
||||
setIsDraggingOver(false)
|
||||
// Don't clear error message, let it time out naturally
|
||||
}
|
||||
|
||||
document.addEventListener("dragend", handleGlobalDragEnd)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("dragend", handleGlobalDragEnd)
|
||||
}
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* Handles the drop event for files and text.
|
||||
* Processes dropped images and text, updating the state accordingly.
|
||||
@@ -1146,14 +1034,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
*/
|
||||
const onDrop = async (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
setIsDraggingOver(false) // Reset state on drop
|
||||
|
||||
// Clear any error message when something is actually dropped
|
||||
setShowUnsupportedFileError(false)
|
||||
if (unsupportedFileTimerRef.current) {
|
||||
clearTimeout(unsupportedFileTimerRef.current)
|
||||
unsupportedFileTimerRef.current = null
|
||||
}
|
||||
|
||||
// --- 1. VSCode Explorer Drop Handling ---
|
||||
let uris: string[] = []
|
||||
@@ -1273,37 +1153,9 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
opacity: textAreaDisabled ? 0.5 : 1,
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
// Drag-over styles moved to DynamicTextArea
|
||||
transition: "background-color 0.1s ease-in-out, border 0.1s ease-in-out",
|
||||
}}
|
||||
onDrop={onDrop}
|
||||
onDragOver={onDragOver}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragLeave={handleDragLeave}>
|
||||
{showUnsupportedFileError && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: "10px 15px",
|
||||
backgroundColor: "rgba(var(--vscode-errorForeground-rgb), 0.1)",
|
||||
border: "2px solid var(--vscode-errorForeground)",
|
||||
borderRadius: 2,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
zIndex: 10,
|
||||
pointerEvents: "none",
|
||||
}}>
|
||||
<span
|
||||
style={{
|
||||
color: "var(--vscode-errorForeground)",
|
||||
fontWeight: "bold",
|
||||
fontSize: "12px",
|
||||
}}>
|
||||
Only image files are supported
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
onDragOver={onDragOver}>
|
||||
{showSlashCommandsMenu && (
|
||||
<div ref={slashCommandsMenuContainerRef}>
|
||||
<SlashCommandMenu
|
||||
@@ -1398,7 +1250,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
}
|
||||
onHeightChange?.(height)
|
||||
}}
|
||||
placeholder={showUnsupportedFileError ? "" : placeholderText}
|
||||
placeholder={placeholderText}
|
||||
maxRows={10}
|
||||
autoFocus={true}
|
||||
style={{
|
||||
@@ -1430,13 +1282,9 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
cursor: textAreaDisabled ? "not-allowed" : undefined,
|
||||
flex: 1,
|
||||
zIndex: 1,
|
||||
outline:
|
||||
isDraggingOver && !showUnsupportedFileError // Only show drag outline if not showing error
|
||||
? "2px dashed var(--vscode-focusBorder)"
|
||||
: isTextAreaFocused
|
||||
? `1px solid ${chatSettings.mode === "plan" ? PLAN_MODE_COLOR : "var(--vscode-focusBorder)"}`
|
||||
: "none",
|
||||
outlineOffset: isDraggingOver && !showUnsupportedFileError ? "1px" : "0px", // Add offset for drag-over outline
|
||||
outline: isTextAreaFocused
|
||||
? `1px solid ${chatSettings.mode === "plan" ? PLAN_MODE_COLOR : "var(--vscode-focusBorder)"}`
|
||||
: "none",
|
||||
}}
|
||||
onScroll={() => updateHighlights()}
|
||||
/>
|
||||
@@ -1498,129 +1346,83 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
||||
</div>
|
||||
|
||||
<ControlsContainer>
|
||||
{/* Always render both components, but control visibility with CSS */}
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
height: "28px", // Fixed height to prevent container shrinking
|
||||
}}>
|
||||
{/* ButtonGroup - always in DOM but visibility controlled */}
|
||||
<ButtonGroup
|
||||
style={{
|
||||
opacity: showShiftDragTip ? 0 : 1,
|
||||
pointerEvents: showShiftDragTip ? "none" : "auto",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
transition: "opacity 0.3s ease-in-out",
|
||||
transitionDelay: showShiftDragTip ? "0s" : "0.2s",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: showShiftDragTip ? 0 : 1,
|
||||
}}>
|
||||
<Tooltip tipText="Add Context" style={{ left: 0 }}>
|
||||
<VSCodeButton
|
||||
data-testid="context-button"
|
||||
appearance="icon"
|
||||
aria-label="Add Context"
|
||||
disabled={textAreaDisabled}
|
||||
onClick={handleContextButtonClick}
|
||||
style={{ padding: "0px 0px", height: "20px" }}>
|
||||
<ButtonContainer>
|
||||
<span className="flex items-center" style={{ fontSize: "13px", marginBottom: 1 }}>
|
||||
@
|
||||
</span>
|
||||
</ButtonContainer>
|
||||
</VSCodeButton>
|
||||
</Tooltip>
|
||||
<ButtonGroup>
|
||||
<Tooltip tipText="Add Context" style={{ left: 0 }}>
|
||||
<VSCodeButton
|
||||
data-testid="context-button"
|
||||
appearance="icon"
|
||||
aria-label="Add Context"
|
||||
disabled={textAreaDisabled}
|
||||
onClick={handleContextButtonClick}
|
||||
style={{ padding: "0px 0px", height: "20px" }}>
|
||||
<ButtonContainer>
|
||||
<span className="flex items-center" style={{ fontSize: "13px", marginBottom: 1 }}>
|
||||
@
|
||||
</span>
|
||||
{/* {showButtonText && <span style={{ fontSize: "10px" }}>Context</span>} */}
|
||||
</ButtonContainer>
|
||||
</VSCodeButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip tipText="Add Images">
|
||||
<VSCodeButton
|
||||
data-testid="images-button"
|
||||
appearance="icon"
|
||||
aria-label="Add Images"
|
||||
disabled={shouldDisableImages}
|
||||
onClick={() => {
|
||||
if (!shouldDisableImages) {
|
||||
onSelectImages()
|
||||
}
|
||||
}}
|
||||
style={{ padding: "0px 0px", height: "20px" }}>
|
||||
<ButtonContainer>
|
||||
<span
|
||||
className="codicon codicon-device-camera flex items-center"
|
||||
style={{ fontSize: "14px", marginBottom: -3 }}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</VSCodeButton>
|
||||
</Tooltip>
|
||||
<ServersToggleModal />
|
||||
<ClineRulesToggleModal />
|
||||
<ModelContainer ref={modelSelectorRef}>
|
||||
<ModelButtonWrapper ref={buttonRef}>
|
||||
<ModelDisplayButton
|
||||
role="button"
|
||||
isActive={showModelSelector}
|
||||
disabled={false}
|
||||
title="Select Model / API Provider"
|
||||
onClick={handleModelButtonClick}
|
||||
tabIndex={0}>
|
||||
<ModelButtonContent>{modelDisplayName}</ModelButtonContent>
|
||||
</ModelDisplayButton>
|
||||
</ModelButtonWrapper>
|
||||
{showModelSelector && (
|
||||
<ModelSelectorTooltip
|
||||
arrowPosition={arrowPosition}
|
||||
menuPosition={menuPosition}
|
||||
style={{
|
||||
bottom: `calc(100vh - ${menuPosition}px + 6px)`,
|
||||
}}>
|
||||
<ApiOptions
|
||||
showModelOptions={true}
|
||||
apiErrorMessage={undefined}
|
||||
modelIdErrorMessage={undefined}
|
||||
isPopup={true}
|
||||
saveImmediately={true} // Ensure popup saves immediately
|
||||
/>
|
||||
</ModelSelectorTooltip>
|
||||
)}
|
||||
</ModelContainer>
|
||||
</ButtonGroup>
|
||||
|
||||
{/* Shift Tip - always in DOM but visibility controlled */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start", // Left align horizontally
|
||||
alignItems: "center", // Center vertically
|
||||
height: "100%", // Fill the container height
|
||||
padding: "4px 0", // Add padding to match button group height
|
||||
boxSizing: "border-box", // Include padding in height calculation
|
||||
opacity: showShiftDragTip ? 1 : 0,
|
||||
pointerEvents: showShiftDragTip ? "auto" : "none",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
transition: "opacity 0.3s ease-in-out",
|
||||
transitionDelay: showShiftDragTip ? "0.2s" : "0s",
|
||||
width: "100%",
|
||||
zIndex: showShiftDragTip ? 1 : 0,
|
||||
}}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
whiteSpace: "nowrap",
|
||||
}}>
|
||||
Hold Shift to Drop Files
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Tooltip for Plan/Act toggle remains outside the conditional rendering */}
|
||||
<Tooltip tipText="Add Images">
|
||||
<VSCodeButton
|
||||
data-testid="images-button"
|
||||
appearance="icon"
|
||||
aria-label="Add Images"
|
||||
disabled={shouldDisableImages}
|
||||
onClick={() => {
|
||||
if (!shouldDisableImages) {
|
||||
onSelectImages()
|
||||
}
|
||||
}}
|
||||
style={{ padding: "0px 0px", height: "20px" }}>
|
||||
<ButtonContainer>
|
||||
<span
|
||||
className="codicon codicon-device-camera flex items-center"
|
||||
style={{ fontSize: "14px", marginBottom: -3 }}
|
||||
/>
|
||||
{/* {showButtonText && <span style={{ fontSize: "10px" }}>Images</span>} */}
|
||||
</ButtonContainer>
|
||||
</VSCodeButton>
|
||||
</Tooltip>
|
||||
<ServersToggleModal />
|
||||
<ClineRulesToggleModal />
|
||||
<ModelContainer ref={modelSelectorRef}>
|
||||
<ModelButtonWrapper ref={buttonRef}>
|
||||
<ModelDisplayButton
|
||||
role="button"
|
||||
isActive={showModelSelector}
|
||||
disabled={false}
|
||||
title="Select Model / API Provider"
|
||||
onClick={handleModelButtonClick}
|
||||
// onKeyDown={(e) => {
|
||||
// if (e.key === "Enter" || e.key === " ") {
|
||||
// e.preventDefault()
|
||||
// handleModelButtonClick()
|
||||
// }
|
||||
// }}
|
||||
tabIndex={0}>
|
||||
<ModelButtonContent>{modelDisplayName}</ModelButtonContent>
|
||||
</ModelDisplayButton>
|
||||
</ModelButtonWrapper>
|
||||
{showModelSelector && (
|
||||
<ModelSelectorTooltip
|
||||
arrowPosition={arrowPosition}
|
||||
menuPosition={menuPosition}
|
||||
style={{
|
||||
bottom: `calc(100vh - ${menuPosition}px + 6px)`,
|
||||
}}>
|
||||
<ApiOptions
|
||||
showModelOptions={true}
|
||||
apiErrorMessage={undefined}
|
||||
modelIdErrorMessage={undefined}
|
||||
isPopup={true}
|
||||
saveImmediately={true} // Ensure popup saves immediately
|
||||
/>
|
||||
</ModelSelectorTooltip>
|
||||
)}
|
||||
</ModelContainer>
|
||||
</ButtonGroup>
|
||||
<Tooltip
|
||||
style={{ zIndex: 1000 }}
|
||||
visible={shownTooltipMode !== null}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
/* @import "tailwindcss/preflight.css" layer(base); */
|
||||
@import "tailwindcss/utilities.css" layer(utilities);
|
||||
|
||||
@config "../tailwind.config.js";
|
||||
|
||||
textarea:focus {
|
||||
outline: 1.5px solid var(--vscode-focusBorder, #007fd4);
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
const { heroui } = require("@heroui/react")
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}", "./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
darkMode: "class",
|
||||
plugins: [
|
||||
heroui({
|
||||
defaultTheme: "vscode",
|
||||
themes: {
|
||||
vscode: {
|
||||
colors: {
|
||||
background: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user