diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9e5842..85c5e19 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,8 +6,14 @@ All notable changes to this project are documented here. The format follows
## [Unreleased]
+## [0.15.0] — 2026-09-14
+
### Fixed
+- Agent and other context submenus expand upward near the bottom edge instead of clipping the available list.
+
+- “Ask an agent” pastes into the receiving tab's current composer or terminal, including running agents that are still producing output.
+
- Automatic PyPI publication uses the registered publishing workflow directly, avoiding the signing-identity mismatch that required a manual retry.
## [0.14.0] — 2026-09-12
@@ -786,7 +792,8 @@ First public release.
nothing compiles; `uv`/`pipx` from the GitHub release everywhere else. Apache 2.0 license; full README,
installation, configuration, troubleshooting, and architecture documentation.
-[Unreleased]: https://github.com/danialfarid/termdeck/compare/v0.14.0...HEAD
+[Unreleased]: https://github.com/danialfarid/termdeck/compare/v0.15.0...HEAD
+[0.15.0]: https://github.com/danialfarid/termdeck/compare/v0.14.0...v0.15.0
[0.14.0]: https://github.com/danialfarid/termdeck/compare/v0.13.0...v0.14.0
[0.13.0]: https://github.com/danialfarid/termdeck/compare/v0.12.3...v0.13.0
[0.12.3]: https://github.com/danialfarid/termdeck/compare/v0.12.2...v0.12.3
diff --git a/termdeck/__init__.py b/termdeck/__init__.py
index 9e78220..9da2f8f 100644
--- a/termdeck/__init__.py
+++ b/termdeck/__init__.py
@@ -1 +1 @@
-__version__ = "0.14.0"
+__version__ = "0.15.0"
diff --git a/termdeck/static/app_markdown_files.js b/termdeck/static/app_markdown_files.js
index bafe3e1..1b74703 100644
--- a/termdeck/static/app_markdown_files.js
+++ b/termdeck/static/app_markdown_files.js
@@ -2879,6 +2879,7 @@ Object.assign(TermdeckApp.prototype, {
schedulePendingAgentPaste(view, delay = 0) {
if (!view || view.closed || !view.pendingAgentPaste) return;
+ if (view.pendingAgentPasteTimer && !view.pendingAgentPasteRequireComposer) return;
if (!view.pendingAgentPasteStartedAt) view.pendingAgentPasteStartedAt = Date.now();
clearTimeout(view.pendingAgentPasteTimer);
const readyDelay = Math.max(0, (view.pendingAgentPasteReadyAt || 0) - Date.now());
@@ -6243,17 +6244,15 @@ Object.assign(TermdeckApp.prototype, {
const session = this.session(sessionId);
if (!value || !session || !session.agent_kind || session.agent_kind === "none" ||
(!session.running && !session.dormant)) return false;
- const useHistoryComposer = this.historyOpen;
this.hideSelectionActions(true);
this.activate(sessionId, { reveal: true });
- const view = this.views.get(sessionId) || this.ensureView(sessionId);
- if (useHistoryComposer && this.sessionSupportsTranscript(session)) {
- if (!this.historyOpen) this.setHistoryMode(true);
+ if (this.historyOpen && this.sessionSupportsTranscript(session)) {
this.appendTextToHistoryPrompt(value);
this.$("status-name").textContent = "selected text added to transcript composer for " + this.agentSessionContextLabel(session);
return true;
}
- if (!this.queuePendingAgentPaste(view, value)) return false;
+ const view = this.views.get(sessionId) || this.ensureView(sessionId);
+ if (!this.queuePendingAgentPaste(view, value, { requireComposer: !!session.dormant })) return false;
if (!view.ws) this.connect(sessionId, view);
this.$("status-name").textContent = "selected text queued for " + this.agentSessionContextLabel(session);
return true;
diff --git a/termdeck/static/app_search_git.js b/termdeck/static/app_search_git.js
index 1fe8358..12e7e96 100644
--- a/termdeck/static/app_search_git.js
+++ b/termdeck/static/app_search_git.js
@@ -3949,6 +3949,25 @@ Object.assign(TermdeckApp.prototype, {
}
wrapper.appendChild(submenu);
menu.appendChild(wrapper);
+ wrapper.addEventListener("mouseenter", () => this.positionContextSubmenu(wrapper));
+ wrapper.addEventListener("focusin", () => this.positionContextSubmenu(wrapper));
+ },
+
+
+ positionContextSubmenu(wrapper) {
+ const submenu = wrapper.querySelector(":scope > .context-submenu-menu");
+ if (!submenu || !submenu.offsetHeight) return;
+ const anchor = wrapper.getBoundingClientRect();
+ submenu.style.maxHeight = Math.max(0, Math.min(420, window.innerHeight - 16)) + "px";
+ submenu.style.maxWidth = Math.max(0, window.innerWidth - 16) + "px";
+ submenu.style.minWidth = Math.min(190, window.innerWidth - 16) + "px";
+ const height = submenu.offsetHeight;
+ const width = submenu.offsetWidth;
+ const top = Math.max(8, Math.min(anchor.top - 4, window.innerHeight - height - 8));
+ const preferredLeft = anchor.right + width <= window.innerWidth - 8 ? anchor.right - 1 : anchor.left - width + 1;
+ const left = Math.max(8, Math.min(preferredLeft, window.innerWidth - width - 8));
+ submenu.style.top = top - anchor.top + "px";
+ submenu.style.left = left - anchor.left + "px";
},
@@ -3961,6 +3980,7 @@ Object.assign(TermdeckApp.prototype, {
const top = y + height > window.innerHeight - 10 && above >= 8 ? above : y;
menu.style.left = Math.max(8, Math.min(x, window.innerWidth - menu.offsetWidth - 10)) + "px";
menu.style.top = Math.max(8, Math.min(top, window.innerHeight - height - 10)) + "px";
+ for (const wrapper of menu.querySelectorAll(".context-submenu")) this.positionContextSubmenu(wrapper);
},
diff --git a/termdeck/static/index.html b/termdeck/static/index.html
index dee89eb..1875d55 100644
--- a/termdeck/static/index.html
+++ b/termdeck/static/index.html
@@ -623,8 +623,8 @@
-
-
+
+