forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1<script setup lang="ts">
2const { isConnected, isConnecting, npmUser, error, hasOperations, connect, disconnect } =
3 useConnector()
4
5const { settings } = useSettings()
6
7const tokenInput = shallowRef('')
8const portInput = shallowRef('31415')
9const { copied, copy } = useClipboard({ copiedDuring: 2000 })
10
11const hasAttemptedConnect = shallowRef(false)
12
13watch(isConnected, connected => {
14 if (!connected) {
15 tokenInput.value = ''
16 hasAttemptedConnect.value = false
17 }
18})
19
20async function handleConnect() {
21 hasAttemptedConnect.value = true
22 const port = Number.parseInt(portInput.value, 10) || 31415
23 await connect(tokenInput.value.trim(), port)
24}
25
26function handleDisconnect() {
27 disconnect()
28}
29
30// function copyCommand() {
31// let command = executeNpmxConnectorCommand.value
32// if (portInput.value !== '31415') {
33// command += ` --port ${portInput.value}`
34// }
35// copy(command)
36// }
37
38// const selectedPM = useSelectedPackageManager()
39
40// const executeNpmxConnectorCommand = computed(() => {
41// return getExecuteCommand({
42// packageName: 'npmx-connector',
43// packageManager: selectedPM.value,
44// })
45// })
46</script>
47
48<template>
49 <Modal
50 :modalTitle="$t('connector.modal.title')"
51 :class="isConnected && hasOperations ? 'max-w-2xl' : 'max-w-md'"
52 id="connector-modal"
53 >
54 <!-- Connected state -->
55 <div v-if="isConnected" class="space-y-4">
56 <div class="flex items-center gap-3 p-4 bg-bg-subtle border border-border rounded-lg">
57 <span class="w-3 h-3 rounded-full bg-green-500" aria-hidden="true" />
58 <div>
59 <p class="font-mono text-sm text-fg">{{ $t('connector.modal.connected') }}</p>
60 <p v-if="npmUser" class="font-mono text-xs text-fg-muted">
61 {{ $t('connector.modal.connected_as_user', { user: npmUser }) }}
62 </p>
63 </div>
64 </div>
65
66 <!-- Connector preferences -->
67 <div class="flex flex-col gap-2">
68 <SettingsToggle
69 :label="$t('connector.modal.auto_open_url')"
70 v-model="settings.connector.autoOpenURL"
71 />
72 </div>
73
74 <div class="border-t border-border my-3" />
75
76 <!-- Operations Queue -->
77 <OrgOperationsQueue />
78
79 <div v-if="!hasOperations" class="text-sm text-fg-muted">
80 {{ $t('connector.modal.connected_hint') }}
81 </div>
82
83 <ButtonBase type="button" class="w-full" @click="handleDisconnect">
84 {{ $t('connector.modal.disconnect') }}
85 </ButtonBase>
86 </div>
87
88 <!-- Disconnected state -->
89 <form v-else class="space-y-4" @submit.prevent="handleConnect">
90 <!-- Contributor-only notice -->
91 <div class="p-3 bg-amber-500/10 border border-amber-500/30 rounded-lg">
92 <div>
93 <span class="inline-block text-xs font-bold uppercase tracking-wider text-fg rounded">
94 {{ $t('connector.modal.contributor_badge') }}
95 </span>
96 <p class="text-sm text-fg-muted">
97 <i18n-t keypath="connector.modal.contributor_notice" scope="global">
98 <template #link>
99 <LinkBase
100 to="https://github.com/npmx-dev/npmx.dev/blob/main/CONTRIBUTING.md#local-connector-cli"
101 >
102 {{ $t('connector.modal.contributor_link') }}
103 </LinkBase>
104 </template>
105 </i18n-t>
106 </p>
107 </div>
108 </div>
109
110 <p class="text-sm text-fg-muted">
111 {{ $t('connector.modal.run_hint') }}
112 </p>
113
114 <div
115 class="flex items-center p-3 bg-bg-muted border border-border rounded-lg font-mono text-sm"
116 dir="ltr"
117 >
118 <span class="text-fg-subtle">$</span>
119 <span class="text-fg-subtle ms-2">pnpm npmx-connector</span>
120 <ButtonBase
121 :aria-label="copied ? $t('connector.modal.copied') : $t('connector.modal.copy_command')"
122 @click="copy('pnpm npmx-connector')"
123 class="ms-auto"
124 :classicon="copied ? 'i-lucide:check text-green-500' : 'i-lucide:copy'"
125 />
126 </div>
127
128 <!-- TODO: Uncomment when npmx-connector is published to npm
129 <div
130 class="flex items-center p-3 bg-bg-muted border border-border rounded-lg font-mono text-sm"
131 >
132 <span class="text-fg-subtle">$</span>
133 <span class="text-fg-subtle ms-2">{{ executeNpmxConnectorCommand }}</span>
134 <div class="ms-auto flex items-center gap-2">
135 <PackageManagerSelect />
136
137 <button
138 type="button"
139 :aria-label="
140 copied ? $t('connector.modal.copied') : $t('connector.modal.copy_command')
141 "
142 class="ms-auto text-fg-subtle p-1.5 -m-1.5 hover:text-fg transition-colors duration-200 focus-visible:outline-accent/70 rounded"
143 @click="copyCommand"
144 >
145 <span v-if="!copied" class="i-lucide:copy block w-5 h-5" aria-hidden="true" />
146 <span
147 v-else
148 class="i-lucide:check block w-5 h-5 text-green-500"
149 aria-hidden="true"
150 />
151 </button>
152 </div>
153 </div>
154 -->
155
156 <p class="text-sm text-fg-muted">{{ $t('connector.modal.paste_token') }}</p>
157
158 <div class="space-y-3">
159 <div>
160 <label
161 for="connector-token"
162 class="block text-xs text-fg-subtle uppercase tracking-wider mb-1.5"
163 >
164 {{ $t('connector.modal.token_label') }}
165 </label>
166 <InputBase
167 id="connector-token"
168 v-model="tokenInput"
169 type="password"
170 name="connector-token"
171 :placeholder="$t('connector.modal.token_placeholder')"
172 no-correct
173 class="w-full"
174 size="medium"
175 />
176 </div>
177
178 <details class="text-sm">
179 <summary class="text-fg-subtle hover:text-fg-muted transition-colors duration-200">
180 {{ $t('connector.modal.advanced') }}
181 </summary>
182 <div class="mt-3">
183 <label
184 for="connector-port"
185 class="block text-xs text-fg-subtle uppercase tracking-wider mb-1.5"
186 >
187 {{ $t('connector.modal.port_label') }}
188 </label>
189 <InputBase
190 id="connector-port"
191 v-model="portInput"
192 type="text"
193 name="connector-port"
194 inputmode="numeric"
195 autocomplete="off"
196 class="w-full"
197 size="medium"
198 />
199
200 <div class="border-t border-border my-3" />
201 <div class="flex flex-col gap-2">
202 <SettingsToggle
203 :label="$t('connector.modal.auto_open_url')"
204 v-model="settings.connector.autoOpenURL"
205 />
206 </div>
207 </div>
208 </details>
209 </div>
210
211 <!-- Error message (only show after user explicitly clicks Connect) -->
212 <div
213 v-if="error && hasAttemptedConnect"
214 role="alert"
215 class="p-3 text-sm text-red-400 bg-red-500/10 border border-red-500/20 rounded-md"
216 >
217 {{ error }}
218 </div>
219
220 <!-- Warning message -->
221 <div
222 role="alert"
223 class="p-3 text-sm text-red-400 bg-red-500/10 border border-red-500/20 rounded-md"
224 >
225 <p class="inline-block text-xs font-bold uppercase tracking-wider text-fg rounded">
226 {{ $t('connector.modal.warning') }}
227 </p>
228 <p class="text-sm text-fg-muted mt-1">
229 {{ $t('connector.modal.warning_text') }}
230 </p>
231 </div>
232
233 <ButtonBase
234 type="submit"
235 variant="primary"
236 :disabled="!tokenInput.trim() || isConnecting"
237 class="w-full"
238 >
239 {{ isConnecting ? $t('connector.modal.connecting') : $t('connector.modal.connect') }}
240 </ButtonBase>
241 </form>
242 </Modal>
243</template>