@poncho-ai/cli 0.27.1 → 0.28.1
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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +17 -0
- package/dist/chunk-3KBZ6IYJ.js +11664 -0
- package/dist/chunk-3M36F3UO.js +11608 -0
- package/dist/chunk-5AKBY5GK.js +11686 -0
- package/dist/chunk-6MNV2A56.js +11669 -0
- package/dist/chunk-BHIS23C2.js +11665 -0
- package/dist/chunk-CH2VUABC.js +11682 -0
- package/dist/chunk-CVCPGIPG.js +11672 -0
- package/dist/chunk-EIEDQU2G.js +11600 -0
- package/dist/chunk-G3BJFUMT.js +11668 -0
- package/dist/chunk-HECB2QW6.js +11623 -0
- package/dist/chunk-I3OMA4G4.js +11655 -0
- package/dist/chunk-KYGAYXZ7.js +11669 -0
- package/dist/chunk-MRIAH4UE.js +11679 -0
- package/dist/chunk-N4KUFNGR.js +11398 -0
- package/dist/chunk-NEY3V2BR.js +11656 -0
- package/dist/chunk-O4G27VDK.js +11413 -0
- package/dist/chunk-P7GNEDMA.js +11682 -0
- package/dist/chunk-UK2WWNCG.js +11403 -0
- package/dist/chunk-VKMSPLAD.js +11687 -0
- package/dist/chunk-W5H5ENIB.js +11682 -0
- package/dist/chunk-WS5SZJVW.js +11606 -0
- package/dist/chunk-YFEPKZCH.js +11667 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/run-interactive-ink-2NXNGTCY.js +2112 -0
- package/dist/run-interactive-ink-2P7BJFTE.js +2112 -0
- package/dist/run-interactive-ink-3CTIVS4B.js +2112 -0
- package/dist/run-interactive-ink-3ZNI2D7E.js +2112 -0
- package/dist/run-interactive-ink-5SPYMVJR.js +2112 -0
- package/dist/run-interactive-ink-AM5B24RW.js +2112 -0
- package/dist/run-interactive-ink-COIXPC7V.js +2112 -0
- package/dist/run-interactive-ink-DNFDANDJ.js +2112 -0
- package/dist/run-interactive-ink-DXABIOVA.js +2112 -0
- package/dist/run-interactive-ink-EIX3MSBW.js +2112 -0
- package/dist/run-interactive-ink-GIPPQN6E.js +2112 -0
- package/dist/run-interactive-ink-I2DZWKAL.js +2112 -0
- package/dist/run-interactive-ink-L4D36J5I.js +2112 -0
- package/dist/run-interactive-ink-LCQQLBCU.js +2112 -0
- package/dist/run-interactive-ink-MXRMDAN7.js +2112 -0
- package/dist/run-interactive-ink-NBGGN5CF.js +2112 -0
- package/dist/run-interactive-ink-NWQZHI5N.js +2112 -0
- package/dist/run-interactive-ink-QCGPGZLG.js +2112 -0
- package/dist/run-interactive-ink-TZRKAZ6X.js +2112 -0
- package/dist/run-interactive-ink-VTCRYYJQ.js +2112 -0
- package/dist/run-interactive-ink-W447UDRN.js +2112 -0
- package/dist/run-interactive-ink-ZKN3EFCE.js +2112 -0
- package/package.json +2 -2
- package/src/index.ts +8 -0
- package/src/web-ui-client.ts +210 -12
- package/src/web-ui-styles.ts +110 -0
- package/src/web-ui.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.1",
|
|
4
4
|
"description": "CLI for building and deploying AI agents",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"react": "^19.2.4",
|
|
28
28
|
"react-devtools-core": "^6.1.5",
|
|
29
29
|
"yaml": "^2.8.1",
|
|
30
|
-
"@poncho-ai/harness": "0.
|
|
30
|
+
"@poncho-ai/harness": "0.26.0",
|
|
31
31
|
"@poncho-ai/messaging": "0.7.0",
|
|
32
32
|
"@poncho-ai/sdk": "1.5.0"
|
|
33
33
|
},
|
package/src/index.ts
CHANGED
|
@@ -3594,6 +3594,14 @@ export const createRequestHandler = async (options?: {
|
|
|
3594
3594
|
return;
|
|
3595
3595
|
}
|
|
3596
3596
|
|
|
3597
|
+
const todosMatch = pathname.match(/^\/api\/conversations\/([^/]+)\/todos$/);
|
|
3598
|
+
if (todosMatch && request.method === "GET") {
|
|
3599
|
+
const conversationId = decodeURIComponent(todosMatch[1] ?? "");
|
|
3600
|
+
const todos = await harness.getTodos(conversationId);
|
|
3601
|
+
writeJson(response, 200, { todos });
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3597
3605
|
const conversationPathMatch = pathname.match(/^\/api\/conversations\/([^/]+)$/);
|
|
3598
3606
|
if (conversationPathMatch) {
|
|
3599
3607
|
const conversationId = decodeURIComponent(conversationPathMatch[1] ?? "");
|
package/src/web-ui-client.ts
CHANGED
|
@@ -27,6 +27,8 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
27
27
|
subagentsParentId: null,
|
|
28
28
|
viewingSubagentId: null,
|
|
29
29
|
parentConversationId: null,
|
|
30
|
+
todos: [],
|
|
31
|
+
todoPanelCollapsed: false,
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
const agentInitial = document.body.dataset.agentInitial || "A";
|
|
@@ -578,7 +580,9 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
578
580
|
state.activeMessages = [];
|
|
579
581
|
state.contextTokens = 0;
|
|
580
582
|
state.contextWindow = 0;
|
|
583
|
+
state.todos = [];
|
|
581
584
|
updateContextRing();
|
|
585
|
+
renderTodoPanel();
|
|
582
586
|
pushConversationUrl(null);
|
|
583
587
|
elements.chatTitle.textContent = "";
|
|
584
588
|
renderMessages([]);
|
|
@@ -610,6 +614,136 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
610
614
|
return item;
|
|
611
615
|
};
|
|
612
616
|
|
|
617
|
+
const _todoPriorityOrder = { high: 0, medium: 1, low: 2 };
|
|
618
|
+
const _todoStatusOrder = { in_progress: 0, pending: 1, completed: 2 };
|
|
619
|
+
const _todoCompletedTimers = new Map();
|
|
620
|
+
|
|
621
|
+
const _scheduleCompletedHide = () => {
|
|
622
|
+
state.todos.forEach(function(todo) {
|
|
623
|
+
if (todo.status === "completed" && !_todoCompletedTimers.has(todo.id)) {
|
|
624
|
+
_todoCompletedTimers.set(todo.id, todo.updatedAt || Date.now());
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
const activeIds = new Set(state.todos.map(function(t) { return t.id; }));
|
|
628
|
+
for (const id of _todoCompletedTimers.keys()) {
|
|
629
|
+
if (!activeIds.has(id)) _todoCompletedTimers.delete(id);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
const _getVisibleTodos = () => {
|
|
634
|
+
const now = Date.now();
|
|
635
|
+
const sorted = state.todos.slice().sort(function(a, b) {
|
|
636
|
+
const sp = (_todoStatusOrder[a.status] || 1) - (_todoStatusOrder[b.status] || 1);
|
|
637
|
+
if (sp !== 0) return sp;
|
|
638
|
+
const pp = (_todoPriorityOrder[a.priority] || 1) - (_todoPriorityOrder[b.priority] || 1);
|
|
639
|
+
if (pp !== 0) return pp;
|
|
640
|
+
return (a.createdAt || 0) - (b.createdAt || 0);
|
|
641
|
+
});
|
|
642
|
+
if (!state.todoPanelCollapsed) return sorted;
|
|
643
|
+
return sorted.filter(function(todo) {
|
|
644
|
+
if (todo.status !== "completed") return true;
|
|
645
|
+
const completedAt = _todoCompletedTimers.get(todo.id);
|
|
646
|
+
return !completedAt || (now - completedAt) < 30000;
|
|
647
|
+
});
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
let _todoHideTimer = null;
|
|
651
|
+
const _ensureHideTimer = () => {
|
|
652
|
+
if (_todoHideTimer) return;
|
|
653
|
+
_todoHideTimer = setInterval(function() {
|
|
654
|
+
const hasExpiring = state.todos.some(function(t) {
|
|
655
|
+
if (t.status !== "completed") return false;
|
|
656
|
+
const at = _todoCompletedTimers.get(t.id);
|
|
657
|
+
return at && (Date.now() - at) >= 30000;
|
|
658
|
+
});
|
|
659
|
+
if (hasExpiring && state.todoPanelCollapsed) renderTodoPanel();
|
|
660
|
+
if (!state.todos.length) {
|
|
661
|
+
clearInterval(_todoHideTimer);
|
|
662
|
+
_todoHideTimer = null;
|
|
663
|
+
}
|
|
664
|
+
}, 5000);
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
const _autoCollapseTodos = (live) => {
|
|
668
|
+
if (!live) {
|
|
669
|
+
state.todoPanelCollapsed = true;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const hasActive = state.todos.some(function(t) {
|
|
673
|
+
return t.status === "pending" || t.status === "in_progress";
|
|
674
|
+
});
|
|
675
|
+
state.todoPanelCollapsed = !hasActive;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
const renderTodoPanel = () => {
|
|
679
|
+
let panel = document.getElementById("todo-panel");
|
|
680
|
+
if (!panel) return;
|
|
681
|
+
|
|
682
|
+
_scheduleCompletedHide();
|
|
683
|
+
const visible = _getVisibleTodos();
|
|
684
|
+
|
|
685
|
+
if (!visible.length) {
|
|
686
|
+
panel.classList.add("hidden");
|
|
687
|
+
panel.innerHTML = "";
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
panel.classList.remove("hidden");
|
|
692
|
+
if (state.todoPanelCollapsed) {
|
|
693
|
+
panel.classList.add("collapsed");
|
|
694
|
+
} else {
|
|
695
|
+
panel.classList.remove("collapsed");
|
|
696
|
+
}
|
|
697
|
+
_ensureHideTimer();
|
|
698
|
+
|
|
699
|
+
const completed = state.todos.filter(t => t.status === "completed").length;
|
|
700
|
+
const total = state.todos.length;
|
|
701
|
+
|
|
702
|
+
const statusIcon = (status) => {
|
|
703
|
+
if (status === "completed") return '<span class="todo-status todo-status-completed">\\u2713</span>';
|
|
704
|
+
if (status === "in_progress") return '<span class="todo-status todo-status-in-progress">\\u25CF</span>';
|
|
705
|
+
return '<span class="todo-status todo-status-pending">\\u25CB</span>';
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
const priorityBadge = (priority) => {
|
|
709
|
+
if (!priority || priority === "medium") return "";
|
|
710
|
+
return '<span class="todo-priority todo-priority-' + priority + '">' + priority + '</span>';
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const isCollapsed = state.todoPanelCollapsed;
|
|
714
|
+
|
|
715
|
+
let html = '<div class="todo-panel-header" id="todo-panel-header">';
|
|
716
|
+
html += '<div class="todo-panel-title">';
|
|
717
|
+
html += '<span class="todo-panel-label">Todos</span>';
|
|
718
|
+
html += '<span class="todo-panel-progress">' + completed + '/' + total + ' done</span>';
|
|
719
|
+
html += '</div>';
|
|
720
|
+
html += '<span class="todo-panel-toggle' + (isCollapsed ? '' : ' open') + '"><svg viewBox="0 0 12 12" fill="none" width="12" height="12"><path d="M4.5 2.75L8 6L4.5 9.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg></span>';
|
|
721
|
+
html += '</div>';
|
|
722
|
+
|
|
723
|
+
if (!isCollapsed) {
|
|
724
|
+
html += '<ul class="todo-panel-list">';
|
|
725
|
+
visible.forEach(function(todo) {
|
|
726
|
+
const isDone = todo.status === "completed";
|
|
727
|
+
html += '<li class="todo-item' + (isDone ? ' todo-item-done' : '') + '">';
|
|
728
|
+
html += statusIcon(todo.status);
|
|
729
|
+
html += '<span class="todo-item-content">' + escapeHtml(todo.content) + '</span>';
|
|
730
|
+
html += priorityBadge(todo.priority);
|
|
731
|
+
html += '</li>';
|
|
732
|
+
});
|
|
733
|
+
html += '</ul>';
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
panel.innerHTML = html;
|
|
737
|
+
|
|
738
|
+
const header = document.getElementById("todo-panel-header");
|
|
739
|
+
if (header) {
|
|
740
|
+
header.onclick = function() {
|
|
741
|
+
state.todoPanelCollapsed = !state.todoPanelCollapsed;
|
|
742
|
+
renderTodoPanel();
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
613
747
|
const renderConversationList = () => {
|
|
614
748
|
elements.list.innerHTML = "";
|
|
615
749
|
const pending = state.conversations.filter(c => c.hasPendingApprovals);
|
|
@@ -624,21 +758,48 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
624
758
|
elements.list.appendChild(buildConversationItem(c));
|
|
625
759
|
appendSubagentsIfActive(c.conversationId);
|
|
626
760
|
}
|
|
627
|
-
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const now = new Date();
|
|
764
|
+
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
765
|
+
const sevenDaysAgo = startOfToday - 7 * 86400000;
|
|
766
|
+
|
|
767
|
+
const latest = [];
|
|
768
|
+
const previous7 = [];
|
|
769
|
+
const older = [];
|
|
770
|
+
for (const c of rest) {
|
|
771
|
+
const ts = c.updatedAt || c.createdAt || 0;
|
|
772
|
+
if (ts >= startOfToday) {
|
|
773
|
+
latest.push(c);
|
|
774
|
+
} else if (ts >= sevenDaysAgo) {
|
|
775
|
+
previous7.push(c);
|
|
776
|
+
} else {
|
|
777
|
+
older.push(c);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
let sectionRendered = pending.length > 0;
|
|
782
|
+
const appendSection = (items, labelText) => {
|
|
783
|
+
if (items.length === 0) return;
|
|
784
|
+
if (sectionRendered) {
|
|
628
785
|
const divider = document.createElement("div");
|
|
629
786
|
divider.className = "sidebar-section-divider";
|
|
630
787
|
elements.list.appendChild(divider);
|
|
631
|
-
const recentLabel = document.createElement("div");
|
|
632
|
-
recentLabel.className = "sidebar-section-label";
|
|
633
|
-
recentLabel.textContent = "Recent";
|
|
634
|
-
elements.list.appendChild(recentLabel);
|
|
635
788
|
}
|
|
636
|
-
|
|
789
|
+
const sectionLabel = document.createElement("div");
|
|
790
|
+
sectionLabel.className = "sidebar-section-label";
|
|
791
|
+
sectionLabel.textContent = labelText;
|
|
792
|
+
elements.list.appendChild(sectionLabel);
|
|
793
|
+
for (const c of items) {
|
|
794
|
+
elements.list.appendChild(buildConversationItem(c));
|
|
795
|
+
appendSubagentsIfActive(c.conversationId);
|
|
796
|
+
}
|
|
797
|
+
sectionRendered = true;
|
|
798
|
+
};
|
|
637
799
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
800
|
+
appendSection(latest, "Latest");
|
|
801
|
+
appendSection(previous7, "Previous 7 days");
|
|
802
|
+
appendSection(older, "Older");
|
|
642
803
|
};
|
|
643
804
|
|
|
644
805
|
const appendSubagentsIfActive = (conversationId) => {
|
|
@@ -1018,12 +1179,22 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
1018
1179
|
loadSubagents(subagentParentId);
|
|
1019
1180
|
}
|
|
1020
1181
|
|
|
1182
|
+
try {
|
|
1183
|
+
const todosPayload = await api("/api/conversations/" + encodeURIComponent(conversationId) + "/todos");
|
|
1184
|
+
state.todos = todosPayload.todos || [];
|
|
1185
|
+
} catch (_e) {
|
|
1186
|
+
state.todos = [];
|
|
1187
|
+
}
|
|
1188
|
+
_autoCollapseTodos();
|
|
1189
|
+
renderTodoPanel();
|
|
1190
|
+
|
|
1021
1191
|
updateContextRing();
|
|
1022
|
-
|
|
1192
|
+
var willStream = !!payload.hasActiveRun;
|
|
1193
|
+
renderMessages(state.activeMessages, willStream, { forceScrollBottom: true });
|
|
1023
1194
|
if (!state.viewingSubagentId) {
|
|
1024
1195
|
elements.prompt.focus();
|
|
1025
1196
|
}
|
|
1026
|
-
if (
|
|
1197
|
+
if (willStream) {
|
|
1027
1198
|
setStreaming(true);
|
|
1028
1199
|
streamConversationEvents(conversationId, { liveOnly: true }).finally(() => {
|
|
1029
1200
|
if (state.activeConversationId === conversationId) {
|
|
@@ -1120,6 +1291,13 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
1120
1291
|
payload.conversation.messages || [],
|
|
1121
1292
|
allPending,
|
|
1122
1293
|
);
|
|
1294
|
+
if (typeof payload.conversation.contextTokens === "number") {
|
|
1295
|
+
state.contextTokens = payload.conversation.contextTokens;
|
|
1296
|
+
}
|
|
1297
|
+
if (typeof payload.conversation.contextWindow === "number" && payload.conversation.contextWindow > 0) {
|
|
1298
|
+
state.contextWindow = payload.conversation.contextWindow;
|
|
1299
|
+
}
|
|
1300
|
+
updateContextRing();
|
|
1123
1301
|
renderMessages(state.activeMessages, payload.hasActiveRun);
|
|
1124
1302
|
}
|
|
1125
1303
|
if (payload.hasActiveRun) {
|
|
@@ -1335,6 +1513,11 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
1335
1513
|
state.contextTokens += payload.outputTokenEstimate;
|
|
1336
1514
|
updateContextRing();
|
|
1337
1515
|
}
|
|
1516
|
+
if (toolName !== "todo_list" && toolName.startsWith("todo_") && payload.output && typeof payload.output === "object" && Array.isArray(payload.output.todos)) {
|
|
1517
|
+
state.todos = payload.output.todos;
|
|
1518
|
+
_autoCollapseTodos(true);
|
|
1519
|
+
renderTodoPanel();
|
|
1520
|
+
}
|
|
1338
1521
|
renderIfActiveConversation(true);
|
|
1339
1522
|
}
|
|
1340
1523
|
if (eventName === "tool:error") {
|
|
@@ -1370,6 +1553,10 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
1370
1553
|
if (eventName === "compaction:completed") {
|
|
1371
1554
|
didCompact = true;
|
|
1372
1555
|
removeActiveActivityForTool(assistantMessage, "__compaction__");
|
|
1556
|
+
if (typeof payload.tokensAfter === "number") {
|
|
1557
|
+
state.contextTokens = payload.tokensAfter;
|
|
1558
|
+
updateContextRing();
|
|
1559
|
+
}
|
|
1373
1560
|
renderIfActiveConversation(true);
|
|
1374
1561
|
}
|
|
1375
1562
|
if (eventName === "browser:status" && payload.active) {
|
|
@@ -2157,6 +2344,11 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
2157
2344
|
state.contextTokens += payload.outputTokenEstimate;
|
|
2158
2345
|
updateContextRing();
|
|
2159
2346
|
}
|
|
2347
|
+
if (toolName !== "todo_list" && toolName.startsWith("todo_") && payload.output && typeof payload.output === "object" && Array.isArray(payload.output.todos)) {
|
|
2348
|
+
state.todos = payload.output.todos;
|
|
2349
|
+
_autoCollapseTodos(true);
|
|
2350
|
+
renderTodoPanel();
|
|
2351
|
+
}
|
|
2160
2352
|
renderIfActiveConversation(true);
|
|
2161
2353
|
}
|
|
2162
2354
|
if (eventName === "tool:error") {
|
|
@@ -2194,6 +2386,10 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
2194
2386
|
if (eventName === "compaction:completed") {
|
|
2195
2387
|
didCompact = true;
|
|
2196
2388
|
removeActiveActivityForTool(assistantMessage, "__compaction__");
|
|
2389
|
+
if (typeof payload.tokensAfter === "number") {
|
|
2390
|
+
state.contextTokens = payload.tokensAfter;
|
|
2391
|
+
updateContextRing();
|
|
2392
|
+
}
|
|
2197
2393
|
renderIfActiveConversation(true);
|
|
2198
2394
|
}
|
|
2199
2395
|
if (eventName === "browser:status" && payload.active) {
|
|
@@ -2440,8 +2636,10 @@ export const getWebUiClientScript = (markedSource: string): string => `
|
|
|
2440
2636
|
state.parentConversationId = null;
|
|
2441
2637
|
state.subagents = [];
|
|
2442
2638
|
state.subagentsParentId = null;
|
|
2639
|
+
state.todos = [];
|
|
2443
2640
|
updateSubagentUi();
|
|
2444
2641
|
updateContextRing();
|
|
2642
|
+
renderTodoPanel();
|
|
2445
2643
|
pushConversationUrl(null);
|
|
2446
2644
|
elements.chatTitle.textContent = "";
|
|
2447
2645
|
renderMessages([]);
|
package/src/web-ui-styles.ts
CHANGED
|
@@ -983,6 +983,8 @@ export const WEB_UI_STYLES = `
|
|
|
983
983
|
}
|
|
984
984
|
.composer-inner { max-width: 680px; margin: 0 auto; }
|
|
985
985
|
.composer-shell {
|
|
986
|
+
position: relative;
|
|
987
|
+
z-index: 1;
|
|
986
988
|
background: var(--bg-alt);
|
|
987
989
|
border: 1px solid var(--border-3);
|
|
988
990
|
border-radius: 24px;
|
|
@@ -1550,6 +1552,114 @@ export const WEB_UI_STYLES = `
|
|
|
1550
1552
|
text-decoration: underline;
|
|
1551
1553
|
}
|
|
1552
1554
|
|
|
1555
|
+
/* Todo panel — inside composer-inner, above the input shell */
|
|
1556
|
+
#todo-panel {
|
|
1557
|
+
max-height: 240px;
|
|
1558
|
+
display: flex;
|
|
1559
|
+
flex-direction: column;
|
|
1560
|
+
overflow: hidden;
|
|
1561
|
+
font-size: 13px;
|
|
1562
|
+
background: var(--surface-2);
|
|
1563
|
+
border-radius: 14px 14px 0 0;
|
|
1564
|
+
padding-bottom: 24px;
|
|
1565
|
+
margin-bottom: -24px;
|
|
1566
|
+
}
|
|
1567
|
+
#todo-panel.hidden { display: none; }
|
|
1568
|
+
|
|
1569
|
+
.todo-panel-header {
|
|
1570
|
+
display: flex;
|
|
1571
|
+
align-items: center;
|
|
1572
|
+
justify-content: space-between;
|
|
1573
|
+
padding: 6px 14px;
|
|
1574
|
+
flex-shrink: 0;
|
|
1575
|
+
cursor: pointer;
|
|
1576
|
+
user-select: none;
|
|
1577
|
+
}
|
|
1578
|
+
.todo-panel-header:hover {
|
|
1579
|
+
background: var(--surface-1);
|
|
1580
|
+
}
|
|
1581
|
+
#todo-panel.collapsed {
|
|
1582
|
+
padding-bottom: 0;
|
|
1583
|
+
}
|
|
1584
|
+
#todo-panel.collapsed .todo-panel-header {
|
|
1585
|
+
padding-bottom: 30px;
|
|
1586
|
+
}
|
|
1587
|
+
.todo-panel-title {
|
|
1588
|
+
display: flex;
|
|
1589
|
+
align-items: center;
|
|
1590
|
+
gap: 8px;
|
|
1591
|
+
}
|
|
1592
|
+
.todo-panel-label {
|
|
1593
|
+
font-weight: 600;
|
|
1594
|
+
color: var(--fg);
|
|
1595
|
+
font-size: 11px;
|
|
1596
|
+
}
|
|
1597
|
+
.todo-panel-progress {
|
|
1598
|
+
font-size: 11px;
|
|
1599
|
+
color: var(--fg-3);
|
|
1600
|
+
}
|
|
1601
|
+
.todo-panel-toggle {
|
|
1602
|
+
color: var(--fg-5);
|
|
1603
|
+
display: flex;
|
|
1604
|
+
align-items: center;
|
|
1605
|
+
transition: transform 0.15s ease;
|
|
1606
|
+
}
|
|
1607
|
+
.todo-panel-toggle.open {
|
|
1608
|
+
transform: rotate(90deg);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
.todo-panel-list {
|
|
1612
|
+
list-style: none;
|
|
1613
|
+
margin: 0;
|
|
1614
|
+
padding: 0 0 4px;
|
|
1615
|
+
overflow-y: auto;
|
|
1616
|
+
flex: 1;
|
|
1617
|
+
}
|
|
1618
|
+
.todo-item {
|
|
1619
|
+
display: flex;
|
|
1620
|
+
align-items: flex-start;
|
|
1621
|
+
gap: 8px;
|
|
1622
|
+
padding: 4px 14px;
|
|
1623
|
+
color: var(--fg-2);
|
|
1624
|
+
line-height: 1.4;
|
|
1625
|
+
}
|
|
1626
|
+
.todo-item-done {
|
|
1627
|
+
opacity: 0.5;
|
|
1628
|
+
}
|
|
1629
|
+
.todo-item-done .todo-item-content {
|
|
1630
|
+
text-decoration: line-through;
|
|
1631
|
+
}
|
|
1632
|
+
.todo-item-content {
|
|
1633
|
+
flex: 1;
|
|
1634
|
+
word-break: break-word;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
.todo-status {
|
|
1638
|
+
flex-shrink: 0;
|
|
1639
|
+
width: 16px;
|
|
1640
|
+
text-align: center;
|
|
1641
|
+
line-height: 1.4;
|
|
1642
|
+
}
|
|
1643
|
+
.todo-status-completed { color: #4ade80; }
|
|
1644
|
+
.todo-status-in-progress { color: #facc15; }
|
|
1645
|
+
.todo-status-pending { color: var(--fg-5); }
|
|
1646
|
+
|
|
1647
|
+
.todo-priority {
|
|
1648
|
+
flex-shrink: 0;
|
|
1649
|
+
font-size: 10px;
|
|
1650
|
+
padding: 1px 5px;
|
|
1651
|
+
border-radius: 4px;
|
|
1652
|
+
line-height: 1.4;
|
|
1653
|
+
}
|
|
1654
|
+
.todo-priority-high {
|
|
1655
|
+
color: #f87171;
|
|
1656
|
+
background: rgba(248,113,113,0.12);
|
|
1657
|
+
}
|
|
1658
|
+
.todo-priority-low {
|
|
1659
|
+
color: var(--fg-5);
|
|
1660
|
+
background: var(--surface-1);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1553
1663
|
/* Reduced motion */
|
|
1554
1664
|
@media (prefers-reduced-motion: reduce) {
|
|
1555
1665
|
*, *::before, *::after {
|
package/src/web-ui.ts
CHANGED
|
@@ -162,6 +162,7 @@ ${WEB_UI_STYLES}
|
|
|
162
162
|
</div>
|
|
163
163
|
<form id="composer" class="composer">
|
|
164
164
|
<div class="composer-inner">
|
|
165
|
+
<div id="todo-panel" class="hidden"></div>
|
|
165
166
|
<div id="attachment-preview" class="attachment-preview" style="display:none"></div>
|
|
166
167
|
<div class="composer-shell">
|
|
167
168
|
<button id="attach-btn" class="attach-btn" type="button" title="Attach files">
|