mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-19 07:54:46 +00:00
feat: configurable screenshot detection
This commit is contained in:
parent
16117036cc
commit
f83ce88984
9 changed files with 390 additions and 30 deletions
|
|
@ -29,6 +29,7 @@ public class ConfigurationComponent {
|
|||
private final JBTextField temperatureField;
|
||||
private final CodeCompletionConfigurationForm codeCompletionForm;
|
||||
private final ChatCompletionConfigurationForm chatCompletionForm;
|
||||
private final ScreenshotConfigurationForm screenshotForm;
|
||||
|
||||
public ConfigurationComponent(
|
||||
Disposable parentDisposable,
|
||||
|
|
@ -74,6 +75,8 @@ public class ConfigurationComponent {
|
|||
|
||||
codeCompletionForm = new CodeCompletionConfigurationForm();
|
||||
chatCompletionForm = new ChatCompletionConfigurationForm();
|
||||
screenshotForm = new ScreenshotConfigurationForm();
|
||||
screenshotForm.loadState(configuration.getScreenshotWatchPaths());
|
||||
|
||||
mainPanel = FormBuilder.createFormBuilder()
|
||||
.addComponent(checkForPluginUpdatesCheckBox)
|
||||
|
|
@ -84,6 +87,9 @@ public class ConfigurationComponent {
|
|||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("configurationConfigurable.section.assistant.title")))
|
||||
.addComponent(createAssistantConfigurationForm())
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("configurationConfigurable.section.screenshots.title")))
|
||||
.addComponent(screenshotForm.createPanel())
|
||||
.addComponent(new TitledSeparator(
|
||||
CodeGPTBundle.get("configurationConfigurable.section.codeCompletion.title")))
|
||||
.addComponent(codeCompletionForm.createPanel())
|
||||
|
|
@ -108,6 +114,11 @@ public class ConfigurationComponent {
|
|||
state.setAutoFormattingEnabled(autoFormattingCheckBox.isSelected());
|
||||
state.setCodeCompletionSettings(codeCompletionForm.getFormState());
|
||||
state.setChatCompletionSettings(chatCompletionForm.getFormState());
|
||||
|
||||
var screenshotPaths = screenshotForm.getState();
|
||||
state.getScreenshotWatchPaths().clear();
|
||||
state.getScreenshotWatchPaths().addAll(screenshotPaths);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +132,7 @@ public class ConfigurationComponent {
|
|||
autoFormattingCheckBox.setSelected(configuration.getAutoFormattingEnabled());
|
||||
codeCompletionForm.resetForm(configuration.getCodeCompletionSettings());
|
||||
chatCompletionForm.resetForm(configuration.getChatCompletionSettings());
|
||||
screenshotForm.loadState(configuration.getScreenshotWatchPaths());
|
||||
}
|
||||
|
||||
// Formatted keys are not referenced in the messages bundle file
|
||||
|
|
|
|||
|
|
@ -8,33 +8,49 @@ import com.intellij.openapi.project.Project
|
|||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil
|
||||
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
|
||||
import ee.carlrobert.codegpt.settings.configuration.ScreenshotPathDetector
|
||||
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTService
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.AttachImageNotifier
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.absolutePathString
|
||||
|
||||
class CodeGPTProjectActivity : ProjectActivity {
|
||||
|
||||
private val watchExtensions = setOf("jpg", "jpeg", "png")
|
||||
private val logger = thisLogger()
|
||||
|
||||
override suspend fun execute(project: Project) {
|
||||
EditorActionsUtil.refreshActions()
|
||||
|
||||
project.service<CodeGPTService>().syncUserDetailsAsync()
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode
|
||||
&& service<ConfigurationSettings>().state.checkForNewScreenshots
|
||||
) {
|
||||
val desktopPath = Paths.get(System.getProperty("user.home"), "Desktop")
|
||||
project.service<FileWatcher>().watch(desktopPath) {
|
||||
if (watchExtensions.contains(getFileExtension(it))) {
|
||||
showImageAttachmentNotification(
|
||||
project,
|
||||
desktopPath.resolve(it).absolutePathString()
|
||||
)
|
||||
val configurationState = service<ConfigurationSettings>().state
|
||||
val watchPaths = configurationState.screenshotWatchPaths.ifEmpty {
|
||||
ScreenshotPathDetector.getDefaultPaths()
|
||||
}
|
||||
val watchExtensions = ScreenshotPathDetector.getDefaultFileExtensions().toSet()
|
||||
logger.debug("Screenshot watch configuration - paths: $watchPaths, extensions: $watchExtensions")
|
||||
val validPaths = watchPaths.filter { ScreenshotPathDetector.isValidWatchPath(it) }
|
||||
logger.debug("Valid watch paths after filtering: $validPaths")
|
||||
if (validPaths.isNotEmpty()) {
|
||||
logger.info("Starting screenshot file watching for ${validPaths.size} paths")
|
||||
project.service<FileWatcher>().watchMultiplePaths(validPaths) { fileName, watchPath ->
|
||||
val fileExtension = getFileExtension(fileName)
|
||||
logger.trace("File detected: fileName=$fileName, extension='$fileExtension', watchPath=$watchPath")
|
||||
if (watchExtensions.contains(fileExtension)) {
|
||||
val fullPath = Paths.get(watchPath).resolve(fileName).absolutePathString()
|
||||
logger.info("New screenshot file created: $fullPath (extension='$fileExtension')")
|
||||
showImageAttachmentNotification(project, fullPath)
|
||||
} else {
|
||||
logger.trace("File extension '$fileExtension' not in watch list: $watchExtensions")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("No valid screenshot watch paths found - screenshot detection disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,42 +2,84 @@ package ee.carlrobert.codegpt
|
|||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE
|
||||
import java.nio.file.WatchService
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.io.path.exists
|
||||
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
class FileWatcher : Disposable {
|
||||
|
||||
private var fileMonitor: Thread? = null
|
||||
private val watchServices = mutableListOf<WatchService>()
|
||||
private val fileMonitors = mutableListOf<Thread>()
|
||||
private val logger = thisLogger()
|
||||
|
||||
fun watch(pathToWatch: Path, onFileCreated: (Path) -> Unit) {
|
||||
fileMonitor = pathToWatch.takeIf { it.exists() }?.let {
|
||||
fun watchMultiplePaths(pathsToWatch: List<String>, onFileCreated: (Path, String) -> Unit) {
|
||||
dispose()
|
||||
|
||||
pathsToWatch.forEach { pathString ->
|
||||
try {
|
||||
FileSystems.getDefault().newWatchService().also {
|
||||
pathToWatch.register(it, ENTRY_CREATE) // watch for new files
|
||||
val path = Paths.get(pathString)
|
||||
if (path.exists()) {
|
||||
val watchService = FileSystems.getDefault().newWatchService()
|
||||
path.register(watchService, ENTRY_CREATE)
|
||||
watchServices.add(watchService)
|
||||
logger.debug("Successfully registered watch service for path: $pathString (absolute: ${path.toAbsolutePath()})")
|
||||
|
||||
val monitor = thread {
|
||||
try {
|
||||
logger.debug("File watch monitor thread started for path: $pathString")
|
||||
generateSequence { watchService.take() }.forEach { key ->
|
||||
logger.trace("Watch event received for path: $pathString")
|
||||
key.pollEvents().forEach { event ->
|
||||
val fileName = event.context() as Path
|
||||
val fullPath = path.resolve(fileName)
|
||||
logger.debug("File event detected: ${event.kind()} - fileName=$fileName, fullPath=$fullPath")
|
||||
onFileCreated(fileName, pathString)
|
||||
}
|
||||
val resetResult = key.reset()
|
||||
if (!resetResult) {
|
||||
logger.warn("Watch key reset failed for path: $pathString - watch may have become invalid")
|
||||
}
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
logger.debug("File watch monitor thread interrupted for path: $pathString")
|
||||
Thread.currentThread().interrupt()
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Error in file watcher for path: $pathString", e)
|
||||
} finally {
|
||||
logger.debug("File watch monitor thread stopped for path: $pathString")
|
||||
}
|
||||
}
|
||||
fileMonitors.add(monitor)
|
||||
|
||||
logger.info("Started watching path: $pathString")
|
||||
} else {
|
||||
logger.warn("Path does not exist or is not accessible: $pathString")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null // WatchService or registration failed
|
||||
}
|
||||
}?.let { watchService ->
|
||||
thread {
|
||||
try {
|
||||
generateSequence { watchService.take() }.forEach { key ->
|
||||
key.pollEvents().forEach { onFileCreated(it.context() as Path) }
|
||||
key.reset()
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
Thread.currentThread().interrupt()
|
||||
}
|
||||
logger.warn("Failed to set up watcher for path: $pathString", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
fileMonitor?.interrupt()
|
||||
logger.debug("Disposing FileWatcher - stopping ${fileMonitors.size} monitor threads and ${watchServices.size} watch services")
|
||||
fileMonitors.forEach { it.interrupt() }
|
||||
fileMonitors.clear()
|
||||
|
||||
watchServices.forEach { watchService ->
|
||||
try {
|
||||
watchService.close()
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Error closing watch service", e)
|
||||
}
|
||||
}
|
||||
watchServices.clear()
|
||||
logger.debug("FileWatcher disposal completed")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class ConfigurationSettingsState : BaseState() {
|
|||
var temperature by property(0.1f) { max(0f, min(1f, it)) }
|
||||
var checkForPluginUpdates by property(true)
|
||||
var checkForNewScreenshots by property(true)
|
||||
var screenshotWatchPaths by list<String>()
|
||||
var ignoreGitCommitTokenLimit by property(false)
|
||||
var methodNameGenerationEnabled by property(true)
|
||||
var captureCompileErrors by property(true)
|
||||
|
|
@ -34,9 +35,14 @@ class ConfigurationSettingsState : BaseState() {
|
|||
var tableData by map<String, String>()
|
||||
var chatCompletionSettings by property(ChatCompletionSettingsState())
|
||||
var codeCompletionSettings by property(CodeCompletionSettingsState())
|
||||
var myAwesomeFeatureEnabled by property(false)
|
||||
|
||||
init {
|
||||
tableData.putAll(EditorActionsUtil.DEFAULT_ACTIONS)
|
||||
|
||||
if (screenshotWatchPaths.isEmpty()) {
|
||||
screenshotWatchPaths.addAll(ScreenshotPathDetector.getDefaultPaths())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package ee.carlrobert.codegpt.settings.configuration
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import com.intellij.util.ui.FormBuilder
|
||||
import com.intellij.util.ui.JBUI
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle
|
||||
import java.awt.Dimension
|
||||
import javax.swing.JComponent
|
||||
|
||||
class PathInputDialog(
|
||||
title: String,
|
||||
private val textField: TextFieldWithBrowseButton
|
||||
) : DialogWrapper(true) {
|
||||
|
||||
init {
|
||||
this.title = title
|
||||
init()
|
||||
}
|
||||
|
||||
override fun createCenterPanel(): JComponent {
|
||||
val panel = FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent(
|
||||
CodeGPTBundle.get("configurationConfigurable.screenshotPaths.dialog.path.label"),
|
||||
textField,
|
||||
true
|
||||
)
|
||||
.panel
|
||||
panel.preferredSize = Dimension(JBUI.scale(500), panel.preferredSize.height)
|
||||
return panel
|
||||
}
|
||||
|
||||
override fun getPreferredFocusedComponent(): JComponent = textField
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package ee.carlrobert.codegpt.settings.configuration
|
||||
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import com.intellij.ui.ToolbarDecorator
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.ui.dsl.builder.AlignX
|
||||
import com.intellij.ui.dsl.builder.panel
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.DefaultListModel
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.ListSelectionModel
|
||||
|
||||
class ScreenshotConfigurationForm {
|
||||
|
||||
private val pathListModel = DefaultListModel<String>()
|
||||
private val pathList = JBList(pathListModel)
|
||||
|
||||
init {
|
||||
pathList.selectionMode = ListSelectionModel.SINGLE_SELECTION
|
||||
pathList.visibleRowCount = 3
|
||||
}
|
||||
|
||||
fun createPanel(): JPanel {
|
||||
val pathPanel = createPathConfigurationPanel()
|
||||
|
||||
return panel {
|
||||
row {
|
||||
label(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.label"))
|
||||
}
|
||||
row {
|
||||
cell(pathPanel)
|
||||
.align(AlignX.FILL)
|
||||
.resizableColumn()
|
||||
.comment(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.comment"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPathConfigurationPanel(): JPanel {
|
||||
val panel = JPanel(BorderLayout())
|
||||
|
||||
val decorator = ToolbarDecorator.createDecorator(pathList)
|
||||
.setAddAction { addPath() }
|
||||
.setRemoveAction { removePath() }
|
||||
.setEditAction { editPath() }
|
||||
.setAddActionName(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.add"))
|
||||
.setRemoveActionName(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.remove"))
|
||||
.setEditActionName(CodeGPTBundle.get("configurationConfigurable.screenshotPaths.edit"))
|
||||
|
||||
panel.add(decorator.createPanel(), BorderLayout.CENTER)
|
||||
return panel
|
||||
}
|
||||
|
||||
|
||||
private fun addPath() {
|
||||
val textField = TextFieldWithBrowseButton()
|
||||
val fileChooserDescriptor = FileChooserDescriptor(false, true, false, false, false, false)
|
||||
fileChooserDescriptor.title =
|
||||
CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.title")
|
||||
fileChooserDescriptor.description =
|
||||
CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.description")
|
||||
|
||||
textField.addBrowseFolderListener(
|
||||
TextBrowseFolderListener(fileChooserDescriptor)
|
||||
)
|
||||
|
||||
val dialog = PathInputDialog(
|
||||
CodeGPTBundle.get("configurationConfigurable.screenshotPaths.add.title"),
|
||||
textField
|
||||
)
|
||||
|
||||
if (dialog.showAndGet()) {
|
||||
val path = textField.text
|
||||
if (path.isNotBlank() && !pathListModel.contains(path)) {
|
||||
pathListModel.addElement(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removePath() {
|
||||
val selectedIndex = pathList.selectedIndex
|
||||
if (selectedIndex >= 0) {
|
||||
pathListModel.removeElementAt(selectedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
private fun editPath() {
|
||||
val selectedIndex = pathList.selectedIndex
|
||||
if (selectedIndex >= 0) {
|
||||
val currentPath = pathListModel.getElementAt(selectedIndex)
|
||||
val textField = TextFieldWithBrowseButton()
|
||||
textField.text = currentPath
|
||||
|
||||
val fileChooserDescriptor =
|
||||
FileChooserDescriptor(false, true, false, false, false, false)
|
||||
fileChooserDescriptor.title =
|
||||
CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.title")
|
||||
fileChooserDescriptor.description =
|
||||
CodeGPTBundle.get("configurationConfigurable.screenshotPaths.chooser.description")
|
||||
|
||||
textField.addBrowseFolderListener(
|
||||
TextBrowseFolderListener(fileChooserDescriptor)
|
||||
)
|
||||
|
||||
val dialog = PathInputDialog(
|
||||
CodeGPTBundle.get("configurationConfigurable.screenshotPaths.edit.title"),
|
||||
textField
|
||||
)
|
||||
|
||||
if (dialog.showAndGet()) {
|
||||
val newPath = textField.text
|
||||
if (newPath.isNotBlank()) {
|
||||
pathListModel.setElementAt(newPath, selectedIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun loadState(paths: List<String>) {
|
||||
pathListModel.clear()
|
||||
paths.forEach { pathListModel.addElement(it) }
|
||||
}
|
||||
|
||||
fun getState(): List<String> {
|
||||
val paths = mutableListOf<String>()
|
||||
for (i in 0 until pathListModel.size()) {
|
||||
paths.add(pathListModel.getElementAt(i))
|
||||
}
|
||||
return paths
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package ee.carlrobert.codegpt.settings.configuration
|
||||
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.exists
|
||||
|
||||
object ScreenshotPathDetector {
|
||||
|
||||
private val logger = thisLogger()
|
||||
|
||||
fun getDefaultPaths(): List<String> {
|
||||
return when {
|
||||
SystemInfo.isWindows -> getWindowsDefaultPaths()
|
||||
SystemInfo.isMac -> getMacDefaultPaths()
|
||||
SystemInfo.isLinux -> getLinuxDefaultPaths()
|
||||
else -> listOf(getDesktopPath())
|
||||
}
|
||||
}
|
||||
|
||||
fun isValidWatchPath(pathString: String): Boolean {
|
||||
return try {
|
||||
val path = Paths.get(pathString)
|
||||
path.exists() && path.toFile().canRead()
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Error validating watch path: $pathString", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun getDefaultFileExtensions(): List<String> {
|
||||
return listOf("jpg", "jpeg", "png", "gif", "bmp", "webp")
|
||||
}
|
||||
|
||||
private fun getWindowsDefaultPaths(): List<String> {
|
||||
val paths = mutableListOf<String>()
|
||||
paths.add(getDesktopPath())
|
||||
|
||||
val screenshotsPath = Paths.get(System.getProperty("user.home"), "Pictures", "Screenshots")
|
||||
if (screenshotsPath.exists()) {
|
||||
paths.add(screenshotsPath.toString())
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun getMacDefaultPaths(): List<String> {
|
||||
val paths = mutableListOf<String>()
|
||||
paths.add(getDesktopPath())
|
||||
|
||||
val picturesPath = Paths.get(System.getProperty("user.home"), "Pictures")
|
||||
if (picturesPath.exists()) {
|
||||
paths.add(picturesPath.toString())
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun getLinuxDefaultPaths(): List<String> {
|
||||
val paths = mutableListOf<String>()
|
||||
paths.add(getDesktopPath())
|
||||
|
||||
val picturesPath = getXdgPicturesPath()
|
||||
if (picturesPath.exists()) {
|
||||
paths.add(picturesPath.toString())
|
||||
}
|
||||
|
||||
val screenshotsPath = picturesPath.resolve("Screenshots")
|
||||
if (screenshotsPath.exists()) {
|
||||
paths.add(screenshotsPath.toString())
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun getDesktopPath(): String {
|
||||
return Paths.get(System.getProperty("user.home"), "Desktop").toString()
|
||||
}
|
||||
|
||||
private fun getXdgPicturesPath(): Path {
|
||||
val xdgPictures = System.getenv("XDG_PICTURES_DIR")
|
||||
return if (xdgPictures != null) {
|
||||
Paths.get(xdgPictures)
|
||||
} else {
|
||||
Paths.get(System.getProperty("user.home"), "Pictures")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -120,6 +120,19 @@ configurationConfigurable.table.action.revertToDefaults.text=Revert to Defaults
|
|||
configurationConfigurable.table.action.addKeymap.text=Add Shortcut
|
||||
configurationConfigurable.checkForPluginUpdates.label=Check for plugin updates automatically
|
||||
configurationConfigurable.checkForNewScreenshots.label=Check for new screenshots automatically
|
||||
|
||||
# Screenshot Configuration
|
||||
configurationConfigurable.section.screenshots.title=Screenshot Detection
|
||||
configurationConfigurable.screenshotPaths.label=Watch Paths:
|
||||
configurationConfigurable.screenshotPaths.add=Add Path
|
||||
configurationConfigurable.screenshotPaths.remove=Remove Path
|
||||
configurationConfigurable.screenshotPaths.edit=Edit Path
|
||||
configurationConfigurable.screenshotPaths.add.title=Add Screenshot Watch Path
|
||||
configurationConfigurable.screenshotPaths.edit.title=Edit Screenshot Watch Path
|
||||
configurationConfigurable.screenshotPaths.chooser.title=Select Directory to Watch for Screenshots
|
||||
configurationConfigurable.screenshotPaths.chooser.description=Choose a directory where screenshot files will be detected
|
||||
configurationConfigurable.screenshotPaths.dialog.path.label=Path:
|
||||
configurationConfigurable.screenshotPaths.comment=Add directories where screenshots are typically saved. The plugin monitors these directories for new image files.
|
||||
configurationConfigurable.openNewTabCheckBox.label=Open a new chat on each action
|
||||
configurationConfigurable.enableMethodNameGeneration.label=Enable method name lookup suggestions
|
||||
configurationConfigurable.autoFormatting.label=Enable automatic code formatting
|
||||
|
|
@ -371,7 +384,7 @@ settings.models.chat.label=Chat:
|
|||
settings.models.code.label=Autocomplete:
|
||||
settings.models.autoApply.label=Auto apply:
|
||||
settings.models.commitMessages.label=Commit messages:
|
||||
settings.models.editCode.label=Edit code:
|
||||
settings.models.inlineEdit.label=Inline edit:
|
||||
settings.models.nextEdit.label=Next edits:
|
||||
settings.models.nameLookups.label=Name lookups:
|
||||
settings.models.selectModel=Select a model
|
||||
|
|
|
|||
|
|
@ -120,6 +120,19 @@ configurationConfigurable.table.action.revertToDefaults.text=\u6062\u590D\u9ED8\
|
|||
configurationConfigurable.table.action.addKeymap.text=\u6DFB\u52A0\u5FEB\u6377\u952E
|
||||
configurationConfigurable.checkForPluginUpdates.label=\u81EA\u52A8\u68C0\u67E5\u63D2\u4EF6\u66F4\u65B0
|
||||
configurationConfigurable.checkForNewScreenshots.label=\u81EA\u52A8\u68C0\u67E5\u65B0\u622A\u56FE
|
||||
|
||||
# Screenshot Configuration
|
||||
configurationConfigurable.section.screenshots.title=\u622A\u56FE\u68C0\u6D4B
|
||||
configurationConfigurable.screenshotPaths.label=\u76D1\u89C6\u8DEF\u5F84:
|
||||
configurationConfigurable.screenshotPaths.add=\u6DFB\u52A0\u8DEF\u5F84
|
||||
configurationConfigurable.screenshotPaths.remove=\u5220\u9664\u8DEF\u5F84
|
||||
configurationConfigurable.screenshotPaths.edit=\u7F16\u8F91\u8DEF\u5F84
|
||||
configurationConfigurable.screenshotPaths.add.title=\u6DFB\u52A0\u622A\u56FE\u76D1\u89C6\u8DEF\u5F84
|
||||
configurationConfigurable.screenshotPaths.edit.title=\u7F16\u8F91\u622A\u56FE\u76D1\u89C6\u8DEF\u5F84
|
||||
configurationConfigurable.screenshotPaths.chooser.title=\u9009\u62E9\u76D1\u89C6\u622A\u56FE\u7684\u76EE\u5F55
|
||||
configurationConfigurable.screenshotPaths.chooser.description=\u9009\u62E9\u4E00\u4E2A\u7528\u4E8E\u68C0\u6D4B\u622A\u56FE\u6587\u4EF6\u7684\u76EE\u5F55
|
||||
configurationConfigurable.screenshotPaths.dialog.path.label=\u8DEF\u5F84:
|
||||
configurationConfigurable.screenshotPaths.comment=\u6DFB\u52A0\u901A\u5E38\u4FDD\u5B58\u622A\u56FE\u7684\u76EE\u5F55\u3002\u63D2\u4EF6\u4F1A\u76D1\u89C6\u8FD9\u4E9B\u76EE\u5F55\u4E2D\u7684\u65B0\u56FE\u50CF\u6587\u4EF6\u3002
|
||||
configurationConfigurable.openNewTabCheckBox.label=\u6BCF\u4E2A\u64CD\u4F5C\u6253\u5F00\u65B0\u804A\u5929\u6807\u7B7E
|
||||
configurationConfigurable.enableMethodNameGeneration.label=\u542F\u7528\u65B9\u6CD5\u540D\u79F0\u67E5\u627E\u5EFA\u8BAE
|
||||
configurationConfigurable.autoFormatting.label=\u542F\u7528\u81EA\u52A8\u4EE3\u7801\u683C\u5F0F\u5316
|
||||
|
|
@ -370,7 +383,7 @@ settings.models.chat.label=\u804A\u5929:
|
|||
settings.models.code.label=\u81EA\u52A8\u8865\u5168:
|
||||
settings.models.autoApply.label=\u81EA\u52A8\u5E94\u7528:
|
||||
settings.models.commitMessages.label=\u63D0\u4EA4\u6D88\u606F:
|
||||
settings.models.editCode.label=\u7F16\u8F91\u4EE3\u7801:
|
||||
settings.models.inlineEdit.label=\u7F16\u8F91\u4EE3\u7801:
|
||||
settings.models.nextEdit.label=\u4E0B\u4E00\u6B65\u7F16\u8F91:
|
||||
settings.models.nameLookups.label=\u540D\u79F0\u67E5\u627E:
|
||||
settings.models.selectModel=\u9009\u62E9\u6A21\u578B
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue