From 9caa94b5620cb60a309d9bfad480c7dd3fe3f6ae Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Thu, 12 Nov 2026 21:31:50 +0800 Subject: [PATCH] docs: reconcile agent guide with resumable runtime behavior --- src/runtime/api/guide.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/runtime/api/guide.rs b/src/runtime/api/guide.rs index 7bd8cac..702f03b 100644 --- a/src/runtime/api/guide.rs +++ b/src/runtime/api/guide.rs @@ -129,15 +129,15 @@ ContainerBlock.insert(position,block) accepts a zero-based position through the Source measure and arrange overrides execute bottom-up and top-down in one read-only VM layout call. Structural Mount links exist before first measurement; bounds queries reject premature geometry. Native base layout uses current occurrence child measurements, and Arrangement must assign every stable child slot exactly once. Layout dependencies changed by lifecycle hooks trigger another pass before publication. Page constraints now use the current logical window viewport. Column/Button propagate cross-axis width and Row propagates cross-axis height; desired sizes are clamped. ui.viewport() provides a managed snapshot whose private input participates in rendering dependencies. Native resize updates are coalesced on the same worker without adding source inputs. Source measurement caches use Mount/method/children/constraints and actual subcall object reads, including returned-result decoding; Mount-reading methods conservatively bypass caching. ui.stats() exposes sourceMeasureHits/sourceMeasureMisses outside read-only rendering. Native TextBlock wrapping now uses the occurrence width in measurement/cache/painting; source measurement/arrangement now repeats until constraints and geometry settle; additional allocation policies remain pending; ScrollBlock scrolling is described below. -Source layout solves repeat in one read-only call until constraints/sizes/bounds are unchanged, reusing one bytecode frame. Source-arranged changed dimensions become exact per-axis constraints for this solve; source container measurements differing from native results constrain content using the same container policy. Selected limits reset on later inputs/resizes. Nonconsecutive repeated states report a cycle. No fixed pass/hop cutoff is used; all passes share the 1000000-instruction/128-frame budget and budget failure is explicit. Measurement clears provisional Mount geometry, and only complete settled frames publish. ui.stats().layoutPasses reports work; source/cache counters include all passes. VM annotation checks distinguish validation-only from content reads, preserving Mount-typed independent measurement caching; actual Mount reads still bypass conservatively. Resumable solving remains pending. See examples/layout-negotiation.endor. +Source layout solves repeat in one read-only call until constraints/sizes/bounds are unchanged, reusing one bytecode frame. Source-arranged changed dimensions become exact per-axis constraints for this solve; source container measurements differing from native results constrain content using the same container policy. Selected limits reset on later inputs/resizes. Nonconsecutive repeated states report a cycle. No fixed pass/hop cutoff is used; all passes share the 1000000-instruction/128-frame budget and budget failure is explicit. Measurement clears provisional Mount geometry, and only complete settled frames publish. ui.stats().layoutPasses reports work; source/cache counters include all passes. VM annotation checks distinguish validation-only from content reads, preserving Mount-typed independent measurement caching; actual Mount reads still bypass conservatively. The solve can pause and resume across worker turns as described below. See examples/layout-negotiation.endor. -TextBlock now wraps words/glyphs at its available width and measures the resulting height. Native and source-base measurement paths use current constraints; native shape/font caches include wrap width. Painting carries allocated width/height, clips glyphs and keeps ancestor scroll clipping. Source text PaintCommand may supply both wrapWidth/wrapHeight (finite nonnegative logical dimensions up to 1000000); neither preserves legacy unwrapped paint, and a partial pair is rejected. Native TextBlock.paint supplies the pair. These are derived draw bounds, not another business object. Management UI text remains unwrapped. Resumable solving and broad fractional-DPI validation remain pending. See examples/wrapped-text.endor. +TextBlock now wraps words/glyphs at its available width and measures the resulting height. Native and source-base measurement paths use current constraints; native shape/font caches include wrap width. Painting carries allocated width/height, clips glyphs and keeps ancestor scroll clipping. Source text PaintCommand may supply both wrapWidth/wrapHeight (finite nonnegative logical dimensions up to 1000000); neither preserves legacy unwrapped paint, and a partial pair is rejected. Native TextBlock.paint supplies the pair. These are derived draw bounds, not another business object. Management UI text remains unwrapped. Layout and paint can pause and resume; broad fractional-DPI validation remains pending. See examples/wrapped-text.endor. Source paint overrides execute in the actual frame pipeline under a read-only heap policy. Source hitTest overrides similarly refine native pointer targets in VM paint order; Point.inside(bounds) and Point.inCircle(cx, cy, radius) provide primitive shape queries. Managed DisplayList/ObjectList/PaintCommand and Constraints/Bounds/Point results are ordinary runtime objects. ScrollBlock(width,height,child) is a ContainerBlock with managed viewport dimensions and independent per-Mount offsets. mount.scrollTo(x,y), scrollBy(dx,dy), scrollPosition() and scrollIntoView() operate on actual occurrences. Position snapshots expose x/y/maxX/maxY from committed layout; relative requests accumulate and offsets clamp after content/viewport changes. Detached Mounts and read-only rendering mutations are rejected. scrollIntoView and requestFocus adjust nested scrollable ancestors. Native/source paint, focus outlines, hit testing and pointer selection share translated geometry and ancestor clips. Remounting starts a new offset. Paint x/y may be signed after scrolling; dimensions/radii remain nonnegative and coordinate budgets still apply. See examples/scroll.endor. Wheel chooses one eligible ancestor (not per-axis split); native scrollbar dragging, touch inertia and PageUp/PageDown remain pending. -Native wheel POST /events uses kind scroll with the normal window/session/layout_version, a scrollable mount ID, point=[x,y], delta=[dx,dy], and no press_point. Deltas are signed finite logical pixels within +/-1000000; positive moves toward the end of content. Other kinds must omit delta. Native Shift+wheel maps to horizontal. The host checks the active committed frame, target attachment and clipped viewport before invoking Mount.scrollBy. These fields are transport metadata; source code uses the Endor methods above. +Native wheel POST /events uses kind scroll with the normal window/session/layout_version, a scrollable mount ID, point=[x,y], delta=[dx,dy], and no press_point. Deltas are signed finite logical pixels within +/-1000000; positive moves toward the end of content. Ordinary pointer clicks omit delta; camera gestures use their own delta contracts described above and below. Native Shift+wheel maps to horizontal. The host checks the active committed frame, target attachment and clipped viewport before invoking Mount.scrollBy. These fields are transport metadata; source code uses the Endor methods above. ui.after(owner,key,milliseconds,callback) returns Task. The zero-argument callback runs on the owning VM worker after at least the integer 0..86400000 ms delay and must return Unit. Idle polling can add up to 100 ms; execution load may delay delivery further. Task.cancel/active/status expose cancellation, eligibility and pending/running/completed/cancelled/failed state. One owner/key replaces the prior pending task. Owner scopes and callback captures are managed strong edges; scheduler and owner links are weak. ui is an application owner. Hidden live objects continue; Page close cancels its and its Mount tasks, even close/reopen within one input. Page movement preserves Page tasks but replaces Mounts. Window close cancels its Pages/tasks; reset/DELETE/program replacement discard old scheduler/VM and generation checks reject stale publication. Failed PUT preserves old tasks. Callback failure stops the domain and cancels remaining tasks; earlier writes do not roll back. Read-only rendering cannot schedule/cancel. Limits: 64 pending per owner, 1024 per runtime, 64 delivered per poll, 128 frames/100000 instructions per callback. Providers support timers, asynchronous text-file reads and HTTP(S) text GET; await is supported in ui.spawn calls. @@ -167,20 +167,20 @@ Finite box layout: Overlay(padding, gap, children...) stacks children (gap is un Independent 2D scenes are now implemented: Scene2D(children...) stores world content at child x/y; CanvasViewport(width,height,scene) is a finite layout box with its own Camera2D(). Camera2D(x=0,y=0,zoom=1) fields are ordinary managed attributes; zoom is 0.01–100. Sharing a scene does not share its camera; assigning second.camera=first.camera does. Scene content may contain layout containers and nested finite viewports; Overlay siblings stay fixed. Mount.fitView() fits finite direct scene-child bounds using that occurrence's allocation. Native wheel/pinch/space or middle drag targets the innermost viewport; drag retains its initial target. viewport-pan/viewport-zoom carry the viewport Mount and current frame identities, with signed logical page coordinates. Pan delta is measured before the viewport projection; zoom delta is [factor,0] with an in-content pointer anchor. Worker validation rejects stale/invalid input before invoking the VM. Draw and hit transforms compose consistently while layout geometry and measurement caches remain in world coordinates. ScrollView is a nominal ScrollBlock subtype. The default page uses these APIs; old Page.mode/panX/panY/zoom remain for compatibility. 3D support is described below. See examples/scene-viewports.endor. -3D basics are now implemented: Scene3D/Group3D, Box3D, immutable Geometry3D made from Vec3/Triangle3D ObjectLists, Mesh3D, DirectionalLight, and finite Viewport3D. PerspectiveCamera and OrthographicCamera expose x/y/z, targetX/Y/Z, upX/Y/Z, near/far, fov or height; viewport aspect is derived separately. New viewports have independent cameras and OrbitController; explicit camera assignment shares a camera. A controller can be replaced with an Endor object defining update(camera,gesture), or disabled by assigning "none". Native scene-orbit/scene-pan/scene-zoom events carry viewport Mount/frame identities. Mesh/group translate and rotate use native arithmetic; camera.translate moves position and target. This avoids the fixed Aster version's unsupported numeric arithmetic expressions. Mount.pick3D(x,y) returns nullable RayHit3D with managed mesh and world intersection data; pointer click Event.hit carries the same result. Rendering uses frustum clipping and a depth buffer in a bounded cached surface. World-space UI composition is described below; visibility and resource work is continuing. See examples/scene3d.endor and docs/scene-architecture.md. +3D basics are now implemented: Scene3D/Group3D, Box3D, immutable Geometry3D made from Vec3/Triangle3D ObjectLists, Mesh3D, DirectionalLight, and finite Viewport3D. PerspectiveCamera and OrthographicCamera expose x/y/z, targetX/Y/Z, upX/Y/Z, near/far, fov or height; viewport aspect is derived separately. New viewports have independent cameras and OrbitController; explicit camera assignment shares a camera. A controller can be replaced with an Endor object defining update(camera,gesture), or disabled by assigning "none". Native scene-orbit/scene-pan/scene-zoom events carry viewport Mount/frame identities. Mesh/group translate and rotate use native arithmetic; camera.translate moves position and target. The same scene attributes remain accessible to ordinary Endor code. Mount.pick3D(x,y) returns nullable RayHit3D with managed mesh and world intersection data; pointer click Event.hit carries the same result. Rendering uses frustum clipping and a depth buffer in a bounded cached surface. World-space UI composition is described below; visibility and resource work is continuing. See examples/scene3d.endor and docs/scene-architecture.md. World UI is implemented with Panel3D(worldWidth,worldHeight,content). uiWidth/uiHeight provide an independent finite UI layout resolution (integer 1–2048); background is opaque. Ordinary Endor measure/arrange/paint/hitTest, shared objects, distinct Mounts and per-occurrence scroll state remain active. Panel transforms place a perspective-correct UI texture in the world. A visible ray hit maps through the plane to UI coordinates, then through any nested 2D camera; opaque geometry blocks UI input. Panel UI may contain another Viewport3D and further panels (maximum 8 world UI levels). Mount.toView(localX,localY) converts component-local coordinates to the page view; toLocal(viewX,viewY) performs the reverse when visible. Camera transport from a world UI viewport requires a pointer so pan/scroll increments can use the plane's projective mapping. Its scroll and camera gestures retain one target instead of driving both parent and child. Cyclic UI/scene composition and nested pixel allocations are bounded. See examples/world-ui.endor. Block.flex is a nonnegative weight (default 0). Native containers reserve fixed measured children, padding and gaps, then share remaining allocated main-axis space between positive weights with zero basis. Actual child allocation feeds remeasurement. It does not request parent expansion. Further controls are described below. -Flex main-axis policy: flexBasis is "auto" by default (zero basis when flex > 0, otherwise the first measured size of this occurrence in the current solve); a number supplies an explicit basis. flexMin defaults to 0, flexMax to 1000000, and flexShrink to 0. All numbers are finite/nonnegative and at most 1000000; flexMin must not exceed flexMax. The parent clamps bases to min/max, distributes positive space by flex, and redistributes space left after a maximum is reached. Deficits shrink by flexShrink times the original basis, freezing at minima. A fixed child can opt into shrink without growth. Unresolvable minimum overflow is preserved, and remaining positive space follows mainAlign. The policy applies to width in a Row and height in Column-like containers; these are not independent width/height bounds. Source arrange overrides remain authoritative. Preferred sizes are derived per occurrence and reset for each new solve, never shared through Block state. See examples/flex-constraints.endor. Layout continuation remains unfinished. +Flex main-axis policy: flexBasis is "auto" by default (zero basis when flex > 0, otherwise the first measured size of this occurrence in the current solve); a number supplies an explicit basis. flexMin defaults to 0, flexMax to 1000000, and flexShrink to 0. All numbers are finite/nonnegative and at most 1000000; flexMin must not exceed flexMax. The parent clamps bases to min/max, distributes positive space by flex, and redistributes space left after a maximum is reached. Deficits shrink by flexShrink times the original basis, freezing at minima. A fixed child can opt into shrink without growth. Unresolvable minimum overflow is preserved, and remaining positive space follows mainAlign. The policy applies to width in a Row and height in Column-like containers; these are not independent width/height bounds. Source arrange overrides remain authoritative. Preferred sizes are derived per occurrence and reset for each new solve, never shared through Block state. See examples/flex-constraints.endor. Layout continuation is implemented through the saved jobs described below. When an occurrence's cross-axis constraint changes during settling, its preferred main-axis size is invalidated and remeasured. Any obsolete native flex allocation on that axis is removed for the replacement measurement, so narrowing a nested Column can increase wrapped text height. Source arrange allocations remain explicit. Preferred-size presence and values participate in convergence/cycle detection. -The source layout driver now uses Aster's resumable read-only call API with a soft quantum of 256 instructions. Pauses retain the same VM frames, operand/local roots, return contracts and read tracking; total instruction/frame limits still apply across slices. ui.stats().layoutSlices counts slices in the most recent solve (native fast path: 1). The worker currently resumes Pending immediately after yielding its OS time slice. Parking the solve across worker turns, scheduling other work and handling newer inputs before completion are not implemented yet. Native callbacks are not preempted. Only the completed settled frame is published. +The source layout driver now uses Aster's resumable read-only call API with a soft quantum of 256 instructions. Pauses retain the same VM frames, operand/local roots, return contracts and read tracking; total instruction/frame limits still apply across slices. ui.stats().layoutSlices counts slices in the most recent solve (native fast path: 1). The worker retains Pending across turns, checks shutdown and newer viewport targets between quanta, and advances the saved continuation without replaying source. Later ordinary mutations wait for the current refresh to settle. Native callbacks are not preempted. Only the completed settled frame is published. source_layout::start now returns either Complete(Draft) or Pending(Work). The host-owned Work retains the unpublished frame, derived runtime/cache snapshots, convergence plan and prepare root without borrowing caller frame/cache variables. Work::resume consumes the saved job and advances the same VM continuation; Work::cancel closes that continuation and releases the prepare root without merging a draft. A second start is rejected while any read-only execution is active. Every pending Work must be resumed or explicitly cancelled with its owning VM; dropping it is not cancellation. Synchronous ReplSession operations drain the same saved refresh state. The worker parks refresh jobs; cancellation restores still-pending Mount preparation. @@ -189,13 +189,13 @@ source_layout::start now returns either Complete(Draft) or Pending(Work). The ho UiBridge::begin_layout_frame/resume_layout_frame now expose PendingLayout around the source job. This bridge-level continuation also owns its target window, page snapshot, tab projection, viewport restoration and retained lifecycle hook root. Only the Complete branch projects scroll/focus, captures dependency versions, merges derived caches, commits Mount geometry and runs deferred hooks. Resume restores the saved window context, and failure releases hook roots. ReplSession uses this protocol through its saved refresh state. Viewport refresh supports cross-worker-turn scheduling and prepared-Mount rollback; callers must not drop a pending bridge layout as a substitute for cancellation. -Viewport refresh now advances through ReplSession's saved refresh state on worker idle turns. A turn advances one traversal stage or a 256-instruction layout quantum; a pending job shortens the next worker wait to 1 ms. Tasks/window commands and document publication wait for complete refresh. ui.stats().refreshYields reports cooperative idle returns. Mutating requests wait across worker turns for the existing job, so they cannot run inside its read-only scope. Mutating requests are serialized; newer viewport targets coalesce by cancelling unpublished layout. Initial source execution remains atomic; initial layout parks across worker turns. Explicit REPL/event refresh parks across worker turns. Idle tasks deliver at most one callback per turn and park subsequent layout; callbacks and individual native nodes remain atomic. Large intrinsic trees use the sliced driver; small intrinsic trees keep the fast path. +Viewport refresh now advances through ReplSession's saved refresh state on worker idle turns. A turn advances one traversal stage or a 256-instruction layout quantum; a pending job or runnable cooperative Task uses a zero-duration queue wait; idle or external waits use up to 100 ms. Tasks/window commands and document publication wait for complete refresh. ui.stats().refreshYields reports cooperative idle returns. Mutating requests wait across worker turns for the existing job, so they cannot run inside its read-only scope. Mutating requests are serialized; newer viewport targets coalesce by cancelling unpublished layout. Initial source execution remains atomic; initial layout parks across worker turns. Explicit REPL/event refresh parks across worker turns. Idle tasks deliver at most one callback per turn and park subsequent layout; callbacks and individual native nodes remain atomic. Large intrinsic trees use the sliced driver; small intrinsic trees keep the fast path. Runtime mutation entry points reject direct calls while refresh work is pending, before executing source or changing metadata; task polling defers. The API worker drains the saved refresh before accepting those mutations, keeping the runtime usable. Incremental refresh keeps the prior complete document map until a stable pass, including across window traversal boundaries where no VM read-only frame is currently active. -New viewport targets now coalesce pending refresh work. The runtime cancels only an unfinished bridge layout, restores its Mount checkpoint, preserves completed per-window drafts/callback effects and restarts traversal with the latest viewport map. refreshRestarts counts target replacements; layoutCancels counts replacements that actually closed a pending VM layout. Unchanged targets resume the same job. A new external target resets its convergence budget; internal settling does not. Ordinary source/event mutations still serialize behind refresh rather than roll back application effects. +New viewport targets now coalesce pending refresh work. The runtime cancels unfinished layout or paint. Layout cancellation restores its Mount checkpoint; paint cancellation discards partial commands while retaining completed layout and lifecycle effects. Completed per-window drafts/callback effects are preserved while traversal restarts with the latest viewport map. refreshRestarts counts target replacements; layoutCancels counts replacements that actually closed a pending VM layout. Unchanged targets resume the same job. A new external target resets its convergence budget; internal settling does not. Ordinary source/event mutations still serialize behind refresh rather than roll back application effects. Cancellation checkpoints own explicit VM roots until restored or released. Initial presentations may have no mount dictionary; cancellation restores that absence too. Saved metadata values retain object-valued attributes. A pending refresh can restart while keeping already completed window drafts; no onMount/onUnmount callback is replayed merely because the viewport target changed. Mount-owned tasks are cancelled immediately before an actual onUnmount callback, never during abandoned preparation. @@ -242,8 +242,8 @@ Defaults above describe the host API; do not write keyword arguments in Endor. Colors use #RRGGBB or #RRGGBBAA. Dimensions are finite numbers in 0..1000000. Use newlines between complete top-level statements. Multiline calls, function bodies and expression continuations are passed to Aster as complete inputs. -Limits: 1 MiB source, 4096 constructed nodes, 64 pages, 64 UI levels, 10000 expanded -layout nodes, 16 KiB per text, 128 VM frames. Script statements each receive an +Limits: 1 MiB source, 4096 constructed nodes, 64 pages, 10000 expanded +layout nodes, 16 KiB per text, 128 VM frames. Source AST nesting has a separate 64-level limit. Script statements each receive an equal share of a total 1000000-instruction allowance. Older module initialization and main execution each retain their own 1000000-instruction allowance. @@ -256,8 +256,8 @@ and font fallback; Chinese needs an installed CJK font. Size 1 is 14 logical pix with 20-pixel line height; larger sizes multiply both. Native Block methods include setText/setColor, container append/insert/remove/replace/move, Page open/close/navigation, and code-driven button.onClick/click. ui.state and ui.textOf provide explicit text -binding. Mouse routing, tab chrome/multiple windows and incremental layout remain -in progress; process isolation is future work. Console retains objects +binding. Mouse routing, sidebar page management, multiple windows and incremental +layout/paint are implemented; broader platform validation and process isolation remain future work. Console retains objects only in its explicitly resettable in-memory VM. It is not an untrusted-code sandbox. "##, // 路由指向当前唯一活 namespace,不由请求查询串或任意模板文件决定。