diff --git a/Dockerfile b/Dockerfile index 7ac8368..b4aa83d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,7 @@ -FROM nginx:alpine - -# Copy the static web application files -COPY index.html /usr/share/nginx/html/index.html -COPY streams.txt /usr/share/nginx/html/streams.txt - -# Nginx default port +FROM node:18-alpine +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm install --only=production +COPY . . EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml index 238963e..46d3375 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,5 +5,5 @@ services: ports: - "8080:80" volumes: - - ./streams.txt:/usr/share/nginx/html/streams.txt:ro + - ./streams.txt:/app/streams.txt restart: unless-stopped diff --git a/index.html b/index.html index 1423b57..9116bb2 100644 --- a/index.html +++ b/index.html @@ -177,16 +177,49 @@ margin-bottom: 24px; } - /* Stream Selector Title */ + /* Channels Section Header Layout */ + .channels-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; + } + .section-title { font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-secondary); - margin-bottom: 12px; font-weight: 600; } + .add-channel-btn { + background: rgba(99, 102, 241, 0.12); + border: 1px solid rgba(99, 102, 241, 0.25); + color: #818cf8; + font-size: 12px; + font-weight: 600; + padding: 6px 12px; + border-radius: 8px; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; + transition: all 0.2s ease; + } + + .add-channel-btn:hover { + background: rgba(99, 102, 241, 0.25); + border-color: var(--accent); + color: white; + } + + .add-channel-btn svg { + width: 14px; + height: 14px; + fill: currentColor; + } + /* Stream list scroll area */ .controls-list-wrapper { flex-grow: 1; @@ -380,6 +413,168 @@ text-align: center; } + /* Modal styling */ + .modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(9, 9, 11, 0.7); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s ease; + z-index: 100; + } + + .modal-backdrop.active { + opacity: 1; + pointer-events: auto; + } + + .modal-card { + background: rgba(15, 23, 42, 0.9); + border: 1px solid var(--panel-border); + border-radius: 16px; + width: 90%; + max-width: 480px; + padding: 28px; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6); + transform: scale(0.9); + transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + } + + .modal-backdrop.active .modal-card { + transform: scale(1); + } + + .modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + } + + .modal-header h2 { + font-size: 20px; + font-weight: 700; + background: linear-gradient(to right, #ffffff, #cbd5e1); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + .close-modal-btn { + background: none; + border: none; + color: var(--text-secondary); + font-size: 24px; + cursor: pointer; + line-height: 1; + transition: color 0.2s; + } + + .close-modal-btn:hover { + color: var(--text-primary); + } + + .form-group { + margin-bottom: 18px; + display: flex; + flex-direction: column; + gap: 6px; + } + + .form-group label { + font-size: 13px; + font-weight: 600; + color: var(--text-secondary); + } + + .form-group input, .form-group textarea { + background: rgba(30, 41, 59, 0.4); + border: 1px solid var(--btn-border); + border-radius: 8px; + padding: 12px; + color: var(--text-primary); + font-family: inherit; + font-size: 14px; + transition: all 0.2s; + } + + .form-group input:focus, .form-group textarea:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 10px rgba(99, 102, 241, 0.2); + background: rgba(30, 41, 59, 0.6); + } + + .form-group textarea { + height: 100px; + resize: none; + } + + .modal-error-message { + color: #fca5a5; + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.2); + border-radius: 8px; + padding: 10px; + font-size: 13px; + margin-bottom: 18px; + display: none; + line-height: 1.4; + } + + .modal-actions { + display: flex; + justify-content: flex-end; + gap: 12px; + } + + .modal-btn-primary { + background: var(--accent); + color: white; + border: none; + padding: 10px 20px; + font-weight: 600; + border-radius: 8px; + cursor: pointer; + font-size: 14px; + transition: background 0.2s; + } + + .modal-btn-primary:hover { + background: var(--accent-hover); + } + + .modal-btn-primary:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .modal-btn-secondary { + background: transparent; + color: var(--text-secondary); + border: 1px solid var(--btn-border); + padding: 10px 20px; + font-weight: 600; + border-radius: 8px; + cursor: pointer; + font-size: 14px; + transition: all 0.2s; + } + + .modal-btn-secondary:hover { + background: var(--btn-hover-bg); + color: var(--text-primary); + border-color: rgba(255, 255, 255, 0.15); + } + @keyframes spin { to { transform: rotate(360deg); } } @@ -422,7 +617,15 @@
-