kryten-webqueue 0.23.0__tar.gz → 0.24.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kryten_webqueue-0.24.0/.github/workflows/python-publish.yml +93 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/.github/workflows/release.yml +6 -12
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/CHANGELOG.md +17 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/PKG-INFO +1 -1
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/static/css/main.css +40 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/user/dashboard.html +96 -2
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/pyproject.toml +1 -1
- kryten_webqueue-0.23.0/.github/workflows/python-publish.yml +0 -75
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/.gitignore +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/README.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/config.example.json +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/deploy/kryten-webqueue.service +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/deploy/nginx-queue.conf +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/IMPLEMENTATION_SPEC.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/IMPL_API_GATE.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/IMPL_ECONOMY.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/IMPL_KRYTEN_PY.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/IMPL_ROBOT.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/PLAN_PRESENCE_AND_PROMOS.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/PRE_PLAN_GAPS.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/PRODUCT_PLAN.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/SPEC_JOBS_AND_BROWSE.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/docs/UX_POLISH_PLAN.md +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/__main__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/api_gate/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/api_gate/client.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/app.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/auth/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/auth/otp.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/auth/rate_limit.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/auth/session.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/catalog/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/catalog/db.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/catalog/images.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/catalog/mediacms.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/catalog/sync.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/config.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/_common.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/enrichmeta.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/enrichtitles.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/enrichtv.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/fetchurls.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/ytpipe/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/ytpipe/downloader.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/jobs/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/jobs/fetchurls_auth.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/jobs/manager.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/jobs/tasks.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/logging_config.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/playlists/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/playlists/bulk_add.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/playlists/fire.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/playlists/importer.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/playlists/ordering.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/playlists/scheduler.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/promos/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/promos/director.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/queue/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/queue/ordering.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/queue/poller.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/queue/presence.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/queue/shadow.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/admin_catalog.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/admin_jobs.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/admin_playlists.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/admin_promos.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/admin_queue.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/admin_schedules.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/auth.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/catalog.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/pages.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/queue.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/routes/user.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/static/js/main.js +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/index.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/playlists.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/promos.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/queue_mgmt.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/schedules.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/auth/login.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/base.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/catalog/browse.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/catalog/item_detail.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/catalog/item_not_found.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/queue/index.html +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/ws/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/ws/handler.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/ws/manager.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/__init__.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_config_persistence.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_fetchurls_sharepoint.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_phase1.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_phase2_jobs.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_phase3_jobs.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_phase4_live_fixes.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_playlist_import.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_presence_refund.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_promo_director.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_promo_pool_exclusion.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_queue_announce.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_save_results_to_playlist.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_schedule_lock.py +0 -0
- {kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/tests/test_search_facets.py +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Publish Python Package to PyPI
|
|
2
|
+
|
|
3
|
+
# Builds and publishes to PyPI. This workflow MUST run top-level (never invoked
|
|
4
|
+
# via a reusable `uses:` call). PyPI Trusted Publishing matches the OIDC
|
|
5
|
+
# `job_workflow_ref` claim — the filename of the workflow that contains the
|
|
6
|
+
# running job — against the configured publisher (python-publish.yml). Running
|
|
7
|
+
# this as a reusable workflow would change that filename and break publishing
|
|
8
|
+
# (and is explicitly unsupported by PyPI).
|
|
9
|
+
on:
|
|
10
|
+
# Primary automated path: fire after "Release Automation" (release.yml)
|
|
11
|
+
# finishes creating the tag + GitHub Release. workflow_run keeps this workflow
|
|
12
|
+
# top-level, unlike a reusable `uses:` invocation.
|
|
13
|
+
workflow_run:
|
|
14
|
+
workflows: ["Release Automation"]
|
|
15
|
+
types: [completed]
|
|
16
|
+
# Manual fallbacks: publishing a GitHub Release by hand, or pushing a tag.
|
|
17
|
+
release:
|
|
18
|
+
types: [published]
|
|
19
|
+
push:
|
|
20
|
+
tags:
|
|
21
|
+
- 'kryten-webqueue-v*'
|
|
22
|
+
- 'v*'
|
|
23
|
+
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
release-build:
|
|
29
|
+
name: Build distribution packages
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
# When triggered by release.yml's completion, only proceed on success.
|
|
32
|
+
# release/tag triggers aren't workflow_run events, so they always build.
|
|
33
|
+
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout repository
|
|
37
|
+
uses: actions/checkout@v7
|
|
38
|
+
with:
|
|
39
|
+
# For a workflow_run trigger, build the exact commit release.yml
|
|
40
|
+
# processed; otherwise build the tag/release commit.
|
|
41
|
+
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
|
|
42
|
+
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
45
|
+
with:
|
|
46
|
+
version: "latest"
|
|
47
|
+
|
|
48
|
+
- name: Set up Python
|
|
49
|
+
uses: actions/setup-python@v6
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.12"
|
|
52
|
+
|
|
53
|
+
- name: Build release distributions
|
|
54
|
+
run: |
|
|
55
|
+
uv build
|
|
56
|
+
echo "📦 Built packages:"
|
|
57
|
+
ls -lh dist/
|
|
58
|
+
|
|
59
|
+
- name: Upload distributions
|
|
60
|
+
uses: actions/upload-artifact@v7
|
|
61
|
+
with:
|
|
62
|
+
name: release-dists
|
|
63
|
+
path: dist/
|
|
64
|
+
|
|
65
|
+
pypi-publish:
|
|
66
|
+
name: Publish to PyPI
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
needs:
|
|
69
|
+
- release-build
|
|
70
|
+
environment:
|
|
71
|
+
name: pypi
|
|
72
|
+
permissions:
|
|
73
|
+
id-token: write
|
|
74
|
+
|
|
75
|
+
steps:
|
|
76
|
+
- name: Retrieve release distributions
|
|
77
|
+
uses: actions/download-artifact@v8
|
|
78
|
+
with:
|
|
79
|
+
name: release-dists
|
|
80
|
+
path: dist/
|
|
81
|
+
|
|
82
|
+
- name: Publish release distributions to PyPI
|
|
83
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
84
|
+
with:
|
|
85
|
+
packages-dir: dist/
|
|
86
|
+
skip-existing: true
|
|
87
|
+
attestations: false
|
|
88
|
+
|
|
89
|
+
- name: Success notification
|
|
90
|
+
if: success()
|
|
91
|
+
run: |
|
|
92
|
+
echo "✅ Successfully published to PyPI!"
|
|
93
|
+
echo "🔗 Package URL: https://pypi.org/project/kryten-webqueue/"
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
name: Release Automation
|
|
2
2
|
|
|
3
|
-
# Automatically create GitHub releases when version in pyproject.toml changes
|
|
3
|
+
# Automatically create GitHub releases when version in pyproject.toml changes.
|
|
4
|
+
# Publishing to PyPI is handled separately by python-publish.yml, which triggers
|
|
5
|
+
# on this workflow's completion (see its workflow_run trigger). Publishing is
|
|
6
|
+
# intentionally NOT inlined here: PyPI Trusted Publishing matches the OIDC
|
|
7
|
+
# job_workflow_ref filename, which is configured as python-publish.yml.
|
|
4
8
|
on:
|
|
5
9
|
push:
|
|
6
10
|
branches:
|
|
@@ -11,8 +15,6 @@ on:
|
|
|
11
15
|
|
|
12
16
|
permissions:
|
|
13
17
|
contents: write
|
|
14
|
-
pull-requests: read
|
|
15
|
-
id-token: write
|
|
16
18
|
|
|
17
19
|
jobs:
|
|
18
20
|
create-release:
|
|
@@ -20,7 +22,7 @@ jobs:
|
|
|
20
22
|
runs-on: ubuntu-latest
|
|
21
23
|
|
|
22
24
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v7
|
|
24
26
|
with:
|
|
25
27
|
fetch-depth: 0 # Full history for changelog
|
|
26
28
|
|
|
@@ -93,11 +95,3 @@ jobs:
|
|
|
93
95
|
if: steps.check_tag.outputs.exists == 'true'
|
|
94
96
|
run: |
|
|
95
97
|
echo "ℹ️ Release ${{ steps.version.outputs.tag }} already exists - skipping"
|
|
96
|
-
|
|
97
|
-
publish-to-pypi:
|
|
98
|
-
name: Publish to PyPI
|
|
99
|
-
needs: [create-release]
|
|
100
|
-
uses: ./.github/workflows/python-publish.yml
|
|
101
|
-
permissions:
|
|
102
|
-
id-token: write
|
|
103
|
-
contents: read
|
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.24.0] — 2026-06-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Live readability preview for the chat-color picker.** The dashboard's chat-color editor now previews the chosen color exactly as it renders in chat — a sample chat line (gray timestamp, username, message) on the near-black chat background (`#111`) — and scores it as you pick, mirroring kryten-economy's server-side guard (combined APCA lightness contrast × red-chroma penalty). A contrast badge reports **ok / warn / reject**: colors that fail the readability threshold (very dark colors and harsh pure reds) show a **reject** badge and the **Save button is disabled**, so users can't buy a color the server would refuse; borderline colors get a non-blocking **warn**. The thresholds (`reject < 30`, `warn < 40`) and background match the economy service so the dashboard and server always agree.
|
|
13
|
+
|
|
14
|
+
[0.24.0]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.24.0
|
|
15
|
+
|
|
16
|
+
## [0.23.1] — 2026-06-21
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Release CI hardening (no runtime changes).** PyPI publishing was moved out of a reusable workflow into a top-level one. PyPI Trusted Publishing matches the OIDC `job_workflow_ref` claim — the filename of the workflow containing the running job — against the configured publisher (`python-publish.yml`); invoking that workflow via a reusable `uses:` call is unsupported by PyPI and raised a "workflow misconfiguration" warning. `python-publish.yml` now triggers via `workflow_run` after `release.yml` completes (keeping it top-level so the publisher filename stays `python-publish.yml`), and `release.yml` no longer calls it reusably. All GitHub Actions were bumped off the deprecated Node 20 runtime: `actions/checkout@v7`, `actions/setup-python@v6`, `actions/upload-artifact@v7`, `actions/download-artifact@v8`, and `astral-sh/setup-uv@v8.2.0`.
|
|
21
|
+
|
|
22
|
+
[0.23.1]: https://github.com/grobertson/kryten-webqueue/releases/tag/v0.23.1
|
|
23
|
+
|
|
7
24
|
## [0.23.0] — 2026-06-21
|
|
8
25
|
|
|
9
26
|
### Fixed
|
|
@@ -1009,6 +1009,46 @@ a.np-chip {
|
|
|
1009
1009
|
text-transform: uppercase;
|
|
1010
1010
|
}
|
|
1011
1011
|
|
|
1012
|
+
/* Chat-color live preview + readability badge */
|
|
1013
|
+
.color-preview {
|
|
1014
|
+
background: #111;
|
|
1015
|
+
border: 1px solid var(--border);
|
|
1016
|
+
border-radius: var(--radius);
|
|
1017
|
+
padding: 0.6rem 0.75rem;
|
|
1018
|
+
margin: 0 0 0.6rem;
|
|
1019
|
+
font-size: 0.95rem;
|
|
1020
|
+
line-height: 1.5;
|
|
1021
|
+
}
|
|
1022
|
+
.color-preview .cp-ts { color: #AAA; }
|
|
1023
|
+
.color-preview .cp-name { font-weight: 700; }
|
|
1024
|
+
.color-preview .cp-msg { /* color set inline to the chosen value */ }
|
|
1025
|
+
.contrast-badge {
|
|
1026
|
+
display: flex;
|
|
1027
|
+
align-items: flex-start;
|
|
1028
|
+
gap: 0.5rem;
|
|
1029
|
+
font-size: 0.82rem;
|
|
1030
|
+
padding: 0.5rem 0.6rem;
|
|
1031
|
+
border-radius: var(--radius);
|
|
1032
|
+
margin-bottom: 0.75rem;
|
|
1033
|
+
border: 1px solid var(--border);
|
|
1034
|
+
}
|
|
1035
|
+
.contrast-badge .cb-icon { flex-shrink: 0; line-height: 1.3; }
|
|
1036
|
+
.contrast-badge.cb-ok {
|
|
1037
|
+
background: rgba(0, 184, 148, 0.10);
|
|
1038
|
+
border-color: var(--success, #00b894);
|
|
1039
|
+
color: var(--success, #00b894);
|
|
1040
|
+
}
|
|
1041
|
+
.contrast-badge.cb-warn {
|
|
1042
|
+
background: rgba(253, 203, 110, 0.12);
|
|
1043
|
+
border-color: var(--warning, #fdcb6e);
|
|
1044
|
+
color: var(--warning, #fdcb6e);
|
|
1045
|
+
}
|
|
1046
|
+
.contrast-badge.cb-reject {
|
|
1047
|
+
background: rgba(225, 112, 85, 0.12);
|
|
1048
|
+
border-color: var(--danger, #e17055);
|
|
1049
|
+
color: var(--danger, #e17055);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1012
1052
|
/* Transaction filter toggle */
|
|
1013
1053
|
.tx-toggle {
|
|
1014
1054
|
display: flex;
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/user/dashboard.html
RENAMED
|
@@ -293,15 +293,92 @@ async function saveGreeting() {
|
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
// ── Chat-color readability scoring (mirrors kryten-economy contrast.py) ──
|
|
297
|
+
// The economy server is authoritative and re-checks on purchase; this gives
|
|
298
|
+
// instant local preview/feedback. Defaults match the economy config defaults.
|
|
299
|
+
const CONTRAST_BG = '#111111';
|
|
300
|
+
const CONTRAST_MIN = 30.0; // reject below this combined score
|
|
301
|
+
const CONTRAST_WARN = 40.0; // warn below this
|
|
302
|
+
const CHROMA_KNEE = 160; // green+blue at which the red penalty clears
|
|
303
|
+
|
|
304
|
+
function _hexRgb(h) {
|
|
305
|
+
let s = h.replace('#', '');
|
|
306
|
+
if (s.length === 3) s = s.split('').map(c => c + c).join('');
|
|
307
|
+
return [parseInt(s.slice(0,2),16), parseInt(s.slice(2,4),16), parseInt(s.slice(4,6),16)];
|
|
308
|
+
}
|
|
309
|
+
function _screenY(h) {
|
|
310
|
+
const [r,g,b] = _hexRgb(h);
|
|
311
|
+
return 0.2126729*Math.pow(r/255,2.4) + 0.7151522*Math.pow(g/255,2.4) + 0.0721750*Math.pow(b/255,2.4);
|
|
312
|
+
}
|
|
313
|
+
function _apcaLc(textHex, bgHex) {
|
|
314
|
+
let yt = _screenY(textHex), yb = _screenY(bgHex);
|
|
315
|
+
if (yt <= 0.022) yt += Math.pow(0.022 - yt, 1.414);
|
|
316
|
+
if (yb <= 0.022) yb += Math.pow(0.022 - yb, 1.414);
|
|
317
|
+
if (Math.abs(yb - yt) < 0.0005) return 0;
|
|
318
|
+
let out;
|
|
319
|
+
if (yb > yt) { const s = (Math.pow(yb,0.56)-Math.pow(yt,0.57))*1.14; out = s < 0.1 ? 0 : s - 0.027; }
|
|
320
|
+
else { const s = (Math.pow(yb,0.65)-Math.pow(yt,0.62))*1.14; out = s > -0.1 ? 0 : s + 0.027; }
|
|
321
|
+
return Math.abs(out * 100);
|
|
322
|
+
}
|
|
323
|
+
function _chromaFactor(h) {
|
|
324
|
+
const [, g, b] = _hexRgb(h);
|
|
325
|
+
return Math.min(g + b, CHROMA_KNEE) / CHROMA_KNEE;
|
|
326
|
+
}
|
|
327
|
+
function classifyColor(hex) {
|
|
328
|
+
const lc = _apcaLc(hex, CONTRAST_BG);
|
|
329
|
+
const score = Math.round(lc * _chromaFactor(hex) * 10) / 10;
|
|
330
|
+
let level;
|
|
331
|
+
if (score < CONTRAST_MIN) level = 'reject';
|
|
332
|
+
else if (score < CONTRAST_WARN) level = 'warn';
|
|
333
|
+
else level = 'ok';
|
|
334
|
+
return { score, level };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function updateColorPreview(hex) {
|
|
338
|
+
const msg = document.getElementById('cp-msg');
|
|
339
|
+
const sw = document.getElementById('color-input');
|
|
340
|
+
const badge = document.getElementById('contrast-badge');
|
|
341
|
+
const save = document.getElementById('color-save');
|
|
342
|
+
const valid = /^#[0-9A-Fa-f]{6}$/.test(hex);
|
|
343
|
+
if (msg) msg.style.color = valid ? hex : '#FFFFFF';
|
|
344
|
+
if (!valid || !badge || !save) return;
|
|
345
|
+
const { score, level } = classifyColor(hex);
|
|
346
|
+
if (level === 'reject') {
|
|
347
|
+
badge.className = 'contrast-badge cb-reject';
|
|
348
|
+
badge.innerHTML = '<span class="cb-icon">⛔</span><span>Too hard to read on the dark chat background. ' +
|
|
349
|
+
'Try a lighter shade, or add some green/blue (e.g. pink instead of pure red).</span>';
|
|
350
|
+
save.disabled = true;
|
|
351
|
+
save.title = 'This color is too low-contrast to use';
|
|
352
|
+
} else if (level === 'warn') {
|
|
353
|
+
badge.className = 'contrast-badge cb-warn';
|
|
354
|
+
badge.innerHTML = '<span class="cb-icon">⚠️</span><span>Readable, but low-contrast — viewers with vision ' +
|
|
355
|
+
'issues may struggle to read your chat lines.</span>';
|
|
356
|
+
save.disabled = false;
|
|
357
|
+
save.title = '';
|
|
358
|
+
} else {
|
|
359
|
+
badge.className = 'contrast-badge cb-ok';
|
|
360
|
+
badge.innerHTML = '<span class="cb-icon">✅</span><span>Good contrast on the chat background.</span>';
|
|
361
|
+
save.disabled = false;
|
|
362
|
+
save.title = '';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
296
366
|
function editColor() {
|
|
297
367
|
if (!accountState) return;
|
|
298
368
|
const sym = accountState.currency_symbol || 'Z';
|
|
299
369
|
const cur = (accountState.vanity && accountState.vanity.custom_color) || '';
|
|
300
370
|
const cost = (accountState.vanity_costs && accountState.vanity_costs.custom_color) || 0;
|
|
301
371
|
const seed = /^#[0-9A-Fa-f]{6}$/.test(cur) ? cur : '#6C5CE7';
|
|
372
|
+
const uname = (accountState.username) || 'You';
|
|
302
373
|
showModal(`
|
|
303
374
|
<h3>Chat color</h3>
|
|
304
375
|
<p class="muted">Pick a color for your chat messages. Cost: ${fmt(cost, sym)} per update.</p>
|
|
376
|
+
<div class="color-preview">
|
|
377
|
+
<span class="cp-ts">[12:34:56]</span>
|
|
378
|
+
<span class="cp-name" id="cp-name">${escapeHtml(uname)}:</span>
|
|
379
|
+
<span class="cp-msg" id="cp-msg">This is how your chat messages will look.</span>
|
|
380
|
+
</div>
|
|
381
|
+
<div class="contrast-badge cb-ok" id="contrast-badge"></div>
|
|
305
382
|
<div class="color-picker-row">
|
|
306
383
|
<input type="color" id="color-input" value="${seed}">
|
|
307
384
|
<input type="text" id="color-hex" class="color-hex" value="${escapeHtml(cur || seed)}" maxlength="7" placeholder="#RRGGBB">
|
|
@@ -312,14 +389,25 @@ function editColor() {
|
|
|
312
389
|
</div>`);
|
|
313
390
|
const picker = document.getElementById('color-input');
|
|
314
391
|
const hex = document.getElementById('color-hex');
|
|
315
|
-
|
|
392
|
+
const normalize = (v) => {
|
|
393
|
+
v = v.trim();
|
|
394
|
+
if (!v.startsWith('#')) v = '#' + v;
|
|
395
|
+
return v.toUpperCase();
|
|
396
|
+
};
|
|
397
|
+
picker.addEventListener('input', () => {
|
|
398
|
+
hex.value = picker.value.toUpperCase();
|
|
399
|
+
updateColorPreview(picker.value.toUpperCase());
|
|
400
|
+
});
|
|
316
401
|
hex.addEventListener('input', () => {
|
|
317
402
|
const val = hex.value.trim();
|
|
318
403
|
if (/^#?[0-9A-Fa-f]{6}$/.test(val)) {
|
|
319
|
-
|
|
404
|
+
const norm = normalize(val);
|
|
405
|
+
picker.value = norm;
|
|
406
|
+
updateColorPreview(norm);
|
|
320
407
|
}
|
|
321
408
|
});
|
|
322
409
|
document.getElementById('color-save').addEventListener('click', saveColor);
|
|
410
|
+
updateColorPreview(normalize(cur || seed));
|
|
323
411
|
}
|
|
324
412
|
|
|
325
413
|
async function saveColor() {
|
|
@@ -329,6 +417,12 @@ async function saveColor() {
|
|
|
329
417
|
return;
|
|
330
418
|
}
|
|
331
419
|
if (!value.startsWith('#')) value = '#' + value;
|
|
420
|
+
value = value.toUpperCase();
|
|
421
|
+
// Block locally on reject (the server enforces this too).
|
|
422
|
+
if (classifyColor(value).level === 'reject') {
|
|
423
|
+
showToast('That color is too low-contrast to use on the chat background', 'error');
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
332
426
|
const btn = document.getElementById('color-save');
|
|
333
427
|
btn.disabled = true;
|
|
334
428
|
let resp;
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
name: Publish Python Package to PyPI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_call:
|
|
5
|
-
release:
|
|
6
|
-
types: [published]
|
|
7
|
-
push:
|
|
8
|
-
tags:
|
|
9
|
-
- 'kryten-webqueue-v*'
|
|
10
|
-
- 'v*'
|
|
11
|
-
|
|
12
|
-
permissions:
|
|
13
|
-
contents: read
|
|
14
|
-
id-token: write
|
|
15
|
-
|
|
16
|
-
jobs:
|
|
17
|
-
release-build:
|
|
18
|
-
name: Build distribution packages
|
|
19
|
-
runs-on: ubuntu-latest
|
|
20
|
-
|
|
21
|
-
steps:
|
|
22
|
-
- name: Checkout repository
|
|
23
|
-
uses: actions/checkout@v4
|
|
24
|
-
|
|
25
|
-
- name: Install uv
|
|
26
|
-
uses: astral-sh/setup-uv@v4
|
|
27
|
-
with:
|
|
28
|
-
version: "latest"
|
|
29
|
-
|
|
30
|
-
- name: Set up Python
|
|
31
|
-
uses: actions/setup-python@v5
|
|
32
|
-
with:
|
|
33
|
-
python-version: "3.12"
|
|
34
|
-
|
|
35
|
-
- name: Build release distributions
|
|
36
|
-
run: |
|
|
37
|
-
uv build
|
|
38
|
-
echo "📦 Built packages:"
|
|
39
|
-
ls -lh dist/
|
|
40
|
-
|
|
41
|
-
- name: Upload distributions
|
|
42
|
-
uses: actions/upload-artifact@v4
|
|
43
|
-
with:
|
|
44
|
-
name: release-dists
|
|
45
|
-
path: dist/
|
|
46
|
-
|
|
47
|
-
pypi-publish:
|
|
48
|
-
name: Publish to PyPI
|
|
49
|
-
runs-on: ubuntu-latest
|
|
50
|
-
needs:
|
|
51
|
-
- release-build
|
|
52
|
-
environment:
|
|
53
|
-
name: pypi
|
|
54
|
-
permissions:
|
|
55
|
-
id-token: write
|
|
56
|
-
|
|
57
|
-
steps:
|
|
58
|
-
- name: Retrieve release distributions
|
|
59
|
-
uses: actions/download-artifact@v4
|
|
60
|
-
with:
|
|
61
|
-
name: release-dists
|
|
62
|
-
path: dist/
|
|
63
|
-
|
|
64
|
-
- name: Publish release distributions to PyPI
|
|
65
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
66
|
-
with:
|
|
67
|
-
packages-dir: dist/
|
|
68
|
-
skip-existing: true
|
|
69
|
-
attestations: false
|
|
70
|
-
|
|
71
|
-
- name: Success notification
|
|
72
|
-
if: success()
|
|
73
|
-
run: |
|
|
74
|
-
echo "✅ Successfully published to PyPI!"
|
|
75
|
-
echo "🔗 Package URL: https://pypi.org/project/kryten-webqueue/"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/_common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/enrichtv.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/cmsutils/fetchurls.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/ytpipe/__init__.py
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/integrations/ytpipe/downloader.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/index.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/playlists.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/promos.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/queue_mgmt.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/admin/schedules.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/catalog/browse.html
RENAMED
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/catalog/item_detail.html
RENAMED
|
File without changes
|
|
File without changes
|
{kryten_webqueue-0.23.0 → kryten_webqueue-0.24.0}/kryten_webqueue/templates/queue/index.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|