22import type { NewOperation } from ' ~/composables/useConnector'
33import type Modal from ' ~/components/Modal.client.vue'
44import { PackageDeprecateParamsSchema , safeParse } from ' ~~/cli/src/schemas'
5+ import { fetchAllPackageVersions } from ' ~/utils/npm/api'
56
67const DEPRECATE_MESSAGE_MAX_LENGTH = 500
78
@@ -27,14 +28,45 @@ const deprecateError = shallowRef<string | null>(null)
2728
2829const connectorModal = useModal (' connector-modal' )
2930
31+ /** Full version list (same as "Other versions"); fetched in modal for deprecated check. */
32+ const allPackageVersions = shallowRef <Awaited <ReturnType <typeof fetchAllPackageVersions >> | null >(
33+ null ,
34+ )
35+
36+ /** Deprecated version strings from fetched full list (includes Other versions). */
37+ const effectiveDeprecatedVersions = computed (() => {
38+ const list = allPackageVersions .value
39+ if (! list ) return []
40+ return list .filter (v => v .deprecated ).map (v => v .version )
41+ })
42+
3043const modalTitle = computed (() =>
3144 deprecateVersion .value
3245 ? ` ${t (' package.deprecation.modal.title' )} ${props .packageName }@${deprecateVersion .value } `
3346 : ` ${t (' package.deprecation.modal.title' )} ${props .packageName } ` ,
3447)
3548
49+ /** True when the user has entered a version in the form that is already deprecated. */
50+ const isSelectedVersionDeprecated = computed (() => {
51+ const v = deprecateVersion .value .trim ()
52+ if (! v || ! effectiveDeprecatedVersions .value .length ) return false
53+ return effectiveDeprecatedVersions .value .includes (v )
54+ })
55+
56+ // Load full version list so deprecated check includes "Other versions"
57+ watch (
58+ () => props .packageName ,
59+ name => {
60+ if (! name ) return
61+ fetchAllPackageVersions (name ).then (versions => {
62+ allPackageVersions .value = versions
63+ })
64+ },
65+ { immediate: true },
66+ )
67+
3668async function handleDeprecate() {
37- if (props .isAlreadyDeprecated ) return
69+ if (props .isAlreadyDeprecated || isSelectedVersionDeprecated . value ) return
3870 const message = deprecateMessage .value .trim ()
3971 if (! isConnected .value ) return
4072
@@ -122,17 +154,22 @@ defineExpose({ open, close })
122154
123155<template >
124156 <Modal ref =" dialogRef" :modal-title =" modalTitle" id =" deprecate-package-modal" class =" max-w-md" >
125- <!-- Already deprecated: read-only, no form -->
126- <div v-if =" isAlreadyDeprecated" class =" space-y-4" >
157+ <!-- Already deprecated: entire module read-only, hint only, no form / no deprecate button -->
158+ <div v-if =" isAlreadyDeprecated" class =" space-y-4" aria-readonly = " true " >
127159 <div
128160 class =" flex items-center gap-3 p-4 bg-amber-500/10 border border-amber-500/20 rounded-lg"
161+ role =" status"
129162 >
130- <span class =" i-carbon-warning-alt text-amber-500 w-6 h-6" aria-hidden =" true" />
163+ <span class =" i-carbon-warning-alt text-amber-500 w-6 h-6 shrink-0 " aria-hidden =" true" />
131164 <div >
132165 <p class =" font-mono text-sm text-fg" >
133- {{ $t('package.deprecation.modal.already_deprecated') }}
166+ {{
167+ deprecateVersion
168+ ? $t('package.deprecation.modal.already_deprecated_version')
169+ : $t('package.deprecation.modal.already_deprecated')
170+ }}
134171 </p >
135- <p class =" text-xs text-fg-muted" >
172+ <p class =" text-xs text-fg-muted mt-0.5 " >
136173 {{ $t('package.deprecation.modal.already_deprecated_detail') }}
137174 </p >
138175 </div >
@@ -168,8 +205,24 @@ defineExpose({ open, close })
168205 </button >
169206 </div >
170207
171- <!-- Form (only shown when not already deprecated and connected) -->
208+ <!-- Form -->
172209 <div v-else class =" space-y-4" >
210+ <!-- Hint when user-entered version is already deprecated -->
211+ <div
212+ v-if =" isSelectedVersionDeprecated"
213+ class =" flex items-center gap-3 p-4 bg-amber-500/10 border border-amber-500/20 rounded-lg"
214+ role =" status"
215+ >
216+ <span class =" i-carbon-warning-alt text-amber-500 w-6 h-6 shrink-0" aria-hidden =" true" />
217+ <div >
218+ <p class =" font-mono text-sm text-fg" >
219+ {{ $t('package.deprecation.modal.already_deprecated_version') }}
220+ </p >
221+ <p class =" text-xs text-fg-muted mt-0.5" >
222+ {{ $t('package.deprecation.modal.already_deprecated_detail') }}
223+ </p >
224+ </div >
225+ </div >
173226 <div >
174227 <label for =" deprecate-message" class =" block text-sm font-medium text-fg mb-1" >
175228 {{ $t('package.deprecation.modal.reason') }}
@@ -179,7 +232,8 @@ defineExpose({ open, close })
179232 v-model =" deprecateMessage"
180233 rows =" 3"
181234 :maxlength =" DEPRECATE_MESSAGE_MAX_LENGTH"
182- class =" w-full px-3 py-2 font-mono text-sm bg-bg border border-border rounded-md text-fg placeholder:text-fg-muted focus:outline-none focus:ring-2 focus:ring-fg/50"
235+ :disabled =" isSelectedVersionDeprecated"
236+ class =" w-full px-3 py-2 font-mono text-sm bg-bg border border-border rounded-md text-fg placeholder:text-fg-muted focus:outline-none focus:ring-2 focus:ring-fg/50 disabled:opacity-60 disabled:cursor-not-allowed"
183237 :placeholder =" $t('package.deprecation.modal.reason_placeholder')"
184238 :aria-describedby ="
185239 deprecateMessage.length >= DEPRECATE_MESSAGE_MAX_LENGTH
@@ -203,7 +257,8 @@ defineExpose({ open, close })
203257 id =" deprecate-version"
204258 v-model =" deprecateVersion"
205259 type =" text"
206- class =" w-full px-3 py-2 font-mono text-sm bg-bg border border-border rounded-md text-fg placeholder:text-fg-muted focus:outline-none focus:ring-2 focus:ring-fg/50"
260+ :disabled =" isSelectedVersionDeprecated"
261+ class =" w-full px-3 py-2 font-mono text-sm bg-bg border border-border rounded-md text-fg placeholder:text-fg-muted focus:outline-none focus:ring-2 focus:ring-fg/50 disabled:opacity-60 disabled:cursor-not-allowed"
207262 :placeholder =" $t('package.deprecation.modal.version_placeholder')"
208263 />
209264 </div >
@@ -216,7 +271,7 @@ defineExpose({ open, close })
216271 </div >
217272 <button
218273 type =" button"
219- :disabled =" isDeprecating || !deprecateMessage.trim()"
274+ :disabled =" isDeprecating || !deprecateMessage.trim() || isSelectedVersionDeprecated "
220275 class =" w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-colors duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
221276 @click =" handleDeprecate"
222277 >
0 commit comments