Skip to content

Commit e2c4da9

Browse files
authored
Merge branch 'npmx-dev:main' into main
2 parents 20cc366 + 5d279f7 commit e2c4da9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1277
-696
lines changed

.github/workflows/autofix.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ jobs:
3232
- name: 📦 Install dependencies
3333
run: pnpm install
3434

35-
- name: 🎨 Check for non-RTL CSS classes
36-
run: pnpm rtl:check
35+
- name: 🎨 Check for non-RTL/non-a11y CSS classes
36+
run: pnpm lint:css
3737

3838
- name: 🌐 Compare translations
3939
run: pnpm i18n:check

app/assets/main.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,26 @@
171171
}
172172
}
173173

174+
/*
175+
* Forced Colors Mode (WHCM) Override for Icons
176+
*
177+
* By default, `forced-color-adjust: preserve-parent-color` (from UnoConfig) works fine
178+
* for most icons as they inherit the correct text color.
179+
*
180+
* However, if icons disappear in specific contexts (e.g., inside buttons with
181+
* complex backgrounds or transparent states), uncomment the following block
182+
* to enforce visibility using `CanvasText`.
183+
*/
184+
/*
185+
@media (forced-colors: active) {
186+
[class^='i-'],
187+
[class*=' i-'] {
188+
forced-color-adjust: none !important;
189+
color: CanvasText !important;
190+
}
191+
}
192+
*/
193+
174194
html {
175195
-webkit-font-smoothing: antialiased;
176196
-moz-osx-font-smoothing: grayscale;

app/components/Compare/PackageSelector.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,18 @@ function removePackage(name: string) {
7777
}
7878
7979
function handleKeydown(e: KeyboardEvent) {
80-
if (e.key === 'Enter' && inputValue.value.trim()) {
80+
const inputValueTrim = inputValue.value.trim()
81+
const hasMatchInPackages = filteredResults.value.find(result => {
82+
return result.name === inputValueTrim
83+
})
84+
85+
if (e.key === 'Enter' && inputValueTrim) {
8186
e.preventDefault()
82-
addPackage(inputValue.value.trim())
87+
if (showNoDependencyOption.value) {
88+
addPackage(NO_DEPENDENCY_ID)
89+
} else if (hasMatchInPackages) {
90+
addPackage(inputValueTrim)
91+
}
8392
}
8493
}
8594

app/components/Header/AuthModal.client.vue

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<script setup lang="ts">
22
import { useAtproto } from '~/composables/atproto/useAtproto'
33
import { authRedirect } from '~/utils/atproto/helpers'
4+
import { ensureValidAtIdentifier } from '@atproto/syntax'
45
56
const handleInput = shallowRef('')
7+
const errorMessage = shallowRef('')
68
const route = useRoute()
79
const { user, logout } = useAtproto()
810
@@ -13,26 +15,41 @@ const localeSubPath = ['ko', 'pt', 'ja'].includes(currentLang) ? currentLang : '
1315
const atprotoLink = `https://atproto.com/${localeSubPath}`
1416
1517
async function handleBlueskySignIn() {
16-
await authRedirect('https://bsky.social', { redirectTo: route.fullPath })
18+
await authRedirect('https://bsky.social', { redirectTo: route.fullPath, locale: locale.value })
1719
}
1820
1921
async function handleCreateAccount() {
20-
await authRedirect('https://npmx.social', { create: true, redirectTo: route.fullPath })
22+
await authRedirect('https://npmx.social', {
23+
create: true,
24+
redirectTo: route.fullPath,
25+
locale: locale.value,
26+
})
2127
}
2228
2329
async function handleLogin() {
2430
if (handleInput.value) {
25-
await authRedirect(handleInput.value)
31+
// URLS to PDSs are valid for oauth redirects
32+
if (!handleInput.value.startsWith('https://')) {
33+
try {
34+
ensureValidAtIdentifier(handleInput.value)
35+
} catch (error) {
36+
errorMessage.value =
37+
error instanceof Error ? error.message : $t('auth.modal.default_input_error')
38+
return
39+
}
40+
}
41+
await authRedirect(handleInput.value, {
42+
redirectTo: route.fullPath,
43+
locale: locale.value,
44+
})
2645
}
2746
}
2847
2948
watch(handleInput, newHandleInput => {
49+
errorMessage.value = ''
3050
if (!newHandleInput) return
3151
32-
const normalized = newHandleInput
33-
.trim()
34-
.toLowerCase()
35-
.replace(/[^a-z0-9.-]/g, '')
52+
const normalized = newHandleInput.trim().toLowerCase().replace(/@/g, '')
3653
3754
if (normalized !== newHandleInput) {
3855
handleInput.value = normalized
@@ -81,6 +98,9 @@ watch(handleInput, newHandleInput => {
8198
v-bind="noCorrect"
8299
class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 hover:border-fg-subtle focus:border-accent focus-visible:(outline-2 outline-accent/70)"
83100
/>
101+
<p v-if="errorMessage" class="text-red-500 text-xs mt-1" role="alert">
102+
{{ errorMessage }}
103+
</p>
84104
</div>
85105

86106
<details class="text-sm">

app/components/LicenseDisplay.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const hasAnyValidLicense = computed(() => tokens.value.some(t => t.type === 'lic
2424
{{ token.value }}
2525
</a>
2626
<span v-else-if="token.type === 'license'">{{ token.value }}</span>
27-
<span v-else-if="token.type === 'operator'" class="text-[0.65em]">{{ token.value }}</span>
27+
<span v-else-if="token.type === 'operator'" class="text-4xs">{{ token.value }}</span>
2828
</template>
2929
<span
3030
v-if="hasAnyValidLicense"

app/components/Package/ClaimPackageModal.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ const isChecking = computed(() => {
3636
})
3737
3838
const mergedError = computed(() => {
39-
return (
40-
publishError.value ??
41-
(checkError.value instanceof Error
42-
? checkError.value.message
43-
: $t('claim.modal.failed_to_check'))
44-
)
39+
return checkResult.value !== null
40+
? null
41+
: (publishError.value ??
42+
(checkError.value instanceof Error
43+
? checkError.value.message
44+
: $t('claim.modal.failed_to_check')))
4545
})
4646
4747
const connectorModal = useModal('connector-modal')

app/components/Package/DownloadAnalytics.vue

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -895,10 +895,10 @@ function buildExportFilename(extension: string): string {
895895
}
896896
897897
const granularityLabels = computed(() => ({
898-
daily: $t('package.downloads.granularity_daily'),
899-
weekly: $t('package.downloads.granularity_weekly'),
900-
monthly: $t('package.downloads.granularity_monthly'),
901-
yearly: $t('package.downloads.granularity_yearly'),
898+
daily: $t('package.trends.granularity_daily'),
899+
weekly: $t('package.trends.granularity_weekly'),
900+
monthly: $t('package.trends.granularity_monthly'),
901+
yearly: $t('package.trends.granularity_yearly'),
902902
}))
903903
904904
function getGranularityLabel(granularity: ChartTimeGranularity) {
@@ -1321,18 +1321,18 @@ const chartConfig = computed(() => {
13211321
userOptions: {
13221322
buttons: { pdf: false, labels: false, fullscreen: false, table: false, tooltip: false },
13231323
buttonTitles: {
1324-
csv: $t('package.downloads.download_file', { fileType: 'CSV' }),
1325-
img: $t('package.downloads.download_file', { fileType: 'PNG' }),
1326-
svg: $t('package.downloads.download_file', { fileType: 'SVG' }),
1327-
annotator: $t('package.downloads.toggle_annotator'),
1324+
csv: $t('package.trends.download_file', { fileType: 'CSV' }),
1325+
img: $t('package.trends.download_file', { fileType: 'PNG' }),
1326+
svg: $t('package.trends.download_file', { fileType: 'SVG' }),
1327+
annotator: $t('package.trends.toggle_annotator'),
13281328
},
13291329
callbacks: {
13301330
img: ({ imageUri }: { imageUri: string }) => {
13311331
loadFile(imageUri, buildExportFilename('png'))
13321332
},
13331333
csv: (csvStr: string) => {
13341334
const PLACEHOLDER_CHAR = '\0'
1335-
const multilineDateTemplate = $t('package.downloads.date_range_multiline', {
1335+
const multilineDateTemplate = $t('package.trends.date_range_multiline', {
13361336
start: PLACEHOLDER_CHAR,
13371337
end: PLACEHOLDER_CHAR,
13381338
})
@@ -1360,8 +1360,9 @@ const chartConfig = computed(() => {
13601360
fontSize: isMobile.value ? 24 : 16,
13611361
color: pending.value ? colors.value.border : colors.value.fgSubtle,
13621362
axis: {
1363-
yLabel: $t('package.downloads.y_axis_label', {
1363+
yLabel: $t('package.trends.y_axis_label', {
13641364
granularity: getGranularityLabel(selectedGranularity.value),
1365+
facet: $t('package.trends.items.downloads'),
13651366
}),
13661367
xLabel: isMultiPackageMode.value ? '' : xAxisLabel.value, // for multiple series, names are displayed in the chart's legend
13671368
yLabelOffsetX: 12,
@@ -1478,7 +1479,7 @@ const chartConfig = computed(() => {
14781479
for="granularity"
14791480
class="text-3xs font-mono text-fg-subtle tracking-wide uppercase"
14801481
>
1481-
{{ $t('package.downloads.granularity') }}
1482+
{{ $t('package.trends.granularity') }}
14821483
</label>
14831484

14841485
<div
@@ -1490,10 +1491,18 @@ const chartConfig = computed(() => {
14901491
:disabled="pending"
14911492
class="w-full px-2.5 py-1.75 bg-bg-subtle font-mono text-sm text-fg outline-none appearance-none focus-visible:outline-accent/70"
14921493
>
1493-
<option value="daily">{{ $t('package.downloads.granularity_daily') }}</option>
1494-
<option value="weekly">{{ $t('package.downloads.granularity_weekly') }}</option>
1495-
<option value="monthly">{{ $t('package.downloads.granularity_monthly') }}</option>
1496-
<option value="yearly">{{ $t('package.downloads.granularity_yearly') }}</option>
1494+
<option value="daily">
1495+
{{ $t('package.trends.granularity_daily') }}
1496+
</option>
1497+
<option value="weekly">
1498+
{{ $t('package.trends.granularity_weekly') }}
1499+
</option>
1500+
<option value="monthly">
1501+
{{ $t('package.trends.granularity_monthly') }}
1502+
</option>
1503+
<option value="yearly">
1504+
{{ $t('package.trends.granularity_yearly') }}
1505+
</option>
14971506
</select>
14981507
</div>
14991508
</div>
@@ -1504,7 +1513,7 @@ const chartConfig = computed(() => {
15041513
for="startDate"
15051514
class="text-3xs font-mono text-fg-subtle tracking-wide uppercase"
15061515
>
1507-
{{ $t('package.downloads.start_date') }}
1516+
{{ $t('package.trends.start_date') }}
15081517
</label>
15091518
<div
15101519
class="flex items-center gap-2 px-2.5 py-1.75 bg-bg-subtle border border-border rounded-md focus-within:(border-border-hover ring-2 ring-accent/70)"
@@ -1522,7 +1531,7 @@ const chartConfig = computed(() => {
15221531

15231532
<div class="flex flex-col gap-1">
15241533
<label for="endDate" class="text-3xs font-mono text-fg-subtle tracking-wide uppercase">
1525-
{{ $t('package.downloads.end_date') }}
1534+
{{ $t('package.trends.end_date') }}
15261535
</label>
15271536
<div
15281537
class="flex items-center gap-2 px-2.5 py-1.75 bg-bg-subtle border border-border rounded-md focus-within:(border-border-hover ring-2 ring-accent/70)"
@@ -1656,9 +1665,7 @@ const chartConfig = computed(() => {
16561665
stroke-linecap="round"
16571666
/>
16581667
</svg>
1659-
<span class="text-fg-subtle">{{
1660-
$t('package.downloads.legend_estimation')
1661-
}}</span>
1668+
<span class="text-fg-subtle">{{ $t('package.trends.legend_estimation') }}</span>
16621669
</div>
16631670
</div>
16641671
</template>
@@ -1747,7 +1754,11 @@ const chartConfig = computed(() => {
17471754
v-if="!chartData.dataset && !pending"
17481755
class="min-h-[260px] flex items-center justify-center text-fg-subtle font-mono text-sm"
17491756
>
1750-
{{ $t('package.downloads.no_data') }}
1757+
{{
1758+
$t('package.trends.no_data', {
1759+
facet: $t('package.trends.items.downloads'),
1760+
})
1761+
}}
17511762
</div>
17521763

17531764
<div
@@ -1756,7 +1767,7 @@ const chartConfig = computed(() => {
17561767
aria-live="polite"
17571768
class="absolute top-1/2 inset-is-1/2 -translate-x-1/2 -translate-y-1/2 text-xs text-fg-subtle font-mono bg-bg/70 backdrop-blur px-3 py-2 rounded-md border border-border"
17581769
>
1759-
{{ $t('package.downloads.loading') }}
1770+
{{ $t('package.trends.loading') }}
17601771
</div>
17611772
</div>
17621773
</template>

app/components/Package/WeeklyDownloadStats.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ watch(
131131
const dataset = computed(() =>
132132
weeklyDownloads.value.map(d => ({
133133
value: d?.downloads ?? 0,
134-
period: $t('package.downloads.date_range', {
134+
period: $t('package.trends.date_range', {
135135
start: d.weekStart ?? '-',
136136
end: d.weekEnd ?? '-',
137137
}),
@@ -267,7 +267,7 @@ const config = computed(() => {
267267
</ClientOnly>
268268
</template>
269269
<p v-else class="py-2 text-sm font-mono text-fg-subtle">
270-
{{ $t('package.downloads.no_data') }}
270+
{{ $t('package.trends.no_data') }}
271271
</p>
272272
</div>
273273
</CollapsibleSection>

app/components/Settings/AccentColorPicker.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,18 @@ onPrehydrate(el => {
5959
</label>
6060
</fieldset>
6161
</template>
62+
63+
<style scoped>
64+
@media (forced-colors: active) {
65+
/* keep accent radio swatches visible in forced colors. */
66+
label {
67+
forced-color-adjust: none;
68+
border: 1px solid CanvasText;
69+
70+
&:has(> input:checked) {
71+
outline: 2px solid Highlight;
72+
outline-offset: 2px;
73+
}
74+
}
75+
}
76+
</style>

app/components/Settings/BgThemePicker.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,18 @@ onPrehydrate(el => {
3737
</label>
3838
</fieldset>
3939
</template>
40+
41+
<style scoped>
42+
@media (forced-colors: active) {
43+
/* keep background theme swatches visible in forced colors. */
44+
label {
45+
forced-color-adjust: none;
46+
border: 1px solid CanvasText;
47+
48+
&:has(> input:checked) {
49+
outline: 2px solid Highlight;
50+
outline-offset: 2px;
51+
}
52+
}
53+
}
54+
</style>

0 commit comments

Comments
 (0)