Compare commits
3 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60b8e7c4a6 | ||
|
|
e14387e399 | ||
|
|
52d6672fb3 |
43 changed files with 3168 additions and 3015 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -30,5 +30,8 @@
|
|||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "file"
|
||||
},
|
||||
"files.associations": {
|
||||
"*.css": "tailwindcss"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
FAQ.md
5
FAQ.md
|
|
@ -3,8 +3,9 @@
|
|||
Certain configuration values can be set via environment variables, using the `-e` parameter on the docker command line,
|
||||
or the `environment:` section in `compose.yaml` file.
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Click to expand</summary>
|
||||
<summary>Click to expand all environment Variables</summary>
|
||||
|
||||
| Environment Variable | Description | Default |
|
||||
| ------------------------------- | ------------------------------------------------------------------- | --------------------- |
|
||||
|
|
@ -64,7 +65,6 @@ or the `environment:` section in `compose.yaml` file.
|
|||
| YTP_THUMB_GENERATE | Enable ffmpeg thumbnail generation when no local thumbnail exists. | `true` |
|
||||
| YTP_THUMB_SIDECAR | Save generated thumbnails next to media instead of temp cache. | `false` |
|
||||
| YTP_DISABLE_EXEC | Strip some dangerous yt-dlp options. | `false` |
|
||||
</details>
|
||||
|
||||
> [!NOTE]
|
||||
> To raise the worker limit for a specific extractor, set an env variable using this format: `YTP_MAX_WORKERS_FOR_<EXTRACTOR_NAME>`
|
||||
|
|
@ -77,6 +77,7 @@ or the `environment:` section in `compose.yaml` file.
|
|||
> `YTP_AUTO_CLEAR_HISTORY_DAYS` `0` days means no automatic clearing of the download history. lowest value that will
|
||||
> trigger the clearing is `1` day. This setting will **NOT** delete the downloaded files, it will only clear the
|
||||
> history from the database.
|
||||
</details>
|
||||
|
||||
# Browser extensions & bookmarklets
|
||||
|
||||
|
|
|
|||
|
|
@ -7,5 +7,69 @@ export default defineAppConfig({
|
|||
success: 'emerald',
|
||||
neutral: 'stone',
|
||||
},
|
||||
formField: {
|
||||
slots: {
|
||||
label: 'block font-bold text-default',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
slots: {
|
||||
content: 'pointer-events-auto',
|
||||
},
|
||||
},
|
||||
dropdownMenu: {
|
||||
slots: {
|
||||
content: 'ytp-floating-surface',
|
||||
},
|
||||
},
|
||||
popover: {
|
||||
slots: {
|
||||
content: 'ytp-floating-surface',
|
||||
},
|
||||
},
|
||||
select: {
|
||||
slots: {
|
||||
content: 'ytp-floating-surface',
|
||||
},
|
||||
variants: {
|
||||
variant: {
|
||||
outline: 'bg-elevated/40 hover:bg-elevated/55 disabled:bg-elevated/20',
|
||||
},
|
||||
},
|
||||
},
|
||||
selectMenu: {
|
||||
slots: {
|
||||
content: 'ytp-floating-surface',
|
||||
},
|
||||
variants: {
|
||||
variant: {
|
||||
outline: 'bg-elevated/40 hover:bg-elevated/55 disabled:bg-elevated/20',
|
||||
},
|
||||
},
|
||||
},
|
||||
button: {
|
||||
compoundVariants: [
|
||||
{
|
||||
color: 'neutral',
|
||||
variant: 'outline',
|
||||
class:
|
||||
'bg-transparent hover:bg-elevated/30 active:bg-elevated/40 disabled:bg-transparent aria-disabled:bg-transparent',
|
||||
},
|
||||
{
|
||||
color: 'neutral',
|
||||
variant: 'soft',
|
||||
class:
|
||||
'bg-elevated/30 hover:bg-elevated/45 active:bg-elevated/55 disabled:bg-elevated/20 aria-disabled:bg-elevated/20',
|
||||
},
|
||||
{
|
||||
color: 'neutral',
|
||||
variant: 'ghost',
|
||||
class: 'hover:bg-elevated/30 active:bg-elevated/40 focus-visible:bg-elevated/30',
|
||||
},
|
||||
],
|
||||
defaultVariants: {
|
||||
size: 'sm',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
@layer theme, base, legacy, components, utilities;
|
||||
|
||||
@import 'tailwindcss';
|
||||
@import '@nuxt/ui';
|
||||
|
||||
|
|
@ -30,6 +32,11 @@
|
|||
html.simple-mode.bg-fanart #__nuxt {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
html.dark.simple-mode.bg-fanart body,
|
||||
html.dark.simple-mode.bg-fanart #__nuxt {
|
||||
background-color: var(--ui-bg) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
|
@ -51,9 +58,9 @@
|
|||
|
||||
.shell-surface {
|
||||
background:
|
||||
radial-gradient(circle at top left, rgb(99 102 241 / 0.1), transparent 28%),
|
||||
radial-gradient(circle at top right, rgb(245 158 11 / 0.08), transparent 22%),
|
||||
linear-gradient(180deg, rgb(255 255 255 / 0.84), rgb(248 250 252 / 0.9));
|
||||
radial-gradient(circle at top left, rgb(99 102 241 / 0.14), transparent 28%),
|
||||
radial-gradient(circle at top right, rgb(245 158 11 / 0.12), transparent 22%),
|
||||
linear-gradient(180deg, rgb(255 255 255 / 0.6), rgb(248 250 252 / 0.72));
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +72,115 @@
|
|||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.ytp-page-header {
|
||||
@apply flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between;
|
||||
}
|
||||
|
||||
.ytp-page-heading {
|
||||
@apply flex min-w-0 items-center gap-3;
|
||||
}
|
||||
|
||||
.ytp-page-kicker {
|
||||
@apply flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned;
|
||||
}
|
||||
|
||||
.ytp-page-icon {
|
||||
@apply inline-flex size-11 shrink-0 items-center justify-center rounded-md border bg-elevated/70 text-primary;
|
||||
border-color: var(--ytp-surface-border-solid);
|
||||
}
|
||||
|
||||
.ytp-section-icon {
|
||||
@apply inline-flex size-10 shrink-0 items-center justify-center rounded-md border bg-elevated/70 text-primary;
|
||||
border-color: var(--ytp-surface-border-solid);
|
||||
}
|
||||
|
||||
.ytp-detail-icon {
|
||||
@apply inline-flex size-9 shrink-0 items-center justify-center rounded-md border bg-elevated/70 text-primary;
|
||||
border-color: var(--ytp-surface-border-solid);
|
||||
}
|
||||
|
||||
.ytp-card {
|
||||
@apply min-w-0 max-w-full rounded-lg border bg-elevated/20;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-card-padded {
|
||||
@apply min-w-0 max-w-full rounded-lg border bg-elevated/20 p-4;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-card-padded-lg {
|
||||
@apply min-w-0 max-w-full rounded-lg border bg-elevated/20 p-4 sm:p-5;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-panel {
|
||||
@apply min-w-0 max-w-full rounded-lg border;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-panel-padded {
|
||||
@apply min-w-0 max-w-full rounded-lg border p-4;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-panel-padded-lg {
|
||||
@apply min-w-0 max-w-full rounded-lg border p-4 sm:p-5;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-frame {
|
||||
@apply min-w-0 max-w-full rounded-lg border;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-table-surface {
|
||||
@apply min-w-0 max-w-full rounded-sm border bg-elevated/20;
|
||||
border-color: var(--ytp-surface-border-solid);
|
||||
}
|
||||
|
||||
.ytp-floating-surface {
|
||||
@apply border bg-default/95 shadow-xl backdrop-blur-md;
|
||||
border-color: var(--ytp-surface-border-solid);
|
||||
box-shadow: 0 1rem 2rem rgb(15 23 42 / 0.1);
|
||||
}
|
||||
|
||||
.ytp-floating-surface [data-slot='item'][data-highlighted]::before,
|
||||
.ytp-floating-surface [data-slot='item'][data-state='open']::before,
|
||||
.ytp-floating-surface [data-slot='item']:hover::before {
|
||||
background-color: color-mix(in oklab, var(--ui-primary) 11%, transparent) !important;
|
||||
}
|
||||
|
||||
.ytp-floating-surface [data-slot='item'][data-disabled]::before {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.dark .ytp-floating-surface {
|
||||
background: color-mix(in oklab, var(--ui-bg) 92%, var(--ui-bg-elevated) 8%);
|
||||
box-shadow: 0 1rem 2rem rgb(0 0 0 / 0.3);
|
||||
}
|
||||
|
||||
.dark .ytp-floating-surface [data-slot='item'][data-highlighted]::before,
|
||||
.dark .ytp-floating-surface [data-slot='item'][data-state='open']::before,
|
||||
.dark .ytp-floating-surface [data-slot='item']:hover::before {
|
||||
background-color: color-mix(in oklab, var(--ui-primary) 16%, transparent) !important;
|
||||
}
|
||||
|
||||
.ytp-border-top-soft {
|
||||
@apply border-t;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-border-bottom-soft {
|
||||
@apply border-b;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-border-left-soft {
|
||||
@apply border-l;
|
||||
border-color: var(--ytp-surface-border-soft);
|
||||
}
|
||||
|
||||
.ytp-terminal {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
|
@ -167,6 +283,15 @@
|
|||
--ui-radius: 0.5rem;
|
||||
--ui-container: 96rem;
|
||||
--ui-header-height: 4.25rem;
|
||||
--ytp-border-strong: var(--ui-border-accented);
|
||||
--ytp-surface-border-soft: var(--ui-border-accented);
|
||||
--ytp-surface-border-solid: var(--ui-border-accented);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--ytp-border-strong: var(--ui-border);
|
||||
--ytp-surface-border-soft: color-mix(in oklab, var(--ui-border) 70%, transparent);
|
||||
--ytp-surface-border-solid: var(--ui-border);
|
||||
}
|
||||
|
||||
html:not(.no-page-anim) .page-enter-active {
|
||||
|
|
|
|||
|
|
@ -261,9 +261,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="grid gap-3 rounded-lg border border-default bg-default p-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto]"
|
||||
>
|
||||
<div class="grid gap-3 ytp-card-padded md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto]">
|
||||
<UFormField :ui="fieldUi">
|
||||
<template #label>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-if="'bool' === type && compact" class="w-full space-y-1.5">
|
||||
<div
|
||||
class="flex min-h-11 items-start justify-between gap-3 rounded-md border border-default bg-default/80 px-3 py-2"
|
||||
class="flex min-h-11 items-start justify-between gap-3 rounded-md border border-default bg-elevated/20 px-3 py-2"
|
||||
>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="inline-flex min-w-0 items-center gap-2 text-sm font-semibold text-default">
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
v-model="boolModel"
|
||||
:disabled="disabled"
|
||||
color="success"
|
||||
:label="boolModel ? 'Yes' : 'No'"
|
||||
label=""
|
||||
size="lg"
|
||||
class="shrink-0"
|
||||
:ui="{ root: 'items-center gap-2', wrapper: 'ms-0 text-sm' }"
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
:disabled="disabled"
|
||||
size="lg"
|
||||
class="w-full"
|
||||
:ui="{ root: 'w-full', base: 'w-full bg-default/90' }"
|
||||
:ui="{ root: 'w-full', base: 'w-full bg-elevated/60' }"
|
||||
/>
|
||||
|
||||
<UTextarea
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<div
|
||||
v-if="open && suggestions.length"
|
||||
ref="dropdownRef"
|
||||
class="absolute inset-x-0 top-full z-20 mt-1 max-h-40 overflow-y-auto rounded-md border border-default bg-default shadow-lg"
|
||||
class="absolute inset-x-0 top-full z-20 mt-1 max-h-40 overflow-y-auto rounded-md ytp-floating-surface"
|
||||
role="listbox"
|
||||
>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
variant="outline"
|
||||
color="neutral"
|
||||
class="w-full"
|
||||
:ui="{ root: 'w-full', base: 'w-full bg-default/90', leadingIcon: iconClass }"
|
||||
:ui="{ root: 'w-full', base: 'w-full bg-elevated/60', leadingIcon: iconClass }"
|
||||
@focus="onFocus"
|
||||
@blur="hideList"
|
||||
@input="onInput"
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
<div
|
||||
v-if="showList && filteredOptions.length"
|
||||
ref="dropdownRef"
|
||||
class="absolute inset-x-0 top-full z-20 mt-1 max-h-40 overflow-y-auto rounded-md border border-default bg-default shadow-lg"
|
||||
class="absolute inset-x-0 top-full z-20 mt-1 max-h-40 overflow-y-auto rounded-md ytp-floating-surface"
|
||||
role="menu"
|
||||
>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -25,10 +25,7 @@
|
|||
</UButton>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!limits && limitsLoading"
|
||||
class="flex min-h-72 items-center justify-center rounded-md border border-default bg-default/90"
|
||||
>
|
||||
<div v-if="!limits && limitsLoading" class="ytp-card flex min-h-72 items-center justify-center">
|
||||
<div class="flex flex-col items-center gap-3 text-center text-toned">
|
||||
<UIcon name="i-lucide-loader-circle" class="size-10 animate-spin text-info" />
|
||||
|
||||
|
|
@ -66,7 +63,7 @@
|
|||
title="Download queue is paused"
|
||||
/>
|
||||
|
||||
<div class="rounded-md border border-default bg-default shadow-sm">
|
||||
<div class="ytp-card shadow-sm">
|
||||
<div class="grid gap-0 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,0.95fr)]">
|
||||
<section
|
||||
class="space-y-4 border-b border-default px-4 py-4 sm:px-5 lg:border-r lg:border-b-0"
|
||||
|
|
@ -197,13 +194,10 @@
|
|||
description="Overrides and extractor usage appear here once activity is detected."
|
||||
/>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-md border border-default bg-default"
|
||||
>
|
||||
<div v-else class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface">
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-180 w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-left [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -119,9 +119,7 @@
|
|||
@click="sourceOpen = !sourceOpen"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-9 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<span class="ytp-detail-icon">
|
||||
<UIcon name="i-lucide-file-code" class="size-4" />
|
||||
</span>
|
||||
<p class="text-base font-semibold text-highlighted">Source</p>
|
||||
|
|
@ -167,9 +165,7 @@
|
|||
@click="fieldsOpen = !fieldsOpen"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-9 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<span class="ytp-detail-icon">
|
||||
<UIcon name="i-lucide-tags" class="size-4" />
|
||||
</span>
|
||||
<p class="text-base font-semibold text-highlighted">Fields</p>
|
||||
|
|
@ -225,14 +221,16 @@
|
|||
>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center justify-between gap-3 px-3 py-2 text-left"
|
||||
class="grid w-full grid-cols-[minmax(7rem,0.45fr)_minmax(0,1fr)] items-center gap-3 px-3 py-2 text-left sm:grid-cols-[minmax(0,12rem)_minmax(0,1fr)]"
|
||||
@click="toggleField(field.key)"
|
||||
>
|
||||
<span class="text-[11px] font-semibold uppercase tracking-wide text-toned">
|
||||
<span
|
||||
class="min-w-0 truncate text-[11px] font-semibold uppercase tracking-wide text-toned"
|
||||
>
|
||||
{{ field.label }}
|
||||
</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span v-if="field.preview" class="max-w-md truncate text-xs text-toned">
|
||||
<div class="flex min-w-0 items-center justify-end gap-2">
|
||||
<span v-if="field.preview" class="min-w-0 truncate text-xs text-toned">
|
||||
{{ field.preview }}
|
||||
</span>
|
||||
<UIcon
|
||||
|
|
@ -306,9 +304,7 @@
|
|||
@click="rawJsonOpen = !rawJsonOpen"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-9 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<span class="ytp-detail-icon">
|
||||
<UIcon name="i-lucide-braces" class="size-4" />
|
||||
</span>
|
||||
<p class="text-base font-semibold text-highlighted">Raw Data</p>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<main class="space-y-4">
|
||||
<form autocomplete="off" class="space-y-4" @submit.prevent="addDownload">
|
||||
<UPageCard variant="outline" :ui="downloadCardUi">
|
||||
<template #body>
|
||||
<div class="ytp-card p-4 sm:p-6 space-y-4">
|
||||
<div class="space-y-4">
|
||||
<UFormField class="w-full" :ui="downloadFieldUi">
|
||||
<template #label>
|
||||
|
|
@ -26,7 +25,7 @@
|
|||
v-model="form.url"
|
||||
:disabled="addInProgress"
|
||||
size="lg"
|
||||
variant="outline"
|
||||
:variant="show_description ? 'soft' : 'outline'"
|
||||
color="neutral"
|
||||
class="w-full"
|
||||
:rows="3"
|
||||
|
|
@ -47,7 +46,7 @@
|
|||
placeholder="URLs to download"
|
||||
:disabled="addInProgress"
|
||||
size="lg"
|
||||
variant="outline"
|
||||
:variant="show_description ? 'soft' : 'outline'"
|
||||
color="neutral"
|
||||
class="w-full"
|
||||
:ui="{
|
||||
|
|
@ -97,7 +96,7 @@
|
|||
<div class="flex w-full gap-2">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:variant="show_description ? 'soft' : 'outline'"
|
||||
icon="i-lucide-info"
|
||||
square
|
||||
class="shrink-0"
|
||||
|
|
@ -158,7 +157,7 @@
|
|||
<UButton
|
||||
type="button"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:variant="showAdvanced ? 'soft' : 'outline'"
|
||||
icon="i-lucide-settings-2"
|
||||
size="lg"
|
||||
class="w-full justify-center"
|
||||
|
|
@ -185,11 +184,9 @@
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
|
||||
<UPageCard v-if="showAdvanced" variant="outline" :ui="downloadCardUi">
|
||||
<template #body>
|
||||
<div v-if="showAdvanced" class="ytp-card p-4 sm:p-6 space-y-4">
|
||||
<div class="space-y-4">
|
||||
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-12">
|
||||
<div class="xl:col-span-2">
|
||||
|
|
@ -402,10 +399,7 @@
|
|||
</UFormField>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="config.dl_fields.length > 0"
|
||||
class="grid gap-4 md:grid-cols-2 xl:grid-cols-2"
|
||||
>
|
||||
<div v-if="config.dl_fields.length > 0" class="grid gap-4 md:grid-cols-2 xl:grid-cols-2">
|
||||
<DLInput
|
||||
v-for="(fi, index) in sortedDLFields"
|
||||
:id="fi?.id || `dlf-${index}`"
|
||||
|
|
@ -421,9 +415,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between gap-2 border-t border-default pt-4"
|
||||
>
|
||||
<div class="flex flex-wrap items-center justify-between gap-2 border-t border-default pt-4">
|
||||
<UDropdownMenu class="sm:hidden" :items="mobileActionGroups" :modal="false">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -438,13 +430,11 @@
|
|||
<div class="hidden flex-wrap items-center gap-2 sm:flex">
|
||||
<UButton
|
||||
type="button"
|
||||
color="info"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
icon="i-lucide-info"
|
||||
:disabled="addInProgress || !hasValidUrl"
|
||||
@click="
|
||||
emitter('getInfo', splitUrls(form.url || '')[0] || '', form.preset, form.cli)
|
||||
"
|
||||
@click="emitter('getInfo', splitUrls(form.url || '')[0] || '', form.preset, form.cli)"
|
||||
>
|
||||
yt-dlp Information
|
||||
</UButton>
|
||||
|
|
@ -452,7 +442,7 @@
|
|||
<UButton
|
||||
v-if="config.app.console_enabled"
|
||||
type="button"
|
||||
color="warning"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
icon="i-lucide-terminal"
|
||||
:disabled="!hasValidUrl"
|
||||
|
|
@ -464,7 +454,7 @@
|
|||
<UButton
|
||||
v-if="config.app.console_enabled"
|
||||
type="button"
|
||||
color="success"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
icon="i-lucide-flask-conical"
|
||||
:disabled="!hasValidUrl"
|
||||
|
|
@ -476,7 +466,7 @@
|
|||
|
||||
<UButton
|
||||
type="button"
|
||||
color="error"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
icon="i-lucide-rotate-ccw"
|
||||
:disabled="!!form?.id"
|
||||
|
|
@ -485,8 +475,7 @@
|
|||
Reset local settings
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<UModal
|
||||
|
|
@ -707,13 +696,6 @@ const testResultsPreClasses = computed(() => [
|
|||
testResultsClasses.value,
|
||||
]);
|
||||
|
||||
const downloadCardUi = {
|
||||
root: 'w-full',
|
||||
container: 'w-full p-4 sm:p-6',
|
||||
wrapper: 'w-full items-stretch',
|
||||
body: 'w-full space-y-4',
|
||||
};
|
||||
|
||||
const downloadFieldUi = {
|
||||
label: 'font-semibold text-default',
|
||||
container: 'space-y-2',
|
||||
|
|
@ -793,7 +775,6 @@ const mobileActionGroups = computed(() => {
|
|||
{
|
||||
label: 'Reset local settings',
|
||||
icon: 'i-lucide-rotate-ccw',
|
||||
color: 'error',
|
||||
disabled: Boolean(form.value?.id),
|
||||
onSelect: () => void resetConfig(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
<UIcon name="i-lucide-check" class="size-3.5" />
|
||||
</UButton>
|
||||
<UButton
|
||||
color="error"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
square
|
||||
|
|
@ -121,7 +121,7 @@
|
|||
</UButton>
|
||||
|
||||
<UButton
|
||||
color="error"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-lucide-trash"
|
||||
|
|
|
|||
36
ui/app/components/PageHeader.vue
Normal file
36
ui/app/components/PageHeader.vue
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<slot name="icon">
|
||||
<UIcon :name="icon" class="size-5" />
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<div class="min-w-0" :class="contentClass">
|
||||
<div class="ytp-page-kicker">
|
||||
<slot name="kicker">
|
||||
<span>{{ sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageLabel }}</span>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="$slots.actions"
|
||||
class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end"
|
||||
>
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { YtpPageHeaderProps } from '~/types';
|
||||
|
||||
withDefaults(defineProps<YtpPageHeaderProps>(), {
|
||||
contentClass: 'space-y-1',
|
||||
});
|
||||
</script>
|
||||
|
|
@ -30,15 +30,15 @@
|
|||
|
||||
<template #body>
|
||||
<div class="w-full space-y-6">
|
||||
<UPageCard variant="subtle" class="w-full" :ui="settingsCardUi">
|
||||
<template #header>
|
||||
<div class="ytp-card w-full">
|
||||
<div class="p-4 sm:p-5 ytp-border-bottom-soft">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-lucide-layout-dashboard" class="size-4 text-toned" />
|
||||
<span class="text-sm font-semibold text-highlighted">Page View</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #body>
|
||||
<div class="p-4 sm:p-5 space-y-4">
|
||||
<UFormField label="" class="w-full" :ui="settingsFieldUi">
|
||||
<USelect
|
||||
v-model="draftMode"
|
||||
|
|
@ -73,18 +73,18 @@
|
|||
:label="page_anims ? 'Animations On' : 'Animations Off'"
|
||||
description="Enable page transition animations."
|
||||
/>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UPageCard variant="subtle" class="w-full" :ui="settingsCardUi">
|
||||
<template #header>
|
||||
<div class="ytp-card w-full">
|
||||
<div class="p-4 sm:p-5 ytp-border-bottom-soft">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-lucide-image" class="size-4 text-toned" />
|
||||
<span class="text-sm font-semibold text-highlighted">Background</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #body>
|
||||
<div class="p-4 sm:p-5 space-y-4">
|
||||
<USwitch
|
||||
v-model="bg_enable"
|
||||
class="w-full"
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
|
||||
<UButton
|
||||
v-if="bg_enable"
|
||||
color="info"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
icon="i-lucide-image-up"
|
||||
class="w-full justify-center"
|
||||
|
|
@ -121,18 +121,18 @@
|
|||
class="w-full"
|
||||
/>
|
||||
</UFormField>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UPageCard variant="subtle" class="w-full" :ui="settingsCardUi">
|
||||
<template #header>
|
||||
<div class="ytp-card w-full">
|
||||
<div class="p-4 sm:p-5 ytp-border-bottom-soft">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-lucide-monitor" class="size-4 text-toned" />
|
||||
<span class="text-sm font-semibold text-highlighted">Downloads</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #body>
|
||||
<div class="p-4 sm:p-5 space-y-4">
|
||||
<UFormField v-if="!modeOn" label="URL Separator" class="w-full" :ui="settingsFieldUi">
|
||||
<USelect
|
||||
v-model="separator"
|
||||
|
|
@ -179,18 +179,18 @@
|
|||
:label="show_popover ? 'Popover On' : 'Popover Off'"
|
||||
description="Show additional information over certain elements."
|
||||
/>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UPageCard variant="subtle" class="w-full" :ui="settingsCardUi">
|
||||
<template #header>
|
||||
<div class="ytp-card w-full">
|
||||
<div class="p-4 sm:p-5 ytp-border-bottom-soft">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-lucide-download" class="size-4 text-toned" />
|
||||
<span class="text-sm font-semibold text-highlighted">Queue</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #body>
|
||||
<div class="p-4 sm:p-5 space-y-4">
|
||||
<USwitch
|
||||
v-model="queue_auto_refresh"
|
||||
class="w-full"
|
||||
|
|
@ -219,18 +219,18 @@
|
|||
How often to refresh the queue (5-60 seconds). Lower values increase server load.
|
||||
</p>
|
||||
</UFormField>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UPageCard variant="subtle" class="w-full" :ui="settingsCardUi">
|
||||
<template #header>
|
||||
<div class="ytp-card w-full">
|
||||
<div class="p-4 sm:p-5 ytp-border-bottom-soft">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-lucide-bell" class="size-4 text-toned" />
|
||||
<span class="text-sm font-semibold text-highlighted">Notifications</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #body>
|
||||
<div class="p-4 sm:p-5 space-y-4">
|
||||
<USwitch
|
||||
v-model="allow_toasts"
|
||||
class="w-full"
|
||||
|
|
@ -288,8 +288,8 @@
|
|||
:ui="settingsSwitchUi"
|
||||
:label="toast_dismiss_on_click ? 'Dismiss on click' : 'Keep on click'"
|
||||
/>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</USlideover>
|
||||
|
|
@ -337,13 +337,6 @@ const queue_auto_refresh = useStorage<boolean>('queue_auto_refresh', true);
|
|||
const queue_auto_refresh_delay = useStorage<number>('queue_auto_refresh_delay', 10000);
|
||||
const isSecureContext = ref<boolean>(false);
|
||||
|
||||
const settingsCardUi = {
|
||||
root: 'w-full',
|
||||
container: 'w-full p-4 sm:p-5',
|
||||
wrapper: 'w-full items-stretch',
|
||||
body: 'w-full space-y-4',
|
||||
};
|
||||
|
||||
const settingsFieldUi = {
|
||||
root: 'w-full',
|
||||
container: 'mt-2 w-full',
|
||||
|
|
@ -415,6 +408,10 @@ const toastPositionItems: Array<{ label: string; value: toastPosition }> = [
|
|||
{ label: 'bottom-right', value: 'bottom-right' },
|
||||
];
|
||||
|
||||
const closeScrollLock = (): void => {
|
||||
document.body.classList.remove('settings-panel-open');
|
||||
};
|
||||
|
||||
const handleKeydown = (e: KeyboardEvent) => {
|
||||
if ('Escape' === e.key && props.isOpen) {
|
||||
e.preventDefault();
|
||||
|
|
@ -434,7 +431,10 @@ onMounted(async () => {
|
|||
document.addEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => document.removeEventListener('keydown', handleKeydown));
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('keydown', handleKeydown);
|
||||
closeScrollLock();
|
||||
});
|
||||
|
||||
const onNotificationTargetChange = async (): Promise<void> => {
|
||||
if ('browser' === toast_target.value) {
|
||||
|
|
@ -455,7 +455,7 @@ watch(
|
|||
draftMode.value = mode.value;
|
||||
document.body.classList.add('settings-panel-open');
|
||||
} else {
|
||||
document.body.classList.remove('settings-panel-open');
|
||||
closeScrollLock();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
<template>
|
||||
<div class="rounded-lg border border-default/70 bg-elevated/40 p-3">
|
||||
<div class="ytp-card bg-elevated/40 p-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="min-w-0 space-y-1">
|
||||
<p class="text-[11px] font-semibold uppercase tracking-[0.18em] text-toned">{{ label }}</p>
|
||||
<UTooltip v-if="tooltip" :text="tooltip" :content="{ side: 'left' }">
|
||||
<div class="flex items-baseline gap-1.5">
|
||||
<div :class="valueWrap ? 'space-y-1' : 'flex items-baseline gap-1.5'">
|
||||
<span class="text-sm font-semibold text-highlighted">{{ value }}</span>
|
||||
<span v-if="hint" class="truncate text-xs text-toned">{{ hint }}</span>
|
||||
</div>
|
||||
</UTooltip>
|
||||
<div v-else class="flex items-baseline gap-1.5">
|
||||
<span class="text-sm font-semibold text-highlighted">{{ value }}</span>
|
||||
<div v-else :class="valueWrap ? 'space-y-1' : 'flex items-baseline gap-1.5'">
|
||||
<span
|
||||
:class="[
|
||||
'text-sm font-semibold text-highlighted',
|
||||
valueWrap ? 'block wrap-break-word' : '',
|
||||
]"
|
||||
>{{ value }}</span
|
||||
>
|
||||
<span v-if="hint" class="truncate text-xs text-toned">{{ hint }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -37,8 +43,9 @@ const props = withDefaults(
|
|||
hint?: string;
|
||||
tooltip?: string;
|
||||
color?: Color;
|
||||
valueWrap?: boolean;
|
||||
}>(),
|
||||
{ color: 'primary', hint: '', tooltip: '' },
|
||||
{ color: 'primary', hint: '', tooltip: '', valueWrap: false },
|
||||
);
|
||||
|
||||
const TILE_BG: Record<Color, string> = {
|
||||
|
|
|
|||
|
|
@ -363,12 +363,10 @@
|
|||
</UButton>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
>
|
||||
<div class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface">
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-215 table-fixed w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-left [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@
|
|||
<span>Result:</span>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden rounded-lg border border-default bg-default">
|
||||
<div class="overflow-hidden ytp-frame">
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<pre
|
||||
class="min-w-0 max-w-full overflow-x-auto p-4 text-xs leading-6 text-default"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
<div
|
||||
v-if="showList && filteredOptions.length"
|
||||
class="absolute inset-x-0 top-full z-20 mt-1 overflow-hidden rounded-md border border-default bg-default shadow-lg"
|
||||
class="absolute inset-x-0 top-full z-20 mt-1 overflow-hidden rounded-md ytp-floating-surface"
|
||||
role="menu"
|
||||
>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
:label="nativeSubtitleTrack.name || 'Subtitles'"
|
||||
default
|
||||
:src="uri(nativeSubtitleTrack.url)"
|
||||
@load="handleNativeTrackLoad"
|
||||
/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
|
@ -170,16 +171,30 @@
|
|||
@click="forceSwitchToHls"
|
||||
/>
|
||||
</UTooltip>
|
||||
<UButton
|
||||
<USelect
|
||||
v-if="hasSubtitles"
|
||||
v-model="subtitleSelectValue"
|
||||
:items="subtitleSelectItems"
|
||||
value-key="value"
|
||||
label-key="label"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
trailing-icon=""
|
||||
:content="subtitleSelectContent"
|
||||
:portal="helpPortal"
|
||||
:ui="subtitleSelectUi"
|
||||
class="opacity-65 transition-opacity hover:opacity-100 focus-visible:opacity-100"
|
||||
:icon="subtitleEnabled ? 'i-lucide-captions' : 'i-lucide-captions-off'"
|
||||
:aria-label="subtitleEnabled ? 'Disable subtitles' : 'Enable subtitles'"
|
||||
@click="subtitleEnabled = !subtitleEnabled"
|
||||
:aria-label="subtitleButtonLabel"
|
||||
>
|
||||
<template #default>
|
||||
<span class="sr-only">{{ subtitleButtonLabel }}</span>
|
||||
<UIcon
|
||||
:name="subtitleEnabled ? 'i-lucide-captions' : 'i-lucide-captions-off'"
|
||||
class="size-4 shrink-0"
|
||||
/>
|
||||
</template>
|
||||
</USelect>
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
|
|
@ -429,9 +444,12 @@ const timeLabel = computed(() => {
|
|||
return `${currentLabel} / ${durationLabel}`;
|
||||
});
|
||||
const {
|
||||
subtitleTracks,
|
||||
subtitleLoading,
|
||||
subtitleLoadError,
|
||||
subtitleEnabled,
|
||||
selectedSubtitleTrack,
|
||||
selectedSubtitleTrackId,
|
||||
nativeSubtitleTrack,
|
||||
usesAssSubtitleTrack,
|
||||
hasSubtitles,
|
||||
|
|
@ -445,6 +463,40 @@ const {
|
|||
overlay: assOverlayElement,
|
||||
});
|
||||
|
||||
const SUBTITLE_OFF_VALUE = '__off__';
|
||||
const subtitleSelectContent = { align: 'end' as const } as const;
|
||||
const subtitleSelectUi = {
|
||||
base: 'size-8 min-h-8 min-w-8 justify-center rounded-md px-0',
|
||||
value: 'flex items-center justify-center px-0',
|
||||
trailing: 'hidden',
|
||||
leading: 'hidden',
|
||||
content: 'z-40 min-w-56 w-auto max-w-[min(24rem,calc(100vw-2rem))]',
|
||||
} as const;
|
||||
const subtitleSelectItems = computed(() => [
|
||||
{ label: 'Off', value: SUBTITLE_OFF_VALUE },
|
||||
...subtitleTracks.value.map((track) => ({ label: track.name, value: track.url })),
|
||||
]);
|
||||
const subtitleButtonLabel = computed(() => {
|
||||
if (!subtitleEnabled.value || !selectedSubtitleTrack.value) {
|
||||
return 'Subtitles off';
|
||||
}
|
||||
return selectedSubtitleTrack.value.name || 'Subtitles';
|
||||
});
|
||||
const subtitleSelectValue = computed<string>({
|
||||
get: () =>
|
||||
subtitleEnabled.value
|
||||
? (selectedSubtitleTrackId.value ?? SUBTITLE_OFF_VALUE)
|
||||
: SUBTITLE_OFF_VALUE,
|
||||
set: (value: string) => {
|
||||
if (SUBTITLE_OFF_VALUE === value) {
|
||||
subtitleEnabled.value = false;
|
||||
return;
|
||||
}
|
||||
selectedSubtitleTrackId.value = value;
|
||||
subtitleEnabled.value = true;
|
||||
},
|
||||
});
|
||||
|
||||
useHead(() => (title.value ? { title: formatPageTitle(`Playing: ${title.value}`) } : {}));
|
||||
|
||||
watch(
|
||||
|
|
@ -960,6 +1012,10 @@ function updateMediaSessionPosition(target: EventTarget | null) {
|
|||
}
|
||||
|
||||
async function restoreDefaultTextTrack() {
|
||||
if (true === destroyed.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el = videoElement.value;
|
||||
if (!el) {
|
||||
return;
|
||||
|
|
@ -971,36 +1027,6 @@ async function restoreDefaultTextTrack() {
|
|||
return;
|
||||
}
|
||||
|
||||
const firstTrack = tracksList[0] as TextTrack | undefined;
|
||||
const needsReload = firstTrack && (!firstTrack.cues || firstTrack.cues.length === 0);
|
||||
|
||||
if (needsReload) {
|
||||
const trackElements = el.querySelectorAll('track');
|
||||
|
||||
trackElements.forEach((trackEl, idx) => {
|
||||
const parent = trackEl.parentNode;
|
||||
const clone = trackEl.cloneNode(true) as HTMLTrackElement;
|
||||
trackEl.remove();
|
||||
setTimeout(() => {
|
||||
if (parent) {
|
||||
parent.appendChild(clone);
|
||||
clone.addEventListener(
|
||||
'load',
|
||||
() => {
|
||||
const trackObj = clone.track;
|
||||
if (trackObj) {
|
||||
trackObj.mode = idx === 0 && subtitleEnabled.value ? 'showing' : 'disabled';
|
||||
}
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
}, idx * 10);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracksList.length; i += 1) {
|
||||
const track = tracksList[i] as TextTrack | undefined;
|
||||
if (track) {
|
||||
|
|
@ -1010,18 +1036,34 @@ async function restoreDefaultTextTrack() {
|
|||
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
|
||||
for (let i = 0; i < tracksList.length; i += 1) {
|
||||
const track = tracksList[i] as TextTrack | undefined;
|
||||
if (el !== videoElement.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (false === subtitleEnabled.value || null === nativeSubtitleTrack.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeTracksList = el.textTracks;
|
||||
for (let i = 0; i < activeTracksList.length; i += 1) {
|
||||
const track = activeTracksList[i] as TextTrack | undefined;
|
||||
if (!track) {
|
||||
continue;
|
||||
}
|
||||
track.mode = i === 0 && subtitleEnabled.value ? 'showing' : 'disabled';
|
||||
track.mode = i === 0 ? 'showing' : 'disabled';
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to restore subtitle track state', error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleNativeTrackLoad() {
|
||||
if (true === destroyed.value) {
|
||||
return;
|
||||
}
|
||||
void restoreDefaultTextTrack();
|
||||
}
|
||||
|
||||
function applyMediaSessionMetadata() {
|
||||
if (false === 'mediaSession' in navigator) {
|
||||
return;
|
||||
|
|
@ -1242,6 +1284,10 @@ watch(subtitleEnabled, () => {
|
|||
void nextTick(() => restoreDefaultTextTrack());
|
||||
});
|
||||
|
||||
watch(selectedSubtitleTrack, () => {
|
||||
void nextTick(() => restoreDefaultTextTrack());
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
disableOpacity();
|
||||
isTouchDevice.value = window.matchMedia('(pointer: coarse)').matches;
|
||||
|
|
|
|||
|
|
@ -123,12 +123,10 @@
|
|||
<UBadge color="neutral" variant="soft" size="sm">{{ group.items.length }}</UBadge>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
>
|
||||
<div class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface">
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-180 w-full table-auto text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-left [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -188,10 +186,7 @@
|
|||
</section>
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
>
|
||||
<div v-else class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface">
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-215 w-full table-auto text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
|
|
|
|||
|
|
@ -11,12 +11,7 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<UPageCard
|
||||
variant="outline"
|
||||
:ui="pageCardUi"
|
||||
class="relative w-full max-w-xl overflow-hidden bg-default/95"
|
||||
>
|
||||
<template #body>
|
||||
<div class="ytp-panel p-0 relative w-full max-w-xl overflow-hidden bg-default/95">
|
||||
<div class="space-y-6 px-5 py-6 sm:px-7 sm:py-8" role="status" aria-live="polite">
|
||||
<div
|
||||
class="inline-flex items-center gap-2 rounded-full border border-default bg-elevated/60 px-3 py-1.5 text-xs font-semibold tracking-[0.22em] text-toned uppercase"
|
||||
|
|
@ -49,16 +44,8 @@
|
|||
description="The native app is closing background services and should exit shortly."
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const pageCardUi = {
|
||||
root: 'w-full bg-transparent shadow-2xl shadow-primary/5',
|
||||
container: 'w-full p-0',
|
||||
wrapper: 'w-full items-stretch',
|
||||
body: 'w-full p-0',
|
||||
};
|
||||
</script>
|
||||
<script setup lang="ts"></script>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,10 @@ export function usePlayerSubtitles(options: UsePlayerSubtitlesOptions) {
|
|||
const subtitleLoading = ref(false);
|
||||
const subtitleLoadError = ref('');
|
||||
const subtitleEnabled = ref(true);
|
||||
const selectedTrack = computed(() => tracks.value[0] || null);
|
||||
const selectedTrackId = ref<string | null>(null);
|
||||
const selectedTrack = computed(
|
||||
() => tracks.value.find((track) => track.url === selectedTrackId.value) || null,
|
||||
);
|
||||
const nativeSubtitleTrack = computed(() => {
|
||||
const track = selectedTrack.value;
|
||||
return subtitleEnabled.value && track?.renderer === 'native' ? track : null;
|
||||
|
|
@ -82,6 +85,7 @@ export function usePlayerSubtitles(options: UsePlayerSubtitlesOptions) {
|
|||
assRequestId += 1;
|
||||
destroyAssRenderer();
|
||||
tracks.value = [];
|
||||
selectedTrackId.value = null;
|
||||
subtitleLoadError.value = '';
|
||||
|
||||
if (!manifestUrl || !isVideo || !canPlay) {
|
||||
|
|
@ -104,6 +108,7 @@ export function usePlayerSubtitles(options: UsePlayerSubtitlesOptions) {
|
|||
|
||||
tracks.value = (payload as SubtitleManifestResponse).subtitles || [];
|
||||
if (tracks.value.length > 0) {
|
||||
selectedTrackId.value = tracks.value[0]?.url || null;
|
||||
subtitleEnabled.value = true;
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -212,6 +217,7 @@ export function usePlayerSubtitles(options: UsePlayerSubtitlesOptions) {
|
|||
subtitleLoadError,
|
||||
subtitleEnabled,
|
||||
selectedSubtitleTrack: selectedTrack,
|
||||
selectedSubtitleTrackId: selectedTrackId,
|
||||
nativeSubtitleTrack,
|
||||
usesAssSubtitleTrack: usesAssTrack,
|
||||
hasSubtitles,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<NuxtLayout name="error-layout">
|
||||
<div class="flex min-h-full flex-1 items-start justify-center py-4 sm:py-8">
|
||||
<UPageCard variant="outline" :ui="pageCardUi" class="w-full">
|
||||
<template #body>
|
||||
<div class="ytp-card p-0 w-full">
|
||||
<div class="space-y-6 px-4 py-5 sm:px-6 sm:py-6 lg:px-7">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div class="min-w-0 space-y-2">
|
||||
|
|
@ -17,8 +16,8 @@
|
|||
</h1>
|
||||
|
||||
<p class="max-w-3xl text-sm leading-6 text-toned">
|
||||
An unexpected error interrupted this view. You can go back home or retry the
|
||||
current route.
|
||||
An unexpected error interrupted this view. You can go back home or retry the current
|
||||
route.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -65,8 +64,7 @@
|
|||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
|
@ -80,13 +78,6 @@ const props = defineProps<{
|
|||
|
||||
const showStacks = ref(false);
|
||||
|
||||
const pageCardUi = {
|
||||
root: 'w-full bg-default',
|
||||
container: 'w-full p-0',
|
||||
wrapper: 'w-full items-stretch',
|
||||
body: 'w-full p-0',
|
||||
};
|
||||
|
||||
const handleRetry = async (): Promise<void> => {
|
||||
await clearError({ redirect: useRoute().fullPath });
|
||||
};
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@
|
|||
|
||||
<UButton
|
||||
v-if="true === config.app.is_native"
|
||||
color="error"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-lucide-power"
|
||||
|
|
|
|||
|
|
@ -6,19 +6,14 @@
|
|||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-start gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 flex-1 space-y-3">
|
||||
<nav
|
||||
aria-label="Breadcrumb"
|
||||
class="flex min-w-0 flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<nav aria-label="Breadcrumb" class="min-w-0 ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -50,7 +45,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
color="neutral"
|
||||
:variant="show_filter ? 'soft' : 'outline'"
|
||||
|
|
@ -123,7 +118,7 @@
|
|||
|
||||
<div
|
||||
v-if="controlEnabled && hasItems"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -169,11 +164,11 @@
|
|||
|
||||
<div
|
||||
v-if="contentStyle === 'list' && hasItems"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-360 w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -320,18 +315,12 @@
|
|||
</div>
|
||||
|
||||
<div v-else-if="hasItems" class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<UCard
|
||||
<div
|
||||
v-for="item in filteredItems"
|
||||
:key="item.path"
|
||||
class="flex h-full flex-col"
|
||||
:ui="{
|
||||
root: 'bg-default border border-default',
|
||||
header: 'p-4 pb-3',
|
||||
body: 'flex flex-1 flex-col gap-4 p-4 pt-0',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
}"
|
||||
class="ytp-card flex h-full flex-col overflow-hidden"
|
||||
>
|
||||
<template #header>
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-start gap-2">
|
||||
|
|
@ -364,8 +353,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<div class="flex flex-wrap gap-2 text-sm *:min-w-32 *:flex-1">
|
||||
<div
|
||||
class="min-w-0 rounded-md border border-default bg-muted/20 px-3 py-2 text-center text-default"
|
||||
|
|
@ -387,8 +377,9 @@
|
|||
</UTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="controlEnabled" #footer>
|
||||
<div v-if="controlEnabled" class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
v-if="item.type === 'file'"
|
||||
|
|
@ -437,8 +428,8 @@
|
|||
Delete
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="localSearch && !hasItems && !isLoading" class="space-y-3">
|
||||
|
|
@ -610,7 +601,6 @@ const bulkActionGroups = computed<DropdownMenuItem[][]>(() => [
|
|||
{
|
||||
label: 'Delete Selected',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error',
|
||||
disabled: !hasSelected.value || isLoading.value,
|
||||
onSelect: () => void handleDeleteSelected(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="logs.length > 0"
|
||||
color="neutral"
|
||||
|
|
@ -64,89 +60,108 @@
|
|||
/>
|
||||
|
||||
<template v-else>
|
||||
<div v-if="filteredLogs.length > 0" class="space-y-4">
|
||||
<UPageCard v-for="log in filteredLogs" :key="log.tag" variant="outline" :ui="pageCardUi">
|
||||
<template #body>
|
||||
<div class="space-y-4">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<div
|
||||
class="inline-flex items-center gap-2 text-base font-semibold text-highlighted"
|
||||
<div v-if="filteredLogs.length > 0" class="space-y-5">
|
||||
<section v-for="log in filteredLogs" :key="log.tag" class="space-y-3">
|
||||
<section class="space-y-3">
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full flex-wrap items-center justify-between gap-3 text-left"
|
||||
@click="toggleRelease(log.tag)"
|
||||
>
|
||||
<UIcon name="i-lucide-git-branch" class="size-4 text-toned" />
|
||||
<span>{{ log.tag }}</span>
|
||||
</div>
|
||||
|
||||
<UBadge v-if="isInstalled(log)" color="success" variant="soft" size="sm">
|
||||
Installed
|
||||
</UBadge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-end gap-2 text-xs text-toned">
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-md border border-default px-2 py-1"
|
||||
>
|
||||
<UIcon name="i-lucide-list" class="size-3.5" />
|
||||
<span>{{ log.commits?.length || 0 }} commits</span>
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span class="ytp-section-icon">
|
||||
<UIcon
|
||||
:name="isInstalled(log) ? 'i-lucide-badge-check' : 'i-lucide-git-branch'"
|
||||
class="size-5"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-1">
|
||||
<h2 class="truncate text-base font-semibold text-highlighted">
|
||||
{{ log.tag }}
|
||||
</h2>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-toned">
|
||||
<UTooltip v-if="log.date" :text="`Release Date: ${log.date}`">
|
||||
<span
|
||||
class="inline-flex cursor-help items-center gap-1 rounded-md border border-default px-2 py-1"
|
||||
>
|
||||
<UIcon name="i-lucide-calendar-days" class="size-3.5" />
|
||||
<span class="inline-flex cursor-help items-center gap-1.5">
|
||||
<UIcon name="i-lucide-calendar-days" class="size-3.5 text-muted" />
|
||||
<span>{{ moment(log.date).fromNow() }}</span>
|
||||
</span>
|
||||
</UTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3 border-t border-default pt-4">
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<UBadge
|
||||
v-if="isInstalled(log)"
|
||||
color="success"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
icon="i-lucide-check"
|
||||
>
|
||||
Installed
|
||||
</UBadge>
|
||||
|
||||
<UBadge color="neutral" variant="outline" size="sm" icon="i-lucide-list">
|
||||
{{ log.commits?.length || 0 }} commits
|
||||
</UBadge>
|
||||
|
||||
<UIcon
|
||||
name="i-lucide-chevron-right"
|
||||
:class="[
|
||||
'size-4 text-toned transition-transform',
|
||||
isReleaseOpen(log.tag) ? 'rotate-90' : '',
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div v-if="isReleaseOpen(log.tag)" class="space-y-2">
|
||||
<article
|
||||
v-for="commit in log.commits"
|
||||
:key="commit.sha"
|
||||
class="flex flex-col gap-2 rounded-md border border-default bg-muted/20 px-3 py-3"
|
||||
class="rounded-md border border-default bg-elevated/20 px-3 py-3 transition-colors hover:bg-elevated/35"
|
||||
>
|
||||
<div class="min-w-0">
|
||||
<NuxtLink
|
||||
:to="`${REPO}/commit/${commit.full_sha}`"
|
||||
target="_blank"
|
||||
class="block min-w-0 text-sm text-default hover:underline"
|
||||
class="block min-w-0 text-sm font-medium leading-5 text-highlighted transition hover:text-primary"
|
||||
>
|
||||
<span class="font-semibold text-highlighted">
|
||||
{{ ucFirst(commit.message).replace(/\.$/, '') }}.
|
||||
</span>
|
||||
{{ formatCommitMessage(commit.message) }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs text-toned">
|
||||
<div class="mt-2 flex flex-wrap items-center gap-2 text-xs text-toned">
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-md border border-default px-2 py-1"
|
||||
class="inline-flex items-center gap-1 rounded-sm border border-default px-2 py-1"
|
||||
>
|
||||
<UIcon name="i-lucide-user" class="size-3.5" />
|
||||
{{ commit.author }}
|
||||
<UIcon name="i-lucide-user" class="size-3.5 text-muted" />
|
||||
<span>{{ commit.author }}</span>
|
||||
</span>
|
||||
|
||||
<UTooltip :text="`Date: ${commit.date}`">
|
||||
<span
|
||||
class="inline-flex cursor-help items-center gap-1 rounded-md border border-default px-2 py-1"
|
||||
<time
|
||||
class="inline-flex cursor-help items-center gap-1 rounded-sm border border-default px-2 py-1"
|
||||
>
|
||||
<UIcon name="i-lucide-clock-3" class="size-3.5" />
|
||||
{{ moment(commit.date).fromNow() }}
|
||||
</span>
|
||||
<UIcon name="i-lucide-clock-3" class="size-3.5 text-muted" />
|
||||
<span>{{ moment(commit.date).fromNow() }}</span>
|
||||
</time>
|
||||
</UTooltip>
|
||||
|
||||
<UTooltip :text="`SHA: ${commit.full_sha}`">
|
||||
<span
|
||||
class="inline-flex cursor-help items-center gap-1 rounded-md border border-default px-2 py-1 font-medium"
|
||||
<NuxtLink
|
||||
:to="`${REPO}/commit/${commit.full_sha}`"
|
||||
target="_blank"
|
||||
class="inline-flex items-center gap-1 rounded-sm border border-default px-2 py-1 font-mono transition hover:border-primary hover:text-primary"
|
||||
>
|
||||
<UIcon name="i-lucide-git-commit-horizontal" class="size-3.5" />
|
||||
{{ commit.sha }}
|
||||
</span>
|
||||
<UIcon name="i-lucide-git-commit-horizontal" class="size-3.5 text-muted" />
|
||||
<span>{{ commit.sha }}</span>
|
||||
</NuxtLink>
|
||||
</UTooltip>
|
||||
|
||||
<span
|
||||
v-if="commit.full_sha === app_sha"
|
||||
class="inline-flex items-center gap-1 rounded-md border border-default px-2 py-1 font-medium"
|
||||
class="inline-flex items-center gap-1 rounded-sm border border-default px-2 py-1 font-medium"
|
||||
>
|
||||
<UIcon name="i-lucide-check" class="size-3.5 text-success" />
|
||||
<span>Installed</span>
|
||||
|
|
@ -154,9 +169,8 @@
|
|||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-3">
|
||||
|
|
@ -206,13 +220,7 @@ const isLoading = ref(true);
|
|||
const query = ref('');
|
||||
const toggleFilter = ref(false);
|
||||
const latestOnly = useStorage<boolean>('changelog_latest_only', true);
|
||||
|
||||
const pageCardUi = {
|
||||
root: 'w-full bg-default',
|
||||
container: 'w-full p-4 sm:p-5',
|
||||
wrapper: 'w-full items-stretch',
|
||||
body: 'w-full',
|
||||
};
|
||||
const openReleases = useStorage<string[]>('changelog_open_releases', []);
|
||||
|
||||
watch(toggleFilter, () => {
|
||||
if (!toggleFilter.value) {
|
||||
|
|
@ -250,6 +258,27 @@ const filteredLogs = computed<changelogs>(() => {
|
|||
.filter((log): log is changeset => log !== null);
|
||||
});
|
||||
|
||||
watch(filteredLogs, (items) => {
|
||||
if (openReleases.value.length > 0 || items.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
openReleases.value = items.slice(0, 3).map((log) => log.tag);
|
||||
});
|
||||
|
||||
const isReleaseOpen = (tag: string): boolean => openReleases.value.includes(tag);
|
||||
|
||||
const toggleRelease = (tag: string): void => {
|
||||
if (isReleaseOpen(tag)) {
|
||||
openReleases.value = openReleases.value.filter((entry) => entry !== tag);
|
||||
return;
|
||||
}
|
||||
|
||||
openReleases.value = [...openReleases.value, tag];
|
||||
};
|
||||
|
||||
const formatCommitMessage = (message: string): string => `${ucFirst(message).replace(/\.$/, '')}.`;
|
||||
|
||||
const loadContent = async (): Promise<void> => {
|
||||
if (app_version.value === '' || logs.value.length > 0) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="items.length > 0"
|
||||
color="neutral"
|
||||
|
|
@ -84,7 +80,7 @@
|
|||
|
||||
<div
|
||||
v-if="!isLoading && filteredItems.length > 0"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -129,11 +125,11 @@
|
|||
|
||||
<div
|
||||
v-if="contentStyle === 'list' && filteredItems.length > 0"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-235 w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -238,16 +234,8 @@
|
|||
|
||||
<div v-else-if="filteredItems.length > 0" class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div v-for="cond in filteredItems" :key="cond.id" class="min-w-0 w-full max-w-full">
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col"
|
||||
:ui="{
|
||||
root: 'bg-default border border-default',
|
||||
header: 'p-4 pb-3',
|
||||
body: 'flex flex-1 flex-col gap-4 p-4 pt-0',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex min-w-0 items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-start gap-2">
|
||||
|
|
@ -283,8 +271,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<div class="space-y-2 text-sm text-default">
|
||||
<div class="flex flex-wrap gap-2 text-xs text-toned *:min-w-32 *:flex-1">
|
||||
<button
|
||||
|
|
@ -319,7 +308,9 @@
|
|||
<UIcon name="i-lucide-filter" class="mt-0.5 size-4 shrink-0 text-toned" />
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="text-xs font-medium text-toned">Filter</div>
|
||||
<span :class="['block', expandClass(cond.id, 'filter')]">{{ cond.filter }}</span>
|
||||
<span :class="['block', expandClass(cond.id, 'filter')]">{{
|
||||
cond.filter
|
||||
}}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
|
@ -375,8 +366,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -398,8 +390,8 @@
|
|||
Delete
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -566,7 +558,6 @@ const bulkActionGroups = computed<DropdownMenuItem[][]>(() => [
|
|||
{
|
||||
label: 'Remove Selected',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error',
|
||||
disabled: !hasSelected.value || massDelete.value,
|
||||
onSelect: () => void deleteSelected(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="flex min-h-0 w-full min-w-0 max-w-full flex-1 flex-col gap-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-start gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading items-start">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -45,19 +41,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<UPageCard variant="naked" :ui="pageCardUi" class="flex min-h-0 flex-1">
|
||||
<template #body>
|
||||
<div class="ytp-card flex min-h-0 flex-1 p-0 sm:p-0 flex-col">
|
||||
<div class="flex min-h-0 flex-1 flex-col gap-4">
|
||||
<div
|
||||
class="flex min-h-72 min-w-0 flex-1 overflow-hidden rounded-sm border border-default bg-neutral-950/95 shadow-sm"
|
||||
>
|
||||
<div
|
||||
ref="terminal_window"
|
||||
class="terminal-host h-full min-h-0 w-full overflow-hidden"
|
||||
/>
|
||||
<div ref="terminal_window" class="terminal-host h-full min-h-0 w-full overflow-hidden" />
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-default bg-default shadow-sm">
|
||||
<div class="ytp-card shadow-sm">
|
||||
<div
|
||||
class="flex flex-col gap-3 border-b border-default bg-muted/10 px-4 py-3 lg:flex-row lg:items-start lg:justify-between"
|
||||
>
|
||||
|
|
@ -172,9 +164,7 @@
|
|||
:ui="historyCardUi"
|
||||
>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div
|
||||
class="flex items-center gap-2 text-sm font-semibold text-highlighted"
|
||||
>
|
||||
<div class="flex items-center gap-2 text-sm font-semibold text-highlighted">
|
||||
<UIcon name="i-lucide-history" class="size-4 text-toned" />
|
||||
<span>Recent runs</span>
|
||||
</div>
|
||||
|
|
@ -201,7 +191,7 @@
|
|||
|
||||
<div
|
||||
v-else
|
||||
class="max-h-96 w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="max-h-96 w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-auto overscroll-contain">
|
||||
<table class="w-full text-sm">
|
||||
|
|
@ -261,9 +251,7 @@
|
|||
.format('YYYY-M-DD H:mm Z')
|
||||
"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 cursor-help"
|
||||
>
|
||||
<span class="inline-flex items-center gap-1.5 cursor-help">
|
||||
<UIcon name="i-lucide-clock-3" class="size-3.5" />
|
||||
<time
|
||||
:datetime="
|
||||
|
|
@ -370,8 +358,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
|
|
@ -436,13 +423,6 @@ const commandInput = ref<InstanceType<typeof InputAutocomplete> | null>(null);
|
|||
const commandTextarea = ref<InstanceType<typeof TextareaAutocomplete> | null>(null);
|
||||
const storedCommand = useStorage<string>('console_command', '');
|
||||
|
||||
const pageCardUi = {
|
||||
root: 'flex min-h-0 flex-1 w-full bg-transparent',
|
||||
container: 'flex min-h-0 flex-1 w-full p-0 sm:p-0',
|
||||
wrapper: 'flex min-h-0 flex-1 w-full items-stretch',
|
||||
body: 'flex min-h-0 flex-1 w-full flex-col',
|
||||
};
|
||||
|
||||
const historyCardUi = {
|
||||
body: 'space-y-3 p-4',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-start gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading items-start">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
|
|
@ -47,10 +43,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!report && isLoading"
|
||||
class="flex min-h-72 items-center justify-center rounded-md border border-default bg-default/90"
|
||||
>
|
||||
<div v-if="!report && isLoading" class="ytp-card flex min-h-72 items-center justify-center">
|
||||
<div class="flex flex-col items-center gap-3 text-center text-toned">
|
||||
<UIcon name="i-lucide-loader-circle" class="size-10 animate-spin text-info" />
|
||||
<div class="space-y-1">
|
||||
|
|
@ -86,28 +79,15 @@
|
|||
</div>
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<article
|
||||
<StatCard
|
||||
v-for="item in summaryCards"
|
||||
:key="item.label"
|
||||
class="rounded-md border border-default bg-default px-3 py-3 shadow-sm"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-toned">
|
||||
{{ item.label }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-toned">{{ item.description }}</p>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-default bg-default"
|
||||
>
|
||||
<UIcon :name="item.icon" class="size-4 text-toned" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p :class="['mt-4 text-2xl font-semibold', item.valueClass]">{{ item.value }}</p>
|
||||
</article>
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:hint="item.description"
|
||||
:icon="item.icon"
|
||||
:color="item.color"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -118,28 +98,16 @@
|
|||
</div>
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
||||
<article
|
||||
<StatCard
|
||||
v-for="row in runtimeRows"
|
||||
:key="row.label"
|
||||
class="rounded-md border border-default bg-default px-3 py-3 shadow-sm"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<p class="text-xs font-medium uppercase tracking-wide text-toned">
|
||||
{{ row.label }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-toned">{{ row.description }}</p>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-default bg-default"
|
||||
>
|
||||
<UIcon :name="row.icon" class="size-4 text-toned" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 wrap-break-word text-sm font-semibold text-default">{{ row.value }}</p>
|
||||
</article>
|
||||
:label="row.label"
|
||||
:value="row.value"
|
||||
:hint="row.description"
|
||||
:icon="row.icon"
|
||||
color="neutral"
|
||||
value-wrap
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -155,11 +123,7 @@
|
|||
</div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-2 2xl:grid-cols-3">
|
||||
<article
|
||||
v-for="item in section.items"
|
||||
:key="item.id"
|
||||
class="rounded-md border border-default bg-default px-4 py-4 shadow-sm"
|
||||
>
|
||||
<article v-for="item in section.items" :key="item.id" class="ytp-card-padded shadow-sm">
|
||||
<div class="min-w-0 space-y-3">
|
||||
<div class="space-y-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
|
|
@ -198,6 +162,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import moment from 'moment';
|
||||
import StatCard from '~/components/StatCard.vue';
|
||||
import type { DiagnosticCheck, DiagnosticStatus } from '~/types/diagnostics';
|
||||
import { requirePageShell } from '~/utils/topLevelNavigation';
|
||||
import { copyText } from '~/utils';
|
||||
|
|
@ -207,7 +172,7 @@ type SummaryCard = {
|
|||
description: string;
|
||||
value: number;
|
||||
icon: string;
|
||||
valueClass: string;
|
||||
color: 'success' | 'error' | 'warning' | 'neutral';
|
||||
};
|
||||
|
||||
type DetailRow = {
|
||||
|
|
@ -295,28 +260,28 @@ const summaryCards = computed<Array<SummaryCard>>(() => {
|
|||
description: 'Checks that passed.',
|
||||
value: current.summary.pass,
|
||||
icon: 'i-lucide-badge-check',
|
||||
valueClass: current.summary.pass > 0 ? 'text-success' : 'text-default',
|
||||
color: current.summary.pass > 0 ? 'success' : 'neutral',
|
||||
},
|
||||
{
|
||||
label: 'Required fails',
|
||||
description: 'Items that block core use.',
|
||||
value: current.summary.required_failed,
|
||||
icon: 'i-lucide-octagon-alert',
|
||||
valueClass: current.summary.required_failed > 0 ? 'text-error' : 'text-default',
|
||||
color: current.summary.required_failed > 0 ? 'error' : 'neutral',
|
||||
},
|
||||
{
|
||||
label: 'Warnings',
|
||||
description: 'Optional or incomplete items.',
|
||||
value: current.summary.warn,
|
||||
icon: 'i-lucide-triangle-alert',
|
||||
valueClass: current.summary.warn > 0 ? 'text-warning' : 'text-default',
|
||||
color: current.summary.warn > 0 ? 'warning' : 'neutral',
|
||||
},
|
||||
{
|
||||
label: 'Skipped',
|
||||
description: 'Not configured or not needed.',
|
||||
value: current.summary.skip,
|
||||
icon: 'i-lucide-minus',
|
||||
valueClass: current.summary.skip > 0 ? 'text-toned' : 'text-default',
|
||||
color: 'neutral',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="items.length > 0"
|
||||
color="neutral"
|
||||
|
|
@ -84,7 +80,7 @@
|
|||
|
||||
<div
|
||||
v-if="!isLoading && filteredItems.length > 0"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -129,11 +125,11 @@
|
|||
|
||||
<div
|
||||
v-if="contentStyle === 'list' && filteredItems.length > 0"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-235 w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -237,16 +233,8 @@
|
|||
|
||||
<div v-else-if="filteredItems.length > 0" class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div v-for="field in filteredItems" :key="field.id" class="min-w-0 w-full max-w-full">
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col"
|
||||
:ui="{
|
||||
root: 'bg-default border border-default',
|
||||
header: 'p-4 pb-3',
|
||||
body: 'flex flex-1 flex-col gap-4 p-4 pt-0',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex min-w-0 items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-start gap-2">
|
||||
|
|
@ -282,8 +270,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<div class="space-y-2 text-sm text-default">
|
||||
<div class="flex flex-wrap gap-2 text-xs text-toned *:min-w-32 *:flex-1">
|
||||
<span
|
||||
|
|
@ -310,7 +299,9 @@
|
|||
<UIcon name="i-lucide-terminal" class="mt-0.5 size-4 shrink-0 text-toned" />
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="text-xs font-medium text-toned">Associated option</div>
|
||||
<span :class="['block', expandClass(field.id, 'field')]">{{ field.field }}</span>
|
||||
<span :class="['block', expandClass(field.id, 'field')]">{{
|
||||
field.field
|
||||
}}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
|
@ -333,8 +324,9 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -356,8 +348,8 @@
|
|||
Delete
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -491,7 +483,6 @@ const bulkActionGroups = computed<DropdownMenuItem[][]>(() => [
|
|||
{
|
||||
label: 'Remove Selected',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error',
|
||||
disabled: !hasSelected.value || massDelete.value,
|
||||
onSelect: () => void deleteSelected(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -20,42 +16,24 @@
|
|||
<p class="max-w-3xl text-sm text-toned">{{ pageShell.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap justify-end gap-2 xl:justify-end">
|
||||
<UButton
|
||||
v-for="entry in docsEntries"
|
||||
:key="entry.id"
|
||||
:to="entry.route"
|
||||
:color="entry.file === docEntry.file ? 'primary' : 'neutral'"
|
||||
:variant="entry.file === docEntry.file ? 'solid' : 'outline'"
|
||||
size="sm"
|
||||
:icon="entry.icon"
|
||||
>
|
||||
{{ entry.navLabel }}
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UPageCard variant="outline" :ui="pageCardUi">
|
||||
<template #body>
|
||||
<div class="px-4 py-5 sm:px-6 sm:py-6 lg:px-7">
|
||||
<div class="ytp-card p-0 overflow-hidden">
|
||||
<div class="min-w-0 max-w-full px-4 py-5 sm:px-6 sm:py-6 lg:px-7">
|
||||
<Markdown :file="`/api/docs/${docEntry.file}`" />
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Markdown from '~/components/Markdown.vue';
|
||||
import { DOCS_ENTRIES, getDocsEntryBySlug } from '~/composables/useDocs';
|
||||
import { getDocsEntryBySlug } from '~/composables/useDocs';
|
||||
import { formatPageTitle } from '~/utils';
|
||||
import { requirePageShell } from '~/utils/topLevelNavigation';
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const docsEntries = DOCS_ENTRIES;
|
||||
|
||||
const docEntry = computed(() => {
|
||||
const entry = getDocsEntryBySlug(route.params.slug as string | string[] | undefined);
|
||||
|
||||
|
|
@ -74,11 +52,4 @@ const pageShell = computed(() => requirePageShell(docEntry.value.id));
|
|||
useHead(() => ({
|
||||
title: formatPageTitle(docEntry.value.title),
|
||||
}));
|
||||
|
||||
const pageCardUi = {
|
||||
root: 'w-full bg-default',
|
||||
container: 'w-full p-0',
|
||||
wrapper: 'w-full items-stretch',
|
||||
body: 'w-full p-0',
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -6,18 +6,14 @@
|
|||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -27,7 +23,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
|
|
@ -102,7 +98,7 @@
|
|||
<div class="w-full min-w-0 max-w-full space-y-4">
|
||||
<div
|
||||
v-if="hasItems"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -143,11 +139,11 @@
|
|||
|
||||
<div
|
||||
v-if="'list' === contentStyle && hasItems"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-210 table-fixed w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -395,16 +391,8 @@
|
|||
:min-height="showThumbnails ? 410 : 210"
|
||||
class="min-h-0 min-w-0 w-full max-w-full"
|
||||
>
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden"
|
||||
:ui="{
|
||||
body: 'flex flex-1 flex-col gap-4 p-4',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
header: 'p-4 pb-3',
|
||||
root: 'bg-default border border-default',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="ytp-border-bottom-soft p-4 pb-3">
|
||||
<div class="flex min-w-0 flex-wrap items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<UTooltip :text="item.title">
|
||||
|
|
@ -474,8 +462,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4">
|
||||
<div
|
||||
v-if="showThumbnails"
|
||||
class="-mx-4 -mt-4 overflow-hidden border-b border-default bg-muted/20"
|
||||
|
|
@ -626,7 +615,7 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
v-if="showRetryAction(item)"
|
||||
|
|
@ -685,8 +674,9 @@
|
|||
</UButton>
|
||||
</UDropdownMenu>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LateLoader>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
color="neutral"
|
||||
:variant="toggleFilter ? 'soft' : 'outline'"
|
||||
|
|
@ -56,7 +52,7 @@
|
|||
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:variant="config.showForm ? 'soft' : 'outline'"
|
||||
size="sm"
|
||||
icon="i-lucide-plus"
|
||||
@click="config.showForm = !config.showForm"
|
||||
|
|
@ -109,7 +105,7 @@
|
|||
<div class="w-full min-w-0 max-w-full space-y-4">
|
||||
<div v-if="!socket.isConnected" class="flex justify-end">
|
||||
<UButton
|
||||
color="info"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
icon="i-lucide-refresh-cw"
|
||||
|
|
@ -122,7 +118,7 @@
|
|||
|
||||
<div
|
||||
v-if="hasItems"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -175,11 +171,11 @@
|
|||
|
||||
<div
|
||||
v-if="'list' === contentStyle && hasItems"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-210 table-fixed w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -409,16 +405,8 @@
|
|||
:min-height="showThumbnails ? 475 : 265"
|
||||
class="min-h-0 min-w-0 w-full max-w-full"
|
||||
>
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden"
|
||||
:ui="{
|
||||
body: 'flex flex-1 flex-col gap-4 p-4',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
header: 'p-4 pb-3',
|
||||
root: 'bg-default border border-default',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex min-w-0 flex-wrap items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<UTooltip :text="item.title">
|
||||
|
|
@ -494,8 +482,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4">
|
||||
<div
|
||||
v-if="showThumbnails"
|
||||
class="-mx-4 -mt-4 overflow-hidden border-b border-default bg-muted/20"
|
||||
|
|
@ -572,7 +561,10 @@
|
|||
@click="toggleExpand(item._id, 'preset')"
|
||||
>
|
||||
<span class="inline-flex w-full items-center justify-center gap-2">
|
||||
<UIcon name="i-lucide-sliders-horizontal" class="size-4 shrink-0 text-toned" />
|
||||
<UIcon
|
||||
name="i-lucide-sliders-horizontal"
|
||||
class="size-4 shrink-0 text-toned"
|
||||
/>
|
||||
<span :class="['min-w-0 text-center', expandClass(item._id, 'preset')]">
|
||||
{{ item.preset }}
|
||||
</span>
|
||||
|
|
@ -610,8 +602,9 @@
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -657,8 +650,8 @@
|
|||
</UButton>
|
||||
</UDropdownMenu>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</LateLoader>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@
|
|||
|
||||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-start gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading items-start">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon
|
||||
name="i-lucide-file-text"
|
||||
:class="[
|
||||
|
|
@ -34,9 +32,7 @@
|
|||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -46,7 +42,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="!autoScroll"
|
||||
color="neutral"
|
||||
|
|
@ -123,9 +119,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<UPageCard variant="naked" :ui="pageCardUi">
|
||||
<template #body>
|
||||
<div class="overflow-hidden rounded-sm border border-default bg-default shadow-sm">
|
||||
<div class="ytp-card min-w-0 max-w-full p-4 sm:p-5 overflow-hidden">
|
||||
<div class="ytp-frame overflow-hidden shadow-sm">
|
||||
<div
|
||||
ref="logContainer"
|
||||
class="w-full min-w-0 max-w-full min-h-[55vh] max-h-[60vh] overflow-y-auto overflow-x-hidden bg-transparent font-mono text-sm text-default overscroll-x-contain"
|
||||
|
|
@ -249,8 +244,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
|
||||
<LogDetailModal v-model="detailsOpen" :log="selectedLog" />
|
||||
</main>
|
||||
|
|
@ -316,13 +310,6 @@ const reachedEnd = ref(false);
|
|||
const detailsOpen = ref(false);
|
||||
const expandedRows = ref<Set<string>>(new Set());
|
||||
|
||||
const pageCardUi = {
|
||||
root: 'w-full min-w-0 max-w-full bg-transparent',
|
||||
container: 'w-full min-w-0 max-w-full p-4 sm:p-5',
|
||||
wrapper: 'w-full min-w-0 items-stretch',
|
||||
body: 'w-full min-w-0 max-w-full overflow-hidden',
|
||||
};
|
||||
|
||||
const query = ref<string>(
|
||||
(() => {
|
||||
const filter = route.query.filter ?? '';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="notifications.length > 0"
|
||||
color="neutral"
|
||||
|
|
@ -97,7 +93,7 @@
|
|||
|
||||
<div
|
||||
v-if="!isLoading && filteredTargets.length > 0"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -142,11 +138,11 @@
|
|||
|
||||
<div
|
||||
v-if="contentStyle === 'list' && filteredTargets.length > 0"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-235 w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -275,16 +271,8 @@
|
|||
|
||||
<div v-else-if="filteredTargets.length > 0" class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div v-for="item in filteredTargets" :key="item.id" class="min-w-0 w-full max-w-full">
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col"
|
||||
:ui="{
|
||||
root: 'bg-default border border-default',
|
||||
header: 'p-4 pb-3',
|
||||
body: 'flex flex-1 flex-col gap-4 p-4 pt-0',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex min-w-0 items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-start gap-2">
|
||||
|
|
@ -323,8 +311,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<div class="space-y-2 text-sm text-default">
|
||||
<div class="flex flex-wrap gap-2 text-xs text-toned *:min-w-32 *:flex-1">
|
||||
<button
|
||||
|
|
@ -434,8 +423,9 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -457,8 +447,8 @@
|
|||
Delete
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -657,7 +647,6 @@ const bulkActionGroups = computed<DropdownMenuItem[][]>(() => [
|
|||
{
|
||||
label: 'Remove Selected',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error',
|
||||
disabled: !hasSelected.value || massDelete.value,
|
||||
onSelect: () => void deleteSelected(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="presets.length > 0"
|
||||
color="neutral"
|
||||
|
|
@ -83,7 +79,7 @@
|
|||
|
||||
<div
|
||||
v-if="!isLoading && filteredPresets.length > 0"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -128,11 +124,11 @@
|
|||
|
||||
<div
|
||||
v-if="contentStyle === 'list' && filteredPresets.length > 0"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-200 w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -236,16 +232,8 @@
|
|||
|
||||
<div v-else-if="filteredPresets.length > 0" class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
<div v-for="item in filteredPresets" :key="item.id" class="min-w-0 w-full max-w-full">
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col"
|
||||
:ui="{
|
||||
root: 'bg-default border border-default',
|
||||
header: 'p-4 pb-3',
|
||||
body: 'flex flex-1 flex-col gap-4 p-4 pt-0',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex min-w-0 items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-start gap-2">
|
||||
|
|
@ -283,8 +271,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<div class="space-y-2 text-sm text-default">
|
||||
<div class="flex flex-wrap gap-2 text-xs text-toned *:min-w-32 *:flex-1">
|
||||
<span
|
||||
|
|
@ -367,8 +356,9 @@
|
|||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -390,8 +380,8 @@
|
|||
Delete
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -540,7 +530,6 @@ const bulkActionGroups = computed<DropdownMenuItem[][]>(() => [
|
|||
{
|
||||
label: 'Remove Selected',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error',
|
||||
disabled: !hasSelected.value || massDelete.value,
|
||||
onSelect: () => void deleteSelected(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<AppRoot mode="simple" @ready="init" v-slot="{ openSettings }">
|
||||
<div class="shell-stage flex min-h-screen flex-col bg-default/95 backdrop-blur-sm">
|
||||
<div class="shell-stage shell-surface flex min-h-screen flex-col">
|
||||
<ConnectionBanner />
|
||||
|
||||
<div
|
||||
|
|
@ -14,8 +15,7 @@
|
|||
|
||||
<div class="transition-transform duration-300">
|
||||
<div class="mx-auto w-full transition-all duration-300" :class="formContainerClass">
|
||||
<UPageCard variant="outline" :ui="formCardUi">
|
||||
<template #body>
|
||||
<div class="ytp-card p-4 sm:p-5">
|
||||
<form autocomplete="off" class="space-y-4" @submit.prevent="addDownload">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0 space-y-1">
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
<div class="flex shrink-0 items-center gap-1 sm:gap-2">
|
||||
<UTooltip v-if="!socketStore.isConnected" text="Reload queue">
|
||||
<UButton
|
||||
color="info"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
icon="i-lucide-refresh-cw"
|
||||
|
|
@ -64,8 +64,8 @@
|
|||
<UTooltip :text="showExtras ? 'Hide extra options' : 'Show extra options'">
|
||||
<UButton
|
||||
type="button"
|
||||
color="info"
|
||||
variant="outline"
|
||||
color="neutral"
|
||||
:variant="showExtras ? 'soft' : 'outline'"
|
||||
size="lg"
|
||||
:icon="showExtras ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'"
|
||||
class="shrink-0 justify-center w-12"
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
</UButton>
|
||||
</div>
|
||||
|
||||
<div v-if="showExtras" class="space-y-3 border-t border-default pt-4">
|
||||
<div v-if="showExtras" class="space-y-3 ytp-border-top-soft pt-4">
|
||||
<UFormField label="Preset" :ui="fieldUi" class="w-full">
|
||||
<template #label>
|
||||
<span class="inline-flex items-center gap-2 font-semibold">
|
||||
|
|
@ -155,8 +155,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
</UPageCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -203,13 +202,12 @@
|
|||
tag="div"
|
||||
class="grid grid-cols-1 gap-3 lg:grid-cols-2"
|
||||
>
|
||||
<UCard
|
||||
<div
|
||||
v-for="item in queueItems"
|
||||
:key="`queue-${item._id}`"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden"
|
||||
:ui="queueCardUi"
|
||||
class="ytp-card w-full min-w-0 max-w-full overflow-hidden"
|
||||
>
|
||||
<template #header>
|
||||
<div class="p-4 pb-0 ytp-border-bottom-soft">
|
||||
<div
|
||||
v-if="downloadingStatuses.has(item.status) || item.status === null"
|
||||
class="queue-progress rounded-md border border-default bg-muted/20"
|
||||
|
|
@ -235,8 +233,9 @@
|
|||
<span>{{ updateProgress(item) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
<div class="flex min-w-0 flex-col gap-4 sm:flex-row">
|
||||
<figure
|
||||
class="relative w-full shrink-0 overflow-hidden rounded-lg border border-default bg-muted/20 sm:w-52"
|
||||
|
|
@ -299,15 +298,33 @@
|
|||
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs">
|
||||
<UBadge
|
||||
:color="downloadingStatuses.has(item.status) ? 'info' : 'warning'"
|
||||
:color="
|
||||
downloadingStatuses.has(item.status) ? 'info' : 'warning'
|
||||
"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
class="gap-1"
|
||||
>
|
||||
{{ downloadingStatuses.has(item.status) ? 'Active' : 'Queued' }}
|
||||
<UIcon
|
||||
:name="getQueueIcon(item)"
|
||||
:class="['size-3.5', getStatusIconAnimation(item)]"
|
||||
/>
|
||||
<span>{{
|
||||
downloadingStatuses.has(item.status) ? 'Active' : 'Queued'
|
||||
}}</span>
|
||||
</UBadge>
|
||||
|
||||
<UBadge :color="getStatusColor(item)" variant="soft" size="sm">
|
||||
{{ getStatusLabel(item) }}
|
||||
<UBadge
|
||||
:color="getStatusColor(item)"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
class="gap-1"
|
||||
>
|
||||
<UIcon
|
||||
:name="getStatusIcon(item)"
|
||||
:class="['size-3.5', getStatusIconAnimation(item)]"
|
||||
/>
|
||||
<span>{{ getStatusLabel(item) }}</span>
|
||||
</UBadge>
|
||||
|
||||
<span
|
||||
|
|
@ -332,10 +349,12 @@
|
|||
</template>
|
||||
</p>
|
||||
|
||||
<div class="mt-auto flex flex-wrap items-center justify-end gap-2 pt-1">
|
||||
<div
|
||||
class="mt-auto flex flex-wrap items-center justify-end gap-2 pt-1"
|
||||
>
|
||||
<UButton
|
||||
v-if="!item.status && item.auto_start === false"
|
||||
color="success"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
size="xs"
|
||||
icon="i-lucide-play-circle"
|
||||
|
|
@ -346,7 +365,7 @@
|
|||
|
||||
<UButton
|
||||
v-if="!item.status && item.auto_start === true"
|
||||
color="warning"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
size="xs"
|
||||
icon="i-lucide-pause"
|
||||
|
|
@ -356,7 +375,7 @@
|
|||
</UButton>
|
||||
|
||||
<UButton
|
||||
color="error"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
size="xs"
|
||||
icon="i-lucide-x"
|
||||
|
|
@ -367,7 +386,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
|
||||
<UAlert
|
||||
|
|
@ -443,12 +463,12 @@
|
|||
v-if="historyEntries.length > 0"
|
||||
class="grid grid-cols-1 gap-3 lg:grid-cols-2"
|
||||
>
|
||||
<UCard
|
||||
<div
|
||||
v-for="item in historyEntries"
|
||||
:key="`history-${historyPagination.page}-${item._id}`"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden"
|
||||
:ui="queueCardUi"
|
||||
class="ytp-card w-full min-w-0 max-w-full overflow-hidden"
|
||||
>
|
||||
<div class="p-4">
|
||||
<div class="flex min-w-0 flex-col gap-4 sm:flex-row">
|
||||
<figure
|
||||
class="relative w-full shrink-0 overflow-hidden rounded-lg border border-default bg-muted/20 sm:w-52"
|
||||
|
|
@ -512,8 +532,17 @@
|
|||
<div class="flex flex-wrap items-center gap-2 text-xs">
|
||||
<UBadge color="neutral" variant="soft" size="sm">History</UBadge>
|
||||
|
||||
<UBadge :color="getStatusColor(item)" variant="soft" size="sm">
|
||||
{{ getStatusLabel(item) }}
|
||||
<UBadge
|
||||
:color="getStatusColor(item)"
|
||||
variant="soft"
|
||||
size="sm"
|
||||
class="gap-1"
|
||||
>
|
||||
<UIcon
|
||||
:name="getStatusIcon(item)"
|
||||
:class="['size-3.5', getStatusIconAnimation(item)]"
|
||||
/>
|
||||
<span>{{ getStatusLabel(item) }}</span>
|
||||
</UBadge>
|
||||
|
||||
<span
|
||||
|
|
@ -538,7 +567,9 @@
|
|||
</template>
|
||||
</p>
|
||||
|
||||
<div class="mt-auto flex flex-wrap items-center justify-end gap-2 pt-1">
|
||||
<div
|
||||
class="mt-auto flex flex-wrap items-center justify-end gap-2 pt-1"
|
||||
>
|
||||
<UButton
|
||||
v-if="getDownloadLink(item)"
|
||||
color="primary"
|
||||
|
|
@ -554,7 +585,7 @@
|
|||
|
||||
<UButton
|
||||
v-if="!item.filename"
|
||||
color="info"
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
size="xs"
|
||||
icon="i-lucide-rotate-cw"
|
||||
|
|
@ -564,7 +595,7 @@
|
|||
</UButton>
|
||||
|
||||
<UButton
|
||||
color="error"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
size="xs"
|
||||
icon="i-lucide-trash"
|
||||
|
|
@ -575,7 +606,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UAlert
|
||||
|
|
@ -663,6 +695,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</AppRoot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -756,19 +789,6 @@ const fieldUi = {
|
|||
container: 'space-y-2',
|
||||
};
|
||||
|
||||
const formCardUi = {
|
||||
root: 'w-full border border-default bg-default',
|
||||
container: 'w-full p-4 sm:p-5',
|
||||
wrapper: 'w-full items-stretch',
|
||||
body: 'w-full',
|
||||
};
|
||||
|
||||
const queueCardUi = {
|
||||
root: 'w-full border border-default bg-default',
|
||||
header: 'p-4 pb-0',
|
||||
body: 'p-4',
|
||||
};
|
||||
|
||||
const urlInputUi = {
|
||||
root: 'w-full',
|
||||
base: 'bg-elevated/60 ring-default focus-visible:ring-primary',
|
||||
|
|
@ -1090,6 +1110,57 @@ const getStatusLabel = (item: StoreItem): string => {
|
|||
return statusOverrides[item.status] ?? ucFirst(item.status.replace(/_/g, ' '));
|
||||
};
|
||||
|
||||
const getQueueIcon = (item: StoreItem): string => {
|
||||
if (downloadingStatuses.has(item.status)) {
|
||||
return item.is_live ? 'i-lucide-globe' : 'i-lucide-download';
|
||||
}
|
||||
|
||||
return 'i-lucide-clock-3';
|
||||
};
|
||||
|
||||
const getStatusIcon = (item: StoreItem): string => {
|
||||
if (!item.auto_start) {
|
||||
return 'i-lucide-clock-3';
|
||||
}
|
||||
|
||||
if (item.status === null && paused.value === true) {
|
||||
return 'i-lucide-circle-pause';
|
||||
}
|
||||
|
||||
if (item.status === null) {
|
||||
return 'i-lucide-circle-question-mark';
|
||||
}
|
||||
|
||||
if (isDownloadSkipped(item)) {
|
||||
return 'i-lucide-circle-slash';
|
||||
}
|
||||
|
||||
if (item.status === 'downloading' && item.is_live) {
|
||||
return 'i-lucide-globe';
|
||||
}
|
||||
|
||||
const map: Record<string, string> = {
|
||||
downloading: 'i-lucide-download',
|
||||
postprocessing: 'i-lucide-settings-2',
|
||||
preparing: 'i-lucide-loader-circle',
|
||||
finished: 'i-lucide-circle-check',
|
||||
error: 'i-lucide-triangle-alert',
|
||||
cancelled: 'i-lucide-circle-x',
|
||||
not_live: 'i-lucide-clock-3',
|
||||
skip: 'i-lucide-circle-slash',
|
||||
};
|
||||
|
||||
return map[item.status] ?? 'i-lucide-circle-question-mark';
|
||||
};
|
||||
|
||||
const getStatusIconAnimation = (item: StoreItem): string => {
|
||||
const icon = getStatusIcon(item);
|
||||
|
||||
return ['i-lucide-globe', 'i-lucide-settings-2', 'i-lucide-loader-circle'].includes(icon)
|
||||
? 'animate-spin'
|
||||
: '';
|
||||
};
|
||||
|
||||
const getStatusColor = (item: StoreItem): 'neutral' | 'info' | 'success' | 'error' | 'warning' => {
|
||||
if (item.status === null) {
|
||||
return 'neutral';
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="definitions.length > 0"
|
||||
color="neutral"
|
||||
|
|
@ -101,7 +97,7 @@
|
|||
|
||||
<div
|
||||
v-if="!isLoading && filteredDefinitions.length > 0"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -134,11 +130,11 @@
|
|||
|
||||
<div
|
||||
v-if="contentStyle === 'list' && filteredDefinitions.length > 0"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-255 w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -266,16 +262,8 @@
|
|||
:key="definition.id"
|
||||
class="min-w-0 w-full max-w-full"
|
||||
>
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col"
|
||||
:ui="{
|
||||
root: 'bg-default border border-default',
|
||||
header: 'p-4 pb-3',
|
||||
body: 'flex flex-1 flex-col gap-4 p-4 pt-0',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex min-w-0 items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-start gap-2">
|
||||
|
|
@ -313,8 +301,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<div class="space-y-2 text-sm text-default">
|
||||
<div class="flex flex-wrap gap-2 text-xs text-toned *:min-w-32 *:flex-1">
|
||||
<button
|
||||
|
|
@ -352,8 +341,9 @@
|
|||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -375,8 +365,8 @@
|
|||
Delete
|
||||
</UButton>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -572,7 +562,6 @@ const bulkActionGroups = computed<DropdownMenuItem[][]>(() => [
|
|||
{
|
||||
label: 'Remove Selected',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error',
|
||||
disabled: !hasSelected.value || massDelete.value,
|
||||
onSelect: () => void deleteSelected(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,17 +1,13 @@
|
|||
<template>
|
||||
<main class="w-full min-w-0 max-w-full space-y-6">
|
||||
<div class="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div class="flex min-w-0 items-center gap-3">
|
||||
<span
|
||||
class="inline-flex size-11 shrink-0 items-center justify-center rounded-md border border-default bg-elevated/70 text-primary"
|
||||
>
|
||||
<div class="ytp-page-header">
|
||||
<div class="ytp-page-heading">
|
||||
<span class="ytp-page-icon">
|
||||
<UIcon :name="pageShell.icon" class="size-5" />
|
||||
</span>
|
||||
|
||||
<div class="min-w-0 space-y-2">
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-2 text-xs font-medium uppercase tracking-[0.2em] text-toned"
|
||||
>
|
||||
<div class="ytp-page-kicker">
|
||||
<span>{{ pageShell.sectionLabel }}</span>
|
||||
<span>/</span>
|
||||
<span>{{ pageShell.pageLabel }}</span>
|
||||
|
|
@ -21,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex min-w-0 flex-wrap items-center justify-end gap-2 xl:justify-end">
|
||||
<div class="flex w-full flex-wrap items-center gap-2 xl:w-auto xl:justify-end">
|
||||
<UButton
|
||||
v-if="tasks.length > 0"
|
||||
color="neutral"
|
||||
|
|
@ -83,7 +79,7 @@
|
|||
|
||||
<div
|
||||
v-if="!isLoading && filteredTasks.length > 0"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-default bg-default px-3 py-3"
|
||||
class="flex flex-wrap items-center justify-between gap-3 ytp-card px-3 py-3"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton
|
||||
|
|
@ -128,11 +124,11 @@
|
|||
|
||||
<div
|
||||
v-if="contentStyle === 'list' && filteredTasks.length > 0"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden rounded-lg border border-default bg-default"
|
||||
class="w-full min-w-0 max-w-full overflow-hidden ytp-table-surface"
|
||||
>
|
||||
<div class="w-full max-w-full overflow-x-auto overscroll-x-contain">
|
||||
<table class="min-w-210 table-fixed w-full text-sm">
|
||||
<thead class="bg-muted/40 text-xs uppercase tracking-wide text-toned">
|
||||
<thead class="bg-elevated/60 text-xs uppercase tracking-wide text-toned">
|
||||
<tr
|
||||
class="text-center [&>th]:border-r [&>th]:border-default/60 [&>th]:px-3 [&>th]:py-3 [&>th]:font-semibold [&>th:last-child]:border-r-0"
|
||||
>
|
||||
|
|
@ -339,16 +335,8 @@
|
|||
class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3"
|
||||
>
|
||||
<div v-for="item in filteredTasks" :key="item.id" class="min-w-0 w-full max-w-full">
|
||||
<UCard
|
||||
class="flex h-full min-w-0 w-full max-w-full flex-col"
|
||||
:ui="{
|
||||
root: 'bg-default border border-default',
|
||||
header: 'p-4 pb-3',
|
||||
body: 'flex flex-1 flex-col gap-4 p-4 pt-0',
|
||||
footer: 'border-t border-default px-4 py-4',
|
||||
}"
|
||||
>
|
||||
<template #header>
|
||||
<div class="ytp-card flex h-full min-w-0 w-full max-w-full flex-col overflow-hidden">
|
||||
<div class="p-4 pb-3 ytp-border-bottom-soft">
|
||||
<div class="flex min-w-0 items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1 space-y-2">
|
||||
<div class="flex items-start gap-2">
|
||||
|
|
@ -414,8 +402,9 @@
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
|
||||
<div class="space-y-2 text-sm text-default">
|
||||
<div class="grid grid-cols-2 gap-2 text-xs text-toned sm:flex sm:flex-wrap">
|
||||
<button
|
||||
|
|
@ -473,7 +462,11 @@
|
|||
|
||||
<div
|
||||
v-if="
|
||||
item.timer || item.folder || item.template || item.cli || willTaskBeProcessed(item)
|
||||
item.timer ||
|
||||
item.folder ||
|
||||
item.template ||
|
||||
item.cli ||
|
||||
willTaskBeProcessed(item)
|
||||
"
|
||||
class="feature-meta-grid"
|
||||
>
|
||||
|
|
@ -565,8 +558,9 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="ytp-border-top-soft px-4 py-4">
|
||||
<div class="flex flex-wrap gap-2 *:min-w-32 *:flex-1">
|
||||
<UButton
|
||||
color="neutral"
|
||||
|
|
@ -600,8 +594,8 @@
|
|||
</UButton>
|
||||
</UDropdownMenu>
|
||||
</div>
|
||||
</template>
|
||||
</UCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -853,7 +847,6 @@ const bulkActionGroups = computed<DropdownMenuItem[][]>(() => [
|
|||
{
|
||||
label: 'Remove Selected',
|
||||
icon: 'i-lucide-trash',
|
||||
color: 'error',
|
||||
disabled: !hasSelected.value || massDelete.value,
|
||||
onSelect: () => void deleteSelected(),
|
||||
},
|
||||
|
|
|
|||
8
ui/app/types/index.d.ts
vendored
8
ui/app/types/index.d.ts
vendored
|
|
@ -35,10 +35,18 @@ type TerminalSessionsResponse = {
|
|||
items: Array<TerminalSessionItem>;
|
||||
};
|
||||
|
||||
type YtpPageHeaderProps = {
|
||||
icon?: string;
|
||||
sectionLabel?: string;
|
||||
pageLabel?: string;
|
||||
contentClass?: string;
|
||||
};
|
||||
|
||||
export {
|
||||
ImportedItem,
|
||||
TerminalSessionItem,
|
||||
TerminalSessionStatus,
|
||||
TerminalSessionsResponse,
|
||||
YtpPageHeaderProps,
|
||||
version_check,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -214,8 +214,7 @@ const NavItems: Array<NavDefinition> = [
|
|||
group: 'docs',
|
||||
label: 'Changelog',
|
||||
pageLabel: 'Changelog',
|
||||
description:
|
||||
'Latest project changes, loaded remotely when available and falling back to the bundled changelog file.',
|
||||
description: 'Latest project changes',
|
||||
icon: 'i-lucide-git-commit-horizontal',
|
||||
to: '/changelog',
|
||||
matchPath: '/changelog',
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
"typecheck": "nuxt typecheck",
|
||||
"format": "oxfmt --write app/",
|
||||
"format:check": "oxfmt --check app/",
|
||||
"lint": "eslint .",
|
||||
"lint:ci": "eslint . --quiet",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "eslint . --no-warn-ignored",
|
||||
"lint:ci": "eslint . --quiet --no-warn-ignored",
|
||||
"lint:fix": "eslint . --fix --no-warn-ignored",
|
||||
"test": "bun test",
|
||||
"test:watch": "bun test --watch",
|
||||
"test:ci": "bun test --ci --dots",
|
||||
|
|
|
|||
224
uv.lock
224
uv.lock
|
|
@ -147,14 +147,14 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "4.13.0"
|
||||
version = "4.14.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "idna" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -253,11 +253,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.5.20"
|
||||
version = "2026.6.17"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -437,7 +437,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "dateparser"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "python-dateutil" },
|
||||
|
|
@ -445,9 +445,9 @@ dependencies = [
|
|||
{ name = "regex" },
|
||||
{ name = "tzlocal" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/46/2d/a0ccdb78788064fa0dc901b8524e50615c42be1d78b78d646d0b28d09180/dateparser-1.4.0.tar.gz", hash = "sha256:97a21840d5ecdf7630c584f673338a5afac5dfe84f647baf4d7e8df98f9354a4", size = 321512, upload-time = "2026-03-26T09:56:10.292Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/f4/561c49bca97af561d34eed27e3e831135eb5cb88e754c1150be41820f5c6/dateparser-1.4.1.tar.gz", hash = "sha256:f265df13c0380e2e07543ba74b67c0681aaa1096981ffcd35227e1aa0cb81c7c", size = 314734, upload-time = "2026-06-15T08:45:47.659Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/0b/3c3bb7cbe757279e693a0be6049048012f794d01f81099609ecd53b899f0/dateparser-1.4.0-py3-none-any.whl", hash = "sha256:7902b8e85d603494bf70a5a0b1decdddb2270b9c6e6b2bc8a57b93476c0df378", size = 300379, upload-time = "2026-03-26T09:56:08.409Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/7c/2e5dcf53909deddd0bf38cbe277ad9806be038276b1c6c436561b4d9b2e2/dateparser-1.4.1-py3-none-any.whl", hash = "sha256:f25d4e051a84be27a35bd297e3e1dc59ff78373701b89be352ba80372d22d0d0", size = 300503, upload-time = "2026-06-15T08:45:45.951Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -551,59 +551,59 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.5.1"
|
||||
version = "3.5.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dd/8b/befc3cb36965f397d87e86fb3b00e3ec0dc67c1ecb0986d7f54ee528f018/greenlet-3.5.2.tar.gz", hash = "sha256:c1b906220d83c140361cdd12eef970fb5881a168b98ee58a43786426173da14c", size = 199243, upload-time = "2026-06-17T20:19:01.317Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/ba/c24110c55dffa55aa6e1d98b45310da33801aeba7686ff0190fe5d46fd32/greenlet-3.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d40a890035c0058cadbdc4af7569800fd28a0e527a0fdbb7b5f9418f176846ce", size = 622911, upload-time = "2026-05-20T14:09:10.598Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/7b/d20db2e8a5ad6c038702f3179b136f93f0a3d1a21a0c0777f3e470cdf4b2/greenlet-3.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:67821bb03e4e98664490edb787ff6af501194c29bbee0f5c1dfdcf1dc3d9d436", size = 425228, upload-time = "2026-05-20T14:01:40.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/cb/c62454606daf5640369c94d8a9dd540599b1bfc090e2d2180cb77f4038d2/greenlet-3.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8ab31c9de8651a2facdd5c5bb0011f2380dd1a7af78ce2adf4b56095294fc07", size = 285579, upload-time = "2026-05-20T13:08:56.396Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/71/c4270398c2eba968a6071af1dfbdcaeee6ec1c24bc8b435b8cc452700da6/greenlet-3.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e300185139abc337ade480c327183adf42a875ac7181bfe66d7d4efea31fbea", size = 651106, upload-time = "2026-05-20T14:00:09.448Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/ab/71e34b78a44ec271fb5f550c17bc46d301ddc5953890d935f270b0dcdb5a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7ffdb990dcaa0234cf9845aead5df2e3c3a8b6507d409274dd87e0d5ab05ffc2", size = 663478, upload-time = "2026-05-20T14:05:45.88Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/2d/2d80842910da44f78c286532d084b8a5c3717c844ae80ceb3858738ae89a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c09df69dc1712d131332054a858a3e5cca400967fa3a672e2324fbb0971448c", size = 667767, upload-time = "2026-05-20T14:09:12.15Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/96/4efd6fa5c62c85426a0c19077a586258ebc3a2a146ff2493e4312a697a22/greenlet-3.5.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f82b3597e9d83b63408affed0b48fd0f54935edac4302237b9a837be0dae33c", size = 660800, upload-time = "2026-05-20T13:14:29.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/d3/dad2eecedfbb1ed7050a20dcfae40c1442b74bc7423608be2c7e03ee7133/greenlet-3.5.1-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:a4764e0bfc6a4d114c865b32520805c16a990ef5f286a514413b05d5ecd6a23d", size = 470786, upload-time = "2026-05-20T14:01:42.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/e0/6c71401a25cac7000261304e866a2f2cc04dc74810d40e2f118aa4799495/greenlet-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c0141e37414c10164e702b8fb1473304221ad98f71600850c6ef7ff4880feba0", size = 1617518, upload-time = "2026-05-20T14:02:28.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/26/c5c06643e8c0af9e7bf18e16cb51d0ab7625155f0392e1c9015d66d556cd/greenlet-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:50ae25a67bea74ea41fb14b960bc532df73eb713417b2d61892dced82fe8d3bc", size = 1681593, upload-time = "2026-05-20T13:14:39.417Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/bd/e11a108317485075e68af9d23039619b86b28130c3b50d227d42edece64b/greenlet-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:8a17c42330e261299766b75ac1ea32caa437a9453c8f65d16a13140db378ecd3", size = 239800, upload-time = "2026-05-20T13:09:30.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/f8/8e8e8417b7bf28639a5a56356ef934d0375e1d0c70a57e04d7701e870ffe/greenlet-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:7b5f5fae05b8ac6d176a61b60c394a8cbdc2b5b91b81793066e68745cf165e54", size = 236862, upload-time = "2026-05-20T13:09:10.498Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/12/41bf27fde4d3605d3773ae57751eda182b8be2f5398011c041173b1d9534/greenlet-3.5.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:ea8da1e900d758d078810d4255d8c6aa572181896a31ec79d779eb79c3adc9ad", size = 293637, upload-time = "2026-05-20T13:12:35.529Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/44/ba14b23e9757707050c2f397d305bbcae62e5d7cad122f8b6baec5ae4a1f/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a19570c52a21420dcbc94e661994bc325c0b5b11304540fed514586da5dc8f2e", size = 650840, upload-time = "2026-05-20T14:00:11.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/37/5ddc2b686a6844f91abecef43411842426da2e1573f60b49ecf2547f4ae1/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d955c89b75eeca4723d7cc14135f393cd47c32e2a6cb4a8e4c6e760a26b0986", size = 656416, upload-time = "2026-05-20T14:05:47.118Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/46/5987dcd1a2570ba84f3b187536b2ca3ae97613387e57f5cfa99df068fe5e/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea37d5a157eb9493820d3792ac4ece28619a394391d2b9f2f78057d396ff0f0f", size = 656607, upload-time = "2026-05-20T14:09:13.949Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/f0/d17510297c35a2992712f0bf84de3779749999f7d3d63aa1f09db7c62dbe/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2daaaebd1a5aa88c49045b6baf9310b3263796bd88db713edf37cf53e7bb4e", size = 654397, upload-time = "2026-05-20T13:14:30.696Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/c1/6da0a9ddcc29d7e51ef14883fa3dc1e53b3f4ffba00582106c7bf55da1d8/greenlet-3.5.1-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:8d8a23250ea3ec7b36de8fa4b541e9e2db3ee82915cc060ab0631609ad8b28de", size = 488287, upload-time = "2026-05-20T14:01:43.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/eb/147387705bb89092645b012586e7273cb5ed3c90ef7eaf3a69173eaf0209/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbd69cc349e43bf3a8ae1c85548ff0718efc887615c2db16c3833d7b0b072d", size = 1614469, upload-time = "2026-05-20T14:02:30.192Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/4e/37ee0da7732b7aa9896f17e15579a9df34b9fcb9dd494f0adfa749af6623/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4378720dd888136c27215a0214d32a4d37c3852765d45bc37aad0623423cfd78", size = 1675115, upload-time = "2026-05-20T13:14:40.972Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/f3/97dfcf4a6eb5077f8a672234216fb5923eb89f2cab7081cb10b2cf75b605/greenlet-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:45718441607f9325d948db98cbc691276059316d0358c188c246da4e1d4d23d2", size = 245246, upload-time = "2026-05-20T13:12:22.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/73/d7f72e34b582f694f4a9b248162db7b09cc458a259ba8f0c0bfa1a34ea7d/greenlet-3.5.1-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:2baee5ca02031757ffe8cc3d69f0cc0aec7065ce362622da74f32d3bcab1c541", size = 285575, upload-time = "2026-05-20T13:12:07.043Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/59/fa9c6e87dc8ad27a95dabe2f29f372b733d05a8a67470f6c901ed9975655/greenlet-3.5.1-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b1ec3274918a81d3ea778b9e75b56b72b33f300edb6cf7f3a7fe1dae56683de", size = 656428, upload-time = "2026-05-20T14:00:12.556Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/f9/e753408871eaa61dfe35e619cfc67512b036fde99893685d50eea9e07146/greenlet-3.5.1-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:111e2390ffffc47d5840b01711dd7fac07d4c09283d0283e7f3264b14e284c64", size = 667064, upload-time = "2026-05-20T14:05:48.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/74/807a047255bf1e09303627c46dc043dca596b6958a354d904f32ab382005/greenlet-3.5.1-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:10a9a1c0bfbc93d41156ffcb90c75fbc05544054faf15dcc1fdf9765f8b607f0", size = 672962, upload-time = "2026-05-20T14:09:15.532Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/27/5565b5b40389f1c7753003a07e21892fda8660926787036d5bc0308b8113/greenlet-3.5.1-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e630136e905fe5ff43e86945ae41220b6d1470956a39220e708110ac48d01ea5", size = 665697, upload-time = "2026-05-20T13:14:32.943Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/32/19d4e13225193c29b13e308015223f7d75fd3d8623d49dd19040d2ce8ec1/greenlet-3.5.1-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:ef08c1567c78074b22d1a200183d52d04a14df447bf70bcbb6a3507a48e776fc", size = 476047, upload-time = "2026-05-20T14:01:44.39Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/82/e7de4178c0c2d1c9a5a3be3cc0b33e46a85b3ee4a77c071bf7ad8600e079/greenlet-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:975eac34b44a7077ca4d421348455b94f0f518246a7f14bc6d2fdcfe5b584368", size = 1621256, upload-time = "2026-05-20T14:02:31.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/10/f2dddcf7dacac17dfc68691809589adad06135eb28930429cf58a6467a2f/greenlet-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9ab3c3a0b2ae6198e67c898dad5215a49f9ae0d0081b3c3ec59f333e39eeca26", size = 1685956, upload-time = "2026-05-20T13:14:42.55Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/17/4a232b32133230ada52f70e9d7f5b65b0caef8772f01849bd8d149e7e4ca/greenlet-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:cbfc69be86e10dcfef5b1e6269d1d6926552aa89ee39e1de3353360c1b6989ab", size = 239802, upload-time = "2026-05-20T13:13:15.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/ae/4e623a7e6d4d2a5f4cb8e4c82de4169fc637942caae68d6e676b8a128ac5/greenlet-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:92fd6d44ac5e5a887c8a5dc4a8ba0ba908527c31c12f78c6bc7dcfe8aab279f6", size = 236853, upload-time = "2026-05-20T13:15:37.301Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/57/816d9cff29119da3505b3d6a5e14a8af89006ac36f47f891ff293ee05af1/greenlet-3.5.1-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:a6fdf2433a5441ef9a95464f7c3e674775da1c8c1177fff311cee1acad4626ed", size = 293877, upload-time = "2026-05-20T13:10:19.078Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/a1/59b0a7c7d140ff1a75626680b9a9899b79a9176cab298b394968fb023295/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7546556f0d649f99f6a361098a55f761181bb2ea12ff150bb16d26092ad88244", size = 655333, upload-time = "2026-05-20T14:00:14.758Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/1b/5efe127597625042218939d01855109f352779050768b670b52edcc16a6c/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5ee3ea898009fa898f85f9982255d35278c477bebe185beca249cab42d4526c", size = 659443, upload-time = "2026-05-20T14:05:50.159Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/9d/1dcdf7b95ab3cf8c7b6d7277c18a5e167312f2b362ddfcc5d5e6d8d84b43/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a57b0d05a0448eed231d59c0ceb287dde984551e54cbc51ac2d4865712838e9c", size = 659998, upload-time = "2026-05-20T14:09:16.912Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/6d/c404246ea4d22d097a7426d0efb5b781bd7eb67715f09e79001bd552ab18/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5c81f74d204d3edd136ebfd50dce53acbb776995d721a0fe801626cfc93b8cd", size = 658356, upload-time = "2026-05-20T13:14:35.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/7e/c4959664fc231d587d66d8e81f2095e98056ba1954beafdcbe635e251052/greenlet-3.5.1-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:b0703c2cef53e01baec47f7a3868009913ad71ec678bbecb42a6f40895e4ce62", size = 494470, upload-time = "2026-05-20T14:01:45.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/02/f8ee37fb6d2219329f350af241c27fcf12df57e723d11f6fc6d3bacdadaa/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c18ef16bf6d4dd410e4dd52996888ea1497be26892fe5bbc73580aba4287b8e", size = 1619216, upload-time = "2026-05-20T14:02:33.403Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/c5/3dc9475ace2c7a3680da12372cddd7f1ac874eb410a1ac48d3e9dab83782/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:17d86354f0ae6b61bf9be5148d0dd34e06c3cb7c602c671f79f29ac3b150e659", size = 1678427, upload-time = "2026-05-20T13:14:43.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/4e/750c15c317a41ffb36f0bf40b933e3d744a7dede61889f74443ea69690cf/greenlet-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e7516cf6ae6b8a582c2770a0caed47b8a48373ed732c33d69a72913ae6ac923e", size = 245225, upload-time = "2026-05-20T13:13:59.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/fd/d3baea2eeb7b617efd47e87ca06e2ec2c6118d303aa9e918e0ce16eadc10/greenlet-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:5028648bf2253ec4745add746129d3904121fa7fe871a76bed23c5720573ce0a", size = 239590, upload-time = "2026-05-20T13:13:37.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/3c/bb37b9d40d65b0741a8b040ca5c307034d0a9822994dff5f825c88dd7a6b/greenlet-3.5.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:0629377725977252159de1ebd3c6e49c170a63856e585446797bb3d66d4d9c34", size = 287178, upload-time = "2026-06-17T17:35:25.132Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/a6/0c5902393f492f8ceb19d0b5cf139284e3a11b333a049739643b1036b6f8/greenlet-3.5.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2ddf9eddc617681108dd071b3feabf3f4a4cd64846254aec4d4ceda098b639a", size = 606900, upload-time = "2026-06-17T18:07:21.692Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/7c/42899c31d4b87148ae4e3f87f63e13398824be6241f4dde42ded95768a34/greenlet-3.5.2-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f41feb9f2b59e2e61ac9bea4e344ddd9396bf3cacb2583f73a3595ed7df6f8e7", size = 619265, upload-time = "2026-06-17T18:29:44.837Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/7e/28f991affb413b232b1e7d768db24c37b3f4d5daecc3f19b455d40bd2dea/greenlet-3.5.2-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9dc23f0e5ad76415457212a4b947d22ebe4dc80baf02adf7dd5647a90f38bb4e", size = 625044, upload-time = "2026-06-17T18:39:29.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/52/4ff8c98d3cfe62b4515f8584ae14510a58f35c549cc5292b78d9b7a40b70/greenlet-3.5.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09201fa698768db245920b00fdc86ee3e73540f01ca6db162be9632642e1a473", size = 616187, upload-time = "2026-06-17T17:39:29.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/05/0cc9ec660e7acff85f93b0a048b6654371c822c884add44c02a465cf70e0/greenlet-3.5.2-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:423167363c510a75b649f5cd58d873c29498ea03598b9e4b1c3b73e0f899f3d5", size = 427322, upload-time = "2026-06-17T18:41:20.892Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/a6/269c8bf9aefc13361ce1088f0e392b154cb21005de7862e42b5d782b81fd/greenlet-3.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1759fa4f14c398508cf20dc8037de55cc23ae8bd14c185c2718257837195ca5", size = 1573778, upload-time = "2026-06-17T18:22:13.497Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/9b/391d015cbc6323e81b14c02cf825fdca7e0049c9bb489bf4ac72883118ba/greenlet-3.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9318cdeb9abdbfdd8bc8464ee4a06dffde2c7846e1def138365a6240ab2c9a5", size = 1638092, upload-time = "2026-06-17T17:40:08.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/53/5b4df711f4356c62e85d9f819d87966d526d1cfb32bae49a8f7d6fc36ea4/greenlet-3.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:2c3b3311af72b3d3b03cc0f1ffd11f072e834be5d0444105cf715fc44434e39c", size = 239352, upload-time = "2026-06-17T17:38:51.593Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/b6/18efc3a329ec035c3f344b8f2b60356451950ddf9b7b64ff00023778a1dd/greenlet-3.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:f9bbd6216c45a563c2a61e478e038b439d9f248bde44f775ea37d339da643af4", size = 237635, upload-time = "2026-06-17T17:35:36.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/89/aaafc8e14de4ac882e02ccb963225329b0e8578aba4365e71eb678e45722/greenlet-3.5.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:1c31219badba285858ba8ed117f403dea7fafee6bade9a1991875aae530c3ceb", size = 287676, upload-time = "2026-06-17T17:33:31.514Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/fc/2308249206c12ac70de7b9a00970f84f07d10b3cd60e05d2fbcaa84124e8/greenlet-3.5.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f96ed6f4adc1066954ae95f45717657cb67468ef3b89e9a3632e14a625a8f39", size = 653552, upload-time = "2026-06-17T18:07:23.493Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/24/47730d1f8f1336b9b089237521ed7a26eee997065dcb4cab81cdca333abc/greenlet-3.5.2-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5795e883e915333c0d5648faaa691857fbc7180136883edc377f50f0d509c2a8", size = 665756, upload-time = "2026-06-17T18:29:46.616Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/5c/2664d290cbd1fef9eb3f69b5d3bc5aa91b6fa907519298ca6af93a90c6cb/greenlet-3.5.2-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6e9e49d732ee92a189bb7035e293029244aeba648297a9b856dc733d17ca7f0d", size = 669989, upload-time = "2026-06-17T18:39:30.79Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/69/d6c99db15dc0b5e892ac3cc7b942c8b21f4a9cc3bd9ea0bc3b0f339ffbd4/greenlet-3.5.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26aed8d9503ca78889141a9739d71b383efea5f472a7c522b5410f7eb2a1b163", size = 663228, upload-time = "2026-06-17T17:39:31.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/d4/fcb53fa9847d7fbd4723fbed9469c3869b9e3544c4e001d9d5aa2f66162d/greenlet-3.5.2-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:537c5c4f30395020bb9f48f53146070e3b997c3c75da14011ab732aaa19ce3ef", size = 472888, upload-time = "2026-06-17T18:41:22.511Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/88/9e603f448e2bc107c883e95817b980fb9b45ba6aea0299b2e9978124bea2/greenlet-3.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dbebc038fcdda8f8f21cce985fd04e34e0f42007e7fc7ab7ad285caf77974b95", size = 1620723, upload-time = "2026-06-17T18:22:14.817Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/91/26da17e3777858c16fdb8d020a4c68f3a03cb92f238de8f5351d5d5186e9/greenlet-3.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a207023f1cf8695fd82580b8099c09c5809be18bc2282362cdfb965dd884a317", size = 1684227, upload-time = "2026-06-17T17:40:09.536Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/44/b3a11f7aa34cb38f1b7f3df8bcd9fcd09bac9d342c2a2c9b8686c804bcd2/greenlet-3.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:c674a1dd4fe41f6a93febe7ab366ceabf15080ea31a9307811c56dac5f435f73", size = 240257, upload-time = "2026-06-17T17:35:23.359Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/e3/3b62145fe917311732041a258adb218248add00542e3131c48bd047fbed5/greenlet-3.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:3c417cd6c593bbbef6f7aa31a79f37d3db7d18832fc56b694a2150130bde784e", size = 239038, upload-time = "2026-06-17T17:37:56.792Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/ac/d3bad483e9f6cd1848604fdffa32cac25846dd6dfcec0e6f81c790185518/greenlet-3.5.2-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:a96457a30384de52d9c5d2fd33abf6c1daae3db392cd556738f408b1a79a1cf0", size = 295668, upload-time = "2026-06-17T17:36:02.293Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/e9/3a7e557b895fd0469b00cd0b2bd498ba950e8bfdf6d7adeecf2c5e4130a6/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4af5d4961818ab651d09c1448a03b1ba2a1726a076266ebb62330bab9f3238c", size = 652820, upload-time = "2026-06-17T18:07:24.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/67/6225d5c5e4afc04be0fd161eec82e4b72017e8a100d222f25d7b42b0140d/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a1789a6244ea1ba61fd4386c9a6a31873e9b0234762103364be98ef87dcb19f3", size = 658697, upload-time = "2026-06-17T18:29:48.365Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/ad/9b3058f999b81750a9c6d9ec424f509462d232b58002086fe2ba63b66407/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ee6288f1933d698b4f098127ed17bda2910a75d2807915bd16294a972055d6c", size = 658945, upload-time = "2026-06-17T18:39:32.509Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/99/6324b8ef916dcaddccb340b304c992ca3f947614ce0f2685d438187300b8/greenlet-3.5.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3be00501fb4a8c37f6b4b3c4773808ceb26ea65c7ea64fd5735d0f330b3786de", size = 656436, upload-time = "2026-06-17T17:39:32.509Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/75/1b6ecd8c027b69ab1b6798a84094df79aab5e69ac7e249c78b9d361dd1fa/greenlet-3.5.2-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:b4cad42662c796334c2d24607c411e3ed82481c1fb4e1e8ec3a5a8416060092e", size = 490529, upload-time = "2026-06-17T18:41:23.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/ee/f5bf9daac27c5e1b011965f64b5630a32b415daf7381b312943629e12c2a/greenlet-3.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1d554cd96841a68d464d75a3736f8e87408a7b02b1930a75fa32feb408ad62f8", size = 1617193, upload-time = "2026-06-17T18:22:16.252Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/21/b05d5b12715bda92ce27c118d64971d21e9b8f3563ed959a7d271e2d4223/greenlet-3.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3dff6cd3aac35f6cd3fc23460105acf576f5faf6c378de0bc088bf37c913864a", size = 1677512, upload-time = "2026-06-17T17:40:10.771Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/97/1b8f1314b868041b327dc1051603e8142b826480cb0ecb8a7b7632aee9c4/greenlet-3.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:36cfea2aa075d544617176b2e84450480f0797070ad8799a8c41ada2fe449d32", size = 243145, upload-time = "2026-06-17T17:34:37.502Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/07/1b5311775e04c718a118c504d7a3a312430e2a1bd1347226aff4774e4549/greenlet-3.5.2-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:a0314aa832c94633355dc6f3ee54f195159533355a323f26926fc63b98b2ccbb", size = 288315, upload-time = "2026-06-17T17:34:34.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/cc/6abcd2a486b58b9f77b7a93b690d59cb2c11a5906ed2ad4c63c7b9c1113d/greenlet-3.5.2-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24c59cb7db9d5c694cb8fd0c76eef8e456b2123afdfa7e4b8f2a67a0860d7682", size = 659130, upload-time = "2026-06-17T18:07:26.354Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/12/f4aaad6d3d383233f700ab322568a4f29f2c701a4861d85f4811d99689b2/greenlet-3.5.2-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7bb811753703739ad318112f16eccfaabdac050037b6d092debaa8b23566b4ce", size = 669724, upload-time = "2026-06-17T18:29:50.13Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/e0/4ce3a046b51e53934eae93d7f9c13975a97285741e9e1fcadf8751314c37/greenlet-3.5.2-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2debcd0ef9455b7d4879589903efc8e497d4b8fb8c0ae772309e44d1ca5e957f", size = 673494, upload-time = "2026-06-17T18:39:34.196Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/2a/a089811fc31c6bf8742f40a4e73470d6d401cef18e4314eb20dc399b377c/greenlet-3.5.2-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6d78b5c1c178dad90447f1b8452262709d3eef4c98f825569e74c9d0b2260ac9", size = 668089, upload-time = "2026-06-17T17:39:33.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/e0/9c18721e63445dce02ee67e4c81c0f281626604ff55ae6f7b7f4354d7129/greenlet-3.5.2-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:9558cae989faeab6fbb425cd98a0cfa4190a47fba6443973fbee0a1eb0b0b6c3", size = 479721, upload-time = "2026-06-17T18:41:25.726Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/1c/2f47c7d5fcfa98a62b705bf9a0505d86f4563c0d81cab1f7159ff1e743b7/greenlet-3.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:0977af2df83136f81c1f76e76d4e2fe7d0dc56ea9c101a86af26a95190b9ca32", size = 1625684, upload-time = "2026-06-17T18:22:17.664Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/bf/661dd24624f70b7b32972d7693d0344ecde10278f647d7b828baf739899c/greenlet-3.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f9ed777c6891d8253e54468576f55e27f8fc1a662a664f946a191003574c0a74", size = 1688043, upload-time = "2026-06-17T17:40:12.403Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/49/d9bde1d15a21296b3b521fe083eb8aabd54ac05d15de9832918f3d639543/greenlet-3.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:c0ea4eb3de23f0bac1d75205e10ccfa9b418b17b01a2d7bf19e3b69dda08900a", size = 240531, upload-time = "2026-06-17T17:35:47.448Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/4d/86d7768bd53e9907de0333df215c2018cd01a593b3715cbd79aa82dd94b7/greenlet-3.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:7a7bfc200be40d04961d7e80e8337d726c0c1a50777e588123c3ed8ba731dcb9", size = 239579, upload-time = "2026-06-17T17:39:39.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/15/907be5e8900901039bae752fa9a31c03a3c1e064833f35a4e49449184581/greenlet-3.5.2-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:98a52d6a50d4deaba304331d83ee3e10ebbdc1517fcca40b2715d1de4534065c", size = 296697, upload-time = "2026-06-17T17:37:15.887Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/5c/08c57be575c3d6a3c023bbf22144a1c7dc6ed4d134527bb36ded4dbf04a8/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1587ff8b58fdf806993ed1490a06ac19c22d47b219c68b30954380029045d8d4", size = 656710, upload-time = "2026-06-17T18:07:28.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/d0/749f917bdc9fc90fceea4aa65fbf6556e617a50714d1496bdc8ad190bb36/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:feb721811d2754bfd16b48de151dd6b1f222c048e625151f2ca44cfdfd69f59c", size = 662629, upload-time = "2026-06-17T18:29:51.728Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/87/10776cd88df54d0f563e9e21e98363f2d6af94bedc553b1da0972fa87f80/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9476cbead736dc48ce89e3cd97acff95ecc48cbf21273603a438f9870c4a014", size = 663191, upload-time = "2026-06-17T18:39:35.639Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/a5/68cefae3a07f6d0093a490cf28ab604f14578f3e60205a2a2b2d5cd70af2/greenlet-3.5.2-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7fe6062b1f35534e1e8fb28dfed406cf4eeff3e0bca3a0d9f8ff69f20a4abb00", size = 660147, upload-time = "2026-06-17T17:39:35.068Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/aa/26ddf92826a99d87bfb8fdb8f3a262a6f16495a5d8e579737baa92fb4543/greenlet-3.5.2-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:5930d3946ecae99fa7fc0e3f3ae515426ad85058ebd9bfc6c00cca8016e6206b", size = 498199, upload-time = "2026-06-17T18:41:27.464Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/6b/b9156d8397e4750220f54c7c5c34650f1e740a8d2f66eab9cfd1b7b53b69/greenlet-3.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b4ac902af825cbac8e9b2fccab8122236fd2ba6c8b71a080116d2c2ec72671b1", size = 1621675, upload-time = "2026-06-17T18:22:18.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/e3/d3250f4fa01c211a93d04e34fded63187e648dbec17b9b1a14d388040593/greenlet-3.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:6f1e473c06ae8be00c9034c2bb10fa277b08a93287e3111c395b839f01d27e1f", size = 1680577, upload-time = "2026-06-17T17:40:14.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/ba/eaee8bda4419770d7096b5a009ebff0ab20a2a28cdd83c4b591bfdf36fa9/greenlet-3.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:3c2315045f9983e2e50d7e89d95405c21bddb8745f2da4487bc080ab3525f904", size = 243482, upload-time = "2026-06-17T17:37:34.741Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/45/f794a81c91e9942c61f9110bd1f9a38a0ea565eab57f8b08cd53d3131e48/greenlet-3.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:db548d5ab6c2a8ead82c013f875090d79b5d7d2b67fc513934ce6cf66492ad7f", size = 242062, upload-time = "2026-06-17T17:35:39.814Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1398,7 +1398,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.1.0"
|
||||
version = "9.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
|
|
@ -1407,9 +1407,9 @@ dependencies = [
|
|||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1727,32 +1727,32 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.15.17"
|
||||
version = "0.15.18"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/a9/3abdf488f1bf3d24c699415e454ed554a6350d5d89ce183be1ee0a3361ac/ruff-0.15.17.tar.gz", hash = "sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219", size = 4743346, upload-time = "2026-06-11T17:54:47.663Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/98/1295ad5a5aa9bc85bdcdfa5d82fe7b49c61af5657df4f227637ff9de0da6/ruff-0.15.18.tar.gz", hash = "sha256:2698a964c70e8bf402dcb99c8810472d270d141e7aa8c4e13599fd52033a2f33", size = 4761437, upload-time = "2026-06-18T18:25:39.224Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/db/4d/e11259f5da07cb6afb2d074c31bf09da9671993f7329d4f15d2fdc458301/ruff-0.15.17-py3-none-linux_armv6l.whl", hash = "sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f", size = 10856677, upload-time = "2026-06-11T17:54:49.533Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/3e/772d679e1a0dc058e58875bd2c0cb713a0530877b4a76fee3c7966df0d49/ruff-0.15.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7", size = 11223443, upload-time = "2026-06-11T17:55:00.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/58/bd41f7688b2fd5623012605130ed70e60aa7f2244baa3d5066bdd61530c8/ruff-0.15.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d", size = 10566458, upload-time = "2026-06-11T17:55:07.52Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/5b/733371013fcf1ec339e477ece6ab42bfe10bdd9bba8ee88a9516aa56bfc0/ruff-0.15.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed", size = 10914483, upload-time = "2026-06-11T17:55:05.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/cc/6f24251cc0252f7239391ccb85833f320efad14ebe5b443943f37ced6332/ruff-0.15.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e", size = 10647497, upload-time = "2026-06-11T17:54:57.733Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/dd/0d10c17ce1a1624d6fc3156309c3f834fdb5dfaad026ec90c85684f3990e/ruff-0.15.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d", size = 11416967, upload-time = "2026-06-11T17:54:51.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/91/556bfb156f6144f355e831c23db00b2fc4120f86b3ce81cc5f7fd2df51f3/ruff-0.15.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c", size = 12335770, upload-time = "2026-06-11T17:54:45.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/82/8b5999aa13355e926f06d9f42a32dcca862f623bf0363785ff89d607dffd/ruff-0.15.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978", size = 11575441, upload-time = "2026-06-11T17:54:32.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/93/f10377bb04109ca0e8cbc483ff1982c54b6d418210041776f93e8cdc7fa9/ruff-0.15.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719", size = 11557614, upload-time = "2026-06-11T17:54:34.698Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/a6/eeeae7f7d5493df41649ab3db92f086b2d0a30199e4efdf8e3dd7a033f24/ruff-0.15.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4", size = 11544450, upload-time = "2026-06-11T17:54:39.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/88/5991ce565129a24dd4a00db1254b3b5db2e53018cbe4018ea5a89738e727/ruff-0.15.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7", size = 10892524, upload-time = "2026-06-11T17:55:09.432Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/1d/0fdd248313425f55223968af04b0a42125466a8d88d21c1d99c6af0a51e8/ruff-0.15.17-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f", size = 10659573, upload-time = "2026-06-11T17:54:36.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/0e/072e8260deb9461062ce9311ced27a8e541229a6ffd483013dd37661e43e/ruff-0.15.17-py3-none-musllinux_1_2_i686.whl", hash = "sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f", size = 11127818, upload-time = "2026-06-11T17:55:03.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/b4/55060a34163121498014696b5f656db5b8c6963768f227dbf0d76b311073/ruff-0.15.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8", size = 11655901, upload-time = "2026-06-11T17:54:53.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/71/9b29d6b87cef468d697f43c6a91e3fae4a80185779d7d5a4ef27d173439f/ruff-0.15.17-py3-none-win32.whl", hash = "sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392", size = 10925574, upload-time = "2026-06-11T17:54:55.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/b2/8fc77f3723228836fa5d12497eb71c808f83782e10d058d2b15cfa14640b/ruff-0.15.17-py3-none-win_amd64.whl", hash = "sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084", size = 12058788, upload-time = "2026-06-11T17:54:41.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/d0/686e984941269621e2be72612d5c1e461f8f7b38415a2a7d7a81c8ae6715/ruff-0.15.18-py3-none-linux_armv6l.whl", hash = "sha256:8b6850172348c8381b8b3084c5915a4393c2373b9b54cd5b5e1ea15812bc10df", size = 10887308, upload-time = "2026-06-18T18:25:03.062Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/21/bc4123e3f5515ee99f8ce1eb93a14a0628fe4d1678663cd08f933ac16931/ruff-0.15.18-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3fccc153a85417dcd976883160cacce486997b0a0058dd18f54b8aaaac7d1ce2", size = 11281305, upload-time = "2026-06-18T18:25:30.026Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/93/4769464c25cf7ab2acb3c7dda9cad3d867eb41c59565b3e2a9d17249c90c/ruff-0.15.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08d4c86a68f2c3ec2c9d56380a71fb4a4f65373055cbb8caabd645e9102f38d4", size = 10641215, upload-time = "2026-06-18T18:25:15.802Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/42/56926d17120db2c208d76bf60a1a019644dd9e91dc27f0f95c9caddb1366/ruff-0.15.18-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37e5108745c2c0705da916d7d4de533ddf547051ef45f62888c31bae73f66318", size = 10957224, upload-time = "2026-06-18T18:25:36.955Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/4f/d43fab8d8189afde803103022d000a8ef9f230616d436d52a8b2b8d63b50/ruff-0.15.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56949a6ce8b3abde54c0bcb22cebfe57e8771cadc84b407ae8b8eaf67ebdcd43", size = 10699024, upload-time = "2026-06-18T18:25:05.707Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/42/1e3e4c68bd408b9768cf3e439acbe2c78245225faef253f7028a0cdb63e0/ruff-0.15.18-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01a754cd6a1b630d3f97e33eb452cf7a98040482318e870f8bc52a5a30e62657", size = 11491458, upload-time = "2026-06-18T18:25:20.275Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/77/47a3484bea8521e14a203d98c389c5c97846675e4f02734672da4a69b52a/ruff-0.15.18-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ba7a07e03a44dbf10bb086ee06705b173625014ec99f73a7e6836a5e5590a0c", size = 12383752, upload-time = "2026-06-18T18:25:22.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/ca/054159590787023d83b658a1a1819c4c8910114e7015069340b71c0961cb/ruff-0.15.18-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a2c40a41a4cadbcf5897b548ab29dfe248b20c540961c0247d98a3973c70403", size = 11577923, upload-time = "2026-06-18T18:25:10.702Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/ff/d353d6b7bbd73cc0ec37f4463d7540e45e894338abdd9964eee0de332708/ruff-0.15.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f0480ce690cbb6c4db6e5d08f19fce98e10ba131a8b60c1bcdac42771e3ae2d", size = 11583925, upload-time = "2026-06-18T18:25:32.391Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/4a/891f89b9c296ed3e5f3ece1a5629badc989d9a8fdaa30431aaf4774bc1c2/ruff-0.15.18-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2330215f1f393fa8733f55edce04fcf94c36a2c460fcde31f78cc84e4951e9b1", size = 11582834, upload-time = "2026-06-18T18:25:27.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/a3/ed9e370154bf85de360b93c03026157f02d4943b2d01ff4945f4429f8e8a/ruff-0.15.18-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6aa6a3d979e48ae617578183674bf264fbe7d0114a796a26bd678d67963c7ff", size = 10927328, upload-time = "2026-06-18T18:25:34.676Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/d1/5cf5909329fedb5d39d555ee818ba5cf4638e1a301b89785d34f2905bfcb/ruff-0.15.18-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a81beadbbff2c9c245561ae3f77b16709d87f35eec650d0501679239d3449b22", size = 10693187, upload-time = "2026-06-18T18:25:08.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/44/ff6c635cf2c4f4e7b618b6640da057376baa36014695487d88aed4794268/ruff-0.15.18-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2186d9e940ae332ab293623a75b5f4fe49565f449954d50a72a046683aa6b809", size = 11208721, upload-time = "2026-06-18T18:25:41.327Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/d9/5baa2a30861adfb7022cf33c1e35b2fc18085b08c16f83eff4c7b99a5f48/ruff-0.15.18-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5c2abf140438032bc77b2284a6c9944ecd8a19e5f1c7b52b1b8e4a0a80d19a7a", size = 11678599, upload-time = "2026-06-18T18:25:13.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/1a/0725a7cfdc32ff769efb96ee782bec882e16448c5d9e3be947ec4c04ce27/ruff-0.15.18-py3-none-win32.whl", hash = "sha256:02299e6e9fa5b297a3f6d5d10d7bcd655c925b028bb8b9d4588214549c6b9ec4", size = 10901903, upload-time = "2026-06-18T18:25:24.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/51/805d9f6fb7970505c3504794a5ec350f605361b807fef4dcf214ebd35e72/ruff-0.15.18-py3-none-win_amd64.whl", hash = "sha256:dac80dc8d26b2257dbefabed62f5d255c3937b4ccb122da1fc634794fa3578b3", size = 12041189, upload-time = "2026-06-18T18:25:17.915Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/4c/67bb45e41609eb4726f1bfeb59e083cf91d14c696d4bd14c234a980be93d/ruff-0.15.18-py3-none-win_arm64.whl", hash = "sha256:b2c9257fcbd4a3e5b977a1904e6facca016bafe2edc17df24db67cfaee03b4e4", size = 11329958, upload-time = "2026-06-18T18:25:43.686Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "selenium"
|
||||
version = "4.44.0"
|
||||
version = "4.45.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
|
|
@ -1762,9 +1762,9 @@ dependencies = [
|
|||
{ name = "urllib3", extra = ["socks"] },
|
||||
{ name = "websocket-client" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2d/4a/6d0a4f4a07e2a91511a51398203ee82bf6ce644a448aaa35c59b44aa9531/selenium-4.44.0.tar.gz", hash = "sha256:b03a831fcfcab9d912b4682f60718c48a04560d6c62f7496c16b7498c9a4427e", size = 993133, upload-time = "2026-05-12T22:48:19.246Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/86/48/486aa67320f27452e9f551b8608f1a59ce7091c8fe7ebc9f4eba274775d4/selenium-4.45.0.tar.gz", hash = "sha256:563f0c4102f112df1cda30d46ce6d177b2e4a7a3d4b0756902d5dc84d3a8a365", size = 1005503, upload-time = "2026-06-16T04:43:57.915Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/bc/885047e975e996cb317db31c4551caa915aafc6befea990f082c7233adc2/selenium-4.44.0-py3-none-any.whl", hash = "sha256:d01ea3e5ecad8149460a765f7cf5177194c21dcc0173093fc05427c289b1bf24", size = 9654291, upload-time = "2026-05-12T22:48:16.836Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/8a/6ff6beb9c7c6cc642f628df9328a8b6637f86602eff8d28e70b5d4e8bca7/selenium-4.45.0-py3-none-any.whl", hash = "sha256:1fd9d0dc08192b2f8100e264ed720f83b05d2dd3a7feff673df04e0c7580df4b", size = 9536616, upload-time = "2026-06-16T04:43:55.968Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1814,36 +1814,36 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.50"
|
||||
version = "2.0.51"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/57/da/6fbf010c8ebb347679d0d100b22fe9ba5e13fd04046c5df7280d2f0bf706/sqlalchemy-2.0.50.tar.gz", hash = "sha256:af5607d11ef90fd6a5c0549fe0045dce1663d427426bcfb506dcb5346a85a3b9", size = 9907424, upload-time = "2026-05-24T19:20:04.018Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/c4/c42356b527296e9862f67990efce31ef78b4cf69cd3f80873a528a060320/sqlalchemy-2.0.50-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06a9210bdc5f4298cff0781087e2ff45683922252dacc452846373a58761f093", size = 2156697, upload-time = "2026-05-24T19:27:54.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/a1/b1a70e3c4365ac7fe9e347f3710f19b562c866fb96d45e3c891588789a7b/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b53784972ade4f8174b9aa661f31a06f8a936d2cfdd602913ff3c6dd40ae873", size = 3284260, upload-time = "2026-05-24T20:09:34.195Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/4a/f3ac3caa19f263d57b0a47f8c91bbf56583dc2d3fc63acfbf644abb24fe0/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31648fa14460537e768a7303b078e4344d208e0d23e06867c1f376a227ed82db", size = 3302280, upload-time = "2026-05-24T20:17:17.825Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/55/ccada3e3d62254587819749a0bc69f41173eb48a6e385d10e66d32a9c88e/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03f4323c980ad0e918cc9e5369b015f759f4e534db5bbaf4dc36832c10d05064", size = 3231580, upload-time = "2026-05-24T20:09:36.406Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/f6/6809349130a2de0e109e7f00fd7d431da9565b9b2868b32ee684754f672b/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b9dcc43afef8ac157cd92fce96985d6b8b0cfbd3df4d666f66b4d55a75d202f", size = 3269375, upload-time = "2026-05-24T20:17:20.34Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/84/278a811ef4e07be9c89dc5cdd7be833268509a66a68c4897cf585e67428f/sqlalchemy-2.0.50-cp313-cp313-win32.whl", hash = "sha256:60922d6599065ddca2c6f376b9aa2f41a6b85a271725e0909490bbc50b1998a5", size = 2117229, upload-time = "2026-05-24T19:50:08.215Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/1c/067cc6187ed32d2ec222fe6d2643acc1659a6d0659f8a7cbc5ad3ae83280/sqlalchemy-2.0.50-cp313-cp313-win_amd64.whl", hash = "sha256:287086e67275a212c4582d166a6fb03a65ccc5551d80866270ce0dd9f34eccd3", size = 2143126, upload-time = "2026-05-24T19:50:09.691Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/32/10ac51b4be7cdecd7e93d069251c86dfbf70b7adbd7c67b48ccea6c49e1c/sqlalchemy-2.0.50-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c966932507a4d7d0a37314927dbfcd89720e3f37d2a1e3352e7ae7939fa8e8a0", size = 2158519, upload-time = "2026-05-24T19:27:56.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/76/e703d2f7681d7d66c4c891af3f07c7ccf4c76ad7f18351de035b5eda007a/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:faffef4bcc20a1892e65e155293d99d60855bbbc79250ab712819cfd56a8e6bb", size = 3282063, upload-time = "2026-05-24T20:09:38.57Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/26/ef168b184a25701f9995e8fb7e503fafd7a99c1c77cda1bc1a26ea2ed486/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c206aec519a2e7bd08abbfb33436e325fd22c632d9c21a9047e376ce241646e", size = 3287069, upload-time = "2026-05-24T20:17:21.942Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/15/765acc2bc693bccc43ca4a95d5b69750da8aaf6db1b5c616536e087f8920/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bef4ac756363227ef6402a75fee025a4bc690f92328e825868939b3b3a446a6d", size = 3230453, upload-time = "2026-05-24T20:09:40.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/61/08e03c3adbf5db0087a0b6816746fec8f3032fb2f7fc899a9bb9b2a48ce4/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96fbee6b19c19cd1556c8bf9419447cf2ec149ffcab7ab64348c23e54ef8547f", size = 3252413, upload-time = "2026-05-24T20:17:24.067Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/0c/370a1f2db38436c615e10134c8a37de3688e74084792380695f3f5083860/sqlalchemy-2.0.50-cp314-cp314-win32.whl", hash = "sha256:8f00e3eb43ba30eb1b238ee03a8a62309486d1321eda3328bb611e0340033ad8", size = 2120063, upload-time = "2026-05-24T19:50:11.08Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/a0/fe92bb9817863bc13ba093bda931979a26cc2ca69f8e8f26d07add3d7c6f/sqlalchemy-2.0.50-cp314-cp314-win_amd64.whl", hash = "sha256:15708c613cd5005b7dffe1f66ee6a63ee8f5e46799f71c70ebad74178c676a39", size = 2145830, upload-time = "2026-05-24T19:50:12.452Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/ff/e5640a98a0b2f491eb8fde10fb6c773621a2e44340de231fafcc9370f4a9/sqlalchemy-2.0.50-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3699dac4be410e97049a1658e9480da9cde956594aa0f3aebc60b88f21c5ba70", size = 2178435, upload-time = "2026-05-24T19:42:58.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/85/337116e186f1236375b5fb70c21cfac98e8e8ab0d3a47be838dc47a59e08/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96233858e3df43932ac11589e22520da6e8aeb624b03fedfeebb0e8ea213086", size = 3566059, upload-time = "2026-05-24T20:01:20.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/34/bb0e190e161c3c2c24314a65add57218be14a4a9486886b7f5047c1ff7c8/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4e70c46fad30c3bcc6a4708bc0130a3173e11a5b25f0ea4a9d8911b450f1f52", size = 3535366, upload-time = "2026-05-24T20:03:56.768Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/5a/a7f759f97e4fd499c5d4e4488c760d5a7fbecf3028b465a04274fcd52384/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1918a3cf564d16d95bca7301005f41ab2ad50b07cd3b9da50d3ed986db148d6a", size = 3474879, upload-time = "2026-05-24T20:01:23.058Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/d9/2907ea38eb60687d297bf9c39e5ee58053c87b57fe8a9cae97090cecbf10/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b00098cdbdbd38c7be3d568b0c9c3122b8c0ec62b911b57cd5e6e0254d60a76d", size = 3486117, upload-time = "2026-05-24T20:03:59.052Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/e3/5aa06f167559f8c0bdae487e297d23ba548150ab016a3418265d617a4985/sqlalchemy-2.0.50-cp314-cp314t-win32.whl", hash = "sha256:1fbd55a969d7ac44a98e3dec75016074f809fa08f871585ace58dde110d1bf3e", size = 2150823, upload-time = "2026-05-24T20:08:58.644Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/9b/112fb8f977582d7489d036e409e3723948bcf5320b3ac465f3c481bbe8f9/sqlalchemy-2.0.50-cp314-cp314t-win_amd64.whl", hash = "sha256:c5c3cdb753a9004183e1ccb634b41611654c989e61bc68617ce878e46d6f1e51", size = 2185794, upload-time = "2026-05-24T20:09:00.319Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861, upload-time = "2026-05-24T19:59:01.119Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184, upload-time = "2026-06-15T16:08:50.374Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735, upload-time = "2026-06-15T16:19:46.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756, upload-time = "2026-06-15T16:26:41.336Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055, upload-time = "2026-06-15T16:19:49.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850, upload-time = "2026-06-15T16:26:43.017Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721, upload-time = "2026-06-15T16:23:12.36Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615, upload-time = "2026-06-15T16:23:13.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/49/a739be2e1d02a96a658eb71ab45d921c874249252358ad24a5bffdd02525/sqlalchemy-2.0.51-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6ea306caaae6bd5afd0a46050003c88f6bf33227377a49298c498c3cb88ff491", size = 2158999, upload-time = "2026-06-15T16:08:51.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/6b/2e0e38cf75c8780eca78d9b2e78164f8bcfd70125e5caa588ff5cbb9c9f4/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c45a496d6bc05dec41dcd4c3a2b183723f47473255c159cd80b503c8f246424d", size = 3282539, upload-time = "2026-06-15T16:19:51.065Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/a1/e77854cb5336fd37dc3c6ae3b71de242c98caac5725120be0b526b31cbd0/sqlalchemy-2.0.51-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4004ada0aafe8ae1991b2cd1d99c6d9146126e123bd6f883c260d974aa012e54", size = 3287545, upload-time = "2026-06-15T16:26:44.735Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/ab/9e17272fd4dac8df3b83c4fbe52b998a1c9d89a843c8c35ff29b74ff7364/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f6bcad487aee1c638d707235682fc96f741de00663619881ab235400d03289e", size = 3230929, upload-time = "2026-06-15T16:19:52.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/3c/52f408ea701781caee975606beccc48845f2aee8711ac29843d612c0306c/sqlalchemy-2.0.51-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:39a76529db6305693d8d4affa58ad5b5e2e18edd62daea628b29b97930b3513d", size = 3252888, upload-time = "2026-06-15T16:26:46.454Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/16/3efd2ee6bc4ca4693a30a1dd17a91b606cae15d517d2a4746611d9b73ce8/sqlalchemy-2.0.51-cp314-cp314-win32.whl", hash = "sha256:08a204d8b5638717c26a24df18fcf40af45a6b22e35b70b1d62f0113c2e278e8", size = 2120551, upload-time = "2026-06-15T16:23:15.629Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/78/55b12e70f45bccc40d9e483925c065027b3b98ea4cbbdf6f8c2546feaf6c/sqlalchemy-2.0.51-cp314-cp314-win_amd64.whl", hash = "sha256:96747bfbadb055466e5b46d572618170046b45ce5a4879167f50d70a5319a499", size = 2146318, upload-time = "2026-06-15T16:23:17.108Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/db/a9574ed40fed418924b1b1a3e54f47ee3963053b3d3d325a0d36b41f2c08/sqlalchemy-2.0.51-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1a213be1fcd5e49d9904c3b9939211ded90bc2a64e93f4c01963474285de", size = 2178920, upload-time = "2026-06-15T15:59:56.285Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/90/a1bb5c7cbba76b7bc1fbd586d0a5479a7bc9c27b4a8298f22ec9423b2bb3/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c6b36ed71f41942bdcd2ad2522be46bfce09d5705be5640ecf19bbc7660e4b7", size = 3566534, upload-time = "2026-06-15T15:58:35.024Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/4b/481f1fed30e0e9e8dd24aecbb49f29eb57fe7657ece5cf06ee9b84bb97d8/sqlalchemy-2.0.51-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c2c62877097e1a0db401fba5cb4debee33265e5b2a55c4ccb489c02c53b4f72", size = 3535844, upload-time = "2026-06-15T16:02:43.973Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/71/0aa64aeda645510af0a43f7d9ee70932f0d1dc4263aed34c50ee891d9df3/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0378d055e9e8cd6ce4d8dff683bdd3d7d413533c4ee51d67a2b1e0f9eacc0f23", size = 3475355, upload-time = "2026-06-15T15:58:36.592Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/db/6061db32316446135a3abae5f308d144ab988a34234726042da3e58b1c63/sqlalchemy-2.0.51-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e46fc36029eff666391e0531e5387b62ce6c4f1d8e50b3fb3099eaca1b42522", size = 3486591, upload-time = "2026-06-15T16:02:45.346Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/c9/f14fdf71bb8957e0c7e39db69bbdf12b5c80f4ef775fdfa127bf4e0d6760/sqlalchemy-2.0.51-cp314-cp314t-win32.whl", hash = "sha256:9161cfc9efce70d1715f47d6ff40f79c6778c00d53be4fbc09d70301e4b83ba7", size = 2151313, upload-time = "2026-06-15T16:03:39.127Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/c6/673e618e6f4f297e126d9b56ea2f6478708f6c1af4e3223835c22e2c3697/sqlalchemy-2.0.51-cp314-cp314t-win_amd64.whl", hash = "sha256:159bb6ba32059f57ad7375a8f50d844dd2f19d14954ecf820cd33e20debd46b2", size = 2186280, upload-time = "2026-06-15T16:03:40.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334, upload-time = "2026-06-15T16:09:22.418Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1909,14 +1909,14 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "tzlocal"
|
||||
version = "5.3.1"
|
||||
version = "5.4.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/48/55/15e2340963d2bfedcc6042da3911438fd336f8ae96b65bdbe3a29766da0c/tzlocal-5.4.3.tar.gz", hash = "sha256:3a8c9bc18cf47e1dcde252ea0e6a72a6cde320a400b6ac6db1f1f8cccd553c00", size = 30873, upload-time = "2026-06-17T04:17:41.764Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/28/fc144409c71569e928585f8f3c629d80d1ca3ef40175e9222f01588f98c9/tzlocal-5.4.3-py3-none-any.whl", hash = "sha256:24ce97bb58e2a973f7640ec2553ab4e6f6d5a0d0d1aa9dc43bca21d89e1feb82", size = 18039, upload-time = "2026-06-17T04:17:40.027Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
Loading…
Reference in a new issue