You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by wy...@apache.org on 2026/10/16 11:27:51 UTC
(maka) 03/05: revert(ui): remove the mixed scheduler and viewport-loading increment
This is an automated email from the ASF dual-hosted git repository.
M4n5ter pushed a commit to branch rust-preview
in repository https://gitbox.apache.org/repos/asf/maka.git
commit 9acf75d0fcc0b3292664839eeb6355ed9cd23c21
Author: Wang <m4...@gmail.com>
AuthorDate: Fri Oct 16 19:19:23 2026 +0800
revert(ui): remove the mixed scheduler and viewport-loading increment
Revert 2aaffdb8c0d0dcde357b18f3fca74e0a628fc19a relative to its
second parent d53d46a4b9dbf06c9c8b72f2945b79a5b8ee96b9. This removes
only the actual 25-file increment (+895/-57), not the 381 files shown
against the other merge parent. The two later reliability fixes remain.
Correctness: long-lived subscriptions exhaust all 64 shared execution
slots and prevent unrelated queries or mutations from starting. A native
144-subscription fixture reproduced the blocked query and still verified
physical cleanup. Viewport-gated tool reads also omit offscreen summaries
from current-content search, making results depend on previous scrolling.
Design: an application-wide count gate couples independent subscriptions
and short operations without evidence that 64 is a necessary resource
bound. Viewport loading changes search semantics to reduce work.
Commit hygiene: scheduling policy, source-loading policy, hover updates,
and diagnostic infrastructure were bundled into a merge carrying conflict
resolution boilerplate. The dirty integration should be replaced with a
focused, independently verified change. Neither the old branch name nor
the larger merge-parent file count is evidence for a broader rollback.
The reverted tree exactly matches 5f7b00544, preserving our existing fixes.
Generated-by: Codex
---
platform/render-terminal/src/application.rs | 37 +----
platform/render-terminal/src/application/choice.rs | 15 +-
platform/render-terminal/src/application/paint.rs | 1 -
.../render-terminal/src/application/pointer.rs | 5 +-
.../render-terminal/src/application/reading.rs | 9 +-
.../render-terminal/src/application/resources.rs | 33 ++--
platform/render-terminal/src/application/rows.rs | 7 +-
platform/render-terminal/src/application/run.rs | 53 ------
platform/render-terminal/src/application/scene.rs | 5 +-
.../src/application/tests/layout.rs | 74 ---------
.../render-terminal/src/application/tests/rows.rs | 27 ----
platform/render-terminal/src/terminal.rs | 2 -
platform/ui-js/tests/runtime/subscriptions.rs | 179 +--------------------
platform/ui-runtime/src/actions.rs | 88 ++--------
platform/ui-runtime/src/actions/mutations.rs | 15 --
platform/ui-runtime/src/actions/subscriptions.rs | 8 -
platform/ui-runtime/src/diagnostics.rs | 144 -----------------
platform/ui-runtime/src/lib.rs | 1 -
platform/ui-runtime/src/tree.rs | 8 +-
plugins/agent/agent-screen/conversation-view.ts | 1 -
plugins/agent/tool-details/index.ts | 2 -
plugins/shared/components.ts | 5 -
plugins/shared/contracts.ts | 23 +--
products/maka/bundle/tests/timeline.test.mjs | 88 ----------
products/maka/bundle/tests/tool-details.test.mjs | 122 --------------
25 files changed, 57 insertions(+), 895 deletions(-)
diff --git a/platform/render-terminal/src/application.rs b/platform/render-terminal/src/application.rs
index c58a39c5b7..2f709504d2 100644
--- a/platform/render-terminal/src/application.rs
+++ b/platform/render-terminal/src/application.rs
@@ -90,15 +90,7 @@ pub trait Widget: std::any::Any {
fn cancel_pointer(&self, _: &mut dyn std::any::Any) {}
/// Local pointer feedback against the last presented target. Leaving clears
/// feedback; hovering never selects, focuses, or invokes guest code.
- /// Return whether native feedback changed and needs another paint.
- fn hover(
- &self,
- _: &mut dyn std::any::Any,
- _: &str,
- _: Option<ratatui::layout::Position>,
- ) -> bool {
- false
- }
+ fn hover(&self, _: &mut dyn std::any::Any, _: &str, _: Option<ratatui::layout::Position>) {}
fn same_presentation(&self, _: &dyn std::any::Any) -> bool {
false
}
@@ -614,6 +606,7 @@ impl Presenter {
/// scene. Crossterm supplies no screen epoch; never re-resolve a captured key.
/// 输入立即绑定到最后成功输出的场景和真实 mount/revision。
pub fn input(&mut self, event: Event) -> maka_ui_runtime::Result<()> {
+ self.resources.redraw = true;
let result = self.input_inner(event, false, None);
self.recover_result(result)
}
@@ -621,6 +614,7 @@ impl Presenter {
&mut self,
event: Event,
) -> maka_ui_runtime::Result<Option<activation::CapturedActivation>> {
+ self.resources.redraw = true;
let mut captured = None;
let result = self.input_inner(event, false, Some(&mut captured));
self.recover_result(result)?;
@@ -631,6 +625,7 @@ impl Presenter {
pub(super) fn wheel(&mut self, event: Event) -> maka_ui_runtime::Result<()> {
debug_assert!(matches!(event, Event::Mouse(mouse) if matches!(mouse.kind,
MouseEventKind::ScrollUp | MouseEventKind::ScrollDown)));
+ self.resources.redraw = true;
let result = self.input_inner(event, true, None);
self.recover_result(result)
}
@@ -640,11 +635,6 @@ impl Presenter {
defer_scroll: bool,
mut capture: Option<&mut Option<activation::CapturedActivation>>,
) -> maka_ui_runtime::Result<()> {
- // Ordinary pointer motion can leave presentation entirely unchanged.
- // Native hover reports its own changes below; other input retains the
- // original redraw policy, including raw terminal pointer forwarding.
- self.resources.redraw |=
- !matches!(event, Event::Mouse(mouse) if mouse.kind == MouseEventKind::Moved);
match event {
Event::FocusLost => self.window_focused = false,
Event::FocusGained => self.window_focused = true,
@@ -703,9 +693,6 @@ impl Presenter {
} else {
None
};
- if raw_pointer_target.is_some() {
- self.resources.redraw = true;
- }
if raw_pointer_target.is_none()
&& (self.editor_context_menu(&event)? || self.open_context_menu(&event)?)
{
@@ -1242,19 +1229,7 @@ impl Presenter {
&& !self.resources.owners.contains_key(&mount.id())
})
.cloned();
- let started = maka_ui_runtime::diagnostics::started();
let result = self.pump_inner(input_waiting);
- if let Some(started) = started
- && started.elapsed() >= std::time::Duration::from_millis(16)
- {
- maka_ui_runtime::diagnostics::record("slow_pump", || {
- json!({
- "elapsed_us": started.elapsed().as_micros(),
- "pending_actions": self.application.pending_action_count(),
- "input_waiting": input_waiting, "failed": result.is_err(),
- })
- });
- }
if let (Some(mount), Err(error @ (Error::Script(_) | Error::Invalid(_)))) =
(replacement, &result)
&& !self.application.logical_pending()
@@ -1319,11 +1294,9 @@ impl Presenter {
if let Err(error) = advancement {
self.report(&error);
}
- let advance_span = maka_ui_runtime::diagnostics::Span::new("slow_advance_ui");
if self.application.advance_ui()? {
self.resources.redraw = true;
}
- drop(advance_span);
self.sync_native()?;
// Each join observer stays with the actual owner, independently of UI.
self.finish_jobs()?;
@@ -1333,7 +1306,6 @@ impl Presenter {
}
if !input_waiting {
let resources = &mut self.resources;
- let _span = maka_ui_runtime::diagnostics::Span::new("slow_feedback");
self.application.deliver_action_results(
&self.root,
|target, recipe, witness| {
@@ -1418,7 +1390,6 @@ impl Presenter {
failure.map_or(Ok(()), Err)
}
fn finish_jobs(&mut self) -> maka_ui_runtime::Result<()> {
- let _span = maka_ui_runtime::diagnostics::Span::new("slow_finish_jobs");
let wake = self.application.waker();
let mut cx = std::task::Context::from_waker(&wake);
while let std::task::Poll::Ready(Some(completion)) =
diff --git a/platform/render-terminal/src/application/choice.rs b/platform/render-terminal/src/application/choice.rs
index a6c0717667..44f21b6a58 100644
--- a/platform/render-terminal/src/application/choice.rs
+++ b/platform/render-terminal/src/application/choice.rs
@@ -183,30 +183,25 @@ impl SceneState {
})
})
}
- pub fn choice_hover(&mut self, choice: &Choice, point: Option<Position>) -> bool {
+ pub fn choice_hover(&mut self, choice: &Choice, point: Option<Position>) {
if !self.dialogs.contains_key(&choice.key) {
- return false;
+ return;
}
let Some(state) = self.choices.get_mut(&choice.key) else {
- return false;
+ return;
};
let Some(geometry) = state
.geometry
.as_ref()
.filter(|geometry| geometry.choice == *choice)
else {
- return false;
+ return;
};
if let Some((menu, point)) = geometry.menu.as_ref().zip(point)
&& let Some(index) = menu.rows.iter().position(|row| row.contains(point))
{
- let key = &choice.options[menu.first + index].key;
- if state.highlighted.as_ref() != Some(key) {
- state.highlighted = Some(key.clone());
- return true;
- }
+ state.highlighted = Some(choice.options[menu.first + index].key.clone());
}
- false
}
}
impl Presenter {
diff --git a/platform/render-terminal/src/application/paint.rs b/platform/render-terminal/src/application/paint.rs
index dcbefa38b8..6180c56130 100644
--- a/platform/render-terminal/src/application/paint.rs
+++ b/platform/render-terminal/src/application/paint.rs
@@ -702,7 +702,6 @@ pub(super) fn color(value: impl Into<maka_ui_runtime::scene::terminal::Color>) -
}
impl Presenter {
pub fn paint(&self, area: Rect, buffer: &mut Buffer) -> maka_ui_runtime::Result<PaintedScene> {
- let _span = maka_ui_runtime::diagnostics::Span::new("slow_paint");
if !self.surface.is_live() {
return Err(maka_ui_runtime::Error::Invalid(
"terminal surface retired".into(),
diff --git a/platform/render-terminal/src/application/pointer.rs b/platform/render-terminal/src/application/pointer.rs
index 89ddf3fd89..7fe4c7d3bb 100644
--- a/platform/render-terminal/src/application/pointer.rs
+++ b/platform/render-terminal/src/application/pointer.rs
@@ -24,7 +24,6 @@ impl Presenter {
pub(super) fn clear_hover(&mut self) -> maka_ui_runtime::Result<()> {
if let Some(target) = self.hover.take() {
self.resources.hover(&target, None)?;
- self.resources.redraw = true;
}
Ok(())
}
@@ -50,9 +49,7 @@ impl Presenter {
self.clear_hover()?;
}
if let Some(target) = target {
- let entering = self.hover.is_none();
- let changed = self.resources.hover(&target, Some(position))?;
- self.resources.redraw |= entering || changed;
+ self.resources.hover(&target, Some(position))?;
self.hover = Some(target);
}
Ok(())
diff --git a/platform/render-terminal/src/application/reading.rs b/platform/render-terminal/src/application/reading.rs
index b86424e2d5..5a8ef42f09 100644
--- a/platform/render-terminal/src/application/reading.rs
+++ b/platform/render-terminal/src/application/reading.rs
@@ -369,13 +369,8 @@ impl Widget for Reading {
fn handles_tab(&self, state: &dyn Any, key: &str) -> bool {
self.node.handles_tab(state, key)
}
- fn hover(
- &self,
- state: &mut dyn Any,
- key: &str,
- point: Option<ratatui::layout::Position>,
- ) -> bool {
- self.node.hover(state, key, point)
+ fn hover(&self, state: &mut dyn Any, key: &str, point: Option<ratatui::layout::Position>) {
+ self.node.hover(state, key, point);
}
fn present(&self, state: &mut dyn Any, geometry: Box<dyn Any>) -> Option<NativeInput> {
let state = state_mut(state);
diff --git a/platform/render-terminal/src/application/resources.rs b/platform/render-terminal/src/application/resources.rs
index ee435be758..94c756ecc8 100644
--- a/platform/render-terminal/src/application/resources.rs
+++ b/platform/render-terminal/src/application/resources.rs
@@ -742,7 +742,6 @@ impl Presenter {
self.sync_native_inner(false)
}
fn sync_native_inner(&mut self, prepare: bool) -> maka_ui_runtime::Result<()> {
- let _span = maka_ui_runtime::diagnostics::Span::new("slow_sync_native");
self.resources.deactivate(&self.application);
self.resources.cancel_pastes(
self.focus
@@ -854,30 +853,30 @@ impl Resources {
&mut self,
target: &paint::Target,
position: Option<ratatui::layout::Position>,
- ) -> maka_ui_runtime::Result<bool> {
+ ) -> maka_ui_runtime::Result<()> {
let Some(owner) = self
.owners
.get_mut(&target.mount.id())
.filter(|owner| owner.live && std::rc::Rc::ptr_eq(&owner.identity, &target.owner))
else {
- return Ok(false);
+ return Ok(());
};
- match catch_unwind(AssertUnwindSafe(|| {
+ if catch_unwind(AssertUnwindSafe(|| {
owner
.recipe
- .hover(owner.state.as_mut(), &target.key, position)
- })) {
- Ok(changed) => Ok(changed),
- Err(_) => {
- owner.live = false;
- self.failed.insert(
- target.mount.id(),
- "terminal native hover cleanup unconfirmed".into(),
- );
- Err(maka_ui_runtime::Error::Invalid(
- "terminal native hover cleanup unconfirmed".into(),
- ))
- }
+ .hover(owner.state.as_mut(), &target.key, position);
+ }))
+ .is_err()
+ {
+ owner.live = false;
+ self.failed.insert(
+ target.mount.id(),
+ "terminal native hover cleanup unconfirmed".into(),
+ );
+ return Err(maka_ui_runtime::Error::Invalid(
+ "terminal native hover cleanup unconfirmed".into(),
+ ));
}
+ Ok(())
}
}
diff --git a/platform/render-terminal/src/application/rows.rs b/platform/render-terminal/src/application/rows.rs
index 2b74fcc1e9..e3033acf8e 100644
--- a/platform/render-terminal/src/application/rows.rs
+++ b/platform/render-terminal/src/application/rows.rs
@@ -156,9 +156,9 @@ impl Widget for Rows {
owner.offset = owner.offset.min(self.props.rows.len().saturating_sub(1));
Ok(None)
}
- fn hover(&self, state: &mut dyn Any, _: &str, point: Option<Position>) -> bool {
+ fn hover(&self, state: &mut dyn Any, _: &str, point: Option<Position>) {
let owner = state.downcast_mut::<Owner>().unwrap();
- let hover = point.and_then(|point| {
+ owner.hover = point.and_then(|point| {
if owner
.geometry
.as_ref()?
@@ -176,9 +176,6 @@ impl Widget for Rows {
.find(|hit| hit.area.contains(point))
.map(|hit| hit.row.clone())
});
- let changed = owner.hover != hover;
- owner.hover = hover;
- changed
}
fn input(
&self,
diff --git a/platform/render-terminal/src/application/run.rs b/platform/render-terminal/src/application/run.rs
index 7f9a15e189..497213a27c 100644
--- a/platform/render-terminal/src/application/run.rs
+++ b/platform/render-terminal/src/application/run.rs
@@ -51,7 +51,6 @@ async fn deadline(at: Option<Instant>) {
}
struct PendingOutput {
- submitted: Option<Instant>,
sequence: u64,
generation: uuid::Uuid,
surface: maka_plugin_core::runtime::Mount,
@@ -80,15 +79,6 @@ fn poll_output(
return Ok(());
};
let output = pending.take().expect("original output receipt");
- if let Some(start) = output.submitted
- && start.elapsed() >= Duration::from_millis(16)
- {
- maka_ui_runtime::diagnostics::record("slow_output_receipt", || {
- json!({
- "sequence": output.sequence, "elapsed_us": start.elapsed().as_micros(),
- })
- });
- }
result
.map_err(|error| sanitize(&error.to_string()))?
.map_err(|error| sanitize(&error.to_string()))?;
@@ -206,7 +196,6 @@ pub fn run(
match output.submit(bytes) {
Ok(receipt) => {
pending_output = Some(PendingOutput {
- submitted: maka_ui_runtime::diagnostics::started(),
sequence: issued_sequence,
generation: screen.as_ref().expect("original terminal").generation,
surface: presenter.surface(),
@@ -343,7 +332,6 @@ pub fn run(
}
issued_sequence += 1;
pending_output = Some(PendingOutput {
- submitted: maka_ui_runtime::diagnostics::started(),
sequence: issued_sequence,
generation: screen.generation,
surface: presenter.surface(),
@@ -403,20 +391,6 @@ pub fn run(
};
let mut next_input = input;
while let Some(mut input) = next_input.take() {
- let _span = maka_ui_runtime::diagnostics::Span::new("slow_input");
- maka_ui_runtime::diagnostics::record("input", || {
- json!({
- "kind": match &input {
- Event::Mouse(mouse) => format!("mouse_{:?}", mouse.kind),
- Event::Key(_) => "key".into(),
- Event::Paste(_) => "paste".into(),
- Event::Resize(_, _) => "resize".into(),
- _ => "focus".into(),
- },
- "output_pending": pending_output.is_some(),
- "replayed": replay.is_some(),
- })
- });
poll_output(
&mut pending_output,
&mut written_sequence,
@@ -434,11 +408,6 @@ pub fn run(
},
Admission::Discard => return Ok(false),
Admission::Pending => {
- maka_ui_runtime::diagnostics::record("input_deferred", || {
- json!({
- "reason": "activation", "output_pending": pending_output.is_some(),
- })
- });
queued_input = Some(QueuedInput::Activation(activation));
wake.0.notify_one();
return Ok(false);
@@ -453,11 +422,6 @@ pub fn run(
if let Some(activation) = presenter.capture_activation(&input)
&& presenter.activation_admission(&activation) == Admission::Pending
{
- maka_ui_runtime::diagnostics::record("input_deferred", || {
- json!({
- "reason": "activation", "output_pending": pending_output.is_some(),
- })
- });
queued_input = Some(QueuedInput::Activation(activation));
presenter.resources.redraw = true;
wake.0.notify_one();
@@ -470,11 +434,6 @@ pub fn run(
|| input_needs_presentation(&presenter, &input))
&& !is_exit(&input)
{
- maka_ui_runtime::diagnostics::record("input_deferred", || {
- json!({
- "reason": "presentation", "output_pending": pending_output.is_some(),
- })
- });
queued_input = Some(QueuedInput::Physical(
input,
presenter.opening_witnesses().map_err(|e| e.to_string())?,
@@ -507,11 +466,6 @@ pub fn run(
.dispatch_focused_shortcut_with_capture(&input, Some(&mut captured))
.map_err(|error| error.to_string())?;
if let Some(activation) = captured {
- maka_ui_runtime::diagnostics::record("input_deferred", || {
- json!({
- "reason": "activation", "output_pending": pending_output.is_some(),
- })
- });
queued_input = Some(QueuedInput::Activation(activation));
presenter.resources.redraw = true;
wake.0.notify_one();
@@ -552,11 +506,6 @@ pub fn run(
.map_err(|error| error.to_string())?
};
if let Some(activation) = captured {
- maka_ui_runtime::diagnostics::record("input_deferred", || {
- json!({
- "reason": "activation", "output_pending": pending_output.is_some(),
- })
- });
queued_input = Some(QueuedInput::Activation(activation));
presenter.resources.redraw = true;
wake.0.notify_one();
@@ -623,8 +572,6 @@ pub fn run(
}
}
}
- maka_ui_runtime::diagnostics::record("run_exit", || json!({"failed": failure.is_some()}));
- maka_ui_runtime::diagnostics::flush();
updates.stop();
scheduler.block_on(updates.drain());
// Failures remain inspectable in this owner; dropping it cannot substitute
diff --git a/platform/render-terminal/src/application/scene.rs b/platform/render-terminal/src/application/scene.rs
index 9d216d5cb6..813f97c431 100644
--- a/platform/render-terminal/src/application/scene.rs
+++ b/platform/render-terminal/src/application/scene.rs
@@ -130,14 +130,13 @@ impl super::Widget for Node {
state: &mut dyn std::any::Any,
key: &str,
point: Option<ratatui::layout::Position>,
- ) -> bool {
+ ) {
if let Some(Node::Choice(choice)) = self.overlay(key) {
- return state
+ state
.downcast_mut::<super::scroll::SceneState>()
.unwrap()
.choice_hover(choice, point);
}
- false
}
fn same_presentation(&self, previous: &dyn std::any::Any) -> bool {
previous
diff --git a/platform/render-terminal/src/application/tests/layout.rs b/platform/render-terminal/src/application/tests/layout.rs
index dc3edc717a..a48ec11488 100644
--- a/platform/render-terminal/src/application/tests/layout.rs
+++ b/platform/render-terminal/src/application/tests/layout.rs
@@ -1515,80 +1515,6 @@ fn anchored_model_dialog_follows_its_trigger_across_viewport_sizes() {
presenter.shutdown_settled().unwrap();
}
-#[test]
-fn unchanged_button_hover_does_not_redraw_or_clear_an_existing_redraw() {
- let source = json!({"kind":"stack","key":"root","direction":"column","gap":0,"padding":0,"background":0,"children":[
- {"kind":"button","key":"tool","label":"Read file","hint":"Show details"}
- ]});
- let (_runtime, mut presenter) = native(View::Scene(source), Rc::new(Cell::new(true)));
- flushed(&mut presenter, 32, 6);
- let mouse = |column, row| {
- Event::Mouse(MouseEvent {
- kind: MouseEventKind::Moved,
- column,
- row,
- modifiers: KeyModifiers::NONE,
- })
- };
- presenter.resources.redraw = false;
- presenter.input(mouse(30, 5)).unwrap();
- assert!(
- !presenter.resources.redraw,
- "empty space has no hover feedback"
- );
- presenter.input(mouse(2, 0)).unwrap();
- assert!(presenter.resources.redraw, "entering shows the hint");
- let buffer = flushed(&mut presenter, 32, 6);
- assert!(
- buffer
- .content
- .iter()
- .map(|cell| cell.symbol())
- .collect::<String>()
- .contains("Show details")
- );
- let revision = presenter
- .application
- .frame(&presenter.root)
- .unwrap()
- .revision;
- presenter.resources.redraw = false;
- for column in [3, 4, 5, 6, 7, 6, 5, 4] {
- presenter.input_with_capture(mouse(column, 0)).unwrap();
- assert!(!presenter.resources.redraw, "same button at {column}");
- }
- assert_eq!(
- presenter
- .application
- .frame(&presenter.root)
- .unwrap()
- .revision,
- revision
- );
- presenter.resources.redraw = true;
- presenter.input(mouse(3, 0)).unwrap();
- assert!(
- presenter.resources.redraw,
- "pending business redraw survives"
- );
- presenter.resources.redraw = false;
- presenter.input(mouse(30, 5)).unwrap();
- assert!(presenter.resources.redraw, "leaving removes the hint");
- let buffer = flushed(&mut presenter, 32, 6);
- assert!(
- !buffer
- .content
- .iter()
- .map(|cell| cell.symbol())
- .collect::<String>()
- .contains("Show details")
- );
- presenter
- .surface_closed(&presenter.surface(), Ok(()))
- .unwrap();
- presenter.shutdown_settled().unwrap();
-}
-
#[test]
fn passive_text_hint_hover_never_steals_focus_or_activation() {
let source = json!({"kind":"stack","key":"root","direction":"column","gap":0,"padding":0,"background":0,"children":[
diff --git a/platform/render-terminal/src/application/tests/rows.rs b/platform/render-terminal/src/application/tests/rows.rs
index 588e4f304b..7797dc06ea 100644
--- a/platform/render-terminal/src/application/tests/rows.rs
+++ b/platform/render-terminal/src/application/tests/rows.rs
@@ -90,33 +90,6 @@ fn native_rows_hover_is_local_and_only_flushed_controls_can_activate() {
assert!(tui.focus.as_ref().unwrap().same(&focus));
close(tui);
}
-#[test]
-fn native_rows_hover_redraws_only_when_the_hovered_row_changes() {
- let (_runtime, mut tui) = layout::native(
- layout::View::Rows(Rc::new(RefCell::new(props())), Rc::new(Cell::new(false))),
- Rc::new(Cell::new(true)),
- );
- draw(&mut tui, 40, 10);
- tui.input(mouse(MouseEventKind::Moved, 2, 2)).unwrap();
- assert!(tui.resources.redraw);
- draw(&mut tui, 40, 10);
- tui.resources.redraw = false;
- tui.input_with_capture(mouse(MouseEventKind::Moved, 3, 2))
- .unwrap();
- assert!(!tui.resources.redraw, "same row in the same native widget");
- tui.input_with_capture(mouse(MouseEventKind::Moved, 3, 3))
- .unwrap();
- assert!(
- tui.resources.redraw,
- "another row in the same native widget"
- );
- draw(&mut tui, 40, 10);
- tui.resources.redraw = false;
- tui.input(mouse(MouseEventKind::Moved, 4, 3)).unwrap();
- assert!(!tui.resources.redraw);
- close(tui);
-}
-
#[test]
fn native_rows_selected_removal_exits_exact_focus_and_does_not_retarget_repeat() {
let props = Rc::new(RefCell::new(props()));
diff --git a/platform/render-terminal/src/terminal.rs b/platform/render-terminal/src/terminal.rs
index 68cc73e88b..29a606067b 100644
--- a/platform/render-terminal/src/terminal.rs
+++ b/platform/render-terminal/src/terminal.rs
@@ -89,7 +89,6 @@ pub(crate) fn render(
screen: &mut Screen,
render: impl FnOnce(&mut ratatui::Frame<'_>),
) -> io::Result<Vec<u8>> {
- let _span = maka_ui_runtime::diagnostics::Span::new("slow_render");
queue!(
screen.terminal.backend_mut().writer_mut(),
BeginSynchronizedUpdate
@@ -130,7 +129,6 @@ impl Output {
.name("maka-terminal-output".into())
.spawn(move || {
while let Ok(request) = receive.recv() {
- let _span = maka_ui_runtime::diagnostics::Span::new("slow_stdout_write");
let result = writer
.write_all(&request.bytes)
.and_then(|_| writer.flush());
diff --git a/platform/ui-js/tests/runtime/subscriptions.rs b/platform/ui-js/tests/runtime/subscriptions.rs
index 1dfa752535..23eaea67cb 100644
--- a/platform/ui-js/tests/runtime/subscriptions.rs
+++ b/platform/ui-js/tests/runtime/subscriptions.rs
@@ -129,7 +129,6 @@ fn sequence(index: usize, stop: CancellationToken, mode: Mode) -> StreamFuture {
enum Mode {
Causal,
Many,
- Burst,
Panic,
BadItem,
WideItem,
@@ -176,15 +175,6 @@ impl NativeComponent<()> for Parent {
on: BTreeMap::from([("change".into(), "changed".into())]),
});
}
- if matches!(self.0, Mode::Burst) {
- slots.extend((0..8).map(|index| ComponentSlot {
- key: format!("tool-{index}"),
- binding: "child".into(),
- member: 0,
- props: json!({"cycle":0}),
- on: BTreeMap::new(),
- }));
- }
let mut output = Frame {
revision: state["revision"].as_u64().unwrap(),
state: state.clone(),
@@ -201,11 +191,7 @@ impl NativeComponent<()> for Parent {
)?,
}
.output(vec![]);
- let count = match self.0 {
- Mode::Many => 16,
- Mode::Burst => 7,
- _ => 1,
- };
+ let count = if matches!(self.0, Mode::Many) { 16 } else { 1 };
output.subscriptions = (0..count)
.map(|i| Action {
recovery: Value::Null,
@@ -263,14 +249,13 @@ impl NativeComponent<()> for Parent {
Ok(output)
}
}
-struct Child(Mode);
+struct Child;
impl NativeComponent<()> for Child {
fn call(
&self,
call: ComponentCall<'_>,
_: &maka_ui_runtime::abi::ValueBindings,
) -> maka_ui_runtime::Result<ComponentOutput<NativeView<()>>> {
- let initial = matches!(&call, ComponentCall::Mount { .. });
let (revision, cycle, emit) = match call {
ComponentCall::Mount { props } => (0, props["cycle"].clone(), false),
ComponentCall::Update { state, props } => (
@@ -278,11 +263,6 @@ impl NativeComponent<()> for Child {
props["cycle"].clone(),
props["cycle"] != state["cycle"],
),
- ComponentCall::Event { state, .. } if matches!(self.0, Mode::Burst) => (
- state["revision"].as_u64().unwrap() + 1,
- state["cycle"].clone(),
- false,
- ),
_ => panic!("no child raw inputs"),
};
let events = if emit {
@@ -293,28 +273,13 @@ impl NativeComponent<()> for Child {
} else {
vec![]
};
- let mut output = Frame {
+ Ok(Frame {
revision,
state: json!({"revision":revision,"cycle":cycle}),
surface: None,
view: NativeView::new(Rc::new(()), BTreeMap::new(), vec![])?,
}
- .output(events);
- if initial && matches!(self.0, Mode::Burst) {
- output.actions = (0..12)
- .map(|index| Action {
- recovery: Value::Null,
- continuations: Vec::new(),
- key: format!("source-{index}"),
- binding: "stream".into(),
- member: 0,
- method: "query".into(),
- input: Value::Null,
- resources: BTreeMap::new(),
- })
- .collect();
- }
- Ok(output)
+ .output(events))
}
}
fn component_contract(name: &str, events: BTreeMap<String, Value>) -> Contract {
@@ -400,30 +365,6 @@ fn fixture_data(
packages.push(package);
let service = native_package("native.stream", "test.stream", service_contract());
let child = native_package("native.child", "test.child", child_contract.clone());
- let child = if matches!(mode, Mode::Burst) {
- let mut declaration = child
- .manifest()
- .application
- .as_ref()
- .unwrap()
- .declaration()
- .clone();
- declaration
- .entries
- .get_mut(&EntryTarget::Desktop)
- .unwrap()
- .imports
- .insert(
- "stream".into(),
- Import {
- contract: service_contract(),
- cardinality: Cardinality::One,
- },
- );
- support::package("native.child", declaration, None)
- } else {
- child
- };
for (name, key, package) in [("stream", 3, &service), ("child", 4, &child)] {
input
.instances
@@ -436,15 +377,6 @@ fn fixture_data(
}],
);
}
- if matches!(mode, Mode::Burst) {
- input.instances.get_mut("child").unwrap().bindings.insert(
- "stream".into(),
- vec![Target::Instance {
- path: vec!["stream".into()],
- export: "Export".into(),
- }],
- );
- }
packages.extend([service, child]);
let starts = Arc::new(AtomicUsize::new(0));
let observed = starts.clone();
@@ -466,7 +398,7 @@ fn fixture_data(
"Export".into(),
NativeExport {
contract: child_contract.clone(),
- value: NativeCapability::Component(Rc::new(Child(mode))),
+ value: NativeCapability::Component(Rc::new(Child)),
},
)]))
})
@@ -577,107 +509,6 @@ fn temporary_causal_demand_changes_do_not_restart_original_committed_execution()
assert_eq!(starts.load(Ordering::SeqCst), 1);
close(&mut app, &[root]);
}
-#[test]
-fn composed_reads_wait_for_executor_and_feedback_capacity_without_rejecting_the_frame() {
- let timer = timer();
- let (mut app, starts) = fixture(Mode::Burst, &timer);
- let root = app.mount_launch(json!({})).unwrap();
- assert_eq!(app.pending_action_count(), 103);
- app.capture_actions(&root, |_| panic!("no resources"))
- .unwrap();
- let mut first = Vec::new();
- while let Some(task) = app.start_task(&root).unwrap() {
- first.push(task);
- }
- assert_eq!(first.len(), 64);
- assert_eq!(app.accepted_action_count(), 64);
- let mut subscriptions = Vec::new();
- for task in first {
- let (id, completion) = run(&timer, task);
- if let Some(next) = app.finish_task(id, completion).unwrap() {
- subscriptions.push(next);
- }
- }
- assert_eq!(app.accepted_action_count(), 7);
- assert_eq!(app.pending_action_count(), 103);
- assert!(
- app.start_task(&root).unwrap().is_none(),
- "undelivered feedback retains capacity"
- );
- assert_eq!(
- app.deliver_action_results(&root, |_, _, _| Ok(false))
- .unwrap(),
- 64
- );
- let mut remaining = Vec::new();
- while let Some(task) = app.start_task(&root).unwrap() {
- remaining.push(task);
- }
- assert_eq!(remaining.len(), 39);
- for task in remaining {
- let (id, completion) = run(&timer, task);
- if let Some(next) = app.finish_task(id, completion).unwrap() {
- subscriptions.push(next);
- }
- }
- assert_eq!(starts.load(Ordering::SeqCst), 7);
- app.deliver_action_results(&root, |_, _, _| Ok(false))
- .unwrap();
- assert_eq!(app.accepted_action_count(), 7);
- app.retire(&root).unwrap();
- for task in subscriptions {
- let (id, completion) = run(&timer, task);
- assert!(app.finish_task(id, completion).unwrap().is_none());
- }
- assert_eq!(app.accepted_action_count(), 0);
- close(&mut app, &[root]);
-}
-
-#[test]
-fn queued_reads_remain_bounded_and_an_overflow_rolls_back_only_its_own_frame() {
- let timer = timer();
- let (mut app, _) = fixture(Mode::Burst, &timer);
- let roots = (0..9)
- .map(|_| app.mount_launch(json!({})).unwrap())
- .collect::<Vec<_>>();
- assert_eq!(app.pending_action_count(), 927);
- assert!(
- matches!(app.mount_launch(json!({})), Err(maka_ui_runtime::Error::Invalid(reason)) if reason == "pending action limit")
- );
- assert_eq!(app.pending_action_count(), 927);
- assert_eq!(app.accepted_action_count(), 0);
- close(&mut app, &roots);
-}
-
-#[test]
-fn retiring_a_full_executor_queue_rejects_unstarted_reads_without_releasing_running_work() {
- let timer = timer();
- let (mut app, _) = fixture(Mode::Burst, &timer);
- let root = app.mount_launch(json!({})).unwrap();
- app.capture_actions(&root, |_| panic!("no resources"))
- .unwrap();
- let mut tasks = Vec::new();
- while let Some(task) = app.start_task(&root).unwrap() {
- tasks.push(task);
- }
- assert_eq!(tasks.len(), 64);
- app.retire(&root).unwrap();
- assert!(app.start_task(&root).unwrap().is_none());
- assert_eq!(
- app.accepted_action_count(),
- 64,
- "retirement is not executor cleanup"
- );
- for task in tasks {
- let (id, completion) = run(&timer, task);
- assert!(app.finish_task(id, completion).unwrap().is_none());
- }
- app.deliver_action_results(&root, |_, _, _| Ok(false))
- .unwrap();
- assert_eq!(app.pending_action_count(), 0);
- close(&mut app, &[root]);
-}
-
#[test]
fn one_pending_feedback_does_not_double_charge_each_of_48_live_requests() {
let timer = timer();
diff --git a/platform/ui-runtime/src/actions.rs b/platform/ui-runtime/src/actions.rs
index 21253efb20..144ced3faa 100644
--- a/platform/ui-runtime/src/actions.rs
+++ b/platform/ui-runtime/src/actions.rs
@@ -34,11 +34,6 @@ use serde::{Deserialize, Serialize};
use std::{collections::VecDeque, future::Future, pin::Pin, sync::Arc};
use tokio_util::sync::CancellationToken;
-// Queued intents are bounded separately from accepted executors and feedback.
-// A composed frame can declare more reads than can execute at once.
-pub(super) const MAX_PENDING_ACTIONS: usize = 1024;
-const MAX_ACTIVE_ACTIONS: usize = 64;
-
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct CaptureSource {
@@ -382,46 +377,6 @@ impl<V: ?Sized> Actions<V> {
.filter(|o| o.request.is_none_or(|id| !self.running.contains_key(&id)))
.count()
}
- fn can_start(&self) -> bool {
- // Pending feedback retains a slot until it is consumed. Otherwise a
- // slow UI callback could let completed query outcomes grow unbounded.
- self.len() - self.queue.len() < MAX_ACTIVE_ACTIONS
- }
- pub(super) fn trace_limit(&self, consumer: &Mount, action: &Action, staged: &[Prepared<V>]) {
- diagnostics::record("pending_action_limit", || {
- json!({
- "incoming": {
- "mount": consumer.id(), "key": action.key,
- "binding": action.binding, "method": action.method,
- },
- "pending": self.len(), "staged_count": staged.len(),
- "staged": staged.iter().map(|a| json!({
- "id": a.id, "mount": a.consumer.id(), "key": a.key,
- "method": a.method, "subscription": a.subscription,
- })).collect::<Vec<_>>(),
- "queue": self.queue.iter().map(|a| json!({
- "id": a.id, "mount": a.consumer.id(), "key": a.key,
- "method": a.method, "subscription": a.subscription,
- "invalidated": a.invalidated,
- })).collect::<Vec<_>>(),
- "running": self.running.iter().map(|(id, a)| json!({
- "id": id, "mount": a.consumer.id(), "key": a.key,
- "method": a.method, "completed": a.completed,
- "subscription": a.stream.is_some(), "mutation": a.mutation.is_some(),
- "stop_requested": a.stream.as_ref().map(|flow| flow.stop.is_cancelled()),
- "awaiting_feedback": a.stream.as_ref().map(|flow| flow.pending_feedback),
- })).collect::<Vec<_>>(),
- "preparing": self.preparing.iter().map(|(id, a)| json!({
- "id": id, "mount": a.action.consumer.id(),
- "key": a.action.key, "method": a.action.method, "failed": a.failed(),
- })).collect::<Vec<_>>(),
- "feedback": self.outcomes.iter().map(|a| json!({
- "id": a.request, "mount": a.consumer.id(), "key": a.key,
- })).collect::<Vec<_>>(),
- })
- });
- diagnostics::flush();
- }
fn bytes(&self) -> usize {
self.queue
.iter()
@@ -963,29 +918,11 @@ impl<V: ?Sized + 'static> Application<V> {
}
Ok(captured)
}
- fn action_valid(&self, action: &Prepared<V>) -> bool {
- !action.invalidated
- && (!action.subscription
- || self.subscription_wanted(&action.consumer, &action.key, action.id))
- && !self.is_retiring(&action.consumer)
- && action
- .captures
- .values()
- .chain(action.witnesses.iter().map(|c| &c.target))
- .all(|target| self.target_valid(target))
- }
- pub(super) fn wake_queued_actions(&self) {
- if self.actions.can_start() && self.actions.queue.iter().any(|a| a.resources.is_some()) {
- self.waker().wake_by_ref();
- }
- }
/// Final Mount and captured registration check. A queue is not accepted Work.
pub fn start_task(&mut self, root: &Mount) -> Result<Option<ActionTask>> {
loop {
- let capacity = self.actions.can_start();
let Some(index) = self.actions.queue.iter().position(|a| {
a.resources.is_some()
- && (capacity || !self.action_valid(a))
&& (self.is_retiring(&a.consumer)
|| self
.mount_root(&a.consumer)
@@ -995,14 +932,16 @@ impl<V: ?Sized + 'static> Application<V> {
return Ok(None);
};
let action = self.actions.queue.remove(index).expect("queue position");
- diagnostics::record("action_dequeued", || {
- json!({
- "id": action.id, "mount": action.consumer.id(), "key": action.key,
- "method": action.method, "subscription": action.subscription,
- "pending": self.actions.len() + 1,
- })
- });
- if !self.action_valid(&action) {
+ if action.invalidated
+ || action.subscription
+ && !self.subscription_wanted(&action.consumer, &action.key, action.id)
+ || self.is_retiring(&action.consumer)
+ || action
+ .captures
+ .values()
+ .chain(action.witnesses.iter().map(|c| &c.target))
+ .any(|t| !self.target_valid(t))
+ {
self.reject_action(action, "consumer retired before admission".into());
continue;
}
@@ -1214,12 +1153,6 @@ impl<V: ?Sized + 'static> Application<V> {
let mut first_error = None;
for _ in 0..self.actions.outcomes.len() {
let outcome = self.actions.outcomes.pop_front().expect("bounded outcomes");
- diagnostics::record("feedback_polled", || {
- json!({
- "id": outcome.request, "mount": outcome.consumer.id(),
- "key": outcome.key, "pending": self.actions.len(),
- })
- });
let item = matches!(&outcome.outcome, ActionOutcome::Updated { .. });
if self.is_retiring(&outcome.consumer)
|| outcome.request.is_some_and(|id| {
@@ -1297,7 +1230,6 @@ impl<V: ?Sized + 'static> Application<V> {
}
}
}
- self.wake_queued_actions();
if let Some(error) = first_error {
return Err(error);
}
diff --git a/platform/ui-runtime/src/actions/mutations.rs b/platform/ui-runtime/src/actions/mutations.rs
index 2cdb0ffba5..6e30538ff3 100644
--- a/platform/ui-runtime/src/actions/mutations.rs
+++ b/platform/ui-runtime/src/actions/mutations.rs
@@ -217,20 +217,6 @@ impl<V: ?Sized + 'static> Application<V> {
id: Uuid,
completion: TaskCompletion,
) -> Result<Option<ActionTask>> {
- diagnostics::record("action_completion", || {
- json!({
- "id": id, "pending": self.actions.len(),
- "kind": match &completion {
- TaskCompletion::Query(_) => "query",
- TaskCompletion::Subscription(_) => "subscription",
- TaskCompletion::Prepared(_) => "prepared",
- TaskCompletion::Mutation(_) => "mutation",
- TaskCompletion::MutationProgress { .. } => "mutation_progress",
- TaskCompletion::Closed(_) => "closed",
- TaskCompletion::Interrupted(_) => "interrupted",
- },
- })
- });
let result = (|| match completion {
TaskCompletion::Subscription(completion) => self.finish_subscription(id, completion),
TaskCompletion::Query(completion) => {
@@ -265,7 +251,6 @@ impl<V: ?Sized + 'static> Application<V> {
}
})();
self.sync_subscription_demand();
- self.wake_queued_actions();
result
}
diff --git a/platform/ui-runtime/src/actions/subscriptions.rs b/platform/ui-runtime/src/actions/subscriptions.rs
index 6265d5ec1b..f974b6e20c 100644
--- a/platform/ui-runtime/src/actions/subscriptions.rs
+++ b/platform/ui-runtime/src/actions/subscriptions.rs
@@ -69,14 +69,6 @@ impl<V: ?Sized + 'static> Application<V> {
if let Some(flow) = &running.stream
&& !self.subscription_wanted(&running.consumer, &running.key, *id)
{
- if !flow.stop.is_cancelled() {
- diagnostics::record("subscription_stop", || {
- json!({
- "id": id, "mount": running.consumer.id(),
- "key": running.key, "method": running.method,
- })
- });
- }
flow.stop.cancel();
}
if let Some(session) = &running.mutation
diff --git a/platform/ui-runtime/src/diagnostics.rs b/platform/ui-runtime/src/diagnostics.rs
deleted file mode 100644
index 580cd6a8cf..0000000000
--- a/platform/ui-runtime/src/diagnostics.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-//! Opt-in UI diagnostics, enabled with `MAKA_UI_TRACE=/absolute/path.jsonl`.
-//! Payloads contain scheduling metadata only, never service inputs or outputs.
-
-use serde_json::{Value, json};
-use std::{
- fs::OpenOptions,
- io::Write,
- sync::{
- OnceLock,
- atomic::{AtomicU64, Ordering},
- mpsc,
- },
- time::{Duration, Instant, SystemTime, UNIX_EPOCH},
-};
-
-struct Trace {
- sender: mpsc::SyncSender<Message>,
- dropped: AtomicU64,
-}
-enum Message {
- Event(Value),
- Flush(mpsc::SyncSender<()>),
-}
-fn trace() -> Option<&'static Trace> {
- static TRACE: OnceLock<Option<Trace>> = OnceLock::new();
- TRACE
- .get_or_init(|| {
- let path = std::env::var_os("MAKA_UI_TRACE")?;
- let mut file = OpenOptions::new()
- .create(true)
- .append(true)
- .open(path)
- .ok()?;
- let (sender, receiver) = mpsc::sync_channel(4096);
- std::thread::Builder::new()
- .name("maka-ui-trace".into())
- .spawn(move || {
- let header = json!({
- "event": "trace_started",
- "pid": std::process::id(),
- "time_us": SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_micros(),
- "fields": {
- "debug_build": cfg!(debug_assertions),
- "version": env!("CARGO_PKG_VERSION"),
- },
- });
- if serde_json::to_writer(&mut file, &header).is_err()
- || file.write_all(b"\n").is_err()
- {
- return;
- }
- while let Ok(message) = receiver.recv() {
- match message {
- Message::Event(value) => {
- if serde_json::to_writer(&mut file, &value).is_err()
- || file.write_all(b"\n").is_err()
- {
- break;
- }
- }
- Message::Flush(done) => {
- let _ = file.flush();
- let _ = done.try_send(());
- }
- }
- }
- })
- .ok()?;
- Some(Trace {
- sender,
- dropped: AtomicU64::new(0),
- })
- })
- .as_ref()
-}
-/// Record lazily without blocking the foreground on filesystem writes.
-/// A saturated diagnostic queue drops records and reports the count next time.
-pub fn record(event: &'static str, fields: impl FnOnce() -> Value) {
- let Some(trace) = trace() else { return };
- let dropped = trace.dropped.swap(0, Ordering::Relaxed);
- let value = json!({
- "time_us": SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_micros(),
- "pid": std::process::id(), "event": event, "dropped": dropped, "fields": fields(),
- });
- if trace.sender.try_send(Message::Event(value)).is_err() {
- trace.dropped.fetch_add(dropped + 1, Ordering::Relaxed);
- }
-}
-/// Give fatal diagnostics a bounded opportunity to reach disk before exit.
-pub fn flush() {
- if let Some(trace) = trace() {
- let (send, receive) = mpsc::sync_channel(1);
- if trace.sender.try_send(Message::Flush(send)).is_ok() {
- let _ = receive.recv_timeout(Duration::from_millis(250));
- }
- }
-}
-/// Start a latency measurement only when diagnostics are enabled.
-pub fn started() -> Option<Instant> {
- trace().map(|_| Instant::now())
-}
-/// Log synchronous work exceeding one 60 Hz frame, including error returns.
-/// Do not wrap waits for input: idle time is not UI work.
-pub struct Span {
- event: &'static str,
- start: Option<Instant>,
-}
-impl Span {
- pub fn new(event: &'static str) -> Self {
- Self {
- event,
- start: trace().map(|_| Instant::now()),
- }
- }
-}
-impl Drop for Span {
- fn drop(&mut self) {
- if let Some(start) = self.start {
- let elapsed = start.elapsed();
- if elapsed >= Duration::from_millis(16) {
- record(self.event, || json!({"elapsed_us": elapsed.as_micros()}));
- }
- }
- }
-}
diff --git a/platform/ui-runtime/src/lib.rs b/platform/ui-runtime/src/lib.rs
index 13dfb57ab0..87676ac912 100644
--- a/platform/ui-runtime/src/lib.rs
+++ b/platform/ui-runtime/src/lib.rs
@@ -26,7 +26,6 @@ pub mod backend;
pub mod budget;
pub mod checkpoint;
mod deployment;
-pub mod diagnostics;
pub mod diff;
pub mod dismiss;
pub mod editor;
diff --git a/platform/ui-runtime/src/tree.rs b/platform/ui-runtime/src/tree.rs
index b7203b7be6..b5fefa57c3 100644
--- a/platform/ui-runtime/src/tree.rs
+++ b/platform/ui-runtime/src/tree.rs
@@ -1115,9 +1115,7 @@ impl<V: ?Sized + 'static> Application<V> {
continue;
}
if let Some(action) = finish.actions.pop_front() {
- if self.actions.len() + update.actions.len() >= actions::MAX_PENDING_ACTIONS {
- self.actions
- .trace_limit(&finish.mount, &action, &update.actions);
+ if self.actions.len() + update.actions.len() >= 64 {
return Err(Error::Invalid("pending action limit".into()));
}
finish.preparing =
@@ -1142,9 +1140,7 @@ impl<V: ?Sized + 'static> Application<V> {
if let Some(old) = old {
finish.demands.insert(action.key.clone(), old.clone());
} else {
- if self.actions.len() + update.actions.len() >= actions::MAX_PENDING_ACTIONS {
- self.actions
- .trace_limit(&finish.mount, &action, &update.actions);
+ if self.actions.len() + update.actions.len() >= 64 {
return Err(Error::Invalid("pending action limit".into()));
}
finish.preparing = Some(self.begin_prepare_action(
diff --git a/plugins/agent/agent-screen/conversation-view.ts b/plugins/agent/agent-screen/conversation-view.ts
index 682f850938..39f3883adb 100644
--- a/plugins/agent/agent-screen/conversation-view.ts
+++ b/plugins/agent/agent-screen/conversation-view.ts
@@ -216,7 +216,6 @@ export function conversationView<N extends Node>(
locale,
target,
);
- props.active = entry === visit;
if (entry !== visit) props.markReadEnabled = false;
return ui.box(
'task-reader-' + entry.key,
diff --git a/plugins/agent/tool-details/index.ts b/plugins/agent/tool-details/index.ts
index db122a964e..a08a9fa652 100644
--- a/plugins/agent/tool-details/index.ts
+++ b/plugins/agent/tool-details/index.ts
@@ -353,7 +353,6 @@ export function toolDetails<N extends Node>(
}
for (const [id, part] of Object.entries(state.parts))
if (
- props.load !== false &&
(props.expanded || compactPart(id)) &&
(!part.page || part.preparing) &&
!part.pending &&
@@ -836,7 +835,6 @@ export function toolDetails<N extends Node>(
};
if (
value.kind === 'preparing' &&
- props.load !== false &&
!part.failed &&
(props.expanded || compactPart(entry![0]))
)
diff --git a/plugins/shared/components.ts b/plugins/shared/components.ts
index c203919c0c..e90c2c6a6f 100644
--- a/plugins/shared/components.ts
+++ b/plugins/shared/components.ts
@@ -711,11 +711,6 @@ export function timeline<N extends Node>(
locale: props.locale,
context: props.toolContext,
tool: message.tool,
- load:
- props.active !== false &&
- (expanded ||
- visible.includes('message-' + message.id) ||
- browsing?.id === message.id),
expanded,
focusOnPointer: !search,
},
diff --git a/plugins/shared/contracts.ts b/plugins/shared/contracts.ts
index 8b89156702..6df1e1e5c5 100644
--- a/plugins/shared/contracts.ts
+++ b/plugins/shared/contracts.ts
@@ -266,7 +266,6 @@ export interface SourcePage {
landing: 'start' | 'end';
}
export interface ToolDetailsProps {
- load?: boolean;
theme: Theme;
locale: string;
context: { sessionId: string; through: number };
@@ -275,7 +274,6 @@ export interface ToolDetailsProps {
focusOnPointer: boolean;
}
export interface TimelineProps {
- active?: boolean;
leadingPrompt?: { source: number; text: string } | null;
activeOutput?: Message | null;
copyRequest: number | null;
@@ -842,18 +840,14 @@ export const imageViewerContract = component(
const filePath = { anyOf: [{ type: 'string', minLength: 1, maxLength: 4096 }, { type: 'null' }] };
export const toolDetailsContract = component(
'maka.agent.ToolDetails',
- object(
- {
- load: { type: 'boolean' },
- theme: { $ref: '#/$defs/Theme' },
- locale,
- context: object({ sessionId: string, through: { type: 'integer', minimum: 1 } }),
- tool: { $ref: '#/$defs/HistoryTool' },
- expanded: { type: 'boolean' },
- focusOnPointer: { type: 'boolean' },
- },
- ['theme', 'locale', 'context', 'tool', 'expanded', 'focusOnPointer'],
- ),
+ object({
+ theme: { $ref: '#/$defs/Theme' },
+ locale,
+ context: object({ sessionId: string, through: { type: 'integer', minimum: 1 } }),
+ tool: { $ref: '#/$defs/HistoryTool' },
+ expanded: { type: 'boolean' },
+ focusOnPointer: { type: 'boolean' },
+ }),
{
context: object({
sessionId: string,
@@ -872,7 +866,6 @@ export const timelineContract = component(
'maka.agent.Timeline',
object(
{
- active: { type: 'boolean' },
leadingPrompt: {
anyOf: [
object({ source: { type: 'integer', minimum: 1 }, text: string }),
diff --git a/products/maka/bundle/tests/timeline.test.mjs b/products/maka/bundle/tests/timeline.test.mjs
index 005c1465a8..91c34f0fbd 100644
--- a/products/maka/bundle/tests/timeline.test.mjs
+++ b/products/maka/bundle/tests/timeline.test.mjs
@@ -786,91 +786,3 @@ test('only an observed latest tail acknowledges the displayed message', () => {
undefined,
);
});
-
-test('tool details follow the visible viewport and suspend in a cached reader', () => {
- const props = {
- theme,
- locale: 'en',
- active: true,
- copyRequest: null,
- browse: null,
- timings: [],
- markReadEnabled: false,
- toolContext: { sessionId: 'session', through: 8 },
- waitingTools: [],
- liveTools: [],
- source: null,
- search: null,
- loading: false,
- older: false,
- newer: false,
- latest: false,
- landing: { sequence: 0, edge: 'end' },
- live: false,
- messages: Array.from({ length: 40 }, (_, i) => ({
- id: 'tool-' + i,
- contentKey: 'tool-' + i,
- role: 'tool_call',
- text: 'Read',
- timestamp: null,
- source: 7,
- image: false,
- continued: false,
- tool: {
- toolUseId: 'read-' + i,
- call: {
- sequence: 7,
- name: 'Read',
- title: null,
- presentation: 'maka.workspace.Files@1/readText',
- ancestorToolUseIds: [],
- pending: false,
- dispatched: true,
- },
- result: { sequence: 8, isError: false, settlement: 'returned', image: false },
- },
- })),
- };
- const find = (node, key) => {
- if (!node || typeof node !== 'object') return undefined;
- if (node.key === key) return node;
- for (const value of Object.values(node)) {
- for (const child of Array.isArray(value) ? value : [value]) {
- const found = find(child, key);
- if (found) return found;
- }
- }
- };
- let out = component.mount(props, {});
- const detail = (index) => find(out.frame.view, 'tool-details-tool-' + index).props;
- assert.equal(detail(0).load, false, 'initial placeholders can present without fetching details');
- out = component.event(
- out.frame.state,
- {
- kind: 'component',
- child: 'reading',
- name: 'observed',
- event: {
- readingRevision: 1,
- selectedChildKey: null,
- pendingCommand: null,
- receipt: null,
- viewport: {
- readingRevision: 1,
- atStart: false,
- atEnd: false,
- visibleChildKeys: ['message-tool-4', 'message-tool-5'],
- },
- },
- },
- props,
- {},
- );
- for (let i = 0; i < 40; i++) assert.equal(detail(i).load, i === 4 || i === 5);
- props.active = false;
- out = component.update(out.frame.state, props, {});
- assert.equal(detail(4).load, false);
- props.active = true;
- out = component.update(out.frame.state, props, {});
- assert.equal(detail(4).load, true);
-});
diff --git a/products/maka/bundle/tests/tool-details.test.mjs b/products/maka/bundle/tests/tool-details.test.mjs
deleted file mode 100644
index e4d97c8073..0000000000
--- a/products/maka/bundle/tests/tool-details.test.mjs
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import assert from 'node:assert/strict';
-import { test } from 'node:test';
-import Ajv from 'ajv/dist/2020.js';
-const { activate } = await import('../dist/packages/appearance/tui.js');
-const theme = activate({ scheme: 'dark' }).Appearance.value;
-const { component, activate: detailsExports } = await import(
- '../dist/packages/tool-details/tui.js'
-);
-
-function props() {
- return {
- theme,
- locale: 'en',
- load: false,
- context: { sessionId: 'session', through: 8 },
- tool: {
- toolUseId: 'read-file',
- call: {
- sequence: 7,
- name: 'Read',
- title: null,
- presentation: 'maka.workspace.Files@1/readText',
- ancestorToolUseIds: [],
- pending: false,
- dispatched: true,
- },
- result: { sequence: 8, isError: false, settlement: 'returned', image: false },
- },
- expanded: false,
- focusOnPointer: true,
- };
-}
-function reply(out, action, value, current) {
- return component.event(
- out.frame.state,
- { kind: 'action', key: action.key, event: { status: 'succeeded', value } },
- current,
- {},
- );
-}
-function page(action, current, text) {
- return {
- kind: 'ready',
- through: 8,
- filePath: null,
- source: {
- sequence: action.input.source.sequence,
- field: action.input.field,
- path: action.input.path,
- format: 'text',
- text,
- offset: 0,
- totalBytes: Buffer.byteLength(text),
- tool: current.tool,
- },
- };
-}
-test('tool summaries wait for demand and reuse completed reads after leaving the viewport', () => {
- const current = props();
- let out = component.mount(current, {});
- assert.equal(out.actions.length, 0);
- current.load = true;
- out = component.update(out.frame.state, current, {});
- const actions = out.actions;
- assert.equal(actions.length, 2);
- current.load = false;
- out = component.update(out.frame.state, current, {});
- assert.equal(out.actions.length, 0);
- for (const action of actions) {
- const text = action.input.field === 'arguments' ? '/workspace/source.rs' : 'completed';
- out = reply(out, action, page(action, current, text), current);
- assert.equal(out.actions.length, 0);
- }
- current.load = true;
- out = component.update(out.frame.state, current, {});
- assert.equal(out.actions.length, 0, 'completed summary does not reload on re-entry');
- current.expanded = true;
- out = component.update(out.frame.state, current, {});
- assert.equal(out.actions.length, 4, 'expanded Read fetches content and line metadata');
-});
-
-test('a preparing source pauses polling when its demand is suspended', () => {
- const current = props();
- current.load = true;
- let out = component.mount(current, {});
- const action = out.actions[0];
- current.load = false;
- out = reply(out, action, { kind: 'preparing', through: 8 }, current);
- assert.equal(out.actions.length, 0);
- current.load = true;
- out = component.update(out.frame.state, current, {});
- assert.equal(out.actions.length, 1);
- assert.deepEqual(out.actions[0].input, action.input);
-});
-
-test('demand is optional for existing tool detail callers', () => {
- const current = props();
- delete current.load;
- const schema = detailsExports().ToolDetails.contract.interface.props;
- const valid = new Ajv({ strict: false }).compile(schema);
- assert.equal(valid(current), true, JSON.stringify(valid.errors));
- assert.equal(component.mount(current, {}).actions.length, 2);
-});
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org