@my-life-buddies/cli 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -7
- package/dist/bin/buddy.js +8 -3
- package/dist/bin/buddy.js.map +2 -2
- package/dist/bin/core.js +475 -312
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +158 -46
- package/dist/bin/preview.js.map +2 -2
- package/dist/web/app.css +15 -8
- package/dist/web/app.js +85 -8
- package/dist/web/index.html +4 -0
- package/package.json +3 -3
package/dist/web/app.css
CHANGED
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
.workspace {
|
|
229
229
|
min-height: calc(100dvh - 4.5rem);
|
|
230
230
|
display: grid;
|
|
231
|
-
grid-template-columns: minmax(0, 1fr) minmax(
|
|
231
|
+
grid-template-columns: minmax(0, 1fr) minmax(26rem, 32rem);
|
|
232
232
|
background: var(--color-paper);
|
|
233
233
|
}
|
|
234
234
|
}
|
|
@@ -365,16 +365,17 @@
|
|
|
365
365
|
inset-block-start: 4.5rem;
|
|
366
366
|
display: grid;
|
|
367
367
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
368
|
-
padding: var(--space-
|
|
368
|
+
padding: var(--space-md) var(--space-md) var(--space-xs);
|
|
369
369
|
border-inline-start: var(--rule-hair) solid var(--color-rule);
|
|
370
370
|
background: var(--color-paper-soft);
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
.simulator-
|
|
373
|
+
.simulator-actions { display: flex; gap: var(--space-xs); flex-wrap: wrap; }
|
|
374
|
+
.simulator-caption { min-height: var(--control-height); display: flex; align-items: center; justify-content: space-between; gap: var(--space-sm); flex-wrap: wrap; }
|
|
374
375
|
.simulator-caption h2 { margin: 0; color: var(--color-ink-strong); font-size: var(--text-lg); font-weight: 600; letter-spacing: -0.02em; }
|
|
375
376
|
|
|
376
|
-
.device-stage { min-height: 0; display: grid; place-items: center; padding-block: var(--space-
|
|
377
|
-
.phone { --phone-width: min(
|
|
377
|
+
.device-stage { min-height: 0; display: grid; place-items: center; padding-block: var(--space-sm) 0; container-type: size; }
|
|
378
|
+
.phone { --phone-width: min(27rem, 96cqw, 48cqh); width: var(--phone-width); aspect-ratio: 415 / 843; container-type: inline-size; }
|
|
378
379
|
.phone iphone-16-max { --width: var(--phone-width); display: block; width: 100%; filter: drop-shadow(var(--shadow-phone)); }
|
|
379
380
|
.phone-screen { position: relative; width: 100%; height: 100%; display: grid; grid-template-rows: 10.6024cqw minmax(0, 1fr) auto; padding: 11.0843cqw 0 0; overflow: clip; background: var(--color-phone-paper); color: var(--color-phone-ink); font-family: var(--font-body); }
|
|
380
381
|
|
|
@@ -389,7 +390,8 @@
|
|
|
389
390
|
.buddy-identity { min-width: 0; }
|
|
390
391
|
.chat-navigation h3 { margin: 0; overflow: hidden; font-size: 3.3735cqw; font-weight: 600; line-height: 1.2; text-overflow: ellipsis; white-space: nowrap; }
|
|
391
392
|
|
|
392
|
-
.chat-messages { min-height: 0; display: flex; flex-direction: column; overflow-y: auto; padding: 2.8916cqw 3.8554cqw 4.8193cqw; background: var(--color-phone-paper); color: var(--color-phone-ink); scrollbar-width: none; }
|
|
393
|
+
.chat-messages { min-height: 0; display: flex; flex-direction: column; overflow-y: auto; overscroll-behavior: contain; overflow-anchor: none; padding: 2.8916cqw 3.8554cqw 4.8193cqw; background: var(--color-phone-paper); color: var(--color-phone-ink); scrollbar-width: none; }
|
|
394
|
+
.latest-messages { position: absolute; z-index: 2; bottom: 19cqw; left: 50%; transform: translateX(-50%); white-space: nowrap; padding: 2cqw 3cqw; border-radius: 5cqw; background: var(--color-phone-paper); color: var(--color-phone-ink); border: 1px solid var(--color-rule); box-shadow: var(--shadow-whisper); font-size: 3cqw; }
|
|
393
395
|
.chat-messages::-webkit-scrollbar { display: none; }
|
|
394
396
|
.chat-empty { min-height: 100%; display: block; }
|
|
395
397
|
.chat-date { align-self: center; margin: 0 0 2.8916cqw; color: var(--color-phone-muted); font-size: 2.6506cqw; }
|
|
@@ -457,10 +459,15 @@
|
|
|
457
459
|
|
|
458
460
|
.debug-tabs button:active, .simulator-device:active, .button:active:not(:disabled), .icon-button:active { transform: translateY(1px); }
|
|
459
461
|
|
|
462
|
+
@media (max-width: 95rem) {
|
|
463
|
+
.overview-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-lg); }
|
|
464
|
+
.environment-note { padding-inline-start: 0; border-inline-start: 0; }
|
|
465
|
+
}
|
|
466
|
+
|
|
460
467
|
@media (max-width: 78rem) {
|
|
461
468
|
.workspace { grid-template-columns: minmax(0, 1fr); }
|
|
462
|
-
.simulator-area { min-height:
|
|
463
|
-
.phone { --phone-width: min(
|
|
469
|
+
.simulator-area { min-height: 52rem; height: 90dvh; max-height: none; position: static; border-block-start: var(--rule-hair) solid var(--color-rule); border-inline-start: 0; }
|
|
470
|
+
.phone { --phone-width: min(27rem, 94cqw, 48cqh); }
|
|
464
471
|
}
|
|
465
472
|
|
|
466
473
|
@media (max-width: 58rem) {
|
package/dist/web/app.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
devControl: $("#dev-control"),
|
|
10
10
|
errorBanner: $("#error-banner"),
|
|
11
11
|
errorMessage: $("#error-message"),
|
|
12
|
+
resetConversation: $("#reset-conversation"),
|
|
13
|
+
latestMessages: $("#latest-messages"),
|
|
12
14
|
pairDevice: $("#pair-device"),
|
|
13
15
|
pairingPanel: $("#pairing-panel"),
|
|
14
16
|
pairingBody: $("#pairing-body"),
|
|
@@ -63,6 +65,10 @@
|
|
|
63
65
|
reportedFailureRevision: -1,
|
|
64
66
|
polling: undefined,
|
|
65
67
|
pollingBusy: false,
|
|
68
|
+
resetting: false,
|
|
69
|
+
resetUnavailable: false,
|
|
70
|
+
sending: false,
|
|
71
|
+
simulatorRequest: 0,
|
|
66
72
|
toastTimer: undefined,
|
|
67
73
|
};
|
|
68
74
|
|
|
@@ -230,7 +236,10 @@
|
|
|
230
236
|
? "正在输入…"
|
|
231
237
|
: "正在思考…"
|
|
232
238
|
: "DEV 在线";
|
|
233
|
-
elements.
|
|
239
|
+
elements.resetConversation.disabled = phase !== "ready" || Boolean(active) || state.resetting || state.sending || state.resetUnavailable;
|
|
240
|
+
elements.resetConversation.title = state.resetUnavailable ? "当前平台暂不支持重开会话" : active ? "请等待当前回复完成后再清空" : "结束当前开发版对话,开始一段全新会话";
|
|
241
|
+
elements.devControl.disabled ||= state.resetting;
|
|
242
|
+
elements.messageInput.disabled = phase !== "ready" || Boolean(active) || state.resetting || state.sending;
|
|
234
243
|
elements.sendMessage.hidden = Boolean(active);
|
|
235
244
|
updateSendButton();
|
|
236
245
|
syncPolling();
|
|
@@ -424,7 +433,20 @@
|
|
|
424
433
|
}
|
|
425
434
|
}
|
|
426
435
|
|
|
436
|
+
let messageSignature = "";
|
|
437
|
+
let followMessages = true;
|
|
438
|
+
|
|
439
|
+
function followLatestMessages() {
|
|
440
|
+
followMessages = true;
|
|
441
|
+
elements.chatMessages.scrollTop = elements.chatMessages.scrollHeight;
|
|
442
|
+
elements.latestMessages.hidden = true;
|
|
443
|
+
}
|
|
444
|
+
|
|
427
445
|
function renderMessages() {
|
|
446
|
+
const signature = JSON.stringify([state.simulator.messages, Boolean(state.simulator.activeRun)]);
|
|
447
|
+
if (signature === messageSignature) return;
|
|
448
|
+
messageSignature = signature;
|
|
449
|
+
const previousTop = elements.chatMessages.scrollTop;
|
|
428
450
|
clear(elements.chatMessages);
|
|
429
451
|
const messages = state.simulator.messages || [];
|
|
430
452
|
if (!messages.length && !state.simulator.activeRun) {
|
|
@@ -451,9 +473,11 @@
|
|
|
451
473
|
row.append(messageAvatar("assistant"), indicator);
|
|
452
474
|
elements.chatMessages.append(row);
|
|
453
475
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
476
|
+
if (followMessages) followLatestMessages();
|
|
477
|
+
else {
|
|
478
|
+
elements.chatMessages.scrollTop = previousTop;
|
|
479
|
+
elements.latestMessages.hidden = false;
|
|
480
|
+
}
|
|
457
481
|
}
|
|
458
482
|
|
|
459
483
|
function renderDebug() {
|
|
@@ -470,17 +494,21 @@
|
|
|
470
494
|
}
|
|
471
495
|
|
|
472
496
|
async function refreshDebug() {
|
|
497
|
+
const request = state.simulatorRequest;
|
|
473
498
|
const value = await api("/api/debug");
|
|
499
|
+
if (state.resetting || request !== state.simulatorRequest) return;
|
|
474
500
|
state.debug = value.debug;
|
|
475
501
|
renderDebug();
|
|
476
502
|
}
|
|
477
503
|
|
|
478
504
|
async function refreshSimulator() {
|
|
479
505
|
if (state.dev.phase !== "ready" || state.pollingBusy) return;
|
|
506
|
+
if (state.resetting) return;
|
|
507
|
+
const request = state.simulatorRequest;
|
|
480
508
|
state.pollingBusy = true;
|
|
481
509
|
try {
|
|
482
510
|
const value = await api("/api/simulator/state");
|
|
483
|
-
state.
|
|
511
|
+
if (state.resetting || request !== state.simulatorRequest || !applySimulatorState(value.state)) return;
|
|
484
512
|
renderDev();
|
|
485
513
|
renderMessages();
|
|
486
514
|
renderContext();
|
|
@@ -517,7 +545,7 @@
|
|
|
517
545
|
}
|
|
518
546
|
|
|
519
547
|
function updateSendButton() {
|
|
520
|
-
const ready = state.dev.phase === "ready" && !state.simulator.activeRun;
|
|
548
|
+
const ready = state.dev.phase === "ready" && !state.simulator.activeRun && !state.resetting && !state.sending;
|
|
521
549
|
elements.sendMessage.disabled = !ready || !elements.messageInput.value.trim();
|
|
522
550
|
}
|
|
523
551
|
|
|
@@ -526,7 +554,10 @@
|
|
|
526
554
|
async function sendMessage(event) {
|
|
527
555
|
event.preventDefault();
|
|
528
556
|
const text = elements.messageInput.value;
|
|
529
|
-
if (!text.trim() || state.dev.phase !== "ready" || state.simulator.activeRun) return;
|
|
557
|
+
if (!text.trim() || state.dev.phase !== "ready" || state.simulator.activeRun || state.resetting || state.sending) return;
|
|
558
|
+
state.sending = true;
|
|
559
|
+
followLatestMessages();
|
|
560
|
+
renderDev();
|
|
530
561
|
elements.messageInput.disabled = true;
|
|
531
562
|
elements.sendMessage.disabled = true;
|
|
532
563
|
// Keep the logical message identity after an unknown outcome; retries are user initiated.
|
|
@@ -550,6 +581,8 @@
|
|
|
550
581
|
await refreshSimulator();
|
|
551
582
|
} catch (cause) {
|
|
552
583
|
showError(cause);
|
|
584
|
+
} finally {
|
|
585
|
+
state.sending = false;
|
|
553
586
|
renderDev();
|
|
554
587
|
}
|
|
555
588
|
}
|
|
@@ -626,6 +659,43 @@
|
|
|
626
659
|
}
|
|
627
660
|
}
|
|
628
661
|
|
|
662
|
+
function applySimulatorState(next) {
|
|
663
|
+
if ((next.conversationRevision || 0) < (state.simulator.conversationRevision || 0)) return false;
|
|
664
|
+
state.simulator = next;
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
async function resetConversation() {
|
|
669
|
+
if (elements.resetConversation.disabled) return;
|
|
670
|
+
if (!window.confirm("清空当前对话并重新开始?\n这会结束当前开发版会话,重置聊天上下文、记忆和该会话的主动服务。旧记录仅在服务端留档;搭子资料和工程文件会保留。")) return;
|
|
671
|
+
state.resetting = true;
|
|
672
|
+
state.simulatorRequest++;
|
|
673
|
+
elements.resetConversation.textContent = "正在清空…";
|
|
674
|
+
renderDev();
|
|
675
|
+
try {
|
|
676
|
+
const value = await api("/api/simulator/reset", { method: "POST", body: JSON.stringify({ confirm: true }) });
|
|
677
|
+
applySimulatorState(value.state);
|
|
678
|
+
applyDevState(value.dev);
|
|
679
|
+
state.debug = value.debug;
|
|
680
|
+
state.selectedRunId = "";
|
|
681
|
+
state.followLatestRun = true;
|
|
682
|
+
pendingSend = undefined;
|
|
683
|
+
elements.messageInput.value = "";
|
|
684
|
+
followMessages = true;
|
|
685
|
+
renderAll();
|
|
686
|
+
toast("对话已清空,可以重新开始");
|
|
687
|
+
} catch (cause) {
|
|
688
|
+
if (cause?.code === "RESET_UNSUPPORTED") state.resetUnavailable = true;
|
|
689
|
+
showError(cause);
|
|
690
|
+
} finally {
|
|
691
|
+
state.resetting = false;
|
|
692
|
+
elements.resetConversation.textContent = "清空对话";
|
|
693
|
+
renderDev();
|
|
694
|
+
resizeComposer();
|
|
695
|
+
await refreshSimulator();
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
629
699
|
function connectEvents() {
|
|
630
700
|
const events = new EventSource("/api/events");
|
|
631
701
|
events.addEventListener("dev.state", (event) => {
|
|
@@ -638,7 +708,7 @@
|
|
|
638
708
|
events.addEventListener("simulator.state", (event) => {
|
|
639
709
|
try {
|
|
640
710
|
const value = JSON.parse(event.data);
|
|
641
|
-
|
|
711
|
+
if (!applySimulatorState(value.state)) return;
|
|
642
712
|
state.debug = value.debug;
|
|
643
713
|
renderDev();
|
|
644
714
|
renderDebug();
|
|
@@ -671,6 +741,13 @@
|
|
|
671
741
|
}
|
|
672
742
|
}
|
|
673
743
|
|
|
744
|
+
elements.resetConversation.addEventListener("click", () => void resetConversation());
|
|
745
|
+
elements.latestMessages.addEventListener("click", followLatestMessages);
|
|
746
|
+
elements.chatMessages.addEventListener("scroll", () => {
|
|
747
|
+
const chat = elements.chatMessages;
|
|
748
|
+
followMessages = chat.scrollHeight - chat.clientHeight - chat.scrollTop <= 32;
|
|
749
|
+
elements.latestMessages.hidden = followMessages;
|
|
750
|
+
}, { passive: true });
|
|
674
751
|
elements.devControl.addEventListener("click", () => void controlDev());
|
|
675
752
|
elements.composer.addEventListener("submit", (event) => void sendMessage(event));
|
|
676
753
|
elements.messageInput.addEventListener("input", resizeComposer);
|
package/dist/web/index.html
CHANGED
|
@@ -178,10 +178,13 @@
|
|
|
178
178
|
<section class="simulator-area" aria-label="搭搭 App 模拟器">
|
|
179
179
|
<div class="simulator-caption">
|
|
180
180
|
<h2>模拟私聊场景</h2>
|
|
181
|
+
<div class="simulator-actions">
|
|
182
|
+
<button class="simulator-device" id="reset-conversation" type="button" disabled>清空对话</button>
|
|
181
183
|
<button class="simulator-device" id="pair-device" type="button" aria-expanded="false" aria-controls="pairing-panel">
|
|
182
184
|
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M8 2.75h8A2.25 2.25 0 0 1 18.25 5v14A2.25 2.25 0 0 1 16 21.25H8A2.25 2.25 0 0 1 5.75 19V5A2.25 2.25 0 0 1 8 2.75Z"/><path d="M10 18.25h4"/></svg>
|
|
183
185
|
<span>真机预览</span>
|
|
184
186
|
</button>
|
|
187
|
+
</div>
|
|
185
188
|
</div>
|
|
186
189
|
|
|
187
190
|
<div class="device-stage">
|
|
@@ -207,6 +210,7 @@
|
|
|
207
210
|
<div class="chat-empty" id="chat-empty" aria-label="暂无消息"></div>
|
|
208
211
|
</div>
|
|
209
212
|
|
|
213
|
+
<button id="latest-messages" class="latest-messages" type="button" hidden>回到最新消息 ↓</button>
|
|
210
214
|
<form class="composer" id="composer">
|
|
211
215
|
<button class="composer-tool composer-add" type="button" aria-label="添加内容(预览中不可用)" disabled tabindex="-1">
|
|
212
216
|
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@my-life-buddies/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "搭搭 Developer Platform 命令行工具",
|
|
6
6
|
"publishConfig": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"ws": "8.21.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@buddy/cli-core": "0.
|
|
43
|
-
"@buddy/preview": "0.
|
|
42
|
+
"@buddy/cli-core": "0.2.0",
|
|
43
|
+
"@buddy/preview": "0.2.0"
|
|
44
44
|
}
|
|
45
45
|
}
|