diff --git a/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/backend/GeminiBackend.kt b/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/backend/GeminiBackend.kt index cb7d3fd6..e7396227 100644 --- a/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/backend/GeminiBackend.kt +++ b/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/backend/GeminiBackend.kt @@ -351,7 +351,7 @@ class GeminiBackend( override fun generate(prompt: String, config: LlmConfig): CompletableFuture { val future = CompletableFuture() - currentJob = scope.launch { + val job = scope.launch { val keyStamp = storedKeyStamp() try { val apiKey = readGeminiApiKey() @@ -381,6 +381,8 @@ class GeminiBackend( future.complete(LlmResponse.failure(formatErrorMessage(e, keyStamp))) } } + currentJob = job + future.cancelJobOnCancel(job) return future } @@ -577,7 +579,7 @@ class GeminiBackend( val future = CompletableFuture() - currentJob = scope.launch { + val job = scope.launch { val keyStamp = storedKeyStamp() try { val apiKey = readGeminiApiKey() @@ -607,6 +609,8 @@ class GeminiBackend( future.complete(LlmResponse.failure(formatErrorMessage(e, keyStamp))) } } + currentJob = job + future.cancelJobOnCancel(job) return future } diff --git a/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/settings/GeminiSettingsFragment.kt b/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/settings/GeminiSettingsFragment.kt index a7c7baca..bfd89fef 100644 --- a/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/settings/GeminiSettingsFragment.kt +++ b/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/settings/GeminiSettingsFragment.kt @@ -32,6 +32,7 @@ import com.itsaky.androidide.plugins.PluginContext import com.itsaky.androidide.plugins.aiagentgemini.plugin.GeminiPlugin import com.itsaky.androidide.plugins.aiagentgemini.R import com.itsaky.androidide.plugins.aiagentgemini.ui.SecretRevealController +import com.itsaky.androidide.plugins.aiagentgemini.ui.applyPaneStyling import com.itsaky.androidide.plugins.base.PluginFragmentHelper import com.itsaky.androidide.plugins.security.KeystoreSecretStore import com.itsaky.androidide.plugins.services.IdeTooltipService @@ -40,6 +41,14 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +/** The pane's secondary actions, drawn outlined; each section's Save stays filled. */ +private val OUTLINED_BUTTON_IDS = setOf( + R.id.btn_clear_api_key, + R.id.btn_edit_api_key, + R.id.btn_get_free_key, + R.id.btn_refresh_models, +) + /** * This backend's settings pane, mounted by whichever screen offers a backend selector. * @@ -108,6 +117,7 @@ class GeminiSettingsFragment : Fragment() { GeminiSettingsViewModelFactory { GeminiPlugin.getContext() } )[GeminiSettingsViewModel::class.java] + view.applyPaneStyling(OUTLINED_BUTTON_IDS) setupApiKeyUi(view) setupModelPicker(view, chatModelPicker()) setupModelPicker(view, embeddingModelPicker()) @@ -580,6 +590,7 @@ class GeminiSettingsFragment : Fragment() { }.isSuccess } + /** * Put the dropdown chevron on [box]'s end icon. * diff --git a/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/ui/PaneStyling.kt b/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/ui/PaneStyling.kt new file mode 100644 index 00000000..75ce661e --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/kotlin/com/itsaky/androidide/plugins/aiagentgemini/ui/PaneStyling.kt @@ -0,0 +1,77 @@ +package com.itsaky.androidide.plugins.aiagentgemini.ui + +import android.content.res.ColorStateList +import android.graphics.Color +import android.view.View +import android.view.ViewGroup +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import com.google.android.material.button.MaterialButton +import com.google.android.material.divider.MaterialDivider +import com.google.android.material.textfield.TextInputLayout +import com.itsaky.androidide.plugins.aiagentgemini.R + +/** How much a pane button stands out: one filled action per section, the rest outlined. */ +private enum class ButtonEmphasis { FILLED, OUTLINED } + +/** + * Gives every Material button, text field and divider under [this] its Material 3 colours, outline + * and ripple in code. The styles' `app:` items are dropped inside the host, so XML alone leaves + * these controls on the host theme's values. + * + * @param outlinedButtonIds the buttons that are secondary actions; every other button is filled. + */ +internal fun View.applyPaneStyling(outlinedButtonIds: Set) { + when (this) { + is MaterialButton -> applyEmphasis( + if (id in outlinedButtonIds) ButtonEmphasis.OUTLINED else ButtonEmphasis.FILLED + ) + is TextInputLayout -> applyOutline() + is MaterialDivider -> applyHairline() + } + if (this is ViewGroup) { + for (i in 0 until childCount) getChildAt(i).applyPaneStyling(outlinedButtonIds) + } +} + +/** Container, label, icon, border and ripple for [emphasis], each with its disabled state. */ +private fun MaterialButton.applyEmphasis(emphasis: ButtonEmphasis) { + val filled = emphasis == ButtonEmphasis.FILLED + val content = colors( + if (filled) R.color.plugin_button_filled_content else R.color.plugin_button_outlined_content + ) + backgroundTintList = if (filled) { + colors(R.color.plugin_button_filled_container) + } else { + ColorStateList.valueOf(Color.TRANSPARENT) + } + setTextColor(content) + iconTint = content + rippleColor = colors( + if (filled) R.color.plugin_button_filled_ripple else R.color.plugin_button_outlined_ripple + ) + strokeColor = colors(R.color.plugin_button_outlined_stroke) + strokeWidth = if (filled) 0 else resources.getDimensionPixelSize(R.dimen.button_stroke_width) + cornerRadius = resources.getDimensionPixelSize(R.dimen.radius_md) +} + +/** Outline, corners, hint and end icon of an outlined-box field. */ +private fun TextInputLayout.applyOutline() { + setBoxStrokeColorStateList(colors(R.color.plugin_box_stroke)) + setBoxStrokeErrorColor(colors(R.color.plugin_error)) + val radius = resources.getDimension(R.dimen.radius_md) + setBoxCornerRadii(radius, radius, radius, radius) + val hint = colors(R.color.plugin_text_muted) + defaultHintTextColor = hint + hintTextColor = hint + setEndIconTintList(colors(R.color.plugin_on_surface_variant)) +} + +private fun MaterialDivider.applyHairline() { + setDividerColorResource(R.color.plugin_outline_variant) + setDividerThicknessResource(R.dimen.divider_thickness) +} + +/** Resolved against this view's context, which carries the plugin's resources. */ +private fun View.colors(@ColorRes id: Int): ColorStateList = + requireNotNull(ContextCompat.getColorStateList(context, id)) diff --git a/plugins/AI-Agent-Gemini/src/main/res/color/plugin_box_stroke.xml b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_box_stroke.xml new file mode 100644 index 00000000..497e1d2d --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_box_stroke.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_container.xml b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_container.xml new file mode 100644 index 00000000..12f92e88 --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_container.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_content.xml b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_content.xml new file mode 100644 index 00000000..ae2cd196 --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_content.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_ripple.xml b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_ripple.xml new file mode 100644 index 00000000..fb923b0e --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_filled_ripple.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_content.xml b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_content.xml new file mode 100644 index 00000000..2c028905 --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_content.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_ripple.xml b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_ripple.xml new file mode 100644 index 00000000..95596266 --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_ripple.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_stroke.xml b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_stroke.xml new file mode 100644 index 00000000..e009bd35 --- /dev/null +++ b/plugins/AI-Agent-Gemini/src/main/res/color/plugin_button_outlined_stroke.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-Gemini/src/main/res/layout/fragment_gemini_settings.xml b/plugins/AI-Agent-Gemini/src/main/res/layout/fragment_gemini_settings.xml index 688a4a51..93c8c402 100644 --- a/plugins/AI-Agent-Gemini/src/main/res/layout/fragment_gemini_settings.xml +++ b/plugins/AI-Agent-Gemini/src/main/res/layout/fragment_gemini_settings.xml @@ -75,7 +75,7 @@ + + + #A3A3A3 #525252 - #1FFAFAFA diff --git a/plugins/AI-Agent-Gemini/src/main/res/values/colors.xml b/plugins/AI-Agent-Gemini/src/main/res/values/colors.xml index 3bd84f25..d0616906 100644 --- a/plugins/AI-Agent-Gemini/src/main/res/values/colors.xml +++ b/plugins/AI-Agent-Gemini/src/main/res/values/colors.xml @@ -36,5 +36,4 @@ #737373 #A3A3A3 - #1F0A0A0A diff --git a/plugins/AI-Agent-Gemini/src/main/res/values/dimens.xml b/plugins/AI-Agent-Gemini/src/main/res/values/dimens.xml index 8279555f..b57850fe 100644 --- a/plugins/AI-Agent-Gemini/src/main/res/values/dimens.xml +++ b/plugins/AI-Agent-Gemini/src/main/res/values/dimens.xml @@ -11,6 +11,9 @@ 16dp 24dp + 1dp + 1dp + 8dp 11sp diff --git a/plugins/AI-Agent-Gemini/src/main/res/values/styles.xml b/plugins/AI-Agent-Gemini/src/main/res/values/styles.xml index c86952f6..8dc1c450 100644 --- a/plugins/AI-Agent-Gemini/src/main/res/values/styles.xml +++ b/plugins/AI-Agent-Gemini/src/main/res/values/styles.xml @@ -95,19 +95,33 @@ by choice, matching the IDE's own grey accent rather than this plugin's orange, on a screen whose chrome belongs to AI Core. Light/dark comes from values-night/colors.xml. - One style for every button on the pane: a secondary action that styled itself differently - is exactly what broke on the OpenAI pane, and the hierarchy is not worth a second way to - get contrast wrong. --> + Filled for the one primary action in a section, outlined for the rest, per Material 3's + button hierarchy. Both pin direct colours, so neither resolves a role against the host. + The app: items here are dropped inside the host; ui/PaneStyling.kt applies them in code. --> + + diff --git a/plugins/AI-Agent-Local/src/main/kotlin/com/itsaky/androidide/plugins/aiagentlocal/backend/LocalLlmBackend.kt b/plugins/AI-Agent-Local/src/main/kotlin/com/itsaky/androidide/plugins/aiagentlocal/backend/LocalLlmBackend.kt index ef762321..644b3ea2 100644 --- a/plugins/AI-Agent-Local/src/main/kotlin/com/itsaky/androidide/plugins/aiagentlocal/backend/LocalLlmBackend.kt +++ b/plugins/AI-Agent-Local/src/main/kotlin/com/itsaky/androidide/plugins/aiagentlocal/backend/LocalLlmBackend.kt @@ -713,7 +713,7 @@ class LocalLlmBackend( val future = CompletableFuture() - currentGenerateJob = scope.launch { + val job = scope.launch { try { // Serialize against other generations on the shared native context. generationMutex.withLock { @@ -767,6 +767,8 @@ class LocalLlmBackend( future.complete(LlmResponse.failure("Error: ${e.message}")) } } + currentGenerateJob = job + future.cancelJobOnCancel(job) return future } @@ -945,3 +947,15 @@ class LocalLlmBackend( cleanup.invokeOnCompletion { cleanupScope.cancel() } } } + +/** + * Cancel [job] when this future is cancelled by its caller. + * + * [CompletableFuture.cancel] only flips the future's own state, so without this a caller that gives + * up leaves the generation holding the native context until it runs out of tokens. + * + * @param job the coroutine producing this future's value + */ +private fun CompletableFuture.cancelJobOnCancel(job: Job) { + whenComplete { _, _ -> if (isCancelled) job.cancel() } +} diff --git a/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/backend/OpenAiBackend.kt b/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/backend/OpenAiBackend.kt index 37d2f227..28566943 100644 --- a/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/backend/OpenAiBackend.kt +++ b/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/backend/OpenAiBackend.kt @@ -307,7 +307,7 @@ class OpenAiBackend( override fun generate(prompt: String, config: LlmConfig): CompletableFuture { val future = CompletableFuture() - currentJob = scope.launch { + val job = scope.launch { val keyStamp = storedKeyStamp() try { val startTime = System.currentTimeMillis() @@ -331,6 +331,8 @@ class OpenAiBackend( future.complete(LlmResponse.failure(formatErrorMessage(e, keyStamp))) } } + currentJob = job + future.cancelJobOnCancel(job) return future } @@ -357,7 +359,7 @@ class OpenAiBackend( val future = CompletableFuture() - currentJob = scope.launch { + val job = scope.launch { val keyStamp = storedKeyStamp() try { val startTime = System.currentTimeMillis() @@ -380,6 +382,8 @@ class OpenAiBackend( future.complete(LlmResponse.failure(formatErrorMessage(e, keyStamp))) } } + currentJob = job + future.cancelJobOnCancel(job) return future } diff --git a/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/settings/OpenAiSettingsFragment.kt b/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/settings/OpenAiSettingsFragment.kt index 4c82f89a..1eba75be 100644 --- a/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/settings/OpenAiSettingsFragment.kt +++ b/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/settings/OpenAiSettingsFragment.kt @@ -34,6 +34,7 @@ import com.itsaky.androidide.plugins.PluginContext import com.itsaky.androidide.plugins.aiagentopenai.R import com.itsaky.androidide.plugins.aiagentopenai.plugin.OpenAiPlugin import com.itsaky.androidide.plugins.aiagentopenai.ui.SecretRevealController +import com.itsaky.androidide.plugins.aiagentopenai.ui.applyPaneStyling import com.itsaky.androidide.plugins.base.PluginFragmentHelper import com.itsaky.androidide.plugins.security.KeystoreSecretStore import com.itsaky.androidide.plugins.services.IdeTooltipService @@ -42,6 +43,14 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +/** The pane's secondary actions, drawn outlined; each section's Save stays filled. */ +private val OUTLINED_BUTTON_IDS = setOf( + R.id.btn_clear_api_key, + R.id.btn_edit_api_key, + R.id.btn_get_key, + R.id.btn_test_connection, +) + /** * This backend's settings pane, mounted by whichever screen offers a backend selector. * @@ -112,6 +121,7 @@ class OpenAiSettingsFragment : Fragment() { // The key section publishes onServerChanged, so it is built before the server section that // fires it, and before the first call below that dresses the pane for the saved server. + view.applyPaneStyling(OUTLINED_BUTTON_IDS) setupApiKeyUi(view) setupServerUi(view) setupModelPicker(view, chatModelPicker()) @@ -199,6 +209,7 @@ class OpenAiSettingsFragment : Fragment() { target.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0) } + /** * Put the dropdown chevron on [box]'s end icon. * diff --git a/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/ui/PaneStyling.kt b/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/ui/PaneStyling.kt new file mode 100644 index 00000000..d65bac24 --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/kotlin/com/itsaky/androidide/plugins/aiagentopenai/ui/PaneStyling.kt @@ -0,0 +1,77 @@ +package com.itsaky.androidide.plugins.aiagentopenai.ui + +import android.content.res.ColorStateList +import android.graphics.Color +import android.view.View +import android.view.ViewGroup +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import com.google.android.material.button.MaterialButton +import com.google.android.material.divider.MaterialDivider +import com.google.android.material.textfield.TextInputLayout +import com.itsaky.androidide.plugins.aiagentopenai.R + +/** How much a pane button stands out: one filled action per section, the rest outlined. */ +private enum class ButtonEmphasis { FILLED, OUTLINED } + +/** + * Gives every Material button, text field and divider under [this] its Material 3 colours, outline + * and ripple in code. The styles' `app:` items are dropped inside the host, so XML alone leaves + * these controls on the host theme's values. + * + * @param outlinedButtonIds the buttons that are secondary actions; every other button is filled. + */ +internal fun View.applyPaneStyling(outlinedButtonIds: Set) { + when (this) { + is MaterialButton -> applyEmphasis( + if (id in outlinedButtonIds) ButtonEmphasis.OUTLINED else ButtonEmphasis.FILLED + ) + is TextInputLayout -> applyOutline() + is MaterialDivider -> applyHairline() + } + if (this is ViewGroup) { + for (i in 0 until childCount) getChildAt(i).applyPaneStyling(outlinedButtonIds) + } +} + +/** Container, label, icon, border and ripple for [emphasis], each with its disabled state. */ +private fun MaterialButton.applyEmphasis(emphasis: ButtonEmphasis) { + val filled = emphasis == ButtonEmphasis.FILLED + val content = colors( + if (filled) R.color.plugin_button_filled_content else R.color.plugin_button_outlined_content + ) + backgroundTintList = if (filled) { + colors(R.color.plugin_button_filled_container) + } else { + ColorStateList.valueOf(Color.TRANSPARENT) + } + setTextColor(content) + iconTint = content + rippleColor = colors( + if (filled) R.color.plugin_button_filled_ripple else R.color.plugin_button_outlined_ripple + ) + strokeColor = colors(R.color.plugin_button_outlined_stroke) + strokeWidth = if (filled) 0 else resources.getDimensionPixelSize(R.dimen.button_stroke_width) + cornerRadius = resources.getDimensionPixelSize(R.dimen.radius_md) +} + +/** Outline, corners, hint and end icon of an outlined-box field. */ +private fun TextInputLayout.applyOutline() { + setBoxStrokeColorStateList(colors(R.color.plugin_box_stroke)) + setBoxStrokeErrorColor(colors(R.color.plugin_error)) + val radius = resources.getDimension(R.dimen.radius_md) + setBoxCornerRadii(radius, radius, radius, radius) + val hint = colors(R.color.plugin_text_muted) + defaultHintTextColor = hint + hintTextColor = hint + setEndIconTintList(colors(R.color.plugin_on_surface_variant)) +} + +private fun MaterialDivider.applyHairline() { + setDividerColorResource(R.color.plugin_outline_variant) + setDividerThicknessResource(R.dimen.divider_thickness) +} + +/** Resolved against this view's context, which carries the plugin's resources. */ +private fun View.colors(@ColorRes id: Int): ColorStateList = + requireNotNull(ContextCompat.getColorStateList(context, id)) diff --git a/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_box_stroke.xml b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_box_stroke.xml new file mode 100644 index 00000000..497e1d2d --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_box_stroke.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_container.xml b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_container.xml new file mode 100644 index 00000000..12f92e88 --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_container.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_content.xml b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_content.xml new file mode 100644 index 00000000..ae2cd196 --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_content.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_ripple.xml b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_ripple.xml new file mode 100644 index 00000000..fb923b0e --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_filled_ripple.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_content.xml b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_content.xml new file mode 100644 index 00000000..2c028905 --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_content.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_ripple.xml b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_ripple.xml new file mode 100644 index 00000000..95596266 --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_ripple.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_stroke.xml b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_stroke.xml new file mode 100644 index 00000000..e009bd35 --- /dev/null +++ b/plugins/AI-Agent-OpenAI/src/main/res/color/plugin_button_outlined_stroke.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/AI-Agent-OpenAI/src/main/res/layout/fragment_openai_settings.xml b/plugins/AI-Agent-OpenAI/src/main/res/layout/fragment_openai_settings.xml index 17dd2819..a6b7c7d8 100644 --- a/plugins/AI-Agent-OpenAI/src/main/res/layout/fragment_openai_settings.xml +++ b/plugins/AI-Agent-OpenAI/src/main/res/layout/fragment_openai_settings.xml @@ -156,7 +156,7 @@