Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/components/CopyToClipboardButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function handleClick() {
class="absolute z-20 inset-is-0 top-full inline-flex items-center gap-1 px-2 py-1 rounded border text-xs font-mono whitespace-nowrap transition-all duration-150 opacity-0 -translate-y-1 pointer-events-none group-hover:opacity-100 group-hover:translate-y-0 group-hover:pointer-events-auto focus-visible:opacity-100 focus-visible:translate-y-0 focus-visible:pointer-events-auto"
:class="[
$style.copyButton,
copied ? 'text-accent bg-accent/10' : 'text-fg-muted bg-bg border-border',
copied ? ['text-accent', $style.copiedBg] : 'text-fg-muted bg-bg border-border',
]"
:aria-label="copied ? buttonAriaLabelCopied : buttonAriaLabelCopy"
v-bind="buttonAttrs"
Expand Down Expand Up @@ -80,6 +80,10 @@ function handleClick() {
translate 0.15s;
}
.copiedBg {
background-color: color-mix(in srgb, var(--accent) 10%, var(--bg));
}
@media (hover: none) {
.copyButton {
display: none;
Expand Down
99 changes: 28 additions & 71 deletions app/components/Header/ConnectorModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ function handleDisconnect() {
disconnect()
}

// function copyCommand() {
// let command = executeNpmxConnectorCommand.value
// if (portInput.value !== '31415') {
// command += ` --port ${portInput.value}`
// }
// copy(command)
// }

// const selectedPM = useSelectedPackageManager()

// const executeNpmxConnectorCommand = computed(() => {
// return getExecuteCommand({
// packageName: 'npmx-connector',
// packageManager: selectedPM.value,
// })
// })
function copyCommand() {
let command = executeNpmxConnectorCommand.value
if (portInput.value !== '31415') {
command += ` --port ${portInput.value}`
}
copy(command)
}

const selectedPM = useSelectedPackageManager()

const executeNpmxConnectorCommand = computed(() => {
return getExecuteCommand({
packageName: 'npmx-connector',
packageManager: selectedPM.value,
})
})
</script>

<template>
Expand Down Expand Up @@ -87,26 +87,6 @@ function handleDisconnect() {

<!-- Disconnected state -->
<form v-else class="space-y-4" @submit.prevent="handleConnect">
<!-- Contributor-only notice -->
<div class="p-3 bg-amber-500/10 border border-amber-500/30 rounded-lg">
<div>
<span class="inline-block text-xs font-bold uppercase tracking-wider text-fg rounded">
{{ $t('connector.modal.contributor_badge') }}
</span>
<p class="text-sm text-fg-muted">
<i18n-t keypath="connector.modal.contributor_notice" scope="global">
<template #link>
<LinkBase
to="https://github.com/npmx-dev/npmx.dev/blob/main/CONTRIBUTING.md#local-connector-cli"
>
{{ $t('connector.modal.contributor_link') }}
</LinkBase>
</template>
</i18n-t>
</p>
</div>
</div>

<p class="text-sm text-fg-muted">
{{ $t('connector.modal.run_hint') }}
</p>
Expand All @@ -116,43 +96,20 @@ function handleDisconnect() {
dir="ltr"
>
<span class="text-fg-subtle">$</span>
<span class="text-fg-subtle ms-2">npx npmx-connector</span>
<ButtonBase
:aria-label="copied ? $t('connector.modal.copied') : $t('connector.modal.copy_command')"
@click="copy('npx npmx-connector')"
class="ms-auto"
:classicon="copied ? 'i-lucide:check text-green-500' : 'i-lucide:copy'"
/>
<span class="text-fg-subtle ms-2">{{ executeNpmxConnectorCommand }}</span>
<div class="ms-auto flex items-center gap-2">
<!-- Disable teleport in a modal dialog -->
<PackageManagerSelect :teleport="false" />

<ButtonBase
:aria-label="copied ? $t('connector.modal.copied') : $t('connector.modal.copy_command')"
@click="copyCommand"
class="ms-auto"
:classicon="copied ? 'i-lucide:check text-green-500' : 'i-lucide:copy'"
/>
</div>
</div>

<!-- TODO: Uncomment when npmx-connector is published to npm
<div
class="flex items-center p-3 bg-bg-muted border border-border rounded-lg font-mono text-sm"
>
<span class="text-fg-subtle">$</span>
<span class="text-fg-subtle ms-2">{{ executeNpmxConnectorCommand }}</span>
<div class="ms-auto flex items-center gap-2">
<PackageManagerSelect />

<button
type="button"
:aria-label="
copied ? $t('connector.modal.copied') : $t('connector.modal.copy_command')
"
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"
@click="copyCommand"
>
<span v-if="!copied" class="i-lucide:copy block w-5 h-5" aria-hidden="true" />
<span
v-else
class="i-lucide:check block w-5 h-5 text-green-500"
aria-hidden="true"
/>
</button>
</div>
</div>
-->

<p class="text-sm text-fg-muted">{{ $t('connector.modal.paste_token') }}</p>

<div class="space-y-3">
Expand Down
3 changes: 2 additions & 1 deletion app/components/Package/LikeCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const { user } = useAtproto()

const authModal = useModal('auth-modal')

const { data: likesData } = useFetch(() => `/api/social/likes/${name.value}`, {
const { data: likesData, status: likesStatus } = useFetch(() => `/api/social/likes/${name.value}`, {
default: () => ({ totalLikes: 0, userHasLiked: false }),
server: false,
})
Expand Down Expand Up @@ -76,6 +76,7 @@ const likeAction = async () => {
<div class="flex items-center gap-4 justify-between shrink-0">
<ClientOnly>
<TooltipApp
v-if="likesStatus !== 'pending'"
:text="likesData?.userHasLiked ? $t('package.likes.unlike') : $t('package.likes.like')"
position="bottom"
>
Expand Down
11 changes: 10 additions & 1 deletion app/components/Package/ManagerSelect.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
<script setup lang="ts">
import { onClickOutside, useEventListener } from '@vueuse/core'

withDefaults(
defineProps<{
teleport?: boolean
}>(),
{
teleport: true,
},
)

const selectedPM = useSelectedPackageManager()

const listRef = useTemplateRef('listRef')
Expand Down Expand Up @@ -121,7 +130,7 @@ function handleKeydown(event: KeyboardEvent) {
</button>

<!-- Dropdown menu (teleported to body to avoid clipping) -->
<Teleport to="body">
<Teleport to="body" :disabled="!teleport">
<Transition
:enter-active-class="prefersReducedMotion ? '' : 'transition-opacity duration-150'"
:enter-from-class="prefersReducedMotion ? '' : 'opacity-0'"
Expand Down
2 changes: 2 additions & 0 deletions app/components/Package/TrendsChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { applyDataCorrection } from '~/utils/chart-data-correction'
import { applyBlocklistCorrection, getAnomaliesForPackages } from '~/utils/download-anomalies'
import { copyAltTextForTrendLineChart } from '~/utils/charts'
import('vue-data-ui/style.css')
const props = withDefaults(
defineProps<{
// For single package downloads history
Expand Down
2 changes: 2 additions & 0 deletions app/components/Package/VersionDistribution.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
import TooltipApp from '~/components/Tooltip/App.vue'
import { copyAltTextForVersionsBarChart } from '~/utils/charts'
import('vue-data-ui/style.css')
const props = defineProps<{
packageName: string
inModal?: boolean
Expand Down
2 changes: 2 additions & 0 deletions app/components/Package/WeeklyDownloadStats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type { RepoRef } from '#shared/utils/git-providers'
import type { VueUiSparklineConfig, VueUiSparklineDatasetItem } from 'vue-data-ui'
import { onKeyDown } from '@vueuse/core'
import('vue-data-ui/style.css')
const props = defineProps<{
packageName: string
createdIso: string | null
Expand Down
21 changes: 21 additions & 0 deletions app/components/ReadmeTocDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,27 @@ function handleKeydown(event: KeyboardEvent) {
break
}
}

const itemScrollIntoView = (index: number) => {
const item = props.toc[index]
if (!item) return
const el = document.getElementById(`${listboxId}-${item.id}`)
if (el) {
el.scrollIntoView({ block: 'center' })
}
}

watch(
isOpen,
open => {
if (open && highlightedIndex.value >= 0) {
itemScrollIntoView(highlightedIndex.value)
}
},
{
flush: 'post',
},
)
</script>

<template>
Expand Down
6 changes: 6 additions & 0 deletions app/pages/blog/alpha-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ headline="Read more from the community"
title: 'Finding an accessibility-first culture in npmx',
authorHandle: 'abbeyperini.dev',
description: 'Abbey Perini talks about how accessibility is a deep part of the npmx culture.'
},
{
url: 'https://jonathanyeong.com/writing/npmx-and-the-open-source-mindset/',
title: 'npmx and the open source mindset',
authorHandle: 'jonathanyeong.com',
description: 'How npmx taught me to embrace the open source mindset.'
}
]"
/>
24 changes: 19 additions & 5 deletions app/pages/package/[[org]]/[name].vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,27 @@ const router = useRouter()
const header = useTemplateRef('header')
const isHeaderPinned = shallowRef(false)
const readmeHeader = useTemplateRef('readmeHeader')
const isReadmeHeaderPinned = shallowRef(false)
const navExtraOffset = shallowRef(0)
const isMobile = useMediaQuery('(max-width: 639.9px)')
function checkHeaderPosition() {
const el = header.value
if (!el) return
const headerBounds = useElementBounding(header)
const readmeStickyTop = computed(() => `${56 + headerBounds.height.value}px`)
function isStickyPinned(el: HTMLElement | null): boolean {
if (!el) return false
const style = getComputedStyle(el)
const top = parseFloat(style.top) || 0
const rect = el.getBoundingClientRect()
isHeaderPinned.value = Math.abs(rect.top - top) < 1
return Math.abs(rect.top - top) < 1
}
function checkHeaderPosition() {
isHeaderPinned.value = isStickyPinned(header.value)
isReadmeHeaderPinned.value = isStickyPinned(readmeHeader.value)
}
useEventListener('scroll', checkHeaderPosition, { passive: true })
Expand Down Expand Up @@ -1394,7 +1403,12 @@ const showSkeleton = shallowRef(false)

<!-- README -->
<section id="readme" class="min-w-0 scroll-mt-20" :class="$style.areaReadme">
<div class="flex flex-wrap items-center justify-between mb-3 px-1">
<div
ref="readmeHeader"
class="flex sticky z-10 flex-wrap items-center justify-between mb-3 py-2 -mx-1 px-2 transition-shadow duration-200"
:class="{ 'bg-bg border-border border-b': isReadmeHeaderPinned }"
:style="{ top: readmeStickyTop }"
>
<h2 id="readme-heading" class="group text-xs text-fg-subtle uppercase tracking-wider">
<LinkBase to="#readme">
{{ $t('package.readme.title') }}
Expand Down
2 changes: 1 addition & 1 deletion app/pages/package/[[org]]/[name]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// stub page to help with paths
definePageMeta({
name: 'package',
scrollMargin: 150,
scrollMargin: 200,
})
</script>

Expand Down
3 changes: 2 additions & 1 deletion app/pages/profile/[identity]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if (!profile.value || profileError.value?.statusCode === 404) {
})
}

const { user } = useAtproto()
const { user, pending: userPending } = useAtproto()
const isEditing = ref(false)
const displayNameInput = ref()
const descriptionInput = ref()
Expand Down Expand Up @@ -84,6 +84,7 @@ const showInviteSection = computed(() => {
profile.value.recordExists === false &&
status.value === 'success' &&
!likes.value?.records?.length &&
!userPending.value &&
user.value?.handle !== profile.value.handle
)
})
Expand Down
3 changes: 0 additions & 3 deletions i18n/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,6 @@
"connector": {
"modal": {
"title": "الموصل المحلي",
"contributor_badge": "للمساهمين فقط",
"contributor_notice": "لم يُنشر الموصل بعد على npm. راجع {link} لمعرفة كيفية التشغيل محليًا.",
"contributor_link": "دليل المساهمة",
"connected": "متصل",
"connected_as_user": "متصل كـ ~{user}",
"connected_hint": "يمكنك الآن إدارة الحزم والمؤسسات من واجهة الويب.",
Expand Down
3 changes: 0 additions & 3 deletions i18n/locales/az-AZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -495,9 +495,6 @@
"connector": {
"modal": {
"title": "Lokal Konnektor",
"contributor_badge": "Yalnız əməkdaşlar üçün",
"contributor_notice": "Konnektor hələ npm-ə dərc edilməyib. Lokal işə salmaq üçün {link} baxın.",
"contributor_link": "töhfə bələdçisi",
"connected": "Qoşulub",
"connected_as_user": "~{user} kimi qoşulub",
"connected_hint": "İndi veb interfeysdən paketləri və təşkilatları idarə edə bilərsiniz.",
Expand Down
3 changes: 0 additions & 3 deletions i18n/locales/bg-BG.json
Original file line number Diff line number Diff line change
Expand Up @@ -533,9 +533,6 @@
"connector": {
"modal": {
"title": "Локален конектор",
"contributor_badge": "Само за сътрудници",
"contributor_notice": "Конекторът все още не е публикуван в npm. Вижте {link} за как да го стартирате локално.",
"contributor_link": "ръководство за сътрудничество",
"connected": "Свързан",
"connected_as_user": "Свързан като ~{user}",
"connected_hint": "Сега можете да управлявате пакети и организации от уеб интерфейса.",
Expand Down
3 changes: 0 additions & 3 deletions i18n/locales/bn-IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,6 @@
"connector": {
"modal": {
"title": "স্থানীয় কনেক্টর",
"contributor_badge": "কন্ট্রিবিউটারদের জন্য মাত্র",
"contributor_notice": "কনেক্টর এখনো npm এ প্রকাশিত হয়নি। স্থানীয়ভাবে চালানোর জন্য {link} দেখুন।",
"contributor_link": "অবদানের নির্দেশনা",
"connected": "সংযুক্ত হয়েছে",
"connected_as_user": "~{user} হিসাবে সংযুক্ত",
"connected_hint": "এখন আপনি ওয়েব UI থেকে প্যাকেজ এবং সংস্থাগুলি পরিচালনা করতে পারবেন।",
Expand Down
3 changes: 0 additions & 3 deletions i18n/locales/cs-CZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -511,9 +511,6 @@
"connector": {
"modal": {
"title": "Lokální konektor",
"contributor_badge": "Pouze přispěvatelé",
"contributor_notice": "Konektor ještě není publikován na npm. Viz {link} pro informace, jak jej spustit lokálně.",
"contributor_link": "příručka pro přispěvatele",
"connected": "Připojeno",
"connected_as_user": "Připojeno jako ~{user}",
"connected_hint": "Nyní můžete spravovat balíčky a organizace z webového rozhraní.",
Expand Down
3 changes: 0 additions & 3 deletions i18n/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -577,9 +577,6 @@
"connector": {
"modal": {
"title": "Lokaler Connector",
"contributor_badge": "Nur für Mitwirkende",
"contributor_notice": "Der Connector ist noch nicht auf npm veröffentlicht. Siehe den {link} für Informationen zum lokalen Ausführen.",
"contributor_link": "Leitfaden für Mitwirkende",
"connected": "Verbunden",
"connected_as_user": "Verbunden als ~{user}",
"connected_hint": "Du kannst jetzt Pakete und Organisationen über die Web-Oberfläche verwalten.",
Expand Down
3 changes: 0 additions & 3 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -577,9 +577,6 @@
"connector": {
"modal": {
"title": "Local Connector",
"contributor_badge": "Contributors only",
"contributor_notice": "The connector is not yet published to npm. See the {link} for how to run locally.",
"contributor_link": "contributing guide",
"connected": "Connected",
"connected_as_user": "Connected as ~{user}",
"connected_hint": "You can now manage packages and organizations from the web UI.",
Expand Down
Loading
Loading