refactor ciagl dalszy
This commit is contained in:
688
static/css/main.css
Normal file
688
static/css/main.css
Normal file
@@ -0,0 +1,688 @@
|
||||
:root {
|
||||
--bg: #0f1115;
|
||||
--bg-elev: #131722;
|
||||
--card: #161b26;
|
||||
--text: #e7eef7;
|
||||
--muted: #a9b4c3;
|
||||
--border: #243043;
|
||||
--brand: #5b9dff;
|
||||
--brand-2: #7bd4ff;
|
||||
--success: #29c36a;
|
||||
--danger: #ff5d5d;
|
||||
--shadow: 0 10px 30px rgba(0, 0, 0, .35);
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--bg: #f6f8fb;
|
||||
--bg-elev: #fff;
|
||||
--card: #fff;
|
||||
--text: #1d2433;
|
||||
--muted: #5b6678;
|
||||
--border: #e6eaf2;
|
||||
--brand: #0054e6;
|
||||
--brand-2: #3aa2ff;
|
||||
--success: #1a9a56;
|
||||
--danger: #d14646;
|
||||
--shadow: 0 8px 24px rgba(0, 0, 0, .08);
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: ui-sans-serif, system-ui, "Segoe UI", Roboto, Arial, sans-serif;
|
||||
background:
|
||||
radial-gradient(1200px 600px at 10% -10%, rgba(91, 157, 255, .08), transparent 60%),
|
||||
radial-gradient(900px 500px at 110% 0%, rgba(123, 212, 255, .10), transparent 60%),
|
||||
var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 18px;
|
||||
background: var(--bg-elev);
|
||||
border-bottom: 1px solid var(--border);
|
||||
backdrop-filter: saturate(140%) blur(8px);
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
font-weight: 700;
|
||||
letter-spacing: .2px
|
||||
}
|
||||
|
||||
.brand svg {
|
||||
color: var(--brand)
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
.container {
|
||||
max-width: 980px;
|
||||
margin: 24px auto;
|
||||
padding: 0 16px;
|
||||
display: grid;
|
||||
gap: 18px
|
||||
}
|
||||
|
||||
.card {
|
||||
background: linear-gradient(180deg, var(--card), color-mix(in srgb, var(--card) 80%, #000 20%));
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.section-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px dashed var(--border)
|
||||
}
|
||||
|
||||
/* Hero */
|
||||
.hero {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24px;
|
||||
gap: 18px
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
margin: 0 0 6px;
|
||||
font-size: clamp(22px, 3.4vw, 30px)
|
||||
}
|
||||
|
||||
.hero .muted {
|
||||
color: var(--muted)
|
||||
}
|
||||
|
||||
.hero-cta .large {
|
||||
font-size: 1.05rem;
|
||||
padding: 14px 20px
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
gap: 14px
|
||||
}
|
||||
|
||||
.col-12 {
|
||||
grid-column: span 12
|
||||
}
|
||||
|
||||
.col-6 {
|
||||
grid-column: span 6
|
||||
}
|
||||
|
||||
@media (max-width:720px) {
|
||||
.col-6 {
|
||||
grid-column: span 12
|
||||
}
|
||||
}
|
||||
|
||||
/* Form */
|
||||
.form-card {
|
||||
padding: 16px
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px
|
||||
}
|
||||
|
||||
input[type="url"],
|
||||
input[type="text"],
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(0deg, var(--bg-elev), var(--bg-elev));
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
outline: none;
|
||||
transition: border .15s, box-shadow .15s, transform .05s;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
select:focus {
|
||||
border-color: color-mix(in srgb, var(--brand) 60%, var(--border) 40%);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 30%, transparent);
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: var(--muted);
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
font-size: .9rem
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--danger);
|
||||
min-height: 1.2em;
|
||||
margin-top: 6px;
|
||||
font-size: .9rem
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
/* Result */
|
||||
.result-box {
|
||||
margin-top: 14px;
|
||||
padding: 12px;
|
||||
border: 1px dashed var(--border);
|
||||
border-radius: 12px;
|
||||
background: var(--bg-elev)
|
||||
}
|
||||
|
||||
.result-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.result-row input[readonly] {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0
|
||||
}
|
||||
|
||||
.result-buttons {
|
||||
display: flex;
|
||||
gap: 8px
|
||||
}
|
||||
|
||||
/* Recent */
|
||||
.recent-card {
|
||||
padding: 0
|
||||
}
|
||||
|
||||
.recent-list {
|
||||
padding: 12px
|
||||
}
|
||||
|
||||
.link-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(180deg, var(--bg-elev), color-mix(in srgb, var(--bg-elev) 92%, #000 8%));
|
||||
transition: transform .12s ease-out, border-color .15s;
|
||||
}
|
||||
|
||||
.link-item:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: color-mix(in srgb, var(--brand) 40%, var(--border) 60%)
|
||||
}
|
||||
|
||||
.link-main {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin
|
||||
}
|
||||
|
||||
.link-main {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
gap: 8px;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.link-main .mono.ellipsis:first-child {
|
||||
min-width: 0
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.arrow {
|
||||
opacity: .6
|
||||
}
|
||||
|
||||
.link-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: var(--muted);
|
||||
font-size: .92rem
|
||||
}
|
||||
|
||||
.link-actions {
|
||||
display: flex;
|
||||
gap: 6px
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.site-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 28px auto;
|
||||
padding: 10px 16px;
|
||||
max-width: 980px;
|
||||
color: var(--muted)
|
||||
}
|
||||
|
||||
.site-footer a {
|
||||
color: color-mix(in srgb, var(--brand) 80%, var(--text) 20%)
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
appearance: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
font-weight: 700;
|
||||
letter-spacing: .2px;
|
||||
background: linear-gradient(180deg, color-mix(in srgb, var(--brand) 80%, var(--brand-2) 20%), var(--brand));
|
||||
color: #fff;
|
||||
box-shadow: 0 10px 20px color-mix(in srgb, var(--brand) 35%, transparent);
|
||||
transition: transform .04s ease, filter .15s ease, box-shadow .15s ease;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
filter: brightness(1.05)
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: translateY(1px)
|
||||
}
|
||||
|
||||
.btn.outline {
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
border: 1px solid color-mix(in srgb, var(--brand) 60%, var(--border) 40%)
|
||||
}
|
||||
|
||||
.btn.ghost {
|
||||
background: transparent;
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border)
|
||||
}
|
||||
|
||||
.btn.tiny {
|
||||
padding: 6px 10px;
|
||||
font-weight: 600;
|
||||
border-radius: 10px
|
||||
}
|
||||
|
||||
.btn.large {
|
||||
padding: 14px 20px;
|
||||
border-radius: 14px
|
||||
}
|
||||
|
||||
/* Toast */
|
||||
#toast {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 24px;
|
||||
transform: translateX(-50%) translateY(20px);
|
||||
background: var(--bg-elev);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
opacity: 0;
|
||||
box-shadow: var(--shadow);
|
||||
pointer-events: none;
|
||||
transition: opacity .2s, transform .2s;
|
||||
}
|
||||
|
||||
#toast.show {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0)
|
||||
}
|
||||
|
||||
/* Links & helpers */
|
||||
a {
|
||||
color: color-mix(in srgb, var(--brand) 80%, var(--text) 20%);
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
select.select,
|
||||
.select {
|
||||
appearance: none;
|
||||
background: var(--bg-elev);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border)
|
||||
}
|
||||
|
||||
select option {
|
||||
background: var(--bg-elev);
|
||||
color: var(--text)
|
||||
}
|
||||
|
||||
select:focus {
|
||||
border-color: color-mix(in srgb, var(--brand) 60%, var(--border) 40%);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 30%, transparent);
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
/* Stats */
|
||||
.kpi-card {
|
||||
padding: 16px
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 10px 12px 0
|
||||
}
|
||||
|
||||
.kpi-grid {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr))
|
||||
}
|
||||
|
||||
@media (max-width:980px) {
|
||||
.kpi-grid {
|
||||
grid-template-columns: repeat(3, 1fr)
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:640px) {
|
||||
.kpi-grid {
|
||||
grid-template-columns: repeat(2, 1fr)
|
||||
}
|
||||
}
|
||||
|
||||
.kpi {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
background: linear-gradient(180deg, var(--bg-elev), color-mix(in srgb, var(--bg-elev) 92%, #000 8%))
|
||||
}
|
||||
|
||||
.kpi-label {
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
font-size: .9rem
|
||||
}
|
||||
|
||||
.kpi-value {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 800;
|
||||
margin-top: 4px
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: .98rem
|
||||
}
|
||||
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
vertical-align: top
|
||||
}
|
||||
|
||||
.data-table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: var(--bg-elev);
|
||||
z-index: 1
|
||||
}
|
||||
|
||||
.data-table .right {
|
||||
text-align: right
|
||||
}
|
||||
|
||||
.scrollbox {
|
||||
max-height: 320px;
|
||||
overflow: auto;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
background: linear-gradient(180deg, var(--bg-elev), color-mix(in srgb, var(--bg-elev) 92%, #000 8%));
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.subhead {
|
||||
margin: 10px 6px
|
||||
}
|
||||
|
||||
.table-filter {
|
||||
padding: 8px 12px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-elev);
|
||||
color: var(--text);
|
||||
min-width: 220px
|
||||
}
|
||||
|
||||
/* Stats wide container */
|
||||
.container.container--wide {
|
||||
max-width: 1280px;
|
||||
padding: 0 20px
|
||||
}
|
||||
|
||||
.container.container--wide .card {
|
||||
padding: 16px
|
||||
}
|
||||
|
||||
.container.container--wide .section-head {
|
||||
padding: 12px 4px;
|
||||
border-bottom: 1px dashed var(--border)
|
||||
}
|
||||
|
||||
.container.container--wide .data-table {
|
||||
table-layout: fixed;
|
||||
font-size: .96rem;
|
||||
line-height: 1.35
|
||||
}
|
||||
|
||||
.container.container--wide .data-table th,
|
||||
.container.container--wide .data-table td {
|
||||
padding: 8px 10px
|
||||
}
|
||||
|
||||
.container.container--wide .data-table td:nth-child(2),
|
||||
.container.container--wide .data-table td:nth-child(5) {
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.container.container--wide .data-table tbody tr:nth-child(odd) td {
|
||||
background: color-mix(in srgb, var(--bg-elev) 96%, #000 4%)
|
||||
}
|
||||
|
||||
.container.container--wide .table-wrap {
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
-webkit-overflow-scrolling: touch
|
||||
}
|
||||
|
||||
.container.container--wide .kpi-value {
|
||||
font-size: 1.5rem
|
||||
}
|
||||
|
||||
@media (max-width:720px) {
|
||||
.container.container--wide {
|
||||
max-width: 100%;
|
||||
padding: 0 12px
|
||||
}
|
||||
|
||||
.container.container--wide .data-table {
|
||||
font-size: .94rem
|
||||
}
|
||||
}
|
||||
|
||||
/* Error page */
|
||||
.error-card {
|
||||
padding: 16px
|
||||
}
|
||||
|
||||
.error-hero {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
align-items: center;
|
||||
padding: 8px 6px 16px
|
||||
}
|
||||
|
||||
.error-illustration {
|
||||
font-size: clamp(44px, 8vw, 72px);
|
||||
filter: drop-shadow(0 10px 20px rgba(0, 0, 0, .25))
|
||||
}
|
||||
|
||||
.error-main {
|
||||
flex: 1 1 auto
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--danger) 18%, var(--bg-elev));
|
||||
border: 1px solid color-mix(in srgb, var(--danger) 60%, var(--border) 40%);
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
letter-spacing: .2px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
margin: 4px 0 6px;
|
||||
font-size: clamp(22px, 3.4vw, 28px)
|
||||
}
|
||||
|
||||
.error-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 10px
|
||||
}
|
||||
|
||||
.error-details {
|
||||
margin-top: 12px
|
||||
}
|
||||
|
||||
.error-details>summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: var(--bg-elev);
|
||||
color: var(--text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.error-details>summary::-webkit-details-marker {
|
||||
display: none
|
||||
}
|
||||
|
||||
.error-details[open]>summary {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0
|
||||
}
|
||||
|
||||
.summary-title {
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
.summary-hint {
|
||||
color: var(--muted);
|
||||
font-size: .9rem
|
||||
}
|
||||
|
||||
.details-body {
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
border-radius: 0 0 10px 10px;
|
||||
background: var(--bg-elev)
|
||||
}
|
||||
|
||||
#error-dump {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
max-height: 360px;
|
||||
overflow: auto;
|
||||
background: linear-gradient(180deg, var(--bg-elev), color-mix(in srgb, var(--bg-elev) 92%, #000 8%));
|
||||
border-bottom: 1px solid var(--border);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@media (max-width:720px) {
|
||||
.error-card {
|
||||
padding: 12px
|
||||
}
|
||||
|
||||
.error-details>summary {
|
||||
padding: 8px 10px
|
||||
}
|
||||
|
||||
#error-dump {
|
||||
max-height: 300px
|
||||
}
|
||||
}
|
25
static/js/error.js
Normal file
25
static/js/error.js
Normal file
@@ -0,0 +1,25 @@
|
||||
(function () {
|
||||
const t = localStorage.getItem('theme') || 'dark';
|
||||
document.documentElement.setAttribute('data-theme', t);
|
||||
// prosty "try again"
|
||||
document.querySelector('[data-action="try-again"]')?.addEventListener('click', () => {
|
||||
location.reload();
|
||||
});
|
||||
// kopiowanie logs
|
||||
document.querySelector('[data-action="copy-text"]')?.addEventListener('click', (e) => {
|
||||
const sel = e.currentTarget.getAttribute('data-target');
|
||||
const el = sel && document.querySelector(sel);
|
||||
if (!el) return;
|
||||
const txt = el.textContent || '';
|
||||
navigator.clipboard.writeText(txt).then(() => {
|
||||
const toast = document.getElementById('toast');
|
||||
if (toast) {
|
||||
toast.textContent = 'Copied!';
|
||||
toast.classList.add('show');
|
||||
setTimeout(() => toast.classList.remove('show'), 1200);
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
|
161
static/js/main.js
Normal file
161
static/js/main.js
Normal file
@@ -0,0 +1,161 @@
|
||||
(function () {
|
||||
|
||||
const $ = (q, c = document) => c.querySelector(q);
|
||||
const $$ = (q, c = document) => Array.from(c.querySelectorAll(q));
|
||||
const setTheme = (t) => { document.documentElement.setAttribute('data-theme', t); try { localStorage.setItem('theme', t) } catch { } };
|
||||
const toast = (msg) => {
|
||||
const el = $('#toast'); if (!el) return;
|
||||
el.textContent = msg; el.classList.add('show');
|
||||
clearTimeout(el._t); el._t = setTimeout(() => el.classList.remove('show'), 2000);
|
||||
};
|
||||
const host = () => `${location.protocol}//${location.host}`;
|
||||
|
||||
function buildLink(url, ip) {
|
||||
if (!url || !ip) return '';
|
||||
try {
|
||||
const enc = encodeURIComponent(url);
|
||||
const ipClean = (ip || '').trim();
|
||||
return `${host()}/convert?url=${enc}&ip=${encodeURIComponent(ipClean)}`;
|
||||
} catch { return ''; }
|
||||
}
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
const t = e.target.closest('[data-action="toggle-theme"]');
|
||||
if (t) {
|
||||
e.preventDefault();
|
||||
const cur = document.documentElement.getAttribute('data-theme') || 'dark';
|
||||
setTheme(cur === 'dark' ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
|
||||
const urlInput = $('#url-input');
|
||||
const ipInput = $('#ip-input');
|
||||
const ipPreset = $('#ip-preset');
|
||||
const out = $('#generated-link');
|
||||
const openBtn = $('#open-link');
|
||||
|
||||
function updatePreview() {
|
||||
const link = buildLink(urlInput.value.trim(), ipInput.value.trim());
|
||||
out.value = link || '';
|
||||
if (link) {
|
||||
openBtn.setAttribute('href', link);
|
||||
openBtn.setAttribute('aria-disabled', 'false');
|
||||
} else {
|
||||
openBtn.setAttribute('href', '#');
|
||||
openBtn.setAttribute('aria-disabled', 'true');
|
||||
}
|
||||
$('.result-box')?.setAttribute('data-state', link ? 'ready' : 'empty');
|
||||
}
|
||||
|
||||
['input', 'change', 'blur'].forEach(evt => {
|
||||
urlInput?.addEventListener(evt, updatePreview);
|
||||
ipInput?.addEventListener(evt, updatePreview);
|
||||
});
|
||||
|
||||
ipPreset?.addEventListener('change', () => {
|
||||
const v = ipPreset.value;
|
||||
if (!v) return;
|
||||
if (v !== 'custom') ipInput.value = v;
|
||||
ipInput.focus();
|
||||
updatePreview();
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
let t = e.target;
|
||||
|
||||
if (t.closest('[data-action="copy"]')) {
|
||||
e.preventDefault();
|
||||
const btn = t.closest('[data-action="copy"]');
|
||||
const sel = btn.getAttribute('data-target') || '#generated-link';
|
||||
const el = $(sel);
|
||||
if (!el) return;
|
||||
const text = el.value || el.textContent || '';
|
||||
navigator.clipboard?.writeText(text).then(() => {
|
||||
btn.classList.add('copied'); setTimeout(() => btn.classList.remove('copied'), 1200);
|
||||
toast('Link copied');
|
||||
}).catch(() => {
|
||||
// Fallback
|
||||
const range = document.createRange(); range.selectNodeContents(el);
|
||||
const selObj = getSelection(); selObj.removeAllRanges(); selObj.addRange(range);
|
||||
try { document.execCommand('copy'); toast('Link copied'); } catch { }
|
||||
selObj.removeAllRanges();
|
||||
});
|
||||
}
|
||||
|
||||
if (t.closest('[data-action="copy-text"]')) {
|
||||
e.preventDefault();
|
||||
const btn = t.closest('[data-action="copy-text"]');
|
||||
const text = btn.getAttribute('data-text') || '';
|
||||
if (!text) return;
|
||||
navigator.clipboard?.writeText(text).then(() => toast('Copied'));
|
||||
}
|
||||
|
||||
if (t.closest('[data-action="clear"]')) {
|
||||
e.preventDefault();
|
||||
urlInput.value = '';
|
||||
|
||||
updatePreview();
|
||||
urlInput.focus();
|
||||
}
|
||||
|
||||
if (t.closest('[data-action="collapse"]')) {
|
||||
e.preventDefault();
|
||||
const btn = t.closest('[data-action="collapse"]');
|
||||
const panel = $('#' + (btn.getAttribute('aria-controls') || ''));
|
||||
if (!panel) return;
|
||||
const expanded = btn.getAttribute('aria-expanded') === 'true';
|
||||
btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
|
||||
panel.style.display = expanded ? 'none' : '';
|
||||
}
|
||||
});
|
||||
|
||||
function showError(input, msg) {
|
||||
const id = input.getAttribute('id');
|
||||
const box = document.querySelector(`.error[data-error-for="${id}"]`);
|
||||
if (box) box.textContent = msg || '';
|
||||
input.setAttribute('aria-invalid', msg ? 'true' : 'false');
|
||||
}
|
||||
|
||||
urlInput?.addEventListener('blur', () => {
|
||||
const v = urlInput.value.trim();
|
||||
if (!v) return showError(urlInput, '');
|
||||
try { new URL(v); showError(urlInput, ''); }
|
||||
catch { showError(urlInput, 'Invalid URL'); }
|
||||
});
|
||||
|
||||
ipInput?.addEventListener('blur', () => {
|
||||
const v = ipInput.value.trim();
|
||||
if (!v) return showError(ipInput, '');
|
||||
const ok = /^\b\d{1,3}(?:\.\d{1,3}){3}\b$/.test(v);
|
||||
showError(ipInput, ok ? '' : 'Invalid IPv4 address');
|
||||
});
|
||||
|
||||
|
||||
(function init() {
|
||||
const serverLink = out?.value?.trim();
|
||||
if (serverLink) {
|
||||
$('.result-box')?.setAttribute('data-state', 'ready');
|
||||
openBtn?.setAttribute('aria-disabled', 'false');
|
||||
} else {
|
||||
updatePreview();
|
||||
}
|
||||
})();
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'c') {
|
||||
const text = out?.value?.trim(); if (!text) return;
|
||||
navigator.clipboard?.writeText(text).then(() => toast('Link copied'));
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
function updateThemeColor() {
|
||||
const meta = document.querySelector('meta[name="theme-color"]');
|
||||
if (!meta) return;
|
||||
const isLight = document.documentElement.getAttribute('data-theme') === 'light';
|
||||
meta.setAttribute('content', isLight ? '#f6f8fb' : '#0f1115');
|
||||
}
|
||||
|
||||
const _setTheme = setTheme;
|
||||
setTheme = function (t) { _setTheme(t); updateThemeColor(); };
|
||||
document.addEventListener('DOMContentLoaded', updateThemeColor);
|
11
static/js/stats.js
Normal file
11
static/js/stats.js
Normal file
@@ -0,0 +1,11 @@
|
||||
document.addEventListener('input', (e) => {
|
||||
const el = e.target.closest('[data-action="filter-table"]');
|
||||
if (!el) return;
|
||||
const table = document.querySelector(el.getAttribute('data-target') || '');
|
||||
if (!table) return;
|
||||
const q = (el.value || '').toLowerCase();
|
||||
table.querySelectorAll('tbody tr').forEach(tr => {
|
||||
const text = (tr.innerText || tr.textContent || '').toLowerCase();
|
||||
tr.style.display = text.includes(q) ? '' : 'none';
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user