wapadil Claude commited on
Commit
2527dd3
·
1 Parent(s): c1719f7

[FEATURE] 历史记录与输入框交互体验升级

Browse files

• 历史记录图片添加复制提示词按钮,一键复制并填入输入框
• iPad/移动端提示词输入框支持动态大小调整(1-12行)
• 用户输入框大小偏好本地存储,刷新后自动恢复
• 优化提示词卡片标题层级,去掉重复的小标题
• 新增generation-actions容器支持多操作按钮
• 完整的移动端触控目标优化和交互反馈

🤖 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 +76 -4
  2. static/style.css +75 -7
  3. templates/index.html +14 -2
static/script.js CHANGED
@@ -325,7 +325,7 @@ function handleModelChange() {
325
 
326
  if (isTextToImage) {
327
  promptTitle.textContent = '生成提示词';
328
- promptLabel.textContent = '生成提示词';
329
  document.getElementById('prompt').placeholder = '例如:美丽的山水风景,湖泊和夕阳';
330
  imageInputCard.style.display = 'none';
331
  uploadedImages = [];
@@ -505,6 +505,9 @@ window.addEventListener('DOMContentLoaded', () => {
505
  document.getElementById('apiKey').value = savedKey;
506
  }
507
 
 
 
 
508
  // Initialize UI state
509
  handleImageSizeChange();
510
  handleModelChange();
@@ -1326,9 +1329,14 @@ function displayHistory() {
1326
  item.innerHTML = `
1327
  <img id="${imageId}" src="${imgSrc}" alt="Generation" loading="lazy" decoding="async"
1328
  onclick="openImageModal('${imageId}', '${imgSrc}', '${generation.prompt.replace(/'/g, "\\'")}', '${new Date(generation.timestamp).toLocaleString()}')">
1329
- <button class="use-as-input-btn" onclick="useAsInput('${imageId}', '${imgSrc}')" title="Use as input">
1330
- 作为输入
1331
- </button>
 
 
 
 
 
1332
  <div class="generation-meta">
1333
  <span class="timestamp">${new Date(generation.timestamp).toLocaleString()}</span>
1334
  <span class="prompt-preview">${generation.prompt}</span>
@@ -1340,6 +1348,70 @@ function displayHistory() {
1340
  });
1341
  }
1342
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1343
  // Use image as input
1344
  async function useAsInput(imageId, imageSrc) {
1345
  try {
 
325
 
326
  if (isTextToImage) {
327
  promptTitle.textContent = '生成提示词';
328
+ promptLabel.textContent = '提示词';
329
  document.getElementById('prompt').placeholder = '例如:美丽的山水风景,湖泊和夕阳';
330
  imageInputCard.style.display = 'none';
331
  uploadedImages = [];
 
505
  document.getElementById('apiKey').value = savedKey;
506
  }
507
 
508
+ // Load saved textarea sizes
509
+ loadTextareaSizes();
510
+
511
  // Initialize UI state
512
  handleImageSizeChange();
513
  handleModelChange();
 
1329
  item.innerHTML = `
1330
  <img id="${imageId}" src="${imgSrc}" alt="Generation" loading="lazy" decoding="async"
1331
  onclick="openImageModal('${imageId}', '${imgSrc}', '${generation.prompt.replace(/'/g, "\\'")}', '${new Date(generation.timestamp).toLocaleString()}')">
1332
+ <div class="generation-actions">
1333
+ <button class="use-as-input-btn" onclick="useAsInput('${imageId}', '${imgSrc}')" title="作为输入">
1334
+ ↻ 作为输入
1335
+ </button>
1336
+ <button class="copy-prompt-btn" onclick="copyPromptFromHistory('${generation.prompt.replace(/'/g, "\\'")}', event)" title="复制提示词">
1337
+ 📋 复制提示词
1338
+ </button>
1339
+ </div>
1340
  <div class="generation-meta">
1341
  <span class="timestamp">${new Date(generation.timestamp).toLocaleString()}</span>
1342
  <span class="prompt-preview">${generation.prompt}</span>
 
1348
  });
1349
  }
1350
 
1351
+ // Adjust textarea size for mobile/iPad
1352
+ function adjustTextareaSize(textareaId, direction) {
1353
+ const textarea = document.getElementById(textareaId);
1354
+ if (!textarea) return;
1355
+
1356
+ const currentRows = parseInt(textarea.getAttribute('rows') || '3');
1357
+ let newRows = currentRows;
1358
+
1359
+ if (direction === 'larger' && currentRows < 12) {
1360
+ newRows = currentRows + 1;
1361
+ } else if (direction === 'smaller' && currentRows > 1) {
1362
+ newRows = currentRows - 1;
1363
+ }
1364
+
1365
+ if (newRows !== currentRows) {
1366
+ textarea.setAttribute('rows', newRows.toString());
1367
+ // 保存用户偏好
1368
+ localStorage.setItem(`textarea-size-${textareaId}`, newRows.toString());
1369
+
1370
+ // 给用户反馈
1371
+ const sizeText = newRows <= 2 ? '小' : newRows <= 4 ? '中' : newRows <= 6 ? '大' : '超大';
1372
+ StatusManager.show(`输入框大小已调整为: ${sizeText}`, 'success');
1373
+ }
1374
+ }
1375
+
1376
+ // Load saved textarea sizes on page load
1377
+ function loadTextareaSizes() {
1378
+ ['prompt', 'drawerPrompt'].forEach(id => {
1379
+ const savedSize = localStorage.getItem(`textarea-size-${id}`);
1380
+ if (savedSize) {
1381
+ const textarea = document.getElementById(id);
1382
+ if (textarea) {
1383
+ textarea.setAttribute('rows', savedSize);
1384
+ }
1385
+ }
1386
+ });
1387
+ }
1388
+
1389
+ // Copy prompt from history
1390
+ function copyPromptFromHistory(prompt, event) {
1391
+ event.stopPropagation(); // 防止触发图片点击事件
1392
+
1393
+ // 解码HTML实体和转义字符
1394
+ const decodedPrompt = prompt.replace(/\\'/g, "'").replace(/&quot;/g, '"').replace(/&amp;/g, '&');
1395
+
1396
+ navigator.clipboard.writeText(decodedPrompt).then(() => {
1397
+ // 显示成功提示
1398
+ StatusManager.show('提示词已复制到剪贴板', 'success');
1399
+
1400
+ // 可选:同时填入当前的prompt输入框
1401
+ const promptTextarea = document.getElementById('prompt');
1402
+ const drawerPromptTextarea = document.getElementById('drawerPrompt');
1403
+ if (promptTextarea) {
1404
+ promptTextarea.value = decodedPrompt;
1405
+ }
1406
+ if (drawerPromptTextarea) {
1407
+ drawerPromptTextarea.value = decodedPrompt;
1408
+ }
1409
+ }).catch(err => {
1410
+ console.error('复制失败:', err);
1411
+ StatusManager.show('复制失败,请手动选择文本复制', 'error');
1412
+ });
1413
+ }
1414
+
1415
  // Use image as input
1416
  async function useAsInput(imageId, imageSrc) {
1417
  try {
static/style.css CHANGED
@@ -441,10 +441,24 @@ label, .meta {
441
  line-height: 1.3;
442
  }
443
 
444
- .use-as-input-btn {
 
445
  position: absolute;
446
  top: 8px;
447
  right: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  background: linear-gradient(135deg, var(--brand-secondary) 0%, var(--brand-primary) 100%);
449
  color: white;
450
  border: none;
@@ -453,16 +467,70 @@ label, .meta {
453
  font-size: 0.75rem;
454
  font-weight: 600;
455
  cursor: pointer;
456
- opacity: 0;
457
- transition: all 0.3s;
 
458
  }
459
 
460
- .generation-item:hover .use-as-input-btn {
461
- opacity: 1;
 
462
  }
463
 
464
- .use-as-input-btn:hover {
465
- transform: scale(1.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  }
467
 
468
  /* Empty State */
 
441
  line-height: 1.3;
442
  }
443
 
444
+ /* Generation action buttons container */
445
+ .generation-actions {
446
  position: absolute;
447
  top: 8px;
448
  right: 8px;
449
+ display: flex;
450
+ flex-direction: column;
451
+ gap: 4px;
452
+ opacity: 0;
453
+ transition: all 0.3s ease;
454
+ }
455
+
456
+ .generation-item:hover .generation-actions {
457
+ opacity: 1;
458
+ }
459
+
460
+ /* Shared button styles */
461
+ .use-as-input-btn, .copy-prompt-btn {
462
  background: linear-gradient(135deg, var(--brand-secondary) 0%, var(--brand-primary) 100%);
463
  color: white;
464
  border: none;
 
467
  font-size: 0.75rem;
468
  font-weight: 600;
469
  cursor: pointer;
470
+ transition: all 0.2s ease;
471
+ white-space: nowrap;
472
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
473
  }
474
 
475
+ /* Copy prompt button specific styling */
476
+ .copy-prompt-btn {
477
+ background: linear-gradient(135deg, #34C759 0%, #30D158 100%);
478
  }
479
 
480
+ .use-as-input-btn:hover, .copy-prompt-btn:hover {
481
+ transform: scale(1.05);
482
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
483
+ }
484
+
485
+ .copy-prompt-btn:hover {
486
+ background: linear-gradient(135deg, #30D158 0%, #32D74B 100%);
487
+ }
488
+
489
+ /* Textarea size controls */
490
+ .textarea-size-controls {
491
+ display: flex;
492
+ gap: 4px;
493
+ }
494
+
495
+ .size-control-btn {
496
+ width: 28px;
497
+ height: 28px;
498
+ border: 1px solid var(--border-medium);
499
+ background: var(--surface);
500
+ border-radius: var(--radius-sm);
501
+ color: var(--text-secondary);
502
+ font-size: 14px;
503
+ font-weight: 500;
504
+ cursor: pointer;
505
+ transition: all 0.2s ease;
506
+ display: flex;
507
+ align-items: center;
508
+ justify-content: center;
509
+ user-select: none;
510
+ }
511
+
512
+ .size-control-btn:hover {
513
+ background: var(--brand-primary);
514
+ color: white;
515
+ border-color: var(--brand-primary);
516
+ transform: scale(1.05);
517
+ }
518
+
519
+ .size-control-btn:active {
520
+ transform: scale(0.95);
521
+ }
522
+
523
+ /* Mobile optimized size controls */
524
+ @media (max-width: 768px) {
525
+ .size-control-btn {
526
+ width: 32px;
527
+ height: 32px;
528
+ font-size: 16px;
529
+ }
530
+
531
+ .textarea-size-controls {
532
+ gap: 6px;
533
+ }
534
  }
535
 
536
  /* Empty State */
templates/index.html CHANGED
@@ -64,7 +64,13 @@
64
 
65
  <!-- Prompt Input for Mobile -->
66
  <div class="card">
67
- <h2>✏️ 编辑指令</h2>
 
 
 
 
 
 
68
  <div class="form-group">
69
  <label for="drawerPrompt">提示词</label>
70
  <textarea id="drawerPrompt" rows="3" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>
@@ -138,7 +144,13 @@
138
  </div>
139
 
140
  <div class="card">
141
- <h2 id="promptTitle">✏️ 编辑指令</h2>
 
 
 
 
 
 
142
  <div class="form-group">
143
  <label for="prompt" id="promptLabel">编辑提示词</label>
144
  <textarea id="prompt" rows="3" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>
 
64
 
65
  <!-- Prompt Input for Mobile -->
66
  <div class="card">
67
+ <div class="card-header">
68
+ <h2>✏️ 编辑指令</h2>
69
+ <div class="textarea-size-controls">
70
+ <button type="button" class="size-control-btn" onclick="adjustTextareaSize('drawerPrompt', 'smaller')" title="缩小输入框">-</button>
71
+ <button type="button" class="size-control-btn" onclick="adjustTextareaSize('drawerPrompt', 'larger')" title="放大输入框">+</button>
72
+ </div>
73
+ </div>
74
  <div class="form-group">
75
  <label for="drawerPrompt">提示词</label>
76
  <textarea id="drawerPrompt" rows="3" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>
 
144
  </div>
145
 
146
  <div class="card">
147
+ <div class="card-header">
148
+ <h2 id="promptTitle">✏️ 编辑指令</h2>
149
+ <div class="textarea-size-controls">
150
+ <button type="button" class="size-control-btn" onclick="adjustTextareaSize('prompt', 'smaller')" title="缩小输入框">-</button>
151
+ <button type="button" class="size-control-btn" onclick="adjustTextareaSize('prompt', 'larger')" title="放大输入框">+</button>
152
+ </div>
153
+ </div>
154
  <div class="form-group">
155
  <label for="prompt" id="promptLabel">编辑提示词</label>
156
  <textarea id="prompt" rows="3" placeholder="例如:给模特穿上衣服和鞋子">给模特穿上衣服和鞋子</textarea>