Logankunfall commited on
Commit
7921cc1
·
verified ·
1 Parent(s): b4f1219

Upload 10 files

Browse files
Files changed (3) hide show
  1. data/models.json +2 -1
  2. public/index.html +54 -6
  3. server.js +13 -0
data/models.json CHANGED
@@ -18,5 +18,6 @@
18
  { "id": "HassakuXL", "name": "HassakuXL", "free": false },
19
  { "id": "nova-cartoon-xl", "name": "nova-cartoon-xl", "free": false },
20
  { "id": "orphic-lora", "name": "orphic-lora", "free": false },
21
- { "id": "diagonalge/ConstShaper", "name": "ConstShaper", "free": false }
 
22
  ]
 
18
  { "id": "HassakuXL", "name": "HassakuXL", "free": false },
19
  { "id": "nova-cartoon-xl", "name": "nova-cartoon-xl", "free": false },
20
  { "id": "orphic-lora", "name": "orphic-lora", "free": false },
21
+ { "id": "diagonalge/ConstShaper", "name": "ConstShaper", "free": false },
22
+ { "id": "hunyuan-image-3", "name": "hunyuan-image-3", "free": false }
23
  ]
public/index.html CHANGED
@@ -409,11 +409,13 @@
409
  opacity: 0;
410
  visibility: hidden;
411
  transition: all 0.3s ease;
 
412
  }
413
 
414
  .overlay.show {
415
  opacity: 1;
416
  visibility: visible;
 
417
  }
418
 
419
  /* Mobile Generate Button - Draggable */
@@ -422,7 +424,7 @@
422
  left: 20px;
423
  top: 50%;
424
  transform: translateY(-50%);
425
- z-index: 50;
426
  background: linear-gradient(135deg, var(--accent), var(--accent-hover));
427
  color: white;
428
  border: none;
@@ -980,6 +982,8 @@
980
  btn.addEventListener('touchstart', startDrag, { passive: false });
981
  document.addEventListener('touchmove', drag, { passive: false });
982
  document.addEventListener('touchend', endDrag);
 
 
983
  }
984
 
985
  function startDrag(e) {
@@ -1025,9 +1029,11 @@
1025
  const btn = qs('#mobileGenerateBtn');
1026
  const maxX = window.innerWidth - 60;
1027
  const maxY = window.innerHeight - 60;
 
 
1028
 
1029
  dragState.currentX = Math.max(0, Math.min(maxX, dragState.currentX));
1030
- dragState.currentY = Math.max(0, Math.min(maxY, dragState.currentY));
1031
 
1032
  btn.style.left = dragState.currentX + 'px';
1033
  btn.style.top = dragState.currentY + 'px';
@@ -1067,11 +1073,22 @@
1067
  if (!btn) return;
1068
 
1069
  const savedPos = ls.get('generateBtnPosition', null);
 
 
 
 
 
 
1070
  if (savedPos) {
1071
- btn.style.left = savedPos.x + 'px';
1072
- btn.style.top = savedPos.y + 'px';
1073
- btn.style.transform = 'none';
 
 
1074
  }
 
 
 
1075
  }
1076
 
1077
  // 移动端优化的下载函数
@@ -1217,6 +1234,23 @@
1217
  if (wasMobile !== state.isMobile && state.sidebarOpen) {
1218
  closeSidebar();
1219
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1220
  }
1221
 
1222
  async function generate() {
@@ -1367,7 +1401,21 @@
1367
  // 响应式检测
1368
  window.addEventListener('resize', checkMobile);
1369
  checkMobile();
1370
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1371
  // 加载模型
1372
  fetchModels();
1373
  }
 
409
  opacity: 0;
410
  visibility: hidden;
411
  transition: all 0.3s ease;
412
+ pointer-events: none;
413
  }
414
 
415
  .overlay.show {
416
  opacity: 1;
417
  visibility: visible;
418
+ pointer-events: auto;
419
  }
420
 
421
  /* Mobile Generate Button - Draggable */
 
424
  left: 20px;
425
  top: 50%;
426
  transform: translateY(-50%);
427
+ z-index: 120;
428
  background: linear-gradient(135deg, var(--accent), var(--accent-hover));
429
  color: white;
430
  border: none;
 
982
  btn.addEventListener('touchstart', startDrag, { passive: false });
983
  document.addEventListener('touchmove', drag, { passive: false });
984
  document.addEventListener('touchend', endDrag);
985
+ document.addEventListener('touchcancel', endDrag, { passive: false });
986
+ btn.addEventListener('touchcancel', endDrag);
987
  }
988
 
989
  function startDrag(e) {
 
1029
  const btn = qs('#mobileGenerateBtn');
1030
  const maxX = window.innerWidth - 60;
1031
  const maxY = window.innerHeight - 60;
1032
+ const headerH = (qs('header') && qs('header').offsetHeight) || 0;
1033
+ const minY = headerH + 8;
1034
 
1035
  dragState.currentX = Math.max(0, Math.min(maxX, dragState.currentX));
1036
+ dragState.currentY = Math.max(minY, Math.min(maxY, dragState.currentY));
1037
 
1038
  btn.style.left = dragState.currentX + 'px';
1039
  btn.style.top = dragState.currentY + 'px';
 
1073
  if (!btn) return;
1074
 
1075
  const savedPos = ls.get('generateBtnPosition', null);
1076
+ const headerH = (qs('header') && qs('header').offsetHeight) || 0;
1077
+ const minY = headerH + 8;
1078
+ const maxX = window.innerWidth - 60;
1079
+ const maxY = window.innerHeight - 60;
1080
+
1081
+ let x, y;
1082
  if (savedPos) {
1083
+ x = Math.max(0, Math.min(maxX, savedPos.x));
1084
+ y = Math.max(minY, Math.min(maxY, savedPos.y));
1085
+ } else {
1086
+ x = 20;
1087
+ y = Math.max(minY, Math.min(maxY, window.innerHeight / 2 - 30));
1088
  }
1089
+ btn.style.left = x + 'px';
1090
+ btn.style.top = y + 'px';
1091
+ btn.style.transform = 'none';
1092
  }
1093
 
1094
  // 移动端优化的下载函数
 
1234
  if (wasMobile !== state.isMobile && state.sidebarOpen) {
1235
  closeSidebar();
1236
  }
1237
+
1238
+ // Clamp floating button inside viewport and below header when geometry changes
1239
+ const btn = qs('#mobileGenerateBtn');
1240
+ if (btn) {
1241
+ const rect = btn.getBoundingClientRect();
1242
+ const maxX = window.innerWidth - 60;
1243
+ const maxY = window.innerHeight - 60;
1244
+ const headerH = (qs('header') && qs('header').offsetHeight) || 0;
1245
+ const minY = headerH + 8;
1246
+ let x = rect.left;
1247
+ let y = rect.top;
1248
+ x = Math.max(0, Math.min(maxX, x));
1249
+ y = Math.max(minY, Math.min(maxY, y));
1250
+ btn.style.left = x + 'px';
1251
+ btn.style.top = y + 'px';
1252
+ btn.style.transform = 'none';
1253
+ }
1254
  }
1255
 
1256
  async function generate() {
 
1401
  // 响应式检测
1402
  window.addEventListener('resize', checkMobile);
1403
  checkMobile();
1404
+
1405
+ // 页面可见性/生命周期:后台恢复后确保拖拽结束,避免输入框无法点击
1406
+ window.addEventListener('visibilitychange', () => {
1407
+ if (document.hidden) {
1408
+ try { endDrag(new Event('touchcancel')); } catch (e) { if (typeof dragState !== 'undefined') dragState.isDragging = false; }
1409
+ }
1410
+ });
1411
+ window.addEventListener('pagehide', () => {
1412
+ try { endDrag(new Event('touchcancel')); } catch (e) { if (typeof dragState !== 'undefined') dragState.isDragging = false; }
1413
+ });
1414
+ window.addEventListener('blur', () => {
1415
+ try { endDrag(new Event('touchcancel')); } catch (e) { if (typeof dragState !== 'undefined') dragState.isDragging = false; }
1416
+ });
1417
+ document.addEventListener('touchcancel', endDrag, { passive: false });
1418
+
1419
  // 加载模型
1420
  fetchModels();
1421
  }
server.js CHANGED
@@ -220,7 +220,20 @@ app.get('/api/models', async (req, res) => {
220
  });
221
  // Merge free flags from local mapping
222
  if (localList.length) {
 
223
  models = mergeFreeFlags(models, localList);
 
 
 
 
 
 
 
 
 
 
 
 
224
  }
225
  return res.json({ source: 'remote', models });
226
  }
 
220
  });
221
  // Merge free flags from local mapping
222
  if (localList.length) {
223
+ // 先合并免费标记
224
  models = mergeFreeFlags(models, localList);
225
+ // 再把仅存在于本地配置但远端没有的模型追加进去(并集)
226
+ const remoteKeys = new Set(models.map(m => ((m.id || m.name || '') + '').toLowerCase()));
227
+ for (const lm of localList) {
228
+ const key = ((lm.id || lm.name || '') + '').toLowerCase();
229
+ if (key && !remoteKeys.has(key)) {
230
+ const id = (lm.id || lm.name || '').toString();
231
+ const name = (lm.name || id).toString();
232
+ const free = !!lm.free;
233
+ models.push({ id, name, free });
234
+ remoteKeys.add(key);
235
+ }
236
+ }
237
  }
238
  return res.json({ source: 'remote', models });
239
  }