nekostream 2026.2.1 → 2026.2.3

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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/player-main.js +22 -7
  3. package/player.html +123 -48
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nekostream",
3
- "version": "2026.2.1",
3
+ "version": "2026.2.3",
4
4
  "description": "Bettter version of NekoStream ",
5
5
  "keywords": [
6
6
  "anime",
package/player-main.js CHANGED
@@ -4,13 +4,28 @@ const path = require('path')
4
4
 
5
5
  let mainWindow = null
6
6
 
7
- function logToRenderer(msg) {
8
- if (mainWindow && !mainWindow.isDestroyed() && mainWindow.webContents) {
9
- mainWindow.webContents.send('main-log', msg);
10
- }
11
- }
12
-
13
- function createWindow() {
7
+ function logToRenderer(msg) {
8
+ if (mainWindow && !mainWindow.isDestroyed() && mainWindow.webContents) {
9
+ mainWindow.webContents.send('main-log', msg);
10
+ }
11
+ }
12
+
13
+ ipcMain.on('player:minimize', () => {
14
+ if (mainWindow && !mainWindow.isDestroyed()) {
15
+ mainWindow.minimize()
16
+ }
17
+ })
18
+
19
+ ipcMain.on('player:toggle-maximize', () => {
20
+ if (!mainWindow || mainWindow.isDestroyed()) return
21
+ if (mainWindow.isMaximized()) {
22
+ mainWindow.unmaximize()
23
+ } else {
24
+ mainWindow.maximize()
25
+ }
26
+ })
27
+
28
+ function createWindow() {
14
29
  const streamData = process.env.NEKOSTREAM_CLI_STREAM
15
30
  if (!streamData) {
16
31
  console.error('No stream data provided.')
package/player.html CHANGED
@@ -14,48 +14,107 @@
14
14
  overflow: hidden;
15
15
  font-family: sans-serif;
16
16
  }
17
- #player-container {
18
- width: 100%;
19
- height: 100%;
20
- }
17
+ #player-container {
18
+ width: 100%;
19
+ height: 100%;
20
+ }
21
21
  webview {
22
22
  width: 100%;
23
23
  height: 100%;
24
24
  border: none;
25
25
  }
26
- #close-btn, .dev-btn {
27
- position: absolute;
28
- top: 20px;
29
- z-index: 9999;
30
- background: rgba(0, 0, 0, 0.5);
31
- color: white;
32
- border: 1px solid rgba(255, 255, 255, 0.2);
33
- padding: 8px 16px;
34
- cursor: pointer;
35
- border-radius: 4px;
36
- font-size: 14px;
37
- transition: background 0.2s;
38
- -webkit-app-region: no-drag;
39
- }
40
- #close-btn {
41
- right: 20px;
42
- }
43
- #close-btn:hover {
44
- background: rgba(255, 0, 0, 0.7);
45
- }
46
- .dev-btn:hover {
47
- background: rgba(37, 99, 235, 0.7);
48
- }
49
- .drag-region {
50
- position: absolute;
51
- top: 0;
52
- left: 0;
53
- right: 80px;
54
- height: 40px;
55
- -webkit-app-region: drag;
56
- z-index: 9998;
57
- }
58
- #loading-overlay {
26
+ #top-nav {
27
+ position: absolute;
28
+ top: 0;
29
+ left: 0;
30
+ right: 0;
31
+ height: 34px;
32
+ z-index: 10000;
33
+ background: transparent;
34
+ -webkit-app-region: drag;
35
+ user-select: none;
36
+ pointer-events: auto;
37
+ }
38
+ .window-controls {
39
+ position: absolute;
40
+ top: 8px;
41
+ right: 10px;
42
+ display: flex;
43
+ flex-direction: row-reverse;
44
+ align-items: center;
45
+ gap: 0;
46
+ min-width: 28px;
47
+ height: 28px;
48
+ pointer-events: auto;
49
+ -webkit-app-region: no-drag;
50
+ }
51
+ .window-dot {
52
+ width: 12px;
53
+ height: 12px;
54
+ padding: 0;
55
+ border: 1px solid rgba(255, 255, 255, 0.58);
56
+ border-radius: 999px;
57
+ background: rgba(255, 255, 255, 0.86);
58
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
59
+ cursor: pointer;
60
+ transition: transform 0.18s ease, background 0.18s ease, border-color 0.18s ease;
61
+ }
62
+ .window-controls:hover .window-dot,
63
+ .window-controls:focus-within .window-dot {
64
+ transform: scale(0.74);
65
+ background: rgba(255, 255, 255, 0.55);
66
+ border-color: rgba(255, 255, 255, 0.75);
67
+ }
68
+ .window-btn {
69
+ width: 0;
70
+ height: 28px;
71
+ margin-right: 0;
72
+ padding: 0;
73
+ display: inline-flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ border: 0;
77
+ border-radius: 999px;
78
+ background: rgba(8, 12, 20, 0.58);
79
+ color: rgba(255, 255, 255, 0.82);
80
+ backdrop-filter: blur(10px);
81
+ box-shadow: 0 4px 18px rgba(0, 0, 0, 0.38);
82
+ font-size: 14px;
83
+ line-height: 1;
84
+ cursor: pointer;
85
+ opacity: 0;
86
+ overflow: hidden;
87
+ transform: translateX(12px) scale(0.82);
88
+ transition: width 0.2s ease, margin-right 0.2s ease, opacity 0.16s ease, transform 0.2s ease, background 0.15s ease, color 0.15s ease;
89
+ }
90
+ .window-controls:hover .window-btn,
91
+ .window-controls:focus-within .window-btn {
92
+ width: 28px;
93
+ margin-right: 7px;
94
+ opacity: 1;
95
+ transform: translateX(0) scale(1);
96
+ }
97
+ .window-controls:hover #window-minimize,
98
+ .window-controls:focus-within #window-minimize {
99
+ transition-delay: 0.02s;
100
+ }
101
+ .window-controls:hover #window-maximize,
102
+ .window-controls:focus-within #window-maximize {
103
+ transition-delay: 0.05s;
104
+ }
105
+ .window-controls:hover #window-close,
106
+ .window-controls:focus-within #window-close {
107
+ transition-delay: 0.08s;
108
+ }
109
+ .window-btn:hover {
110
+ background: rgba(255, 255, 255, 0.18);
111
+ color: #fff;
112
+ }
113
+ .window-btn.close:hover {
114
+ background: #e5484d;
115
+ color: #fff;
116
+ }
117
+ #loading-overlay {
59
118
  position: absolute;
60
119
  top: 0;
61
120
  left: 0;
@@ -92,10 +151,16 @@
92
151
  <script src="./node_modules/hls.js/dist/hls.min.js"></script>
93
152
  <script src="./node_modules/artplayer/dist/artplayer.js"></script>
94
153
  </head>
95
- <body>
96
- <div class="drag-region"></div>
97
- <button id="close-btn" onclick="window.close()">✕ Đóng</button>
98
- <div id="loading-overlay">
154
+ <body>
155
+ <header id="top-nav">
156
+ <div class="window-controls">
157
+ <button class="window-btn close" id="window-close" title="Close">×</button>
158
+ <button class="window-btn" id="window-maximize" title="Maximize">□</button>
159
+ <button class="window-btn" id="window-minimize" title="Minimize">-</button>
160
+ <button class="window-dot" title="Window controls" aria-label="Window controls"></button>
161
+ </div>
162
+ </header>
163
+ <div id="loading-overlay">
99
164
  <div class="spinner"></div>
100
165
  <div class="loading-text">Đang tải và làm sạch trình phát...</div>
101
166
  </div>
@@ -103,9 +168,19 @@
103
168
 
104
169
  <script>
105
170
  const { ipcRenderer } = require('electron');
106
- ipcRenderer.on('main-log', (e, msg) => {
107
- console.log('[Main Process]', msg);
108
- });
171
+ ipcRenderer.on('main-log', (e, msg) => {
172
+ console.log('[Main Process]', msg);
173
+ });
174
+
175
+ document.getElementById('window-minimize').addEventListener('click', () => {
176
+ ipcRenderer.send('player:minimize');
177
+ });
178
+ document.getElementById('window-maximize').addEventListener('click', () => {
179
+ ipcRenderer.send('player:toggle-maximize');
180
+ });
181
+ document.getElementById('window-close').addEventListener('click', () => {
182
+ window.close();
183
+ });
109
184
 
110
185
  // Keyboard shortcut: Escape to close
111
186
  document.addEventListener('keydown', (e) => {
@@ -113,10 +188,10 @@
113
188
  });
114
189
 
115
190
 
116
- window.initPlayer = function(streamInfo, baseDir, cleanUA) {
117
- const container = document.getElementById('player-container');
118
-
119
- if (streamInfo.type === 'iframe') {
191
+ window.initPlayer = function(streamInfo, baseDir, cleanUA) {
192
+ const container = document.getElementById('player-container');
193
+
194
+ if (streamInfo.type === 'iframe') {
120
195
  const overlay = document.getElementById('loading-overlay');
121
196
  overlay.style.display = 'flex';
122
197