Commit c9009a549fa3a201c4d0e6c5ca17dec64270260a

Authored by yibo
1 parent c9834ccb

Move nickname UI above the chat input, trigger on input focus

- "昵称 X ✎" button (next to the "聊天" title) replaced with a row directly
  above the chat input: hidden until a nickname is set, then shows
  "<nickname>  change name" — matches infiniteslop.ai's own placement/label
- Nickname prompt now triggers on focusing the chat input itself, not just
  on hitting send — so a first-time visitor sees it the moment they click in

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
library.json
1 1 [
2 2 {
  3 + "id": "8024972d6019",
  4 + "source": "h3console",
  5 + "prompt": "蛤蟆文太跳出来了打架",
  6 + "fullPrompt": "蛤蟆文太跳出来了打架",
  7 + "edited": false,
  8 + "width": 832,
  9 + "height": 480,
  10 + "duration": 5,
  11 + "partition": "text",
  12 + "likes": 0,
  13 + "createdAt": 1788249485816
  14 + },
  15 + {
3 16 "id": "0ade3648412f",
4 17 "source": "h3console",
5 18 "prompt": "来一只猫在草地上玩耍",
... ... @@ -53,7 +66,7 @@
53 66 "likes": 0,
54 67 "createdAt": 1788192915845,
55 68 "promptEn": "scholar watches the room warily",
56   - "lastPlayedAt": 1788246004885
  69 + "lastPlayedAt": 1788249895446
57 70 },
58 71 {
59 72 "id": "e8cbb73560ef",
... ... @@ -307,7 +320,7 @@
307 320 "partition": "ref2va",
308 321 "likes": 0,
309 322 "createdAt": 1788187365789,
310   - "lastPlayedAt": 1788247402036,
  323 + "lastPlayedAt": 1788249805778,
311 324 "promptEn": "woman in white being gentle and comforting"
312 325 },
313 326 {
... ... @@ -488,7 +501,7 @@
488 501 "likes": 0,
489 502 "createdAt": 1788138492364,
490 503 "promptEn": "astronaut on a Martian canyon, helmet reflecting an alien ruined city",
491   - "lastPlayedAt": 1788247866434
  504 + "lastPlayedAt": 1788250133927
492 505 },
493 506 {
494 507 "id": "f84b8f044ec5",
... ... @@ -503,7 +516,7 @@
503 516 "likes": 0,
504 517 "createdAt": 1788120171725,
505 518 "promptEn": "constable bursts in and blocks the door, talks to a pale woman behind the screen",
506   - "lastPlayedAt": 1788248607084
  519 + "lastPlayedAt": 1788249899449
507 520 },
508 521 {
509 522 "id": "5c364152e54c",
... ... @@ -563,7 +576,7 @@
563 576 "likes": 0,
564 577 "createdAt": 1788071067702,
565 578 "promptEn": "constable stands still, quietly says he won't make a sound, woman stays silent behind the screen",
566   - "lastPlayedAt": 1788247289944
  579 + "lastPlayedAt": 1788249939841
567 580 },
568 581 {
569 582 "id": "2e09fcd3d42c",
... ... @@ -638,7 +651,7 @@
638 651 "likes": 0,
639 652 "createdAt": 1788026877547,
640 653 "promptEn": "a deity in gold crown and red veil, quietly judging you in a moonlit bridal chamber",
641   - "lastPlayedAt": 1788248094627
  654 + "lastPlayedAt": 1788249765707
642 655 },
643 656 {
644 657 "id": "d5f811737e95",
... ... @@ -683,7 +696,7 @@
683 696 "likes": 0,
684 697 "createdAt": 1788026569123,
685 698 "promptEn": "pale dream demon in a suit, reaching out a hand inside a dream train car",
686   - "lastPlayedAt": 1788248166697
  699 + "lastPlayedAt": 1788250129929
687 700 },
688 701 {
689 702 "id": "bd6e2b264243",
... ... @@ -743,7 +756,7 @@
743 756 "likes": 0,
744 757 "createdAt": 1788026068497,
745 758 "promptEn": "black-haired prince in a red cloak, pressing down an execution order in the trial hall",
746   - "lastPlayedAt": 1788247147812
  759 + "lastPlayedAt": 1788249833797
747 760 },
748 761 {
749 762 "id": "0a5e37bb328c",
... ... @@ -758,7 +771,7 @@
758 771 "likes": 0,
759 772 "createdAt": 1788025642732,
760 773 "promptEn": "Vecna reaching out a claw in his lair in the Upside Down, red lightning",
761   - "lastPlayedAt": 1788246201053
  774 + "lastPlayedAt": 1788249929837
762 775 },
763 776 {
764 777 "id": "00862f966c3f",
... ... @@ -1133,7 +1146,7 @@
1133 1146 "likes": 0,
1134 1147 "createdAt": 1788014099497,
1135 1148 "promptEn": "glass bridge game, exploiting a flaw to push back player 101",
1136   - "lastPlayedAt": 1788248180707
  1149 + "lastPlayedAt": 1788249885816
1137 1150 },
1138 1151 {
1139 1152 "id": "24576c264a25",
... ... @@ -1148,7 +1161,7 @@
1148 1161 "likes": 0,
1149 1162 "createdAt": 1788013870849,
1150 1163 "promptEn": "glass bridge game, exploiting a flaw to push back player 101",
1151   - "lastPlayedAt": 1788244557694
  1164 + "lastPlayedAt": 1788249963847
1152 1165 },
1153 1166 {
1154 1167 "id": "dd12bbb55060",
... ... @@ -1178,7 +1191,7 @@
1178 1191 "likes": 0,
1179 1192 "createdAt": 1788013632065,
1180 1193 "promptEn": "glass bridge game, held the goal but paid a price",
1181   - "lastPlayedAt": 1788247840412
  1194 + "lastPlayedAt": 1788249933837
1182 1195 },
1183 1196 {
1184 1197 "id": "892b923eed29",
... ... @@ -1193,7 +1206,7 @@
1193 1206 "likes": 0,
1194 1207 "createdAt": 1788013393463,
1195 1208 "promptEn": "guessed wrong in a survival game, got countered",
1196   - "lastPlayedAt": 1788248362870
  1209 + "lastPlayedAt": 1788250071902
1197 1210 },
1198 1211 {
1199 1212 "id": "7880a91928c6",
... ... @@ -1253,7 +1266,7 @@
1253 1266 "likes": 0,
1254 1267 "createdAt": 1788012679740,
1255 1268 "promptEn": "in the mental lair, exploiting a flaw to counter Vecna",
1256   - "lastPlayedAt": 1788245658603
  1269 + "lastPlayedAt": 1788250124975
1257 1270 },
1258 1271 {
1259 1272 "id": "3913752bbe3a",
... ... @@ -1403,7 +1416,7 @@
1403 1416 "likes": 0,
1404 1417 "createdAt": 1788010775613,
1405 1418 "promptEn": "facing Voldemort, stayed passive and got cornered",
1406   - "lastPlayedAt": 1788241054655
  1419 + "lastPlayedAt": 1788249909833
1407 1420 },
1408 1421 {
1409 1422 "id": "795842efa4a1",
... ... @@ -1583,7 +1596,7 @@
1583 1596 "likes": 0,
1584 1597 "createdAt": 1788007915610,
1585 1598 "promptEn": "in the ruined city, exploiting a flaw to counter Tomura",
1586   - "lastPlayedAt": 1788244427575
  1599 + "lastPlayedAt": 1788249919450
1587 1600 },
1588 1601 {
1589 1602 "id": "f67685cd6862",
... ... @@ -1628,7 +1641,7 @@
1628 1641 "likes": 0,
1629 1642 "createdAt": 1788007201908,
1630 1643 "promptEn": "breaking into the palace, Neferpitou stalling to protect the king",
1631   - "lastPlayedAt": 1788248014560
  1644 + "lastPlayedAt": 1788249889814
1632 1645 },
1633 1646 {
1634 1647 "id": "628026375795",
... ... @@ -1718,7 +1731,7 @@
1718 1731 "likes": 0,
1719 1732 "createdAt": 1788005778588,
1720 1733 "promptEn": "facing Frieza, held the goal but paid a price",
1721   - "lastPlayedAt": 1788247462098
  1734 + "lastPlayedAt": 1788249925828
1722 1735 },
1723 1736 {
1724 1737 "id": "b96cc9762cf6",
... ... @@ -1808,7 +1821,7 @@
1808 1821 "likes": 0,
1809 1822 "createdAt": 1788004353928,
1810 1823 "promptEn": "facing Mahito, went in head-on and got seen through",
1811   - "lastPlayedAt": 1788244924008
  1824 + "lastPlayedAt": 1788250119920
1812 1825 },
1813 1826 {
1814 1827 "id": "971d80ec087c",
... ... @@ -1928,7 +1941,7 @@
1928 1941 "likes": 0,
1929 1942 "createdAt": 1788002691822,
1930 1943 "promptEn": "facing Reiner, held the goal but paid a price",
1931   - "lastPlayedAt": 1788244299472
  1944 + "lastPlayedAt": 1788249915447
1932 1945 },
1933 1946 {
1934 1947 "id": "f65b15d67966",
... ... @@ -2258,7 +2271,7 @@
2258 2271 "likes": 0,
2259 2272 "createdAt": 1787762342686,
2260 2273 "promptEn": "woman tells him from behind the screen to go check outside, he steps out warily",
2261   - "lastPlayedAt": 1788248000548
  2274 + "lastPlayedAt": 1788250091902
2262 2275 },
2263 2276 {
2264 2277 "id": "47a6de809c61",
... ... @@ -2288,7 +2301,7 @@
2288 2301 "likes": 0,
2289 2302 "createdAt": 1787762311804,
2290 2303 "promptEn": "constable sits with a cup of water, slowly looking up at the woman behind the screen",
2291   - "lastPlayedAt": 1788246833570
  2304 + "lastPlayedAt": 1788250038963
2292 2305 },
2293 2306 {
2294 2307 "id": "850f13881576",
... ... @@ -2393,7 +2406,7 @@
2393 2406 "likes": 0,
2394 2407 "createdAt": 1787724313620,
2395 2408 "promptEn": "first-person view bursting into the room, glancing back in fear, the woman startled",
2396   - "lastPlayedAt": 1788247658274
  2409 + "lastPlayedAt": 1788249869811
2397 2410 },
2398 2411 {
2399 2412 "id": "81b6c3789111",
... ... @@ -2498,7 +2511,7 @@
2498 2511 "likes": 0,
2499 2512 "createdAt": 1787708653181,
2500 2513 "promptEn": "constable holds her gaze, quietly says \"go on\", then waits in silence",
2501   - "lastPlayedAt": 1788244341508
  2514 + "lastPlayedAt": 1788249737695
2502 2515 },
2503 2516 {
2504 2517 "id": "55f98ceec9e4",
... ... @@ -2663,7 +2676,7 @@
2663 2676 "likes": 0,
2664 2677 "createdAt": 1787674737743,
2665 2678 "promptEn": "constable slowly looks around the room, discovers her watching him motionlessly through the screen",
2666   - "lastPlayedAt": 1788248444942
  2679 + "lastPlayedAt": 1788249837798
2667 2680 },
2668 2681 {
2669 2682 "id": "c74118d0413f",
... ... @@ -2708,7 +2721,7 @@
2708 2721 "likes": 0,
2709 2722 "createdAt": 1787674695703,
2710 2723 "promptEn": "constable stops, faces the screen, quietly promises to keep his voice down",
2711   - "lastPlayedAt": 1788247540173
  2724 + "lastPlayedAt": 1788250023879
2712 2725 },
2713 2726 {
2714 2727 "id": "94dc455e26bf",
... ... @@ -2753,7 +2766,7 @@
2753 2766 "likes": 0,
2754 2767 "createdAt": 1787674642139,
2755 2768 "promptEn": "constable forms hand seals and summons a giant toad out of smoke",
2756   - "lastPlayedAt": 1788247129805
  2769 + "lastPlayedAt": 1788250076972
2757 2770 },
2758 2771 {
2759 2772 "id": "e9a6320e18a9",
... ... @@ -2783,7 +2796,7 @@
2783 2796 "likes": 0,
2784 2797 "createdAt": 1787674639055,
2785 2798 "promptEn": "constable checks his reflection in the mirror, sees her behind his shoulder in it, turns around to find the screen empty",
2786   - "lastPlayedAt": 1788246273115
  2799 + "lastPlayedAt": 1788249777714
2787 2800 },
2788 2801 {
2789 2802 "id": "ebaf0dde3d9f",
... ... @@ -3023,7 +3036,7 @@
3023 3036 "likes": 0,
3024 3037 "createdAt": 1787615243753,
3025 3038 "promptEn": "robot arm picks up a red block and puts it down",
3026   - "lastPlayedAt": 1788246909631
  3039 + "lastPlayedAt": 1788249857808
3027 3040 },
3028 3041 {
3029 3042 "id": "d040eec16a88",
... ... @@ -3038,7 +3051,7 @@
3038 3051 "likes": 0,
3039 3052 "createdAt": 1787615242602,
3040 3053 "promptEn": "otome game male lead showcase PV",
3041   - "lastPlayedAt": 1788247347993
  3054 + "lastPlayedAt": 1788249753702
3042 3055 },
3043 3056 {
3044 3057 "id": "423a20afba5a",
... ... @@ -3068,7 +3081,7 @@
3068 3081 "likes": 0,
3069 3082 "createdAt": 1787615240988,
3070 3083 "promptEn": "clay fox sprints off a cliff, slow-mo leap over the canyon",
3071   - "lastPlayedAt": 1788247880445
  3084 + "lastPlayedAt": 1788249951846
3072 3085 },
3073 3086 {
3074 3087 "id": "db125dc6a831",
... ... @@ -3128,7 +3141,7 @@
3128 3141 "likes": 0,
3129 3142 "createdAt": 1787588545243,
3130 3143 "promptEn": "ergonomic office chair 360 showcase ad",
3131   - "lastPlayedAt": 1788248711178
  3144 + "lastPlayedAt": 1788250058966
3132 3145 },
3133 3146 {
3134 3147 "id": "b0d258352c3c",
... ... @@ -3218,7 +3231,7 @@
3218 3231 "likes": 0,
3219 3232 "createdAt": 1787588541726,
3220 3233 "promptEn": "woman opens the trunk on a desert road, takes out a bag",
3221   - "lastPlayedAt": 1788245330307
  3234 + "lastPlayedAt": 1788250107920
3222 3235 },
3223 3236 {
3224 3237 "id": "97846542ab09",
... ... @@ -3397,7 +3410,7 @@
3397 3410 "partition": "text",
3398 3411 "likes": 0,
3399 3412 "createdAt": 1787576506447,
3400   - "lastPlayedAt": 1788247534166,
  3413 + "lastPlayedAt": 1788250053889,
3401 3414 "promptEn": "man does as she says but stays on guard"
3402 3415 },
3403 3416 {
... ... @@ -3548,7 +3561,7 @@
3548 3561 "likes": 0,
3549 3562 "createdAt": 1787542770522,
3550 3563 "promptEn": "clip cut to match a reference video's editing rhythm",
3551   - "lastPlayedAt": 1788248438939
  3564 + "lastPlayedAt": 1788249969849
3552 3565 },
3553 3566 {
3554 3567 "id": "f9286d694bad",
... ... @@ -3608,7 +3621,7 @@
3608 3621 "likes": 0,
3609 3622 "createdAt": 1787470101960,
3610 3623 "promptEn": "cute hand-drawn beagle dog making faces, game screen",
3611   - "lastPlayedAt": 1788248134665
  3624 + "lastPlayedAt": 1788249793727
3612 3625 },
3613 3626 {
3614 3627 "id": "4067993ff824",
... ... @@ -3833,7 +3846,7 @@
3833 3846 "likes": 0,
3834 3847 "createdAt": 1787450754171,
3835 3848 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3836   - "lastPlayedAt": 1788246511317
  3849 + "lastPlayedAt": 1788249973852
3837 3850 },
3838 3851 {
3839 3852 "id": "fd0183c54763",
... ... @@ -3878,7 +3891,7 @@
3878 3891 "likes": 0,
3879 3892 "createdAt": 1787442174982,
3880 3893 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
3881   - "lastPlayedAt": 1788248334845
  3894 + "lastPlayedAt": 1788249819782
3882 3895 },
3883 3896 {
3884 3897 "id": "18105bc2900b",
... ... @@ -4058,7 +4071,7 @@
4058 4071 "likes": 0,
4059 4072 "createdAt": 1787412606465,
4060 4073 "promptEn": "protagonist leaps across rooftops fleeing pursuers",
4061   - "lastPlayedAt": 1788246453270
  4074 + "lastPlayedAt": 1788249769708
4062 4075 },
4063 4076 {
4064 4077 "id": "29a99dc412b2",
... ... @@ -4358,7 +4371,7 @@
4358 4371 "likes": 0,
4359 4372 "createdAt": 1787326019290,
4360 4373 "promptEn": "misty Jiangnan old town at dawn, woman walking with an oil-paper umbrella",
4361   - "lastPlayedAt": 1788247508139
  4374 + "lastPlayedAt": 1788249851805
4362 4375 },
4363 4376 {
4364 4377 "id": "fd8534ce588c",
... ...
live.css
... ... @@ -176,21 +176,26 @@ body {
176 176 color: var(--muted);
177 177 padding: 12px 14px 6px;
178 178 }
179   -.chat-title-row { display: flex; align-items: center; justify-content: space-between; }
180   -.nickname-edit-btn {
  179 +.chat-identity-row {
  180 + display: flex;
  181 + align-items: center;
  182 + gap: 8px;
  183 + padding: 0 14px 6px;
  184 + font-size: 11.5px;
  185 + font-weight: 700;
  186 +}
  187 +.chat-identity-row #currentNickname { color: var(--accent); }
  188 +.chat-identity-row #changeNameBtn {
181 189 background: transparent;
182   - border: 1px solid var(--border);
  190 + border: none;
183 191 color: var(--muted);
184 192 font-size: 11px;
185 193 font-weight: 700;
186   - text-transform: none;
187   - letter-spacing: normal;
188   - border-radius: 999px;
189   - padding: 3px 10px;
  194 + text-decoration: underline;
190 195 cursor: pointer;
  196 + padding: 0;
191 197 }
192   -.nickname-edit-btn:hover { color: #fff; border-color: var(--muted); }
193   -.nickname-edit-btn #currentNickname { color: var(--accent); }
  198 +.chat-identity-row #changeNameBtn:hover { color: #fff; }
194 199 .chat-list {
195 200 flex: 1;
196 201 overflow-y: auto;
... ...
live.html
... ... @@ -40,11 +40,12 @@
40 40 </div>
41 41  
42 42 <aside class="right-col" id="rightCol" hidden>
43   - <div class="section-title chat-title-row">
44   - <span>聊天</span>
45   - <button class="nickname-edit-btn" id="nicknameEditBtn">昵称 <span id="currentNickname">未设置</span> ✎</button>
46   - </div>
  43 + <div class="section-title">聊天</div>
47 44 <div class="chat-list" id="chatList"></div>
  45 + <div class="chat-identity-row" id="chatIdentityRow" hidden>
  46 + <span id="currentNickname"></span>
  47 + <button id="changeNameBtn">change name</button>
  48 + </div>
48 49 <div class="chat-input-row">
49 50 <input type="text" id="chatInput" placeholder="说点什么,或者想看什么…" maxlength="300" />
50 51 <button id="chatSendBtn">发送</button>
... ... @@ -97,7 +98,8 @@ const muteBtn = document.getElementById(&quot;muteBtn&quot;);
97 98 const nicknameModal = document.getElementById("nicknameModal");
98 99 const nicknameInput = document.getElementById("nicknameInput");
99 100 const nicknameConfirmBtn = document.getElementById("nicknameConfirmBtn");
100   -const nicknameEditBtn = document.getElementById("nicknameEditBtn");
  101 +const chatIdentityRow = document.getElementById("chatIdentityRow");
  102 +const changeNameBtn = document.getElementById("changeNameBtn");
101 103 const currentNickname = document.getElementById("currentNickname");
102 104 const clientId = Math.random().toString(36).slice(2);
103 105  
... ... @@ -130,7 +132,8 @@ function getIdentity() {
130 132 }
131 133 function updateNicknameLabel() {
132 134 const existing = getIdentity();
133   - currentNickname.textContent = existing ? existing.nickname : "未设置";
  135 + chatIdentityRow.hidden = !existing;
  136 + if (existing) currentNickname.textContent = existing.nickname;
134 137 }
135 138  
136 139 // Shared by first-time setup and the always-visible "昵称 ✎" edit button.
... ... @@ -167,7 +170,7 @@ function ensureIdentity() {
167 170 });
168 171 }
169 172  
170   -nicknameEditBtn.addEventListener("click", async () => {
  173 +changeNameBtn.addEventListener("click", async () => {
171 174 const existing = getIdentity();
172 175 const nickname = await openNicknameModal(existing ? existing.nickname : "");
173 176 const uid = existing ? existing.uid : crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
... ... @@ -176,6 +179,10 @@ nicknameEditBtn.addEventListener(&quot;click&quot;, async () =&gt; {
176 179 updateNicknameLabel();
177 180 });
178 181  
  182 +// First click into the chat box is what prompts for a nickname — not just
  183 +// hitting send — so the identity row above the input appears right away.
  184 +chatInput.addEventListener("focus", () => ensureIdentity());
  185 +
179 186 updateNicknameLabel();
180 187  
181 188 function renderBrowseList() {
... ...