feat: ability to copy initial and current env value to eachother (#5195)

This commit is contained in:
Nivedin 2025-06-25 14:12:51 +05:30 committed by GitHub
parent 08e5fa974c
commit 7952dbf34d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 249 additions and 53 deletions

View file

@ -414,8 +414,8 @@
"empty_variables": "No variables",
"global": "Global",
"global_variables": "Global variables",
"initial_value": "Initial value",
"import_or_create": "Import or create a environment",
"initial_value": "Initial value",
"invalid_name": "Please provide a name for the environment",
"list": "Environment variables",
"my_environments": "Personal Environments",
@ -426,6 +426,10 @@
"no_environment": "No environment",
"no_environment_description": "No environments were selected. Choose what to do with the following variables.",
"quick_peek": "Environment Quick Peek",
"replace_all_current_with_initial": "Replace all current with initial",
"replace_all_initial_with_current": "Replace all initial with current",
"replace_current_with_initial": "Replace with initial",
"replace_initial_with_current": "Replace with current",
"replace_with_variable": "Replace with variable",
"scope": "Scope",
"secrets": "Secrets",

View file

@ -46,6 +46,59 @@
:title="t('add.new')"
@click="addEnvironmentVariable"
/>
<tippy
ref="options"
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions!.focus()"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('action.more')"
:icon="IconMoreVertical"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
role="menu"
@keyup.escape="hide()"
>
<HoppSmartItem
v-tippy="{ theme: 'tooltip' }"
:icon="IconCopyLeft"
:label="
t('environment.replace_all_initial_with_current')
"
@click="
() => {
vars.forEach((v) => {
v.env.initialValue = v.env.currentValue
})
hide()
}
"
/>
<HoppSmartItem
v-tippy="{ theme: 'tooltip' }"
:icon="IconCopyRight"
:label="
t('environment.replace_all_current_with_initial')
"
@click="
() => {
vars.forEach((v) => {
v.env.currentValue = v.env.initialValue
})
hide()
}
"
/>
</div>
</template>
</tippy>
</div>
</template>
@ -87,26 +140,52 @@
})}`"
:name="'variable' + index"
/>
<SmartEnvInput
v-model="env.initialValue"
:placeholder="`${t('count.initialValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'initialValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
/>
<SmartEnvInput
v-model="env.currentValue"
:placeholder="`${t('count.currentValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'currentValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
/>
<div class="flex items-center flex-1">
<SmartEnvInput
v-model="env.initialValue"
:placeholder="`${t('count.initialValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'initialValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('environment.replace_initial_with_current')"
:icon="IconCopyLeft"
@click="
() => {
env.initialValue = env.currentValue
}
"
/>
</div>
<div class="flex items-center flex-1">
<SmartEnvInput
v-model="env.currentValue"
:placeholder="`${t('count.currentValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'currentValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('environment.replace_current_with_initial')"
:icon="IconCopyRight"
@click="
() => {
env.currentValue = env.initialValue
}
"
/>
</div>
<div class="flex">
<HoppButtonSecondary
id="variable"
@ -180,6 +259,10 @@ import IconHelpCircle from "~icons/lucide/help-circle"
import IconPlus from "~icons/lucide/plus"
import IconTrash from "~icons/lucide/trash"
import IconTrash2 from "~icons/lucide/trash-2"
import IconCopyRight from "~icons/lucide/clipboard-paste"
import IconCopyLeft from "~icons/lucide/clipboard-copy"
import IconMoreVertical from "~icons/lucide/more-vertical"
import { TippyComponent } from "vue-tippy"
type EnvironmentVariable = {
id: number
@ -242,6 +325,9 @@ const tabsData: ComputedRef<
]
})
const options = ref<TippyComponent | null>(null)
const tippyActions = ref<HTMLDivElement | null>(null)
const editingName = ref<string | null>(null)
const editingID = ref<string>("")
const vars = ref<EnvironmentVariable[]>([

View file

@ -48,6 +48,60 @@
:title="t('add.new')"
@click="addEnvironmentVariable"
/>
<tippy
ref="options"
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions!.focus()"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('action.more')"
:icon="IconMoreVertical"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
role="menu"
@keyup.escape="hide()"
>
<HoppSmartItem
v-tippy="{ theme: 'tooltip' }"
:icon="IconCopyLeft"
:label="
t('environment.replace_all_initial_with_current')
"
:disabled="isViewer"
@click="
() => {
vars.forEach((v) => {
v.env.initialValue = v.env.currentValue
})
hide()
}
"
/>
<HoppSmartItem
v-tippy="{ theme: 'tooltip' }"
:icon="IconCopyRight"
:label="
t('environment.replace_all_current_with_initial')
"
@click="
() => {
vars.forEach((v) => {
v.env.currentValue = v.env.initialValue
})
hide()
}
"
/>
</div>
</template>
</tippy>
</div>
</template>
@ -94,27 +148,54 @@
:name="'variable' + index"
:disabled="isViewer"
/>
<SmartEnvInput
v-model="env.initialValue"
:placeholder="`${t('count.initialValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'initialValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
:readonly="isViewer"
/>
<SmartEnvInput
v-model="env.currentValue"
:placeholder="`${t('count.currentValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'currentValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
/>
<div class="flex items-center flex-1">
<SmartEnvInput
v-model="env.initialValue"
:placeholder="`${t('count.initialValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'initialValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
:readonly="isViewer"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('environment.replace_initial_with_current')"
:icon="IconCopyLeft"
:disabled="isViewer"
@click="
() => {
env.initialValue = env.currentValue
}
"
/>
</div>
<div class="flex items-center flex-1">
<SmartEnvInput
v-model="env.currentValue"
:placeholder="`${t('count.currentValue', { count: index + 1 })}`"
:envs="liveEnvs"
:name="'currentValue' + index"
:secret="tab.isSecret"
:select-text-on-mount="
env.key ? env.key === editingVariableName : false
"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('environment.replace_current_with_initial')"
:icon="IconCopyRight"
@click="
() => {
env.currentValue = env.initialValue
}
"
/>
</div>
<div v-if="!isViewer" class="flex">
<HoppButtonSecondary
id="variable"
@ -175,11 +256,6 @@ import {
import { GQLError } from "~/helpers/backend/GQLClient"
import { TeamEnvironment } from "~/helpers/teams/TeamEnvironment"
import { useColorMode } from "~/composables/theming"
import IconTrash from "~icons/lucide/trash"
import IconTrash2 from "~icons/lucide/trash-2"
import IconDone from "~icons/lucide/check"
import IconPlus from "~icons/lucide/plus"
import IconHelpCircle from "~icons/lucide/help-circle"
import { platform } from "~/platform"
import { useService } from "dioc/vue"
import { SecretEnvironmentService } from "~/services/secret-environment.service"
@ -187,6 +263,15 @@ import { getEnvActionErrorMessage } from "~/helpers/error-messages"
import { CurrentValueService } from "~/services/current-environment-value.service"
import { useReadonlyStream } from "~/composables/stream"
import { globalEnv$ } from "~/newstore/environments"
import IconTrash from "~icons/lucide/trash"
import IconTrash2 from "~icons/lucide/trash-2"
import IconDone from "~icons/lucide/check"
import IconPlus from "~icons/lucide/plus"
import IconHelpCircle from "~icons/lucide/help-circle"
import IconCopyRight from "~icons/lucide/clipboard-paste"
import IconCopyLeft from "~icons/lucide/clipboard-copy"
import IconMoreVertical from "~icons/lucide/more-vertical"
import { TippyComponent } from "vue-tippy"
type EnvironmentVariable = {
id: number
@ -253,6 +338,9 @@ const tabsData: ComputedRef<
]
})
const options = ref<TippyComponent | null>(null)
const tippyActions = ref<HTMLDivElement | null>(null)
const editingName = ref<string | null>(null)
const editingID = ref<string | null>(null)
const vars = ref<EnvironmentVariable[]>([

View file

@ -101,7 +101,10 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
alternates: ["tab", "previous", "prev", "switch"],
icon: markRaw(IconArrowLeft),
excludeFromSearch: computed(
() => !this.showAction.value || !this.isDesktopMode.value || this.isOnlyTab.value
() =>
!this.showAction.value ||
!this.isDesktopMode.value ||
this.isOnlyTab.value
),
},
tab_next: {
@ -109,23 +112,38 @@ export class TabSpotlightSearcherService extends StaticSpotlightSearcherService<
alternates: ["tab", "next", "switch"],
icon: markRaw(IconArrowRight),
excludeFromSearch: computed(
() => !this.showAction.value || !this.isDesktopMode.value || this.isOnlyTab.value
() =>
!this.showAction.value ||
!this.isDesktopMode.value ||
this.isOnlyTab.value
),
},
tab_switch_to_first: {
text: [this.t("spotlight.tab.title"), this.t("spotlight.tab.switch_to_first")],
text: [
this.t("spotlight.tab.title"),
this.t("spotlight.tab.switch_to_first"),
],
alternates: ["tab", "first", "switch", "go to first"],
icon: markRaw(IconChevronsLeft),
excludeFromSearch: computed(
() => !this.showAction.value || !this.isDesktopMode.value || this.isOnlyTab.value
() =>
!this.showAction.value ||
!this.isDesktopMode.value ||
this.isOnlyTab.value
),
},
tab_switch_to_last: {
text: [this.t("spotlight.tab.title"), this.t("spotlight.tab.switch_to_last")],
text: [
this.t("spotlight.tab.title"),
this.t("spotlight.tab.switch_to_last"),
],
alternates: ["tab", "last", "switch", "go to last"],
icon: markRaw(IconChevronsRight),
excludeFromSearch: computed(
() => !this.showAction.value || !this.isDesktopMode.value || this.isOnlyTab.value
() =>
!this.showAction.value ||
!this.isDesktopMode.value ||
this.isOnlyTab.value
),
},
})