@my-life-buddies/cli 0.3.0 → 0.5.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 +54 -33
- package/dist/bin/core.js +907 -436
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +147 -12
- package/dist/bin/preview.js.map +3 -3
- package/dist/web/app.css +8 -0
- package/dist/web/app.js +7 -2
- package/dist/web/index.html +11 -3
- package/dist/web/widget-delivery.js +28 -0
- package/dist/web/widgets.css +31 -0
- package/dist/web/widgets.js +127 -0
- package/package.json +3 -3
- package/resources/agent-template/package-lock.json +4 -4
- package/resources/agent-template/package.json +1 -1
package/dist/web/app.css
CHANGED
|
@@ -513,3 +513,11 @@
|
|
|
513
513
|
*, *::before, *::after { animation-duration: 120ms !important; animation-iteration-count: 1 !important; transition-duration: 120ms !important; }
|
|
514
514
|
}
|
|
515
515
|
}
|
|
516
|
+
|
|
517
|
+
@layer debugger {
|
|
518
|
+
.widget-delivery { margin: var(--space-lg) var(--page-gutter) 0; padding: var(--space-lg); border: var(--rule-hair) solid var(--color-rule); border-radius: var(--radius-lg); background: var(--color-paper-raised); }
|
|
519
|
+
.widget-delivery summary { cursor: pointer; font-weight: 600; }
|
|
520
|
+
.widget-delivery p, .widget-delivery li { font-size: var(--text-sm); line-height: 1.65; color: var(--color-ink-soft); }
|
|
521
|
+
.widget-delivery li + li { margin-top: var(--space-sm); }
|
|
522
|
+
.widget-delivery button { padding: var(--space-sm) var(--space-md); border: var(--rule-hair) solid var(--color-rule); border-radius: var(--radius-md); background: var(--color-paper); }
|
|
523
|
+
}
|
package/dist/web/app.js
CHANGED
|
@@ -433,6 +433,9 @@
|
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
const widgets = window.createSimulatorWidgets?.({ api, avatar: () => messageAvatar("assistant") });
|
|
437
|
+
function renderWidgets() { widgets?.update({ active: state.dev.phase === "ready" && !state.resetting, conversationRevision: state.simulator.conversationRevision, devRevision: state.dev.revision }); }
|
|
438
|
+
|
|
436
439
|
let messageSignature = "";
|
|
437
440
|
let followMessages = true;
|
|
438
441
|
|
|
@@ -443,6 +446,7 @@
|
|
|
443
446
|
}
|
|
444
447
|
|
|
445
448
|
function renderMessages() {
|
|
449
|
+
renderWidgets();
|
|
446
450
|
const signature = JSON.stringify([state.simulator.messages, Boolean(state.simulator.activeRun)]);
|
|
447
451
|
if (signature === messageSignature) return;
|
|
448
452
|
messageSignature = signature;
|
|
@@ -461,7 +465,7 @@
|
|
|
461
465
|
}
|
|
462
466
|
const row = node("div", { class: "message-row", "data-role": message.role });
|
|
463
467
|
const wrap = node("div", { class: "message-content" });
|
|
464
|
-
wrap.append(node("div", { class: "message-bubble" }, message.text));
|
|
468
|
+
wrap.append(message.widget && widgets ? widgets.card(message.widget) : node("div", { class: "message-bubble" }, message.text));
|
|
465
469
|
if (message.role === "user") row.append(wrap);
|
|
466
470
|
else row.append(messageAvatar("assistant"), wrap);
|
|
467
471
|
elements.chatMessages.append(row);
|
|
@@ -669,6 +673,7 @@
|
|
|
669
673
|
if (elements.resetConversation.disabled) return;
|
|
670
674
|
if (!window.confirm("清空当前对话并重新开始?\n这会结束当前开发版会话,重置聊天上下文、记忆和该会话的主动服务。旧记录仅在服务端留档;搭子资料和工程文件会保留。")) return;
|
|
671
675
|
state.resetting = true;
|
|
676
|
+
widgets?.reset();
|
|
672
677
|
state.simulatorRequest++;
|
|
673
678
|
elements.resetConversation.textContent = "正在清空…";
|
|
674
679
|
renderDev();
|
|
@@ -700,7 +705,7 @@
|
|
|
700
705
|
const events = new EventSource("/api/events");
|
|
701
706
|
events.addEventListener("dev.state", (event) => {
|
|
702
707
|
try {
|
|
703
|
-
if (applyDevState(JSON.parse(event.data))) renderDev();
|
|
708
|
+
if (applyDevState(JSON.parse(event.data))) { renderDev(); renderWidgets(); }
|
|
704
709
|
} catch {
|
|
705
710
|
// A malformed local event is ignored; the next API refresh repairs state.
|
|
706
711
|
}
|
package/dist/web/index.html
CHANGED
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
<link rel="stylesheet" href="/assets/tokens.css">
|
|
13
13
|
<link rel="stylesheet" href="/assets/app.css">
|
|
14
14
|
<script src="/assets/iphone-16-max.js" defer></script>
|
|
15
|
+
<script src="/assets/widgets.js" defer></script>
|
|
16
|
+
<script src="/assets/widget-delivery.js" defer></script>
|
|
15
17
|
<script src="/assets/app.js" defer></script>
|
|
18
|
+
<link rel="stylesheet" href="/assets/widgets.css">
|
|
16
19
|
</head>
|
|
17
20
|
<body>
|
|
18
21
|
<a class="skip-link" href="#debug-content">跳到调试内容</a>
|
|
@@ -119,6 +122,12 @@
|
|
|
119
122
|
</div>
|
|
120
123
|
|
|
121
124
|
<div class="debug-panels">
|
|
125
|
+
<section id="widget-delivery" class="widget-delivery" aria-label="小挂件设计落地检查" hidden>
|
|
126
|
+
<details><summary>小挂件设计落地:<span id="widget-delivery-status"></span></summary>
|
|
127
|
+
<p id="widget-delivery-notice"></p><ul id="widget-delivery-items"></ul>
|
|
128
|
+
<button id="widget-delivery-refresh" type="button">重新检查</button>
|
|
129
|
+
</details>
|
|
130
|
+
</section>
|
|
122
131
|
<section id="panel-overview" class="debug-panel" role="tabpanel" aria-labelledby="tab-overview">
|
|
123
132
|
<div class="overview-grid">
|
|
124
133
|
<article class="run-summary">
|
|
@@ -197,15 +206,14 @@
|
|
|
197
206
|
</button>
|
|
198
207
|
<div class="buddy-identity"><h3 id="buddy-name">我的搭子</h3><p id="buddy-status" class="sr-only">DEV 未连接</p></div>
|
|
199
208
|
<div class="chat-navigation-actions" aria-hidden="true">
|
|
200
|
-
<button class="chat-widgets" type="button" aria-label="查看 Widget(预览中不可用)" disabled tabindex="-1">
|
|
201
|
-
<svg viewBox="0 0 24 24"><rect x="4" y="4" width="6" height="6" rx="1.5"/><rect x="14" y="4" width="6" height="6" rx="1.5"/><rect x="4" y="14" width="6" height="6" rx="1.5"/><rect x="14" y="14" width="6" height="6" rx="1.5"/></svg>
|
|
202
|
-
</button>
|
|
203
209
|
<button class="chat-more" type="button" aria-label="更多(预览中不可用)" disabled tabindex="-1">
|
|
204
210
|
<svg viewBox="0 0 24 24"><circle cx="5" cy="12" r="1.3"/><circle cx="12" cy="12" r="1.3"/><circle cx="19" cy="12" r="1.3"/></svg>
|
|
205
211
|
</button>
|
|
206
212
|
</div>
|
|
207
213
|
</header>
|
|
208
214
|
|
|
215
|
+
<div id="widget-shortcut" class="widget-shortcut" hidden></div>
|
|
216
|
+
<div id="widget-overlay" class="widget-overlay" hidden></div>
|
|
209
217
|
<div class="chat-messages" id="chat-messages" aria-live="polite" aria-label="模拟器聊天记录">
|
|
210
218
|
<div class="chat-empty" id="chat-empty" aria-label="暂无消息"></div>
|
|
211
219
|
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// This report is local-only and separate from runtime connectivity.
|
|
2
|
+
(() => {
|
|
3
|
+
const panel = document.getElementById("widget-delivery");
|
|
4
|
+
if (!panel) return;
|
|
5
|
+
const refresh = async () => {
|
|
6
|
+
const button = document.getElementById("widget-delivery-refresh");
|
|
7
|
+
button.disabled = true;
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetch("/api/widgets/delivery", { credentials: "same-origin" });
|
|
10
|
+
if (!response.ok) throw new Error("检查暂时无法完成,请重新检查");
|
|
11
|
+
const report = await response.json();
|
|
12
|
+
panel.hidden = report.status === "not-configured";
|
|
13
|
+
document.getElementById("widget-delivery-status").textContent = report.status === "ready" ? "结构与核对记录齐全" : "尚未完成";
|
|
14
|
+
document.getElementById("widget-delivery-notice").textContent = report.notice;
|
|
15
|
+
const list = document.getElementById("widget-delivery-items");
|
|
16
|
+
list.replaceChildren();
|
|
17
|
+
for (const message of [...report.issues, ...report.items.map(item => `${item.name}:${item.status === "verified" ? "核对已记录" : item.status === "implemented" ? "待预览核对" : "待补齐实现"}${item.issues.length ? ";" + item.issues.join(";") : ""}`)]) {
|
|
18
|
+
const li = document.createElement("li"); li.textContent = message; list.append(li);
|
|
19
|
+
}
|
|
20
|
+
} catch (error) {
|
|
21
|
+
panel.hidden = false;
|
|
22
|
+
document.getElementById("widget-delivery-status").textContent = "检查失败";
|
|
23
|
+
document.getElementById("widget-delivery-notice").textContent = error.message;
|
|
24
|
+
} finally { button.disabled = false; }
|
|
25
|
+
};
|
|
26
|
+
document.getElementById("widget-delivery-refresh").addEventListener("click", refresh);
|
|
27
|
+
void refresh();
|
|
28
|
+
})();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Native points expressed against the simulator's 415 pt canvas. */
|
|
2
|
+
.widget-shortcut { position:absolute; z-index:3; top:24.1cqw; left:2.89cqw; max-width:calc(100% - 5.78cqw); }
|
|
3
|
+
.widget-shortcut[hidden], .widget-overlay[hidden] { display:none; }
|
|
4
|
+
.widget-pill { display:flex; align-items:center; gap:1.93cqw; min-height:12.53cqw; max-width:62.65cqw; border-radius:99px; padding:1.93cqw 4.82cqw 1.93cqw 3.855cqw; color:#20252b; background:linear-gradient(135deg,#fff,#bbf1a7); border:.5px solid #20252b1f; font-size:3.855cqw; font-weight:400; box-shadow:none; }
|
|
5
|
+
.widget-pill .widget-pill-avatar { width:6.747cqw; height:6.747cqw; flex:none; border-radius:50%; }
|
|
6
|
+
.widget-pill-title { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
7
|
+
.widget-chevron { margin-left:1.9cqw; font-size:2.65cqw; }
|
|
8
|
+
.widget-choices,.widget-options { position:absolute; z-index:5; top:calc(100% + 2cqw); left:0; width:55.9cqw; padding:1cqw 0; border-radius:3cqw; background:#f7f7f9; box-shadow:0 3cqw 9cqw #0003; overflow:hidden; }
|
|
9
|
+
.widget-choices button,.widget-options button { display:block; width:100%; min-height:10.6cqw; padding:2.9cqw 3.855cqw; text-align:left; background:transparent; color:#20252b; font-size:3.37cqw; font-weight:400; border-bottom:.3px solid #0002; }
|
|
10
|
+
.widget-overlay { position:absolute; z-index:10; inset:0; display:flex; align-items:center; justify-content:center; background:#0003; animation:widget-fade .2s ease-in-out; }
|
|
11
|
+
.widget-modal { position:relative; width:calc(100% - 7.71cqw); height:min(134.94cqw,calc(100% - 11.57cqw)); max-width:560px; border-radius:5.783cqw; background:#fff; color:#20252b; overflow:hidden; display:flex; flex-direction:column; padding-bottom:2.89cqw; }
|
|
12
|
+
.widget-modal-header { display:flex; gap:1.93cqw; align-items:center; padding:1.93cqw 1.93cqw 0; flex:none; }
|
|
13
|
+
.widget-modal-header button { width:10.6cqw; height:10.6cqw; flex:none; padding:0; background:transparent; color:inherit; font-size:5.3cqw; font-weight:400; }
|
|
14
|
+
.widget-modal-header h3 { flex:1; margin:0; text-align:center; font-size:3.37cqw; font-weight:500; line-height:1.4; }
|
|
15
|
+
.widget-modal-body { flex:1; min-height:0; overflow:auto; text-align:center; font-size:3.37cqw; }
|
|
16
|
+
.widget-modal-body>p,.widget-modal-body>h4 { padding:0 5.78cqw; }
|
|
17
|
+
.widget-modal-body>h4,.widget-loading { margin-top:25%; }
|
|
18
|
+
.widget-modal-body>button { background:#f3f3f5; color:inherit; padding:2.9cqw; border-radius:2cqw; }
|
|
19
|
+
.widget-document { width:100%; height:100%; border:0; display:block; background:white; }
|
|
20
|
+
.widget-options { top:12.5cqw; right:2cqw; left:auto; }
|
|
21
|
+
.widget-options p { padding:3cqw; font-size:3.37cqw; }
|
|
22
|
+
.widget-pin-note { position:absolute; top:14.45cqw; left:50%; transform:translateX(-50%); white-space:nowrap; border-radius:99px; background:#f5f5f5ed; padding:2.9cqw; font-size:2.89cqw; }
|
|
23
|
+
.widget-entry { width:71.57cqw; max-width:100%; padding:2.89cqw; border:.3px solid #dadbdd; border-radius:3.855cqw; background:#fff; color:#20252b; font-size:3.3735cqw; }
|
|
24
|
+
.widget-entry h4 { margin:0 0 2.89cqw; font:500 3.3735cqw/1.5 sans-serif; }
|
|
25
|
+
.widget-entry p { margin:0; color:#717782; line-height:1.5; overflow-wrap:anywhere; }
|
|
26
|
+
.widget-entry-details { display:flex; align-items:center; gap:2.89cqw; }
|
|
27
|
+
.widget-entry-photo { width:15.42cqw; height:15.42cqw; border-radius:50%; object-fit:cover; flex:none; }
|
|
28
|
+
.widget-entry-open { width:100%; min-height:10.6cqw; margin-top:2.89cqw; background:#f3f3f5; color:inherit; border-radius:1.93cqw; font-size:3.3735cqw; font-weight:500; }
|
|
29
|
+
.widget-entry button:focus-visible,.widget-shortcut button:focus-visible,.widget-overlay button:focus-visible { outline:2px solid #007a9b; outline-offset:2px; }
|
|
30
|
+
@keyframes widget-fade { from { opacity:0; } to { opacity:1; } }
|
|
31
|
+
@media(prefers-reduced-motion:reduce) { .widget-overlay { animation-duration:.1s; } }
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* Client reference: my-life-buddies-ios dev 547818d, DadaConversationWidgets / DadaHostedWidgetPage. */
|
|
2
|
+
(() => {
|
|
3
|
+
const el = (tag, className, text) => { const n = document.createElement(tag); n.className = className; if (text !== undefined) n.textContent = text; return n; };
|
|
4
|
+
window.createSimulatorWidgets = ({ api, avatar }) => {
|
|
5
|
+
const shortcut = document.querySelector('#widget-shortcut');
|
|
6
|
+
const overlay = document.querySelector('#widget-overlay');
|
|
7
|
+
const phone = document.querySelector('.phone-screen');
|
|
8
|
+
let items = [], scope = '', generation = 0, ready = false, loading = false, lastRefresh = 0, selected, focusBefore, openRevision = 0;
|
|
9
|
+
let failed = false, renderSignature = '', frameObserver;
|
|
10
|
+
function close() {
|
|
11
|
+
frameObserver?.disconnect(); frameObserver = undefined;
|
|
12
|
+
const wasOpen = !overlay.hidden;
|
|
13
|
+
openRevision++; selected = undefined; overlay.hidden = true; overlay.replaceChildren();
|
|
14
|
+
for (const child of phone.children) if (child !== overlay) child.inert = false;
|
|
15
|
+
if (wasOpen) focusBefore?.focus?.();
|
|
16
|
+
}
|
|
17
|
+
function button(label, action, className = '') { const n = el('button', className, label); n.type = 'button'; n.addEventListener('click', action); return n; }
|
|
18
|
+
function render() {
|
|
19
|
+
const signature = JSON.stringify([ready, items, failed]);
|
|
20
|
+
if (signature === renderSignature) return;
|
|
21
|
+
renderSignature = signature;
|
|
22
|
+
shortcut.replaceChildren(); shortcut.hidden = !ready || (!items.length && !failed);
|
|
23
|
+
if (shortcut.hidden) return;
|
|
24
|
+
if (!items.length) { shortcut.append(button('重新加载小挂件', () => refresh(true), 'widget-pill')); return; }
|
|
25
|
+
const pill = button('', () => {
|
|
26
|
+
if (items.length === 1) { void open(items[0]); return; }
|
|
27
|
+
const old = shortcut.querySelector('.widget-choices');
|
|
28
|
+
if (old) { old.remove(); pill.setAttribute('aria-expanded', 'false'); return; }
|
|
29
|
+
const choices = el('div', 'widget-choices'); choices.setAttribute('role', 'menu');
|
|
30
|
+
for (const item of items) { const b = button(item.title + ' ›', () => { choices.remove(); pill.setAttribute('aria-expanded', 'false'); void open(item); }); b.setAttribute('role', 'menuitem'); choices.append(b); }
|
|
31
|
+
shortcut.append(choices); pill.setAttribute('aria-expanded', 'true'); choices.firstChild?.focus();
|
|
32
|
+
}, 'widget-pill');
|
|
33
|
+
const image = avatar(); image.textContent = ''; image.classList.add('widget-pill-avatar');
|
|
34
|
+
pill.append(image, el('span', 'widget-pill-title', items.length === 1 ? items[0].title : '全部小挂件'));
|
|
35
|
+
if (items.length > 1) { pill.append(el('span', 'widget-chevron', '⌄')); pill.setAttribute('aria-haspopup', 'menu'); pill.setAttribute('aria-expanded', 'false'); }
|
|
36
|
+
shortcut.append(pill);
|
|
37
|
+
}
|
|
38
|
+
async function refresh(force = false) {
|
|
39
|
+
if (!ready || loading || (!force && Date.now() - lastRefresh < 1500)) return;
|
|
40
|
+
loading = true; const mine = generation;
|
|
41
|
+
try {
|
|
42
|
+
const value = await api('/api/simulator/widgets');
|
|
43
|
+
if (mine !== generation || !ready) return;
|
|
44
|
+
items = value.widgets || []; failed = false;
|
|
45
|
+
if (selected && !items.some(item => item.id === selected.id)) close();
|
|
46
|
+
} catch { if (mine === generation) failed = true; }
|
|
47
|
+
finally { if (mine === generation) { loading = false; lastRefresh = Date.now(); render(); } }
|
|
48
|
+
}
|
|
49
|
+
async function open(item) {
|
|
50
|
+
if (!item.type || !ready) return;
|
|
51
|
+
const mine = ++openRevision, current = generation;
|
|
52
|
+
if (overlay.hidden) focusBefore = document.activeElement;
|
|
53
|
+
frameObserver?.disconnect();
|
|
54
|
+
selected = item; document.querySelector('#message-input').blur();
|
|
55
|
+
overlay.replaceChildren(); overlay.hidden = false;
|
|
56
|
+
const card = el('section', 'widget-modal'); card.setAttribute('role', 'dialog'); card.setAttribute('aria-modal', 'true'); card.setAttribute('aria-label', item.title);
|
|
57
|
+
const header = el('header', 'widget-modal-header');
|
|
58
|
+
const dismiss = button('×', close); dismiss.setAttribute('aria-label', '关闭小挂件');
|
|
59
|
+
const more = button('···', () => {
|
|
60
|
+
const old = card.querySelector('.widget-options'); if (old) { old.remove(); return; }
|
|
61
|
+
const menu = el('div', 'widget-options'); menu.setAttribute('role', 'menu');
|
|
62
|
+
const currentItem = items.find(value => value.id === item.id);
|
|
63
|
+
if (currentItem) menu.append(button(currentItem.isPinned ? '从首页取消固定' : '固定到首页', async () => {
|
|
64
|
+
menu.querySelectorAll('button').forEach(b => { b.disabled = true; });
|
|
65
|
+
try {
|
|
66
|
+
await api(`/api/simulator/widgets/${encodeURIComponent(item.id)}/pin`, { method: currentItem.isPinned ? 'DELETE' : 'PUT' });
|
|
67
|
+
if (mine !== openRevision || current !== generation) return;
|
|
68
|
+
currentItem.isPinned = !currentItem.isPinned; menu.remove();
|
|
69
|
+
const note = el('div', 'widget-pin-note', currentItem.isPinned ? '已固定到首页' : '已从首页取消固定'); note.setAttribute('role', 'status'); card.append(note); setTimeout(() => note.remove(), 4000);
|
|
70
|
+
} catch (error) { if (mine === openRevision) { menu.replaceChildren(el('p', '', error.message), button('知道了', () => menu.remove())); } }
|
|
71
|
+
}));
|
|
72
|
+
menu.append(button('刷新', () => open(item))); card.append(menu);
|
|
73
|
+
}); more.setAttribute('aria-label', '小挂件更多');
|
|
74
|
+
header.append(dismiss, el('h3', '', item.title), more);
|
|
75
|
+
const body = el('div', 'widget-modal-body'); body.append(el('p', 'widget-loading', '正在打开小挂件…'));
|
|
76
|
+
card.append(header, body); overlay.append(card);
|
|
77
|
+
for (const child of phone.children) if (child !== overlay) child.inert = true;
|
|
78
|
+
dismiss.focus();
|
|
79
|
+
try {
|
|
80
|
+
const url = `/api/simulator/widgets/${encodeURIComponent(item.id)}`;
|
|
81
|
+
// Probe errors in the parent; actual HTML executes only inside the opaque sandbox document.
|
|
82
|
+
const response = await fetch(url, { credentials: 'same-origin' });
|
|
83
|
+
if (!response.ok) { const error = await response.json(); throw new Error(error.error?.message || error.error || '小挂件暂时无法打开'); }
|
|
84
|
+
await response.arrayBuffer();
|
|
85
|
+
if (mine !== openRevision || current !== generation) return;
|
|
86
|
+
const frame = el('iframe', 'widget-document'); frame.title = item.title; frame.setAttribute('sandbox', 'allow-scripts'); frame.referrerPolicy = 'no-referrer'; frame.src = url;
|
|
87
|
+
body.replaceChildren(frame);
|
|
88
|
+
frameObserver = new ResizeObserver(() => {
|
|
89
|
+
const scale = body.clientWidth / 383;
|
|
90
|
+
if (!scale) return;
|
|
91
|
+
frame.style.width = '383px'; frame.style.height = `${body.clientHeight / scale}px`;
|
|
92
|
+
frame.style.transform = `scale(${scale})`; frame.style.transformOrigin = 'top left';
|
|
93
|
+
});
|
|
94
|
+
frameObserver.observe(body);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (mine !== openRevision || current !== generation) return;
|
|
97
|
+
body.replaceChildren(el('h4', '', '小挂件暂时无法打开'), el('p', '', error.message), button('重新加载', () => open(item)));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
overlay.addEventListener('click', event => { if (event.target === overlay) close(); });
|
|
101
|
+
document.addEventListener('keydown', event => {
|
|
102
|
+
if (event.key === 'Escape') { if (!overlay.hidden) close(); else { shortcut.querySelector('.widget-choices')?.remove(); shortcut.querySelector('button')?.setAttribute('aria-expanded', 'false'); } }
|
|
103
|
+
if (event.key === 'Tab' && !overlay.hidden) {
|
|
104
|
+
const controls = [...overlay.querySelectorAll('button:not(:disabled), iframe')]; const first = controls[0], last = controls.at(-1);
|
|
105
|
+
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last?.focus(); }
|
|
106
|
+
else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first?.focus(); }
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
document.addEventListener('visibilitychange', () => { if (!document.hidden) void refresh(true); });
|
|
110
|
+
return {
|
|
111
|
+
update({ active, conversationRevision, devRevision }) {
|
|
112
|
+
const key = `${conversationRevision || 0}:${devRevision || 0}`;
|
|
113
|
+
if (key !== scope || !active) { scope = key; generation++; items = []; failed = false; loading = false; lastRefresh = 0; close(); }
|
|
114
|
+
ready = active; render(); void refresh();
|
|
115
|
+
},
|
|
116
|
+
reset() { generation++; ready = false; items = []; loading = false; failed = false; close(); render(); },
|
|
117
|
+
card(item) {
|
|
118
|
+
const card = el('div', 'widget-entry'); card.append(el('h4', '', item.title));
|
|
119
|
+
const details = el('div', 'widget-entry-details');
|
|
120
|
+
if (item.type && item.imageMediaId) { const image = el('img', 'widget-entry-photo'); image.alt = `${item.title}的照片`; image.src = `/api/simulator/widgets/${encodeURIComponent(item.id)}/image?mediaId=${encodeURIComponent(item.imageMediaId)}`; image.addEventListener('error', () => { image.hidden = true; }); details.append(image); }
|
|
121
|
+
details.append(el('p', '', item.type ? item.summary?.trim() || '打开查看最新内容' : '此小挂件已删除或不可用')); card.append(details);
|
|
122
|
+
if (item.type) card.append(button('去查看', () => open(item), 'widget-entry-open'));
|
|
123
|
+
return card;
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@my-life-buddies/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.5.0",
|
|
43
|
+
"@buddy/preview": "0.5.0"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"name": "buddy-agent-project",
|
|
9
9
|
"version": "0.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@my-life-buddies/buddy-runtime": "0.
|
|
11
|
+
"@my-life-buddies/buddy-runtime": "0.11.0",
|
|
12
12
|
"typebox": "1.3.7"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
@@ -981,9 +981,9 @@
|
|
|
981
981
|
}
|
|
982
982
|
},
|
|
983
983
|
"node_modules/@my-life-buddies/buddy-runtime": {
|
|
984
|
-
"version": "0.
|
|
985
|
-
"resolved": "https://registry.npmjs.org/@my-life-buddies/buddy-runtime/-/buddy-runtime-0.
|
|
986
|
-
"integrity": "sha512-
|
|
984
|
+
"version": "0.11.0",
|
|
985
|
+
"resolved": "https://registry.npmjs.org/@my-life-buddies/buddy-runtime/-/buddy-runtime-0.11.0.tgz",
|
|
986
|
+
"integrity": "sha512-6mqO05OK3GRtJwbQXlyKFxk63iq5MUctnihA0d75iBQlbSoxbOSs06FI4+dvIrZ8JBU0brioag4+8jD9Fy482g==",
|
|
987
987
|
"dependencies": {
|
|
988
988
|
"@earendil-works/pi-agent-core": "0.85.1",
|
|
989
989
|
"@earendil-works/pi-ai": "0.85.1",
|