Logankunfall commited on
Commit
c7e25ff
·
verified ·
1 Parent(s): 62e2f5c

Upload 21 files

Browse files
frontend/assets/app.js CHANGED
@@ -4,21 +4,6 @@
4
  const $ = (sel, root = document) => root.querySelector(sel);
5
  const $$ = (sel, root = document) => [...root.querySelectorAll(sel)];
6
  const byId = (id) => document.getElementById(id);
7
- // HF 环境识别(用于禁用不支持的本地能力)
8
- const onHF = typeof location !== "undefined" && /\bhf\.space$/i.test(location.hostname);
9
- function hideById(id) {
10
- const el = byId(id);
11
- if (el) {
12
- el.style.display = "none";
13
- if ("disabled" in el) el.disabled = true;
14
- }
15
- }
16
- function hideLabelForInput(id) {
17
- const el = byId(id);
18
- const label = el && el.closest ? el.closest("label") : null;
19
- if (label) label.style.display = "none";
20
- }
21
-
22
  // 提示音状态(运行时),默认关闭,URL 指向 /ring/ring.mp3
23
  let soundEnabled = false;
24
  let soundUrl = "/ring/ring.mp3";
@@ -28,8 +13,11 @@
28
  function applyTheme(theme) {
29
  rootEl.setAttribute('data-theme', theme);
30
  localStorage.setItem('theme', theme);
 
31
  const btn = byId('theme-toggle');
32
- if (btn) btn.textContent = '主题:' + (theme === 'dark' ? '深色' : '浅色');
 
 
33
 
34
  // 主题切换时重新应用背景方案
35
  const schemeSel = byId('cfg-color-scheme');
@@ -42,27 +30,77 @@
42
  }
43
  const initialTheme = localStorage.getItem('theme') || 'dark';
44
  applyTheme(initialTheme);
45
- const themeBtn = byId('theme-toggle');
46
- if (themeBtn) {
47
- themeBtn.addEventListener('click', () => {
 
 
48
  const next = (rootEl.getAttribute('data-theme') === 'dark') ? 'light' : 'dark';
49
  applyTheme(next);
50
- });
 
 
51
  }
 
52
  // 提示音开关(在主题按钮旁)
53
- const soundBtn = byId('sound-toggle');
54
  const soundPlayer = byId('sound-player');
55
  function updateSoundToggle() {
56
- if (!soundBtn) return;
57
- soundBtn.setAttribute('aria-pressed', soundEnabled ? 'true' : 'false');
58
- soundBtn.textContent = '提示音:' + (soundEnabled ? '' : '关');
 
 
 
 
 
 
 
 
 
59
  }
60
- if (soundBtn) {
61
- soundBtn.addEventListener('click', () => {
 
 
 
62
  soundEnabled = !soundEnabled;
63
  updateSoundToggle();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  });
65
  }
 
66
 
67
  // ===== 背景方案处理(非字体/主按钮):根据配置切换页面背景 =====
68
  function setBackground(color) {
@@ -89,12 +127,14 @@
89
  applyBackgroundScheme(scheme, custom);
90
  }
91
 
92
- // Tabs
93
- $$(".tab-btn").forEach((btn) => {
94
  btn.addEventListener("click", () => {
95
- $$(".tab-btn").forEach((b) => b.classList.remove("active"));
96
- btn.classList.add("active");
97
  const tabId = btn.getAttribute("data-tab");
 
 
 
 
98
  $$(".tab").forEach((tab) => tab.classList.remove("active"));
99
  const targetTab = byId(tabId);
100
  if (targetTab) targetTab.classList.add("active");
@@ -154,15 +194,6 @@
154
  if (byId("cfg-custom-primary")) byId("cfg-custom-primary").value = cfg.custom_primary ?? "#7ba23f";
155
  applyAccentFromConfig(cfg);
156
 
157
- // HF 环境不支持选择/打开本地目录,端口也由平台分配,不可编辑
158
- if (onHF) {
159
- hideLabelForInput("cfg-port");
160
- hideById("btn-select-output-dir");
161
- hideById("btn-open-output-dir");
162
- const msg = byId("cfg-message");
163
- if (msg) msg.textContent = "HF 环境:端口由平台分配(已忽略本地端口设置),目录选择/打开功能已禁用。";
164
- }
165
-
166
  // 提示音配置
167
  soundEnabled = !!cfg.sound_enabled;
168
  soundUrl = cfg.sound_url || "/ring/ring.mp3";
@@ -545,10 +576,6 @@
545
  const selOutBtn = byId("btn-select-output-dir");
546
  if (selOutBtn) {
547
  selOutBtn.addEventListener("click", async () => {
548
- if (onHF) {
549
- toast("HF 环境不支持系统目录选择,请在输入框手填或留空使用默认。", "info");
550
- return;
551
- }
552
  try {
553
  loading.show();
554
  const res = await fetch("/api/select-output-dir");
@@ -580,10 +607,6 @@
580
  const openOutBtn = byId("btn-open-output-dir");
581
  if (openOutBtn) {
582
  openOutBtn.addEventListener("click", async () => {
583
- if (onHF) {
584
- toast("HF 环境不支持打开系统目录(不影响生成与下载)。", "info");
585
- return;
586
- }
587
  try {
588
  loading.show();
589
  const p = byId("cfg-output-dir")?.value || "";
@@ -620,8 +643,5 @@
620
  byId("btn-inpaint").addEventListener("click", handleInpaint);
621
 
622
  // Init
623
- if (onHF) {
624
- toast("HF 环境:端口由平台分配,目录选择/打开已禁用。", "info");
625
- }
626
  loadConfig();
627
  })();
 
4
  const $ = (sel, root = document) => root.querySelector(sel);
5
  const $$ = (sel, root = document) => [...root.querySelectorAll(sel)];
6
  const byId = (id) => document.getElementById(id);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  // 提示音状态(运行时),默认关闭,URL 指向 /ring/ring.mp3
8
  let soundEnabled = false;
9
  let soundUrl = "/ring/ring.mp3";
 
13
  function applyTheme(theme) {
14
  rootEl.setAttribute('data-theme', theme);
15
  localStorage.setItem('theme', theme);
16
+ const text = '主题:' + (theme === 'dark' ? '深色' : '浅色');
17
  const btn = byId('theme-toggle');
18
+ const btnMobile = byId('theme-toggle-mobile');
19
+ if (btn) btn.textContent = text;
20
+ if (btnMobile) btnMobile.textContent = text;
21
 
22
  // 主题切换时重新应用背景方案
23
  const schemeSel = byId('cfg-color-scheme');
 
30
  }
31
  const initialTheme = localStorage.getItem('theme') || 'dark';
32
  applyTheme(initialTheme);
33
+
34
+ function setupThemeToggle() {
35
+ const themeBtn = byId('theme-toggle');
36
+ const themeBtnMobile = byId('theme-toggle-mobile');
37
+ const toggleTheme = () => {
38
  const next = (rootEl.getAttribute('data-theme') === 'dark') ? 'light' : 'dark';
39
  applyTheme(next);
40
+ };
41
+ if (themeBtn) themeBtn.addEventListener('click', toggleTheme);
42
+ if (themeBtnMobile) themeBtnMobile.addEventListener('click', toggleTheme);
43
  }
44
+ setupThemeToggle();
45
  // 提示音开关(在主题按钮旁)
 
46
  const soundPlayer = byId('sound-player');
47
  function updateSoundToggle() {
48
+ const text = '提示音:' + (soundEnabled ? '开' : '关');
49
+ const pressed = soundEnabled ? 'true' : 'false';
50
+ const soundBtn = byId('sound-toggle');
51
+ const soundBtnMobile = byId('sound-toggle-mobile');
52
+ if (soundBtn) {
53
+ soundBtn.setAttribute('aria-pressed', pressed);
54
+ soundBtn.textContent = text;
55
+ }
56
+ if (soundBtnMobile) {
57
+ soundBtnMobile.setAttribute('aria-pressed', pressed);
58
+ soundBtnMobile.textContent = text;
59
+ }
60
  }
61
+
62
+ function setupSoundToggle() {
63
+ const soundBtn = byId('sound-toggle');
64
+ const soundBtnMobile = byId('sound-toggle-mobile');
65
+ const toggle = () => {
66
  soundEnabled = !soundEnabled;
67
  updateSoundToggle();
68
+ };
69
+ if (soundBtn) soundBtn.addEventListener('click', toggle);
70
+ if (soundBtnMobile) soundBtnMobile.addEventListener('click', toggle);
71
+ }
72
+ setupSoundToggle();
73
+
74
+ // 侧边栏控制
75
+ function setupSidebar() {
76
+ const menuToggle = byId('menu-toggle');
77
+ const sidebar = byId('sidebar');
78
+ const sidebarClose = byId('sidebar-close');
79
+ const sidebarOverlay = byId('sidebar-overlay');
80
+
81
+ function openSidebar() {
82
+ if (sidebar) sidebar.classList.add('active');
83
+ if (sidebarOverlay) sidebarOverlay.classList.add('active');
84
+ }
85
+
86
+ function closeSidebar() {
87
+ if (sidebar) sidebar.classList.remove('active');
88
+ if (sidebarOverlay) sidebarOverlay.classList.remove('active');
89
+ }
90
+
91
+ if (menuToggle) menuToggle.addEventListener('click', openSidebar);
92
+ if (sidebarClose) sidebarClose.addEventListener('click', closeSidebar);
93
+ if (sidebarOverlay) sidebarOverlay.addEventListener('click', closeSidebar);
94
+
95
+ // 侧边栏中的Tab按钮也要同步
96
+ const sidebarBtns = sidebar ? sidebar.querySelectorAll('.tab-btn[data-tab]') : [];
97
+ sidebarBtns.forEach(btn => {
98
+ btn.addEventListener('click', () => {
99
+ closeSidebar();
100
+ });
101
  });
102
  }
103
+ setupSidebar();
104
 
105
  // ===== 背景方案处理(非字体/主按钮):根据配置切换页面背景 =====
106
  function setBackground(color) {
 
127
  applyBackgroundScheme(scheme, custom);
128
  }
129
 
130
+ // Tabs(桌面端和侧边栏都要同步)
131
+ $$(".tab-btn[data-tab]").forEach((btn) => {
132
  btn.addEventListener("click", () => {
 
 
133
  const tabId = btn.getAttribute("data-tab");
134
+ // 同步所有tab按钮状态
135
+ $$(".tab-btn[data-tab]").forEach((b) => b.classList.remove("active"));
136
+ $$(".tab-btn[data-tab='" + tabId + "']").forEach((b) => b.classList.add("active"));
137
+ // 切换tab内容
138
  $$(".tab").forEach((tab) => tab.classList.remove("active"));
139
  const targetTab = byId(tabId);
140
  if (targetTab) targetTab.classList.add("active");
 
194
  if (byId("cfg-custom-primary")) byId("cfg-custom-primary").value = cfg.custom_primary ?? "#7ba23f";
195
  applyAccentFromConfig(cfg);
196
 
 
 
 
 
 
 
 
 
 
197
  // 提示音配置
198
  soundEnabled = !!cfg.sound_enabled;
199
  soundUrl = cfg.sound_url || "/ring/ring.mp3";
 
576
  const selOutBtn = byId("btn-select-output-dir");
577
  if (selOutBtn) {
578
  selOutBtn.addEventListener("click", async () => {
 
 
 
 
579
  try {
580
  loading.show();
581
  const res = await fetch("/api/select-output-dir");
 
607
  const openOutBtn = byId("btn-open-output-dir");
608
  if (openOutBtn) {
609
  openOutBtn.addEventListener("click", async () => {
 
 
 
 
610
  try {
611
  loading.show();
612
  const p = byId("cfg-output-dir")?.value || "";
 
643
  byId("btn-inpaint").addEventListener("click", handleInpaint);
644
 
645
  // Init
 
 
 
646
  loadConfig();
647
  })();
frontend/assets/style.css CHANGED
@@ -11,7 +11,7 @@
11
  --border: #2a2a2a;
12
  --card: #121212;
13
  --shadow: 0 10px 30px rgba(0,0,0,0.3);
14
- --radius: 12px;
15
  --ring: rgba(255,255,255,0.12);
16
  --active-text: #111111;
17
  --switch-knob: #ffffff;
@@ -108,9 +108,8 @@ img { max-width: 100%; display: block; }
108
  color: var(--text);
109
  border: 1px solid var(--border);
110
  padding: 10px 14px;
111
- border-radius: 999px;
112
  white-space: nowrap;
113
- /* 彻底固定:移除过渡,避免任何切换动画引起的视觉“移动” */
114
  transition: none;
115
  cursor: pointer;
116
  font-weight: 700;
@@ -147,6 +146,104 @@ h2 {
147
  padding: 14px;
148
  }
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  /* Form grid - mobile first */
151
  .form-grid {
152
  display: grid;
@@ -193,7 +290,7 @@ select {
193
  background: var(--card);
194
  color: var(--text);
195
  border: 1px solid var(--border);
196
- border-radius: 12px;
197
  outline: none;
198
  transition: border-color .18s ease, box-shadow .18s ease;
199
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.03);
@@ -255,13 +352,12 @@ button {
255
  border: 1px solid var(--border);
256
  background: #1a2030;
257
  color: #e8ecff;
258
- border-radius: 12px;
259
  padding: 12px 16px;
260
  font-weight: 800;
261
  font-size: 16px;
262
  letter-spacing: .2px;
263
  cursor: pointer;
264
- /* 去除 transform 过渡,避免点击“抽搐” */
265
  transition: background .18s ease, border-color .18s ease, box-shadow .2s ease, color .18s ease;
266
  }
267
  button:hover { border-color: #3a3a3a; background: #222222; }
@@ -295,7 +391,7 @@ button.primary:hover {
295
  }
296
  .result img {
297
  width: 100%;
298
- border-radius: 12px;
299
  border: 1px solid var(--border);
300
  background: #0e1118;
301
  }
@@ -314,17 +410,17 @@ button.primary:hover {
314
  width: 100%;
315
  }
316
 
317
- /* Toast: 固定状态栏,不弹出动画 */
318
  .toast {
319
  position: fixed;
320
  left: 50%;
321
- bottom: 14px;
322
- transform: translateX(-50%); /* 不再上下弹出 */
323
  background: rgba(17, 23, 34, 0.96);
324
  color: #fff;
325
  border: 1px solid var(--border);
326
  padding: 8px 14px;
327
- border-radius: 10px;
328
  box-shadow: var(--shadow);
329
  z-index: 100;
330
  transition: none;
@@ -332,6 +428,7 @@ button.primary:hover {
332
  white-space: nowrap;
333
  overflow: hidden;
334
  text-overflow: ellipsis;
 
335
  }
336
  .toast:empty { display: none; }
337
  .toast.success { border-color: rgba(40, 199, 111, .4); }
@@ -369,7 +466,7 @@ button.primary:hover {
369
  input[type="file"] {
370
  padding: 10px;
371
  background: var(--card);
372
- border-radius: 10px;
373
  border: 1px solid var(--border);
374
  }
375
 
@@ -480,7 +577,7 @@ select {
480
  --placeholder: #8a93a8;
481
  }
482
 
483
- /* ===== 移动端优化(参考 NAI 生成页交互) ===== */
484
  @media (max-width: 640px) {
485
  /* 手机端背景图片 */
486
  body {
@@ -489,42 +586,28 @@ select {
489
  background-position: center;
490
  }
491
 
492
- /* 顶部导航栏优化 */
493
- .app-header {
494
- padding: 8px 12px;
495
- flex-direction: column;
496
- align-items: stretch;
497
- gap: 8px;
498
  }
499
 
500
- .brand {
501
- font-size: 18px;
502
- text-align: center;
 
503
  }
504
 
505
- .header-actions {
506
- display: flex;
507
- justify-content: center;
508
- gap: 8px;
 
 
509
  }
510
 
511
- .tabs {
512
- display: flex;
513
- width: 100%;
514
- overflow-x: auto;
515
- -webkit-overflow-scrolling: touch;
516
- gap: 6px;
517
- padding-bottom: 4px;
518
- scrollbar-width: none;
519
- }
520
- .tabs::-webkit-scrollbar {
521
- display: none;
522
- }
523
- .tabs .tab-btn {
524
- flex: 0 0 auto;
525
- padding: 10px 16px;
526
- font-size: 14px;
527
- min-width: fit-content;
528
  }
529
 
530
  /* 主容器调整 */
 
11
  --border: #2a2a2a;
12
  --card: #121212;
13
  --shadow: 0 10px 30px rgba(0,0,0,0.3);
14
+ --radius: 4px;
15
  --ring: rgba(255,255,255,0.12);
16
  --active-text: #111111;
17
  --switch-knob: #ffffff;
 
108
  color: var(--text);
109
  border: 1px solid var(--border);
110
  padding: 10px 14px;
111
+ border-radius: var(--radius);
112
  white-space: nowrap;
 
113
  transition: none;
114
  cursor: pointer;
115
  font-weight: 700;
 
146
  padding: 14px;
147
  }
148
 
149
+ /* 汉堡菜单按钮(仅手机端显示) */
150
+ .menu-toggle {
151
+ display: none;
152
+ appearance: none;
153
+ background: transparent;
154
+ border: none;
155
+ font-size: 24px;
156
+ color: var(--text);
157
+ cursor: pointer;
158
+ padding: 8px;
159
+ line-height: 1;
160
+ }
161
+
162
+ /* 侧边栏 */
163
+ .sidebar {
164
+ position: fixed;
165
+ top: 0;
166
+ left: -280px;
167
+ width: 280px;
168
+ height: 100%;
169
+ background: var(--panel);
170
+ border-right: 1px solid var(--border);
171
+ z-index: 1000;
172
+ transition: left 0.3s ease;
173
+ overflow-y: auto;
174
+ display: flex;
175
+ flex-direction: column;
176
+ }
177
+
178
+ .sidebar.active {
179
+ left: 0;
180
+ }
181
+
182
+ .sidebar-overlay {
183
+ position: fixed;
184
+ top: 0;
185
+ left: 0;
186
+ right: 0;
187
+ bottom: 0;
188
+ background: rgba(0, 0, 0, 0.5);
189
+ z-index: 999;
190
+ opacity: 0;
191
+ pointer-events: none;
192
+ transition: opacity 0.3s ease;
193
+ }
194
+
195
+ .sidebar-overlay.active {
196
+ opacity: 1;
197
+ pointer-events: auto;
198
+ }
199
+
200
+ .sidebar-header {
201
+ display: flex;
202
+ justify-content: space-between;
203
+ align-items: center;
204
+ padding: 16px;
205
+ border-bottom: 1px solid var(--border);
206
+ }
207
+
208
+ .sidebar-close {
209
+ appearance: none;
210
+ background: transparent;
211
+ border: none;
212
+ font-size: 32px;
213
+ color: var(--text);
214
+ cursor: pointer;
215
+ line-height: 1;
216
+ padding: 0;
217
+ width: 32px;
218
+ height: 32px;
219
+ }
220
+
221
+ .sidebar-nav {
222
+ flex: 1;
223
+ padding: 16px;
224
+ display: flex;
225
+ flex-direction: column;
226
+ gap: 8px;
227
+ }
228
+
229
+ .sidebar-nav .tab-btn {
230
+ width: 100%;
231
+ text-align: left;
232
+ justify-content: flex-start;
233
+ }
234
+
235
+ .sidebar-actions {
236
+ padding: 16px;
237
+ border-top: 1px solid var(--border);
238
+ display: flex;
239
+ flex-direction: column;
240
+ gap: 8px;
241
+ }
242
+
243
+ .sidebar-actions .tab-btn {
244
+ width: 100%;
245
+ }
246
+
247
  /* Form grid - mobile first */
248
  .form-grid {
249
  display: grid;
 
290
  background: var(--card);
291
  color: var(--text);
292
  border: 1px solid var(--border);
293
+ border-radius: var(--radius);
294
  outline: none;
295
  transition: border-color .18s ease, box-shadow .18s ease;
296
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.03);
 
352
  border: 1px solid var(--border);
353
  background: #1a2030;
354
  color: #e8ecff;
355
+ border-radius: var(--radius);
356
  padding: 12px 16px;
357
  font-weight: 800;
358
  font-size: 16px;
359
  letter-spacing: .2px;
360
  cursor: pointer;
 
361
  transition: background .18s ease, border-color .18s ease, box-shadow .2s ease, color .18s ease;
362
  }
363
  button:hover { border-color: #3a3a3a; background: #222222; }
 
391
  }
392
  .result img {
393
  width: 100%;
394
+ border-radius: var(--radius);
395
  border: 1px solid var(--border);
396
  background: #0e1118;
397
  }
 
410
  width: 100%;
411
  }
412
 
413
+ /* Toast: 固定顶部,不占用底部空间 */
414
  .toast {
415
  position: fixed;
416
  left: 50%;
417
+ top: 70px;
418
+ transform: translateX(-50%);
419
  background: rgba(17, 23, 34, 0.96);
420
  color: #fff;
421
  border: 1px solid var(--border);
422
  padding: 8px 14px;
423
+ border-radius: var(--radius);
424
  box-shadow: var(--shadow);
425
  z-index: 100;
426
  transition: none;
 
428
  white-space: nowrap;
429
  overflow: hidden;
430
  text-overflow: ellipsis;
431
+ pointer-events: none;
432
  }
433
  .toast:empty { display: none; }
434
  .toast.success { border-color: rgba(40, 199, 111, .4); }
 
466
  input[type="file"] {
467
  padding: 10px;
468
  background: var(--card);
469
+ border-radius: var(--radius);
470
  border: 1px solid var(--border);
471
  }
472
 
 
577
  --placeholder: #8a93a8;
578
  }
579
 
580
+ /* ===== 移动端优化 ===== */
581
  @media (max-width: 640px) {
582
  /* 手机端背景图片 */
583
  body {
 
586
  background-position: center;
587
  }
588
 
589
+ /* 显示汉堡菜单 */
590
+ .menu-toggle {
591
+ display: block;
 
 
 
592
  }
593
 
594
+ /* 隐藏顶部tabs和actions */
595
+ .app-header .tabs,
596
+ .app-header .header-actions {
597
+ display: none;
598
  }
599
 
600
+ /* 顶部导航栏简化 */
601
+ .app-header {
602
+ padding: 12px 16px;
603
+ flex-direction: row;
604
+ align-items: center;
605
+ gap: 12px;
606
  }
607
 
608
+ .brand {
609
+ font-size: 18px;
610
+ flex: 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  }
612
 
613
  /* 主容器调整 */
frontend/index.html CHANGED
@@ -7,17 +7,37 @@
7
  <link rel="stylesheet" href="assets/style.css" />
8
  </head>
9
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  <header class="app-header">
 
11
  <div class="brand">New NAI</div>
12
  <nav class="tabs">
13
- <button type="button" class="tab-btn active" data-tab="tab-config">配置</button>
14
- <button type="button" class="tab-btn" data-tab="tab-t2i">文生图</button>
15
- <button type="button" class="tab-btn" data-tab="tab-i2i">图生图</button>
16
- <button type="button" class="tab-btn" data-tab="tab-inpaint">局部重绘</button>
17
  </nav>
18
  <div class="header-actions">
19
- <button type="button" id="theme-toggle" class="tab-btn">主题:深色</button>
20
- <button type="button" id="sound-toggle" class="tab-btn" aria-pressed="false">提示音:关</button>
21
  </div>
22
  </header>
23
 
@@ -114,10 +134,10 @@
114
  </label>
115
  </div>
116
  <div class="actions">
117
- <button type="button" id="btn-load-config">读取配置</button>
118
- <button type="button" id="btn-select-output-dir">选择保存目录</button>
119
- <button type="button" id="btn-open-output-dir">打开保存目录</button>
120
- <button type="button" id="btn-save-config" class="primary">保存配置</button>
121
  </div>
122
  <p id="cfg-message" class="message"></p>
123
  </div>
@@ -193,7 +213,7 @@
193
  </label>
194
  </div>
195
  <div class="actions">
196
- <button type="button" id="btn-t2i" class="primary">生成</button>
197
  </div>
198
  <div class="result">
199
  <img id="t2i-img" alt="生成结果" />
@@ -287,7 +307,7 @@
287
  </label>
288
  </div>
289
  <div class="actions">
290
- <button type="button" id="btn-i2i" class="primary">生成</button>
291
  </div>
292
  <div class="result">
293
  <img id="i2i-img" alt="生成结果" />
@@ -389,7 +409,7 @@
389
  </label>
390
  </div>
391
  <div class="actions">
392
- <button type="button" id="btn-inpaint" class="primary">生成</button>
393
  </div>
394
  <div class="result">
395
  <img id="inpaint-img" alt="生成结果" />
 
7
  <link rel="stylesheet" href="assets/style.css" />
8
  </head>
9
  <body>
10
+ <!-- 手机端侧边栏 -->
11
+ <div id="sidebar" class="sidebar">
12
+ <div class="sidebar-header">
13
+ <div class="brand">New NAI</div>
14
+ <button id="sidebar-close" class="sidebar-close">×</button>
15
+ </div>
16
+ <nav class="sidebar-nav">
17
+ <button class="tab-btn active" data-tab="tab-config">配置</button>
18
+ <button class="tab-btn" data-tab="tab-t2i">文生图</button>
19
+ <button class="tab-btn" data-tab="tab-i2i">图生图</button>
20
+ <button class="tab-btn" data-tab="tab-inpaint">局部重绘</button>
21
+ </nav>
22
+ <div class="sidebar-actions">
23
+ <button id="theme-toggle-mobile" class="tab-btn">主题:深色</button>
24
+ <button id="sound-toggle-mobile" class="tab-btn" aria-pressed="false">提示音:关</button>
25
+ </div>
26
+ </div>
27
+ <div id="sidebar-overlay" class="sidebar-overlay"></div>
28
+
29
  <header class="app-header">
30
+ <button id="menu-toggle" class="menu-toggle">☰</button>
31
  <div class="brand">New NAI</div>
32
  <nav class="tabs">
33
+ <button class="tab-btn active" data-tab="tab-config">配置</button>
34
+ <button class="tab-btn" data-tab="tab-t2i">文生图</button>
35
+ <button class="tab-btn" data-tab="tab-i2i">图生图</button>
36
+ <button class="tab-btn" data-tab="tab-inpaint">局部重绘</button>
37
  </nav>
38
  <div class="header-actions">
39
+ <button id="theme-toggle" class="tab-btn">主题:深色</button>
40
+ <button id="sound-toggle" class="tab-btn" aria-pressed="false">提示音:关</button>
41
  </div>
42
  </header>
43
 
 
134
  </label>
135
  </div>
136
  <div class="actions">
137
+ <button id="btn-load-config">读取配置</button>
138
+ <button id="btn-select-output-dir">选择保存目录</button>
139
+ <button id="btn-open-output-dir">打开保存目录</button>
140
+ <button id="btn-save-config" class="primary">保存配置</button>
141
  </div>
142
  <p id="cfg-message" class="message"></p>
143
  </div>
 
213
  </label>
214
  </div>
215
  <div class="actions">
216
+ <button id="btn-t2i" class="primary">生成</button>
217
  </div>
218
  <div class="result">
219
  <img id="t2i-img" alt="生成结果" />
 
307
  </label>
308
  </div>
309
  <div class="actions">
310
+ <button id="btn-i2i" class="primary">生成</button>
311
  </div>
312
  <div class="result">
313
  <img id="i2i-img" alt="生成结果" />
 
409
  </label>
410
  </div>
411
  <div class="actions">
412
+ <button id="btn-inpaint" class="primary">生成</button>
413
  </div>
414
  <div class="result">
415
  <img id="inpaint-img" alt="生成结果" />