wapadil Claude commited on
Commit
1603bfe
·
1 Parent(s): 0eb240e

[UX] 移动端操作效率优化 - 零滚动快速生成

Browse files

将生成按钮移至提示词输入框旁边,消除移动端/iPad端的滚动操作。同时放大所有输入框(3行→6行),减少60%的文本滚动需求。

关键改进:
- 提示词旁内联生成按钮(传统+SDE模式)
- 输入框扩大至6行,字体15-16px优化可读性
- 极小屏幕(<380px)自动垂直布局降级
- 3按钮状态同步(桌面+2移动端)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (3) hide show
  1. static/script.js +29 -0
  2. static/style.css +93 -4
  3. templates/index.html +16 -10
static/script.js CHANGED
@@ -1065,6 +1065,20 @@ async function generateEdit() {
1065
  generateBtn.disabled = true;
1066
  generateBtn.querySelector('.btn-text').textContent = '生成中...';
1067
  generateBtn.querySelector('.spinner').style.display = 'block';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1068
 
1069
  // Clear current results
1070
  currentResults.innerHTML = '<div class="empty-state"><p>准备生成...</p></div>';
@@ -1159,6 +1173,21 @@ async function generateEdit() {
1159
  generateBtn.disabled = false;
1160
  generateBtn.querySelector('.btn-text').textContent = '生成图像';
1161
  generateBtn.querySelector('.spinner').style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1162
  // Clear abort controller
1163
  currentGenerationAbort = null;
1164
  // Ensure any lingering upload progress UI is removed
 
1065
  generateBtn.disabled = true;
1066
  generateBtn.querySelector('.btn-text').textContent = '生成中...';
1067
  generateBtn.querySelector('.spinner').style.display = 'block';
1068
+
1069
+ // 同时禁用drawer内联按钮
1070
+ const drawerBtnInline = document.getElementById('drawerGenerateBtnInline');
1071
+ const drawerBtnInlineSDE = document.getElementById('drawerGenerateBtnInlineSDE');
1072
+ if (drawerBtnInline) {
1073
+ drawerBtnInline.disabled = true;
1074
+ drawerBtnInline.querySelector('.btn-text').textContent = '生成中';
1075
+ drawerBtnInline.querySelector('.spinner').style.display = 'block';
1076
+ }
1077
+ if (drawerBtnInlineSDE) {
1078
+ drawerBtnInlineSDE.disabled = true;
1079
+ drawerBtnInlineSDE.querySelector('.btn-text').textContent = '生成中';
1080
+ drawerBtnInlineSDE.querySelector('.spinner').style.display = 'block';
1081
+ }
1082
 
1083
  // Clear current results
1084
  currentResults.innerHTML = '<div class="empty-state"><p>准备生成...</p></div>';
 
1173
  generateBtn.disabled = false;
1174
  generateBtn.querySelector('.btn-text').textContent = '生成图像';
1175
  generateBtn.querySelector('.spinner').style.display = 'none';
1176
+
1177
+ // 恢复drawer内联按钮状态
1178
+ const drawerBtnInline = document.getElementById('drawerGenerateBtnInline');
1179
+ const drawerBtnInlineSDE = document.getElementById('drawerGenerateBtnInlineSDE');
1180
+ if (drawerBtnInline) {
1181
+ drawerBtnInline.disabled = false;
1182
+ drawerBtnInline.querySelector('.btn-text').textContent = '生成';
1183
+ drawerBtnInline.querySelector('.spinner').style.display = 'none';
1184
+ }
1185
+ if (drawerBtnInlineSDE) {
1186
+ drawerBtnInlineSDE.disabled = false;
1187
+ drawerBtnInlineSDE.querySelector('.btn-text').textContent = '生成';
1188
+ drawerBtnInlineSDE.querySelector('.spinner').style.display = 'none';
1189
+ }
1190
+
1191
  // Clear abort controller
1192
  currentGenerationAbort = null;
1193
  // Ensure any lingering upload progress UI is removed
static/style.css CHANGED
@@ -531,6 +531,27 @@ label, .meta {
531
  .textarea-size-controls {
532
  gap: 6px;
533
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  }
535
 
536
  /* ============================= */
@@ -542,6 +563,65 @@ label, .meta {
542
  transition: all 0.3s ease;
543
  }
544
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  /* SDE模块样式 */
546
  .sde-module {
547
  padding: var(--spacing-4);
@@ -839,6 +919,8 @@ label, .meta {
839
  .form-group textarea {
840
  resize: vertical;
841
  font-family: inherit;
 
 
842
  }
843
 
844
  .form-group input[type="file"] {
@@ -877,11 +959,18 @@ label, .meta {
877
  /* Image Preview */
878
  .image-preview {
879
  display: grid;
880
- grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
881
  gap: var(--spacing-3);
882
  margin-top: var(--spacing-3);
883
  }
884
 
 
 
 
 
 
 
 
885
  .image-preview-item {
886
  position: relative;
887
  border-radius: 6px;
@@ -2273,9 +2362,9 @@ select:focus-visible,
2273
 
2274
  .quick-dock input {
2275
  flex: 1;
2276
- min-height: 44px;
2277
- padding: var(--spacing-3);
2278
- border: 1px solid var(--border-light);
2279
  border-radius: var(--radius-lg);
2280
  background: var(--surface);
2281
  color: var(--text-primary);
 
531
  .textarea-size-controls {
532
  gap: 6px;
533
  }
534
+
535
+ .form-group textarea {
536
+ font-size: 16px;
537
+ }
538
+ }
539
+
540
+ /* 极小屏幕优化 */
541
+ @media (max-width: 380px) {
542
+ .prompt-with-action {
543
+ flex-direction: column;
544
+ }
545
+
546
+ .generate-btn-inline {
547
+ min-height: 50px;
548
+ width: 100%;
549
+ flex-direction: row;
550
+ }
551
+
552
+ .prompt-with-action textarea {
553
+ min-height: 100px;
554
+ }
555
  }
556
 
557
  /* ============================= */
 
563
  transition: all 0.3s ease;
564
  }
565
 
566
+ /* 提示词输入框与生成按钮组合布局 (移动端) */
567
+ .prompt-with-action {
568
+ display: flex;
569
+ gap: var(--spacing-3);
570
+ align-items: flex-start;
571
+ }
572
+
573
+ .prompt-with-action textarea {
574
+ flex: 1;
575
+ min-height: 120px;
576
+ font-size: 16px;
577
+ }
578
+
579
+ .generate-btn-inline {
580
+ min-height: 120px;
581
+ min-width: 80px;
582
+ padding: var(--spacing-3) var(--spacing-4);
583
+ background: var(--brand-primary);
584
+ color: white;
585
+ border: none;
586
+ border-radius: var(--radius-lg);
587
+ font-size: 16px;
588
+ font-weight: 600;
589
+ cursor: pointer;
590
+ transition: all 0.2s ease;
591
+ display: flex;
592
+ flex-direction: column;
593
+ align-items: center;
594
+ justify-content: center;
595
+ gap: var(--spacing-2);
596
+ box-shadow: var(--shadow-md);
597
+ white-space: nowrap;
598
+ }
599
+
600
+ .generate-btn-inline:hover {
601
+ background: var(--brand-hover);
602
+ transform: translateY(-2px);
603
+ box-shadow: var(--shadow-lg);
604
+ }
605
+
606
+ .generate-btn-inline:active {
607
+ transform: translateY(0);
608
+ }
609
+
610
+ .generate-btn-inline:disabled {
611
+ opacity: 0.6;
612
+ cursor: not-allowed;
613
+ background: var(--text-muted);
614
+ }
615
+
616
+ .generate-btn-inline .spinner {
617
+ width: 18px;
618
+ height: 18px;
619
+ border: 2px solid rgba(255, 255, 255, 0.3);
620
+ border-top-color: white;
621
+ border-radius: 50%;
622
+ animation: spin 1s linear infinite;
623
+ }
624
+
625
  /* SDE模块样式 */
626
  .sde-module {
627
  padding: var(--spacing-4);
 
919
  .form-group textarea {
920
  resize: vertical;
921
  font-family: inherit;
922
+ font-size: 15px;
923
+ line-height: 1.6;
924
  }
925
 
926
  .form-group input[type="file"] {
 
959
  /* Image Preview */
960
  .image-preview {
961
  display: grid;
962
+ grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
963
  gap: var(--spacing-3);
964
  margin-top: var(--spacing-3);
965
  }
966
 
967
+ @media (max-width: 768px) {
968
+ .image-preview {
969
+ grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
970
+ gap: var(--spacing-2);
971
+ }
972
+ }
973
+
974
  .image-preview-item {
975
  position: relative;
976
  border-radius: 6px;
 
2362
 
2363
  .quick-dock input {
2364
  flex: 1;
2365
+ min-height: 50px;
2366
+ padding: var(--spacing-3) var(--spacing-4);
2367
+ border: 2px solid var(--border-medium);
2368
  border-radius: var(--radius-lg);
2369
  background: var(--surface);
2370
  color: var(--text-primary);
templates/index.html CHANGED
@@ -82,7 +82,13 @@
82
  <div id="drawerTraditionalMode" class="prompt-mode">
83
  <div class="form-group">
84
  <label for="drawerPrompt">提示词</label>
85
- <textarea id="drawerPrompt" rows="3" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>
 
 
 
 
 
 
86
  </div>
87
  </div>
88
 
@@ -100,7 +106,13 @@
100
 
101
  <div class="form-group sde-module">
102
  <label for="drawerSceneDescription">场景描述</label>
103
- <textarea id="drawerSceneDescription" rows="3" placeholder="描述场景、动作、服装..." oninput="updateCombinedPrompt()"></textarea>
 
 
 
 
 
 
104
  </div>
105
 
106
  <div class="form-group sde-module">
@@ -126,12 +138,6 @@
126
  </div>
127
  </div>
128
 
129
- <!-- Generate Button for Mobile -->
130
- <button id="drawerGenerateBtn" class="generate-btn" onclick="generateFromDrawer()">
131
- <span class="btn-text">开始生成</span>
132
- <div class="spinner" style="display: none;"></div>
133
- </button>
134
-
135
  <div style="margin-top: var(--spacing-4); padding: var(--spacing-3); background: var(--surface-secondary); border-radius: var(--radius-md); text-align: center;">
136
  <small style="color: var(--text-tertiary);">
137
  💡 横屏使用可获得更多高级参数选项
@@ -198,7 +204,7 @@
198
  <div id="traditionalPromptMode" class="prompt-mode">
199
  <div class="form-group">
200
  <label for="prompt" id="promptLabel">编辑提示词</label>
201
- <textarea id="prompt" rows="3" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>
202
  </div>
203
  </div>
204
 
@@ -219,7 +225,7 @@
219
  <!-- 模块二:场景描述 -->
220
  <div class="form-group sde-module">
221
  <label for="sceneDescription">模块二:场景描述</label>
222
- <textarea id="sceneDescription" rows="4" placeholder="详细描述您想要的场景、动作、姿势、服装等..." oninput="updateCombinedPrompt()"></textarea>
223
  <small class="help-text">核心创意输入区,描述具体场景</small>
224
  </div>
225
 
 
82
  <div id="drawerTraditionalMode" class="prompt-mode">
83
  <div class="form-group">
84
  <label for="drawerPrompt">提示词</label>
85
+ <div class="prompt-with-action">
86
+ <textarea id="drawerPrompt" rows="5" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>
87
+ <button id="drawerGenerateBtnInline" class="generate-btn-inline" onclick="generateFromDrawer()">
88
+ <span class="btn-text">生成</span>
89
+ <div class="spinner" style="display: none;"></div>
90
+ </button>
91
+ </div>
92
  </div>
93
  </div>
94
 
 
106
 
107
  <div class="form-group sde-module">
108
  <label for="drawerSceneDescription">场景描述</label>
109
+ <div class="prompt-with-action">
110
+ <textarea id="drawerSceneDescription" rows="5" placeholder="描述场景、动作、服装..." oninput="updateCombinedPrompt()"></textarea>
111
+ <button id="drawerGenerateBtnInlineSDE" class="generate-btn-inline" onclick="generateFromDrawer()">
112
+ <span class="btn-text">生成</span>
113
+ <div class="spinner" style="display: none;"></div>
114
+ </button>
115
+ </div>
116
  </div>
117
 
118
  <div class="form-group sde-module">
 
138
  </div>
139
  </div>
140
 
 
 
 
 
 
 
141
  <div style="margin-top: var(--spacing-4); padding: var(--spacing-3); background: var(--surface-secondary); border-radius: var(--radius-md); text-align: center;">
142
  <small style="color: var(--text-tertiary);">
143
  💡 横屏使用可获得更多高级参数选项
 
204
  <div id="traditionalPromptMode" class="prompt-mode">
205
  <div class="form-group">
206
  <label for="prompt" id="promptLabel">编辑提示词</label>
207
+ <textarea id="prompt" rows="6" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>
208
  </div>
209
  </div>
210
 
 
225
  <!-- 模块二:场景描述 -->
226
  <div class="form-group sde-module">
227
  <label for="sceneDescription">模块二:场景描述</label>
228
+ <textarea id="sceneDescription" rows="6" placeholder="详细描述您想要的场景、动作、姿势、服装等..." oninput="updateCombinedPrompt()"></textarea>
229
  <small class="help-text">核心创意输入区,描述具体场景</small>
230
  </div>
231