@inhuman.tools/quickdraw 0.0.0-bootstrap.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +7 -0
- package/README.md +102 -1
- package/aurora.vert +168 -0
- package/dist/bench/host.d.ts +62 -0
- package/dist/bench/host.d.ts.map +1 -0
- package/dist/bench/host.js +77 -0
- package/dist/bench/host.js.map +1 -0
- package/dist/bench/main.d.ts +6 -0
- package/dist/bench/main.d.ts.map +1 -0
- package/dist/bench/main.js +118 -0
- package/dist/bench/main.js.map +1 -0
- package/dist/bench/model.d.ts +36 -0
- package/dist/bench/model.d.ts.map +1 -0
- package/dist/bench/model.js +112 -0
- package/dist/bench/model.js.map +1 -0
- package/dist/bench/tea.d.ts +14 -0
- package/dist/bench/tea.d.ts.map +1 -0
- package/dist/bench/tea.js +6 -0
- package/dist/bench/tea.js.map +1 -0
- package/dist/bench/view.d.ts +9 -0
- package/dist/bench/view.d.ts.map +1 -0
- package/dist/bench/view.js +88 -0
- package/dist/bench/view.js.map +1 -0
- package/dist/bench/worker.d.ts +6 -0
- package/dist/bench/worker.d.ts.map +1 -0
- package/dist/bench/worker.js +46 -0
- package/dist/bench/worker.js.map +1 -0
- package/dist/buffer/bgfx-mark.d.ts +32 -0
- package/dist/buffer/bgfx-mark.d.ts.map +1 -0
- package/dist/buffer/bgfx-mark.js +43 -0
- package/dist/buffer/bgfx-mark.js.map +1 -0
- package/dist/buffer/index.d.ts +191 -0
- package/dist/buffer/index.d.ts.map +1 -0
- package/dist/buffer/index.js +353 -0
- package/dist/buffer/index.js.map +1 -0
- package/dist/capabilities/env.d.ts +23 -0
- package/dist/capabilities/env.d.ts.map +1 -0
- package/dist/capabilities/env.js +35 -0
- package/dist/capabilities/env.js.map +1 -0
- package/dist/capabilities/index.d.ts +13 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +13 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/parse.d.ts +41 -0
- package/dist/capabilities/parse.d.ts.map +1 -0
- package/dist/capabilities/parse.js +86 -0
- package/dist/capabilities/parse.js.map +1 -0
- package/dist/capabilities/probe.d.ts +37 -0
- package/dist/capabilities/probe.d.ts.map +1 -0
- package/dist/capabilities/probe.js +75 -0
- package/dist/capabilities/probe.js.map +1 -0
- package/dist/capabilities/sequences.d.ts +33 -0
- package/dist/capabilities/sequences.d.ts.map +1 -0
- package/dist/capabilities/sequences.js +36 -0
- package/dist/capabilities/sequences.js.map +1 -0
- package/dist/capabilities/types.d.ts +25 -0
- package/dist/capabilities/types.d.ts.map +1 -0
- package/dist/capabilities/types.js +5 -0
- package/dist/capabilities/types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/input/decode.d.ts +167 -0
- package/dist/input/decode.d.ts.map +1 -0
- package/dist/input/decode.js +825 -0
- package/dist/input/decode.js.map +1 -0
- package/dist/input/draft.d.ts +85 -0
- package/dist/input/draft.d.ts.map +1 -0
- package/dist/input/draft.js +276 -0
- package/dist/input/draft.js.map +1 -0
- package/dist/input/index.d.ts +3 -0
- package/dist/input/index.d.ts.map +1 -0
- package/dist/input/index.js +3 -0
- package/dist/input/index.js.map +1 -0
- package/dist/input/prompt-blocks.d.ts +58 -0
- package/dist/input/prompt-blocks.d.ts.map +1 -0
- package/dist/input/prompt-blocks.js +48 -0
- package/dist/input/prompt-blocks.js.map +1 -0
- package/dist/input/rich-draft.d.ts +46 -0
- package/dist/input/rich-draft.d.ts.map +1 -0
- package/dist/input/rich-draft.js +391 -0
- package/dist/input/rich-draft.js.map +1 -0
- package/dist/input/rich-hit.d.ts +17 -0
- package/dist/input/rich-hit.d.ts.map +1 -0
- package/dist/input/rich-hit.js +89 -0
- package/dist/input/rich-hit.js.map +1 -0
- package/dist/input/rich-intake.d.ts +16 -0
- package/dist/input/rich-intake.d.ts.map +1 -0
- package/dist/input/rich-intake.js +22 -0
- package/dist/input/rich-intake.js.map +1 -0
- package/dist/input/rich-paint.d.ts +13 -0
- package/dist/input/rich-paint.d.ts.map +1 -0
- package/dist/input/rich-paint.js +18 -0
- package/dist/input/rich-paint.js.map +1 -0
- package/dist/internals/bgfx/index.d.ts +17 -0
- package/dist/internals/bgfx/index.d.ts.map +1 -0
- package/dist/internals/bgfx/index.js +17 -0
- package/dist/internals/bgfx/index.js.map +1 -0
- package/dist/internals/bgfx/materialize.d.ts +31 -0
- package/dist/internals/bgfx/materialize.d.ts.map +1 -0
- package/dist/internals/bgfx/materialize.js +117 -0
- package/dist/internals/bgfx/materialize.js.map +1 -0
- package/dist/internals/bgfx/opacity.d.ts +37 -0
- package/dist/internals/bgfx/opacity.d.ts.map +1 -0
- package/dist/internals/bgfx/opacity.js +76 -0
- package/dist/internals/bgfx/opacity.js.map +1 -0
- package/dist/internals/bgfx/registry.d.ts +13 -0
- package/dist/internals/bgfx/registry.d.ts.map +1 -0
- package/dist/internals/bgfx/registry.js +24 -0
- package/dist/internals/bgfx/registry.js.map +1 -0
- package/dist/internals/bgfx/sab-layout.d.ts +61 -0
- package/dist/internals/bgfx/sab-layout.d.ts.map +1 -0
- package/dist/internals/bgfx/sab-layout.js +107 -0
- package/dist/internals/bgfx/sab-layout.js.map +1 -0
- package/dist/internals/bgfx/shader-frame-host.d.ts +22 -0
- package/dist/internals/bgfx/shader-frame-host.d.ts.map +1 -0
- package/dist/internals/bgfx/shader-frame-host.js +165 -0
- package/dist/internals/bgfx/shader-frame-host.js.map +1 -0
- package/dist/internals/bgfx/shadertoy-catalog.d.ts +41 -0
- package/dist/internals/bgfx/shadertoy-catalog.d.ts.map +1 -0
- package/dist/internals/bgfx/shadertoy-catalog.js +200 -0
- package/dist/internals/bgfx/shadertoy-catalog.js.map +1 -0
- package/dist/internals/bgfx/shadertoy-gl.d.ts +38 -0
- package/dist/internals/bgfx/shadertoy-gl.d.ts.map +1 -0
- package/dist/internals/bgfx/shadertoy-gl.js +283 -0
- package/dist/internals/bgfx/shadertoy-gl.js.map +1 -0
- package/dist/internals/bgfx/types.d.ts +51 -0
- package/dist/internals/bgfx/types.d.ts.map +1 -0
- package/dist/internals/bgfx/types.js +14 -0
- package/dist/internals/bgfx/types.js.map +1 -0
- package/dist/internals/bgfx-worker.d.ts +6 -0
- package/dist/internals/bgfx-worker.d.ts.map +1 -0
- package/dist/internals/bgfx-worker.js +131 -0
- package/dist/internals/bgfx-worker.js.map +1 -0
- package/dist/layout/birth.d.ts +77 -0
- package/dist/layout/birth.d.ts.map +1 -0
- package/dist/layout/birth.js +148 -0
- package/dist/layout/birth.js.map +1 -0
- package/dist/layout/children.d.ts +5 -0
- package/dist/layout/children.d.ts.map +1 -0
- package/dist/layout/children.js +19 -0
- package/dist/layout/children.js.map +1 -0
- package/dist/layout/chrome_frame.d.ts +254 -0
- package/dist/layout/chrome_frame.d.ts.map +1 -0
- package/dist/layout/chrome_frame.js +1080 -0
- package/dist/layout/chrome_frame.js.map +1 -0
- package/dist/layout/chrome_mask.d.ts +40 -0
- package/dist/layout/chrome_mask.d.ts.map +1 -0
- package/dist/layout/chrome_mask.js +150 -0
- package/dist/layout/chrome_mask.js.map +1 -0
- package/dist/layout/engine.d.ts +49 -0
- package/dist/layout/engine.d.ts.map +1 -0
- package/dist/layout/engine.js +1269 -0
- package/dist/layout/engine.js.map +1 -0
- package/dist/layout/flex.d.ts +36 -0
- package/dist/layout/flex.d.ts.map +1 -0
- package/dist/layout/flex.js +111 -0
- package/dist/layout/flex.js.map +1 -0
- package/dist/layout/grid.d.ts +36 -0
- package/dist/layout/grid.d.ts.map +1 -0
- package/dist/layout/grid.js +126 -0
- package/dist/layout/grid.js.map +1 -0
- package/dist/layout/index.d.ts +30 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +30 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/layout/indication.d.ts +34 -0
- package/dist/layout/indication.d.ts.map +1 -0
- package/dist/layout/indication.js +68 -0
- package/dist/layout/indication.js.map +1 -0
- package/dist/layout/length.d.ts +18 -0
- package/dist/layout/length.d.ts.map +1 -0
- package/dist/layout/length.js +24 -0
- package/dist/layout/length.js.map +1 -0
- package/dist/layout/live_paint.d.ts +145 -0
- package/dist/layout/live_paint.d.ts.map +1 -0
- package/dist/layout/live_paint.js +255 -0
- package/dist/layout/live_paint.js.map +1 -0
- package/dist/layout/markdown.d.ts +22 -0
- package/dist/layout/markdown.d.ts.map +1 -0
- package/dist/layout/markdown.js +167 -0
- package/dist/layout/markdown.js.map +1 -0
- package/dist/layout/measure.d.ts +46 -0
- package/dist/layout/measure.d.ts.map +1 -0
- package/dist/layout/measure.js +307 -0
- package/dist/layout/measure.js.map +1 -0
- package/dist/layout/paint-hotkey.d.ts +45 -0
- package/dist/layout/paint-hotkey.d.ts.map +1 -0
- package/dist/layout/paint-hotkey.js +152 -0
- package/dist/layout/paint-hotkey.js.map +1 -0
- package/dist/layout/paint.d.ts +25 -0
- package/dist/layout/paint.d.ts.map +1 -0
- package/dist/layout/paint.js +189 -0
- package/dist/layout/paint.js.map +1 -0
- package/dist/layout/paint_theme.d.ts +85 -0
- package/dist/layout/paint_theme.d.ts.map +1 -0
- package/dist/layout/paint_theme.js +76 -0
- package/dist/layout/paint_theme.js.map +1 -0
- package/dist/layout/progress.d.ts +31 -0
- package/dist/layout/progress.d.ts.map +1 -0
- package/dist/layout/progress.js +83 -0
- package/dist/layout/progress.js.map +1 -0
- package/dist/layout/selection.d.ts +136 -0
- package/dist/layout/selection.d.ts.map +1 -0
- package/dist/layout/selection.js +312 -0
- package/dist/layout/selection.js.map +1 -0
- package/dist/layout/selection_gesture.d.ts +88 -0
- package/dist/layout/selection_gesture.d.ts.map +1 -0
- package/dist/layout/selection_gesture.js +239 -0
- package/dist/layout/selection_gesture.js.map +1 -0
- package/dist/layout/selection_paint.d.ts +47 -0
- package/dist/layout/selection_paint.d.ts.map +1 -0
- package/dist/layout/selection_paint.js +320 -0
- package/dist/layout/selection_paint.js.map +1 -0
- package/dist/layout/serialize.d.ts +49 -0
- package/dist/layout/serialize.d.ts.map +1 -0
- package/dist/layout/serialize.js +437 -0
- package/dist/layout/serialize.js.map +1 -0
- package/dist/layout/squeeze.d.ts +58 -0
- package/dist/layout/squeeze.d.ts.map +1 -0
- package/dist/layout/squeeze.js +239 -0
- package/dist/layout/squeeze.js.map +1 -0
- package/dist/layout/text.d.ts +44 -0
- package/dist/layout/text.d.ts.map +1 -0
- package/dist/layout/text.js +230 -0
- package/dist/layout/text.js.map +1 -0
- package/dist/layout/theme_store.d.ts +21 -0
- package/dist/layout/theme_store.d.ts.map +1 -0
- package/dist/layout/theme_store.js +133 -0
- package/dist/layout/theme_store.js.map +1 -0
- package/dist/layout/types.d.ts +564 -0
- package/dist/layout/types.d.ts.map +1 -0
- package/dist/layout/types.js +2 -0
- package/dist/layout/types.js.map +1 -0
- package/dist/layout/visual_draft.d.ts +60 -0
- package/dist/layout/visual_draft.d.ts.map +1 -0
- package/dist/layout/visual_draft.js +199 -0
- package/dist/layout/visual_draft.js.map +1 -0
- package/dist/lexicon/color.d.ts +22 -0
- package/dist/lexicon/color.d.ts.map +1 -0
- package/dist/lexicon/color.js +6 -0
- package/dist/lexicon/color.js.map +1 -0
- package/dist/lexicon/index.d.ts +15 -0
- package/dist/lexicon/index.d.ts.map +1 -0
- package/dist/lexicon/index.js +15 -0
- package/dist/lexicon/index.js.map +1 -0
- package/dist/lexicon/prominence.d.ts +46 -0
- package/dist/lexicon/prominence.d.ts.map +1 -0
- package/dist/lexicon/prominence.js +73 -0
- package/dist/lexicon/prominence.js.map +1 -0
- package/dist/lexicon/quarantine.d.ts +21 -0
- package/dist/lexicon/quarantine.d.ts.map +1 -0
- package/dist/lexicon/quarantine.js +15 -0
- package/dist/lexicon/quarantine.js.map +1 -0
- package/dist/lexicon/roles.d.ts +37 -0
- package/dist/lexicon/roles.d.ts.map +1 -0
- package/dist/lexicon/roles.js +52 -0
- package/dist/lexicon/roles.js.map +1 -0
- package/dist/lexicon/tiers.d.ts +18 -0
- package/dist/lexicon/tiers.d.ts.map +1 -0
- package/dist/lexicon/tiers.js +17 -0
- package/dist/lexicon/tiers.js.map +1 -0
- package/dist/measure/index.d.ts +43 -0
- package/dist/measure/index.d.ts.map +1 -0
- package/dist/measure/index.js +120 -0
- package/dist/measure/index.js.map +1 -0
- package/dist/render.d.ts +72 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +164 -0
- package/dist/render.js.map +1 -0
- package/dist/runtime/agents_keys.d.ts +8 -0
- package/dist/runtime/agents_keys.d.ts.map +1 -0
- package/dist/runtime/agents_keys.js +24 -0
- package/dist/runtime/agents_keys.js.map +1 -0
- package/dist/runtime/agents_peek.d.ts +44 -0
- package/dist/runtime/agents_peek.d.ts.map +1 -0
- package/dist/runtime/agents_peek.js +96 -0
- package/dist/runtime/agents_peek.js.map +1 -0
- package/dist/runtime/agents_preview.d.ts +27 -0
- package/dist/runtime/agents_preview.d.ts.map +1 -0
- package/dist/runtime/agents_preview.js +122 -0
- package/dist/runtime/agents_preview.js.map +1 -0
- package/dist/runtime/box_bus.d.ts +144 -0
- package/dist/runtime/box_bus.d.ts.map +1 -0
- package/dist/runtime/box_bus.js +74 -0
- package/dist/runtime/box_bus.js.map +1 -0
- package/dist/runtime/clipboard-image.d.ts +20 -0
- package/dist/runtime/clipboard-image.d.ts.map +1 -0
- package/dist/runtime/clipboard-image.js +62 -0
- package/dist/runtime/clipboard-image.js.map +1 -0
- package/dist/runtime/copy_toast.d.ts +46 -0
- package/dist/runtime/copy_toast.d.ts.map +1 -0
- package/dist/runtime/copy_toast.js +117 -0
- package/dist/runtime/copy_toast.js.map +1 -0
- package/dist/runtime/echo.d.ts +41 -0
- package/dist/runtime/echo.d.ts.map +1 -0
- package/dist/runtime/echo.js +112 -0
- package/dist/runtime/echo.js.map +1 -0
- package/dist/runtime/frame.d.ts +79 -0
- package/dist/runtime/frame.d.ts.map +1 -0
- package/dist/runtime/frame.js +258 -0
- package/dist/runtime/frame.js.map +1 -0
- package/dist/runtime/index.d.ts +16 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +16 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/key_buffer.d.ts +19 -0
- package/dist/runtime/key_buffer.d.ts.map +1 -0
- package/dist/runtime/key_buffer.js +24 -0
- package/dist/runtime/key_buffer.js.map +1 -0
- package/dist/runtime/keys.d.ts +66 -0
- package/dist/runtime/keys.d.ts.map +1 -0
- package/dist/runtime/keys.js +220 -0
- package/dist/runtime/keys.js.map +1 -0
- package/dist/runtime/paint_snapshot.d.ts +9 -0
- package/dist/runtime/paint_snapshot.d.ts.map +1 -0
- package/dist/runtime/paint_snapshot.js +8 -0
- package/dist/runtime/paint_snapshot.js.map +1 -0
- package/dist/runtime/paint_source.d.ts +266 -0
- package/dist/runtime/paint_source.d.ts.map +1 -0
- package/dist/runtime/paint_source.js +532 -0
- package/dist/runtime/paint_source.js.map +1 -0
- package/dist/runtime/present_loop.d.ts +11 -0
- package/dist/runtime/present_loop.d.ts.map +1 -0
- package/dist/runtime/present_loop.js +46 -0
- package/dist/runtime/present_loop.js.map +1 -0
- package/dist/runtime/present_profile.d.ts +58 -0
- package/dist/runtime/present_profile.d.ts.map +1 -0
- package/dist/runtime/present_profile.js +130 -0
- package/dist/runtime/present_profile.js.map +1 -0
- package/dist/runtime/raster.d.ts +24 -0
- package/dist/runtime/raster.d.ts.map +1 -0
- package/dist/runtime/raster.js +43 -0
- package/dist/runtime/raster.js.map +1 -0
- package/dist/runtime/renderer.d.ts +151 -0
- package/dist/runtime/renderer.d.ts.map +1 -0
- package/dist/runtime/renderer.js +1607 -0
- package/dist/runtime/renderer.js.map +1 -0
- package/dist/runtime/screenshot.d.ts +9 -0
- package/dist/runtime/screenshot.d.ts.map +1 -0
- package/dist/runtime/screenshot.js +111 -0
- package/dist/runtime/screenshot.js.map +1 -0
- package/dist/runtime/selection_host.d.ts +41 -0
- package/dist/runtime/selection_host.d.ts.map +1 -0
- package/dist/runtime/selection_host.js +190 -0
- package/dist/runtime/selection_host.js.map +1 -0
- package/dist/runtime/stderr_hold.d.ts +16 -0
- package/dist/runtime/stderr_hold.d.ts.map +1 -0
- package/dist/runtime/stderr_hold.js +23 -0
- package/dist/runtime/stderr_hold.js.map +1 -0
- package/dist/runtime/thought_birth.d.ts +25 -0
- package/dist/runtime/thought_birth.d.ts.map +1 -0
- package/dist/runtime/thought_birth.js +122 -0
- package/dist/runtime/thought_birth.js.map +1 -0
- package/dist/runtime/thought_collapsed.d.ts +35 -0
- package/dist/runtime/thought_collapsed.d.ts.map +1 -0
- package/dist/runtime/thought_collapsed.js +94 -0
- package/dist/runtime/thought_collapsed.js.map +1 -0
- package/dist/runtime/tty.d.ts +58 -0
- package/dist/runtime/tty.d.ts.map +1 -0
- package/dist/runtime/tty.js +130 -0
- package/dist/runtime/tty.js.map +1 -0
- package/dist/theme/ansi16.d.ts +34 -0
- package/dist/theme/ansi16.d.ts.map +1 -0
- package/dist/theme/ansi16.js +181 -0
- package/dist/theme/ansi16.js.map +1 -0
- package/dist/theme/ansi256.d.ts +15 -0
- package/dist/theme/ansi256.d.ts.map +1 -0
- package/dist/theme/ansi256.js +94 -0
- package/dist/theme/ansi256.js.map +1 -0
- package/dist/theme/ground.d.ts +8 -0
- package/dist/theme/ground.d.ts.map +1 -0
- package/dist/theme/ground.js +11 -0
- package/dist/theme/ground.js.map +1 -0
- package/dist/theme/index.d.ts +10 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +10 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/region.d.ts +42 -0
- package/dist/theme/region.d.ts.map +1 -0
- package/dist/theme/region.js +105 -0
- package/dist/theme/region.js.map +1 -0
- package/dist/theme/resolver.d.ts +126 -0
- package/dist/theme/resolver.d.ts.map +1 -0
- package/dist/theme/resolver.js +366 -0
- package/dist/theme/resolver.js.map +1 -0
- package/dist/theme/salience.d.ts +90 -0
- package/dist/theme/salience.d.ts.map +1 -0
- package/dist/theme/salience.js +208 -0
- package/dist/theme/salience.js.map +1 -0
- package/dist/theme/seeds.d.ts +113 -0
- package/dist/theme/seeds.d.ts.map +1 -0
- package/dist/theme/seeds.js +52 -0
- package/dist/theme/seeds.js.map +1 -0
- package/dist/theme/selection.d.ts +94 -0
- package/dist/theme/selection.d.ts.map +1 -0
- package/dist/theme/selection.js +135 -0
- package/dist/theme/selection.js.map +1 -0
- package/dist/theme/theme.d.ts +41 -0
- package/dist/theme/theme.d.ts.map +1 -0
- package/dist/theme/theme.js +34 -0
- package/dist/theme/theme.js.map +1 -0
- package/dist/widgets/composer-caret.d.ts +47 -0
- package/dist/widgets/composer-caret.d.ts.map +1 -0
- package/dist/widgets/composer-caret.js +90 -0
- package/dist/widgets/composer-caret.js.map +1 -0
- package/dist/widgets/composer-click.d.ts +30 -0
- package/dist/widgets/composer-click.d.ts.map +1 -0
- package/dist/widgets/composer-click.js +89 -0
- package/dist/widgets/composer-click.js.map +1 -0
- package/dist/widgets/composer-command.d.ts +64 -0
- package/dist/widgets/composer-command.d.ts.map +1 -0
- package/dist/widgets/composer-command.js +127 -0
- package/dist/widgets/composer-command.js.map +1 -0
- package/dist/widgets/composer-frame.d.ts +74 -0
- package/dist/widgets/composer-frame.d.ts.map +1 -0
- package/dist/widgets/composer-frame.js +284 -0
- package/dist/widgets/composer-frame.js.map +1 -0
- package/dist/widgets/composer-picker.d.ts +89 -0
- package/dist/widgets/composer-picker.d.ts.map +1 -0
- package/dist/widgets/composer-picker.js +240 -0
- package/dist/widgets/composer-picker.js.map +1 -0
- package/dist/widgets/diff.d.ts +11 -0
- package/dist/widgets/diff.d.ts.map +1 -0
- package/dist/widgets/diff.js +19 -0
- package/dist/widgets/diff.js.map +1 -0
- package/dist/widgets/expand.d.ts +28 -0
- package/dist/widgets/expand.d.ts.map +1 -0
- package/dist/widgets/expand.js +94 -0
- package/dist/widgets/expand.js.map +1 -0
- package/dist/widgets/index.d.ts +126 -0
- package/dist/widgets/index.d.ts.map +1 -0
- package/dist/widgets/index.js +455 -0
- package/dist/widgets/index.js.map +1 -0
- package/dist/widgets/internals/arrival-lang.d.ts +88 -0
- package/dist/widgets/internals/arrival-lang.d.ts.map +1 -0
- package/dist/widgets/internals/arrival-lang.js +211 -0
- package/dist/widgets/internals/arrival-lang.js.map +1 -0
- package/dist/widgets/internals/diff-expand.d.ts +93 -0
- package/dist/widgets/internals/diff-expand.d.ts.map +1 -0
- package/dist/widgets/internals/diff-expand.js +1106 -0
- package/dist/widgets/internals/diff-expand.js.map +1 -0
- package/dist/widgets/internals/embedded-syntax.d.ts +62 -0
- package/dist/widgets/internals/embedded-syntax.d.ts.map +1 -0
- package/dist/widgets/internals/embedded-syntax.js +465 -0
- package/dist/widgets/internals/embedded-syntax.js.map +1 -0
- package/dist/widgets/internals/markdown-expand.d.ts +57 -0
- package/dist/widgets/internals/markdown-expand.d.ts.map +1 -0
- package/dist/widgets/internals/markdown-expand.js +980 -0
- package/dist/widgets/internals/markdown-expand.js.map +1 -0
- package/dist/widgets/internals/overlay-syntax.d.ts +24 -0
- package/dist/widgets/internals/overlay-syntax.d.ts.map +1 -0
- package/dist/widgets/internals/overlay-syntax.js +90 -0
- package/dist/widgets/internals/overlay-syntax.js.map +1 -0
- package/dist/widgets/internals/pierre-parse/cleanLastNewline.d.ts +6 -0
- package/dist/widgets/internals/pierre-parse/cleanLastNewline.d.ts.map +1 -0
- package/dist/widgets/internals/pierre-parse/cleanLastNewline.js +8 -0
- package/dist/widgets/internals/pierre-parse/cleanLastNewline.js.map +1 -0
- package/dist/widgets/internals/pierre-parse/constants.d.ts +13 -0
- package/dist/widgets/internals/pierre-parse/constants.d.ts.map +1 -0
- package/dist/widgets/internals/pierre-parse/constants.js +13 -0
- package/dist/widgets/internals/pierre-parse/constants.js.map +1 -0
- package/dist/widgets/internals/pierre-parse/detachString.d.ts +3 -0
- package/dist/widgets/internals/pierre-parse/detachString.d.ts.map +1 -0
- package/dist/widgets/internals/pierre-parse/detachString.js +24 -0
- package/dist/widgets/internals/pierre-parse/detachString.js.map +1 -0
- package/dist/widgets/internals/pierre-parse/index.d.ts +8 -0
- package/dist/widgets/internals/pierre-parse/index.d.ts.map +1 -0
- package/dist/widgets/internals/pierre-parse/index.js +8 -0
- package/dist/widgets/internals/pierre-parse/index.js.map +1 -0
- package/dist/widgets/internals/pierre-parse/parseDiffFromFile.d.ts +15 -0
- package/dist/widgets/internals/pierre-parse/parseDiffFromFile.d.ts.map +1 -0
- package/dist/widgets/internals/pierre-parse/parseDiffFromFile.js +39 -0
- package/dist/widgets/internals/pierre-parse/parseDiffFromFile.js.map +1 -0
- package/dist/widgets/internals/pierre-parse/parsePatchFiles.d.ts +11 -0
- package/dist/widgets/internals/pierre-parse/parsePatchFiles.d.ts.map +1 -0
- package/dist/widgets/internals/pierre-parse/parsePatchFiles.js +621 -0
- package/dist/widgets/internals/pierre-parse/parsePatchFiles.js.map +1 -0
- package/dist/widgets/internals/pierre-parse/types.d.ts +73 -0
- package/dist/widgets/internals/pierre-parse/types.d.ts.map +1 -0
- package/dist/widgets/internals/pierre-parse/types.js +7 -0
- package/dist/widgets/internals/pierre-parse/types.js.map +1 -0
- package/dist/widgets/internals/syntax.d.ts +55 -0
- package/dist/widgets/internals/syntax.d.ts.map +1 -0
- package/dist/widgets/internals/syntax.js +261 -0
- package/dist/widgets/internals/syntax.js.map +1 -0
- package/dist/widgets/markdown.d.ts +14 -0
- package/dist/widgets/markdown.d.ts.map +1 -0
- package/dist/widgets/markdown.js +9 -0
- package/dist/widgets/markdown.js.map +1 -0
- package/dist/widgets/shadow-stream.d.ts +88 -0
- package/dist/widgets/shadow-stream.d.ts.map +1 -0
- package/dist/widgets/shadow-stream.js +359 -0
- package/dist/widgets/shadow-stream.js.map +1 -0
- package/dist/widgets/speech.d.ts +36 -0
- package/dist/widgets/speech.d.ts.map +1 -0
- package/dist/widgets/speech.js +238 -0
- package/dist/widgets/speech.js.map +1 -0
- package/dist/widgets/stream-birth.d.ts +13 -0
- package/dist/widgets/stream-birth.d.ts.map +1 -0
- package/dist/widgets/stream-birth.js +70 -0
- package/dist/widgets/stream-birth.js.map +1 -0
- package/dist/widgets/syntax-boot.d.ts +25 -0
- package/dist/widgets/syntax-boot.d.ts.map +1 -0
- package/dist/widgets/syntax-boot.js +181 -0
- package/dist/widgets/syntax-boot.js.map +1 -0
- package/dist/widgets/text.d.ts +16 -0
- package/dist/widgets/text.d.ts.map +1 -0
- package/dist/widgets/text.js +14 -0
- package/dist/widgets/text.js.map +1 -0
- package/dist/widgets/transcript.d.ts +44 -0
- package/dist/widgets/transcript.d.ts.map +1 -0
- package/dist/widgets/transcript.js +106 -0
- package/dist/widgets/transcript.js.map +1 -0
- package/doom.vert +1840 -0
- package/fonts/DejaVuSans.ttf +0 -0
- package/fonts/DejaVuSansMono.ttf +0 -0
- package/fonts/LICENSE-DejaVu.txt +187 -0
- package/fonts/README.md +6 -0
- package/gradient.vert +75 -0
- package/package.json +147 -4
|
@@ -0,0 +1,1607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Box-cadence UI host — on-demand present + mailbox + echo + selection + caret.
|
|
3
|
+
*
|
|
4
|
+
* Cadence law: **idle means zero timers, zero paints.** Every paint is caused —
|
|
5
|
+
* a wake source calls {@link Renderer.requestPaint} (coalesced, next
|
|
6
|
+
* macrotask) or paints synchronously (mouse press/release, first drag cell).
|
|
7
|
+
* Drag-select after that is latest-wins and presents at {@link SELECTION_PAINT_HZ}
|
|
8
|
+
* — interim SGR cells update the gesture, they do not each tick. A repeating
|
|
9
|
+
* timer is armed only while the painted frame demands it (`Frame.liveDemand.maxHz`
|
|
10
|
+
* from spinners/elapsed, a thought-birth frontier still animating, or a bgFx
|
|
11
|
+
* field) and is disarmed the first frame demand disappears. Spinner Hz stays on
|
|
12
|
+
* `liveDemand.maxHz`. Birth / progress / bgFx arm at `flourishHz` (30 local),
|
|
13
|
+
* not `targetFps` (the emit cap). Input echoes on the very next
|
|
14
|
+
* macrotask — sub-frame, not next-interval.
|
|
15
|
+
*
|
|
16
|
+
* AESTHETIC REUSE: when this tick's `prepared` is the same object as last
|
|
17
|
+
* (computed snapshot identity) and extras match, tick reuses the last Frame.
|
|
18
|
+
* Idle (no birth / progress / spinner) clones the last pre-materialize buffer.
|
|
19
|
+
* A spinner/elapsed/birth tick only `paintFrame`s that Frame with a new `nowMs`
|
|
20
|
+
* — it must not `layout()` the tape to advance a glyph. Progress still relayouts
|
|
21
|
+
* (it patches the tree). `contentIdentity` only clears text selection.
|
|
22
|
+
*
|
|
23
|
+
* Admission law: only work with a visual-performance reason runs here —
|
|
24
|
+
* paint/diff, damage, input decode, speculative echo, click hit-test, selection
|
|
25
|
+
* overlay, copy-toast chrome. Everything else is postMessage to the worker.
|
|
26
|
+
* Copy-toast is a present-path overlay (not a LayoutNode): one-shot 3s/10s
|
|
27
|
+
* timers while it is up, then idle again.
|
|
28
|
+
*
|
|
29
|
+
* Mutable state lives on private fields; frame.ts is pure over
|
|
30
|
+
* (mailbox, geometry, state record) — F11.
|
|
31
|
+
*
|
|
32
|
+
* Ready signal = worker's first `structure` / `paint_init`, or the first
|
|
33
|
+
* retained snapshot that returns a non-null root (no mailbox required).
|
|
34
|
+
*
|
|
35
|
+
* SNAPSHOT: tick reads `paintSource.snapshot` (the live computed) when
|
|
36
|
+
* present. `prepare()` is only the pull fallback. Aesthetic ticks must
|
|
37
|
+
* not rebuild the tree.
|
|
38
|
+
*
|
|
39
|
+
* Product keys (composer box / frame) and worker lane envelopes are injected —
|
|
40
|
+
* this module does not import agent packages.
|
|
41
|
+
*/
|
|
42
|
+
import { execFileSync } from "node:child_process";
|
|
43
|
+
import { reaction, runInAction } from "mobx";
|
|
44
|
+
import { agentsRowKeyFromHit } from "./agents_keys.js";
|
|
45
|
+
import { APP_AGENTS_PEEK_KEY } from "./agents_peek.js";
|
|
46
|
+
import { APP_AGENTS_PREVIEW_KEY, APP_AGENTS_PREVIEW_OVERLAY_KEY } from "./agents_preview.js";
|
|
47
|
+
import { applyBoxBusBatch, emptyMailbox, } from "./box_bus.js";
|
|
48
|
+
import { COPY_TOAST_ARM_MS, COPY_TOAST_HOLD_MS, paintCopyToast, placeCopyToast } from "./copy_toast.js";
|
|
49
|
+
import { emptyEcho, predict, reconcile, withComposerBox } from "./echo.js";
|
|
50
|
+
import { renderFrame } from "./frame.js";
|
|
51
|
+
import { deliver, emptyQueue, markReady, offer } from "./key_buffer.js";
|
|
52
|
+
import { composerCaretEvent, isRangeEditKey, toKeyEventBatch } from "./keys.js";
|
|
53
|
+
import { paintSnapshotFromMailbox } from "./paint_snapshot.js";
|
|
54
|
+
import { agentsPreviewViewportMetrics, agentsViewportMetrics, emptyPreparedPaint, nextTapeScroll, tapeViewportMetrics, writePaintEcho, writePaintModHeld, writePaintPreviewScroll, writePaintTapeExpanded, writePaintTapeScroll, } from "./paint_source.js";
|
|
55
|
+
import { startPresentLoop } from "./present_loop.js";
|
|
56
|
+
import { DEFAULT_FLOURISH_HZ } from "./present_profile.js";
|
|
57
|
+
import { createSelectionHost, mouseToGesture } from "./selection_host.js";
|
|
58
|
+
import { anyThoughtBirthLive, stepThoughtBirthFromSites, stepThoughtBirthFromSnapshot } from "./thought_birth.js";
|
|
59
|
+
import { paintSnapshotWithThoughtCollapsed, toggleCollapsedFromHit } from "./thought_collapsed.js";
|
|
60
|
+
import { mouseAnyMotionBytes, mousePointerBytes } from "./tty.js";
|
|
61
|
+
import { cloneBuffer, createBuffer, emitAnsiDetailed } from "../buffer/index.js";
|
|
62
|
+
import { OSC11_QUERY, THEME_REPORT_DISABLE, THEME_REPORT_ENABLE } from "../capabilities/sequences.js";
|
|
63
|
+
import { consumeKeys, flushPendingEsc, normalizePaste, selectionRange, } from "../input/index.js";
|
|
64
|
+
import { bufferHasBgFx, materializeBgFx } from "../internals/bgfx/materialize.js";
|
|
65
|
+
import { setBgFxGround } from "../internals/bgfx/opacity.js";
|
|
66
|
+
import { attuneTheme, setActiveTheme, setColorDepth, CHROME_FRAME_BODY_CHROME, collectProgressSites, elementAt, elementAtPath, paintFrame, progressBarLive, stepProgressBar, ThemeStore, } from "../layout/index.js";
|
|
67
|
+
import { canvasPolarity } from "../theme/ansi16.js";
|
|
68
|
+
import { caretAnsi, composerDraftSelectionFromCells, composerOffsetAtCell, parkComposerCaret, } from "../widgets/index.js";
|
|
69
|
+
import { setHighlightPolarity } from "../widgets/internals/syntax.js";
|
|
70
|
+
/** Lone-ESC hold before it counts as Escape (cannot tell Escape from a split CSI). */
|
|
71
|
+
export const ESC_QUIET_MS = 30;
|
|
72
|
+
function readSystemClipboard() {
|
|
73
|
+
try {
|
|
74
|
+
if (process.platform === "darwin") {
|
|
75
|
+
return execFileSync("pbpaste", { encoding: "utf8", timeout: 250, maxBuffer: 1_048_576 });
|
|
76
|
+
}
|
|
77
|
+
if (process.platform === "win32") {
|
|
78
|
+
return execFileSync("powershell", ["-NoProfile", "-Command", "Get-Clipboard -Raw"], {
|
|
79
|
+
encoding: "utf8",
|
|
80
|
+
timeout: 500,
|
|
81
|
+
maxBuffer: 1_048_576,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
return execFileSync("wl-paste", ["-n"], { encoding: "utf8", timeout: 250, maxBuffer: 1_048_576 });
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return execFileSync("xclip", ["-selection", "clipboard", "-o"], {
|
|
89
|
+
encoding: "utf8",
|
|
90
|
+
timeout: 250,
|
|
91
|
+
maxBuffer: 1_048_576,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Ceiling for live-demand presents (spinners, birth). Not an idle loop rate. */
|
|
100
|
+
export const TARGET_FPS = 60;
|
|
101
|
+
/**
|
|
102
|
+
* Drag-select present rate. Gesture state tracks every SGR cell; paint does not.
|
|
103
|
+
* Press/release still tick immediately (clear, copy, click). Knob if 30 is wrong.
|
|
104
|
+
*/
|
|
105
|
+
export const SELECTION_PAINT_HZ = 30;
|
|
106
|
+
export class Renderer {
|
|
107
|
+
#options;
|
|
108
|
+
#now;
|
|
109
|
+
#composerBoxKey;
|
|
110
|
+
#composerFrameKey;
|
|
111
|
+
#mailbox = emptyMailbox();
|
|
112
|
+
#echo = emptyEcho();
|
|
113
|
+
#queue = emptyQueue();
|
|
114
|
+
#geometry;
|
|
115
|
+
#pendingBytes = "";
|
|
116
|
+
#lastByteAt = 0;
|
|
117
|
+
#previous = null;
|
|
118
|
+
#frame = null;
|
|
119
|
+
#liveLoop = null;
|
|
120
|
+
#liveHz = 0;
|
|
121
|
+
#paintScheduled = false;
|
|
122
|
+
#escTimer = null;
|
|
123
|
+
/** Last drag-caused present. Trailing timer paints the latest cell after the gap. */
|
|
124
|
+
#selectionPaintAt = Number.NEGATIVE_INFINITY;
|
|
125
|
+
#selectionPaintTimer = null;
|
|
126
|
+
/** Range-end plaque after OSC 52. */
|
|
127
|
+
#copyToast = null;
|
|
128
|
+
#copyToastArmed = false;
|
|
129
|
+
#copyToastArmTimer = null;
|
|
130
|
+
#copyToastHoldTimer = null;
|
|
131
|
+
/** Terminal focus (CSI I/O) — caret hides on OS blur. */
|
|
132
|
+
#termFocused = true;
|
|
133
|
+
/** First Ctrl+C arms exit hint; second quits; any other key disarms. */
|
|
134
|
+
#quitArmed = false;
|
|
135
|
+
#selection = createSelectionHost();
|
|
136
|
+
/** Display-only collapse overrides; worker still ships collapsed:true. */
|
|
137
|
+
#thoughtCollapsed = new Map();
|
|
138
|
+
/** Birth frontiers — stepped each present tick (FPS, not token rate). */
|
|
139
|
+
#thoughtBirth = new Map();
|
|
140
|
+
/** Drag since last press → suppress activate. */
|
|
141
|
+
#mouseDragged = false;
|
|
142
|
+
/** Last mouse cell (0-based) — progress-annotation hover without 1003 flood. */
|
|
143
|
+
#pointer = null;
|
|
144
|
+
/** Sticky until button-up so picker/drawer press cannot arm transcript selection. */
|
|
145
|
+
#gestureOwner = null;
|
|
146
|
+
#hoverKey = null;
|
|
147
|
+
#pressKey = null;
|
|
148
|
+
/** Host-local drawer hover; mirrored to worker focus for action semantics. */
|
|
149
|
+
#agentsHoverKey = null;
|
|
150
|
+
#hoverSuppressKey = null;
|
|
151
|
+
/** Last slash-complete payload for hit-map / wheel clamp (Tea owns the window). */
|
|
152
|
+
#pickerLast = null;
|
|
153
|
+
/** 1003 any-motion: slash-complete, or copy-toast after its 3s arm. */
|
|
154
|
+
#anyMotion = false;
|
|
155
|
+
/** OSC 22 shape. Edge-written; `pointer` only over `intent: activate`. */
|
|
156
|
+
#pointerShape = "default";
|
|
157
|
+
/** Host-eased chrome_frame progress, keyed by frame key. */
|
|
158
|
+
#progress = new Map();
|
|
159
|
+
#themes;
|
|
160
|
+
#themeStop = null;
|
|
161
|
+
#pendingResize = null;
|
|
162
|
+
#stopped = false;
|
|
163
|
+
#paintedOnce = false;
|
|
164
|
+
/** First bgFx frame kicks catalog/worker; never on the cold path. */
|
|
165
|
+
#bgfxArmed = false;
|
|
166
|
+
/**
|
|
167
|
+
* `materializeBgFx` clock origin. Shadertoy `iTime` is float32 seconds
|
|
168
|
+
* since start — `Date.now()/1000` (~1.7e9) has a 128s ULP, so a frame
|
|
169
|
+
* delta is zero and the field freezes. Epoch is the first fx present.
|
|
170
|
+
*/
|
|
171
|
+
#fxEpoch = null;
|
|
172
|
+
#paintSource = null;
|
|
173
|
+
#paintLocal = null;
|
|
174
|
+
#sourceBinding = null;
|
|
175
|
+
/** Last PreparedPaint that produced the visible frame. Input reads this. */
|
|
176
|
+
#committed = null;
|
|
177
|
+
/** Retained content scope — selection dies when the source says it changed. */
|
|
178
|
+
#contentIdentity = undefined;
|
|
179
|
+
/** Pre-materialize buffer for aesthetic reuse. */
|
|
180
|
+
#structural = null;
|
|
181
|
+
#structuralKey = "";
|
|
182
|
+
#lastPrepared = null;
|
|
183
|
+
constructor(options) {
|
|
184
|
+
this.#options = options;
|
|
185
|
+
this.#now = options.now ?? Date.now;
|
|
186
|
+
this.#geometry = { cols: options.output.cols, rows: options.output.rows };
|
|
187
|
+
const seed = options.theme ?? attuneTheme();
|
|
188
|
+
this.#themes = new ThemeStore({ theme: seed.theme, depth: seed.depth });
|
|
189
|
+
this.#composerBoxKey = options.composerBoxKey ?? "composer";
|
|
190
|
+
this.#composerFrameKey = options.composerFrameKey ?? "composer:frame";
|
|
191
|
+
this.#paintSource = options.paintSource ?? null;
|
|
192
|
+
this.#paintLocal = options.paintLocal ?? null;
|
|
193
|
+
this.#selection.setViewport(this.#geometry.cols, this.#geometry.rows);
|
|
194
|
+
}
|
|
195
|
+
get mailbox() {
|
|
196
|
+
return this.#mailbox;
|
|
197
|
+
}
|
|
198
|
+
start() {
|
|
199
|
+
this.#options.input.setRawMode(true);
|
|
200
|
+
this.#options.input.onData((chunk) => this.onBytes(chunk));
|
|
201
|
+
this.#options.output.onResize(() => this.onResize(this.#options.output.cols, this.#options.output.rows));
|
|
202
|
+
this.#options.port.onBatch((batch) => this.onPaintBatch(batch));
|
|
203
|
+
if (this.#paintSource !== null && this.#paintLocal !== null) {
|
|
204
|
+
this.#sourceBinding = this.#paintSource.bind({ requestPaint: () => this.requestPaint() }, this.#paintLocal);
|
|
205
|
+
}
|
|
206
|
+
this.#applyThemeSink(this.#themes.theme, { relayout: false });
|
|
207
|
+
this.#themeStop = reaction(() => this.#themes.binding, ({ theme }) => {
|
|
208
|
+
if (this.#stopped)
|
|
209
|
+
return;
|
|
210
|
+
this.#applyThemeSink(theme, { relayout: true });
|
|
211
|
+
this.requestPaint();
|
|
212
|
+
});
|
|
213
|
+
// Subscribe before re-querying to close the boot-probe / live-host gap.
|
|
214
|
+
// Unsupported terminals ignore the mode; focus-in also refreshes ground.
|
|
215
|
+
this.#options.output.write(THEME_REPORT_ENABLE + OSC11_QUERY);
|
|
216
|
+
// Worker has no TTY. Announce geometry before the first structure.
|
|
217
|
+
this.#send({ kind: "resize", cols: this.#geometry.cols, rows: this.#geometry.rows });
|
|
218
|
+
this.requestPaint(); // first frame (forceFull: #previous === null)
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Push a new canvas ground/depth. Theme is computed; the reaction drops
|
|
222
|
+
* cached RGB and (on polarity flip) the Frame so diffs re-highlight.
|
|
223
|
+
*/
|
|
224
|
+
attune(opts) {
|
|
225
|
+
this.#themes.attune(opts);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Coalesced on-demand paint: any number of wakes inside one macrotask batch
|
|
229
|
+
* into a single tick on the next one. The wake sources are enumerated in
|
|
230
|
+
* the class preamble — nothing else may paint.
|
|
231
|
+
*/
|
|
232
|
+
requestPaint() {
|
|
233
|
+
if (this.#stopped || this.#paintScheduled)
|
|
234
|
+
return;
|
|
235
|
+
this.#paintScheduled = true;
|
|
236
|
+
const schedule = this.#options.schedule ?? ((fn) => void setImmediate(fn));
|
|
237
|
+
schedule(() => {
|
|
238
|
+
this.#paintScheduled = false;
|
|
239
|
+
if (!this.#stopped)
|
|
240
|
+
this.tick();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/** Arm/disarm the live timer to match current frame demand. */
|
|
244
|
+
#setLiveHz(hz) {
|
|
245
|
+
if (hz <= 0) {
|
|
246
|
+
this.#liveLoop?.stop();
|
|
247
|
+
this.#liveLoop = null;
|
|
248
|
+
this.#liveHz = 0;
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (this.#liveLoop !== null && this.#liveHz === hz)
|
|
252
|
+
return;
|
|
253
|
+
this.#liveLoop?.stop();
|
|
254
|
+
this.#liveHz = hz;
|
|
255
|
+
this.#liveLoop = (this.#options.startLoop ?? startPresentLoop)({
|
|
256
|
+
targetFps: hz,
|
|
257
|
+
onFrame: () => this.tick(),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
#armBgfx() {
|
|
261
|
+
if (this.#bgfxArmed)
|
|
262
|
+
return;
|
|
263
|
+
this.#bgfxArmed = true;
|
|
264
|
+
const width = this.#geometry.cols;
|
|
265
|
+
const height = this.#geometry.rows;
|
|
266
|
+
void import("../internals/bgfx/shadertoy-catalog.js")
|
|
267
|
+
.then((mod) => mod.registerShadertoyCatalog({ width, height }))
|
|
268
|
+
.then((r) => {
|
|
269
|
+
console.error(`[bgfx] catalog mode=${r.mode} ${r.ids.join(",") || "—"} ${width}×${height}`);
|
|
270
|
+
if (!this.#stopped)
|
|
271
|
+
this.requestPaint();
|
|
272
|
+
return r;
|
|
273
|
+
})
|
|
274
|
+
.catch(() => {
|
|
275
|
+
// Catalog load is optional. Do not skip CPU materializeBgFx on present.
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/** Stop paint/read. Does NOT restore the terminal — teardown owns that order. */
|
|
279
|
+
stop() {
|
|
280
|
+
if (this.#stopped)
|
|
281
|
+
return;
|
|
282
|
+
this.#stopped = true;
|
|
283
|
+
this.#options.output.write(THEME_REPORT_DISABLE);
|
|
284
|
+
this.#liveLoop?.stop();
|
|
285
|
+
this.#liveLoop = null;
|
|
286
|
+
this.#liveHz = 0;
|
|
287
|
+
if (this.#escTimer !== null) {
|
|
288
|
+
clearTimeout(this.#escTimer);
|
|
289
|
+
this.#escTimer = null;
|
|
290
|
+
}
|
|
291
|
+
this.#cancelSelectionPaintTimer();
|
|
292
|
+
this.#cancelCopyToastTimers();
|
|
293
|
+
this.#copyToast = null;
|
|
294
|
+
this.#copyToastArmed = false;
|
|
295
|
+
this.#options.input.setRawMode(false);
|
|
296
|
+
if (this.#anyMotion) {
|
|
297
|
+
this.#anyMotion = false;
|
|
298
|
+
this.#options.output.write(mouseAnyMotionBytes(false));
|
|
299
|
+
}
|
|
300
|
+
if (this.#pointerShape !== "default") {
|
|
301
|
+
this.#pointerShape = "default";
|
|
302
|
+
this.#options.output.write(mousePointerBytes("default"));
|
|
303
|
+
}
|
|
304
|
+
if (this.#bgfxArmed) {
|
|
305
|
+
void import("../internals/bgfx/shadertoy-catalog.js").then((mod) => mod.unregisterShadertoyCatalog());
|
|
306
|
+
}
|
|
307
|
+
this.#themeStop?.();
|
|
308
|
+
this.#themeStop = null;
|
|
309
|
+
this.#sourceBinding?.[Symbol.dispose]();
|
|
310
|
+
this.#sourceBinding = null;
|
|
311
|
+
}
|
|
312
|
+
/** Imperative theme side-effects: polarity, bgfx ground, ambient, RGB cache. */
|
|
313
|
+
#applyThemeSink(theme, opts) {
|
|
314
|
+
setHighlightPolarity(canvasPolarity(theme.ground));
|
|
315
|
+
setActiveTheme(theme);
|
|
316
|
+
setColorDepth(this.#themes.depth);
|
|
317
|
+
setBgFxGround(theme.groundRgb);
|
|
318
|
+
if (!opts.relayout)
|
|
319
|
+
return;
|
|
320
|
+
this.#structural = null;
|
|
321
|
+
// Chrome progress fuse + syntax tokens bake at layout. Dual-dirty recasts
|
|
322
|
+
// role RGB, but the omnibox ramp and shiki runs need a new Frame.
|
|
323
|
+
this.#frame = null;
|
|
324
|
+
this.#lastPrepared = null;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Decode → translate → queue → deliver, even during boot.
|
|
328
|
+
* Bytes become events when carry state is correct; only delivery waits (F1).
|
|
329
|
+
*/
|
|
330
|
+
onBytes(chunk) {
|
|
331
|
+
const decoded = consumeKeys(chunk, this.#pendingBytes);
|
|
332
|
+
this.#pendingBytes = decoded.pending;
|
|
333
|
+
this.#lastByteAt = this.#now();
|
|
334
|
+
this.#acceptDecoded(decoded.events);
|
|
335
|
+
// Lone ESC resolves after the quiet window — a one-shot timer, not a poll.
|
|
336
|
+
if (this.#escTimer !== null) {
|
|
337
|
+
clearTimeout(this.#escTimer);
|
|
338
|
+
this.#escTimer = null;
|
|
339
|
+
}
|
|
340
|
+
if (this.#pendingBytes === "\u001B" && !this.#stopped) {
|
|
341
|
+
this.#escTimer = setTimeout(() => {
|
|
342
|
+
this.#escTimer = null;
|
|
343
|
+
this.#maybeFlushEsc();
|
|
344
|
+
}, ESC_QUIET_MS + 1);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Lifecycle / local-geometry before translation:
|
|
349
|
+
* ctrl_c → double-press quit (never worker)
|
|
350
|
+
* mouse → selection / wheel (never Seam C key)
|
|
351
|
+
* esc + live selection → collapse/clear and stop (do not deliver Esc)
|
|
352
|
+
*/
|
|
353
|
+
#acceptDecoded(events) {
|
|
354
|
+
let quitNow = false;
|
|
355
|
+
let chromeDirty = false;
|
|
356
|
+
const rest = [];
|
|
357
|
+
for (const event of events) {
|
|
358
|
+
if (event.type === "theme_changed") {
|
|
359
|
+
this.#options.output.write(OSC11_QUERY);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (event.type === "osc_ground") {
|
|
363
|
+
this.attune({ groundRgb: event.rgb });
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (event.type === "mod") {
|
|
367
|
+
this.#setModHeld(event.down);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (event.type === "ctrl_c") {
|
|
371
|
+
this.#copyToastNotice("input");
|
|
372
|
+
if (this.#quitArmed) {
|
|
373
|
+
quitNow = true;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
this.#quitArmed = true;
|
|
377
|
+
chromeDirty = true;
|
|
378
|
+
}
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (this.#quitArmed) {
|
|
382
|
+
this.#quitArmed = false;
|
|
383
|
+
chromeDirty = true;
|
|
384
|
+
}
|
|
385
|
+
if (event.type === "focus_in") {
|
|
386
|
+
this.#options.output.write(OSC11_QUERY);
|
|
387
|
+
this.#termFocused = true;
|
|
388
|
+
chromeDirty = true;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (event.type === "focus_out") {
|
|
392
|
+
this.#termFocused = false;
|
|
393
|
+
this.#setModHeld(false);
|
|
394
|
+
this.#clearAgentsHover();
|
|
395
|
+
chromeDirty = true;
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (event.type === "mouse") {
|
|
399
|
+
this.#onMouse(event);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (event.type === "page_up" || event.type === "page_down")
|
|
403
|
+
this.#copyToastNotice("scroll");
|
|
404
|
+
else
|
|
405
|
+
this.#copyToastNotice("input");
|
|
406
|
+
if (event.type === "esc") {
|
|
407
|
+
const overlay = this.#selection.selection();
|
|
408
|
+
const draftSel = standingDraftSelection(this.#composerDraftState());
|
|
409
|
+
if (overlay || draftSel) {
|
|
410
|
+
this.#selection.clear();
|
|
411
|
+
if (draftSel)
|
|
412
|
+
this.#accept([composerCaretEvent(draftSel.cursor, null)]);
|
|
413
|
+
else
|
|
414
|
+
this.requestPaint();
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (event.type === "page_up" || event.type === "page_down") {
|
|
419
|
+
if (this.#agentsPreviewOpen()) {
|
|
420
|
+
const vp = this.#previewViewport();
|
|
421
|
+
if (vp !== null) {
|
|
422
|
+
const page = Math.max(1, vp.h - 1);
|
|
423
|
+
this.#scrollPreview(event.type === "page_up" ? -page : page);
|
|
424
|
+
}
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
const vp = this.#tapeViewport();
|
|
428
|
+
if (vp !== null && this.#paintLocal !== null) {
|
|
429
|
+
const page = Math.max(1, vp.h - 1);
|
|
430
|
+
this.#scrollTape(event.type === "page_up" ? -page : page);
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (event.type === "ctrl_o") {
|
|
435
|
+
if (this.#agentsPreviewOpen())
|
|
436
|
+
continue;
|
|
437
|
+
this.#toggleTapeExpanded();
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (event.type === "ctrl_v") {
|
|
441
|
+
const raw = (this.#options.readClipboard ?? readSystemClipboard)() || this.#selection.lastCopy();
|
|
442
|
+
const text = normalizePaste(raw);
|
|
443
|
+
if (text.length > 0)
|
|
444
|
+
rest.push({ type: "paste", text });
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
rest.push(event);
|
|
448
|
+
}
|
|
449
|
+
const keys = toKeyEventBatch(rest);
|
|
450
|
+
this.#noteKeyMods(keys);
|
|
451
|
+
if (this.#agentsViewport() !== null) {
|
|
452
|
+
for (const key of keys) {
|
|
453
|
+
if (key.key === "j" || key.key === "k" || key.key === "ArrowDown" || key.key === "ArrowUp") {
|
|
454
|
+
this.#armHoverSuppress();
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
this.#accept(keys);
|
|
460
|
+
if (chromeDirty)
|
|
461
|
+
this.requestPaint();
|
|
462
|
+
if (quitNow)
|
|
463
|
+
this.#options.onQuit?.();
|
|
464
|
+
}
|
|
465
|
+
#setModHeld(next) {
|
|
466
|
+
if (this.#paintLocal === null || this.#paintLocal.modHeld === next)
|
|
467
|
+
return;
|
|
468
|
+
writePaintModHeld(this.#paintLocal, next);
|
|
469
|
+
this.requestPaint();
|
|
470
|
+
}
|
|
471
|
+
#noteKeyMods(events) {
|
|
472
|
+
if (!events.some((event) => event.ctrl === true || event.meta === true))
|
|
473
|
+
return;
|
|
474
|
+
this.#setModHeld(true);
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* SGR mouse → picker chrome, selection gesture, or bare-click hit.
|
|
478
|
+
* Wheel over picker is eaten (Tea owns scrollOffset); over the transcript
|
|
479
|
+
* viewport scrolls the tape (present-side follow-tail). Elsewhere discarded.
|
|
480
|
+
* Sneak-peek: wheel scrolls the peeked tape; bare click inside the modal
|
|
481
|
+
* toggles disclosures; click outside dismisses; drawer hover/activate stay
|
|
482
|
+
* inert.
|
|
483
|
+
*/
|
|
484
|
+
#onMouse(ev) {
|
|
485
|
+
this.#pointer = { col: ev.col - 1, row: ev.row - 1 };
|
|
486
|
+
this.#selection.setViewport(this.#geometry.cols, this.#geometry.rows);
|
|
487
|
+
this.#syncPointerShape();
|
|
488
|
+
const mapped = mouseToGesture(ev, this.#now());
|
|
489
|
+
if (mapped.kind === "ignore")
|
|
490
|
+
return;
|
|
491
|
+
if (this.#agentsPreviewOpen()) {
|
|
492
|
+
if (mapped.kind === "wheel") {
|
|
493
|
+
this.#copyToastNotice("scroll");
|
|
494
|
+
if (this.#pointerInPreview())
|
|
495
|
+
this.#scrollPreview(mapped.delta);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (mapped.kind === "move")
|
|
499
|
+
return;
|
|
500
|
+
if (mapped.event.kind === "press") {
|
|
501
|
+
this.#copyToastNotice("press");
|
|
502
|
+
this.#mouseDragged = false;
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (mapped.event.kind === "drag") {
|
|
506
|
+
this.#mouseDragged = true;
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (mapped.event.kind === "release" && !this.#mouseDragged) {
|
|
510
|
+
this.onClick(ev.col, ev.row);
|
|
511
|
+
}
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (mapped.kind === "move") {
|
|
515
|
+
this.#copyToastNotice("move");
|
|
516
|
+
if (this.#pointerInPickerChrome()) {
|
|
517
|
+
const next = this.#pickerKeyAt();
|
|
518
|
+
if (next !== this.#hoverKey) {
|
|
519
|
+
this.#hoverKey = next;
|
|
520
|
+
this.requestPaint();
|
|
521
|
+
}
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (this.#hoverKey !== null) {
|
|
525
|
+
this.#hoverKey = null;
|
|
526
|
+
this.requestPaint();
|
|
527
|
+
}
|
|
528
|
+
if (this.#agentsViewport() !== null && !this.#agentsPreviewOpen())
|
|
529
|
+
this.#applyAgentsPointer();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (mapped.kind === "wheel") {
|
|
533
|
+
this.#copyToastNotice("scroll");
|
|
534
|
+
if (this.#pointerInPickerChrome()) {
|
|
535
|
+
const last = this.#pickerLast;
|
|
536
|
+
if (!last || last.n <= last.viewport)
|
|
537
|
+
return;
|
|
538
|
+
const sign = mapped.delta < 0 ? -1 : 1;
|
|
539
|
+
const maxS = Math.max(0, last.n - last.viewport);
|
|
540
|
+
const next = Math.max(0, Math.min(maxS, last.scrollOffset + sign));
|
|
541
|
+
if (next === last.scrollOffset)
|
|
542
|
+
return;
|
|
543
|
+
this.#send({ kind: "picker_scroll", delta: sign });
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (this.#agentsViewport() !== null) {
|
|
547
|
+
if (this.#agentsPreviewOpen())
|
|
548
|
+
return;
|
|
549
|
+
const sign = mapped.delta < 0 ? -1 : 1;
|
|
550
|
+
this.#send({ kind: "agents_scroll", delta: sign });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (this.#pointerInTape())
|
|
554
|
+
this.#scrollTape(mapped.delta);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (mapped.event.kind === "press") {
|
|
558
|
+
this.#copyToastNotice("press");
|
|
559
|
+
if (this.#pointerInPickerChrome()) {
|
|
560
|
+
this.#gestureOwner = "picker";
|
|
561
|
+
this.#mouseDragged = false;
|
|
562
|
+
this.#pressKey = this.#pickerKeyAt();
|
|
563
|
+
this.#hoverKey = this.#pressKey;
|
|
564
|
+
this.tick();
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (this.#pointerInAgentsPanel()) {
|
|
568
|
+
this.#gestureOwner = "agents";
|
|
569
|
+
this.#mouseDragged = false;
|
|
570
|
+
this.#applyAgentsPointer();
|
|
571
|
+
this.#pressKey = this.#agentsHoverableKeyAt();
|
|
572
|
+
this.tick();
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
this.#gestureOwner = "selection";
|
|
576
|
+
}
|
|
577
|
+
if (this.#gestureOwner === "agents") {
|
|
578
|
+
if (mapped.event.kind === "drag") {
|
|
579
|
+
this.#applyAgentsPointer();
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (mapped.event.kind === "release") {
|
|
583
|
+
const at = this.#agentsHoverableKeyAt();
|
|
584
|
+
const press = this.#pressKey;
|
|
585
|
+
if (press != null && at === press) {
|
|
586
|
+
this.#send({ kind: "activate", key: press });
|
|
587
|
+
}
|
|
588
|
+
this.#pressKey = null;
|
|
589
|
+
this.#gestureOwner = null;
|
|
590
|
+
this.requestPaint();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (this.#gestureOwner === "picker") {
|
|
595
|
+
if (mapped.event.kind === "drag") {
|
|
596
|
+
this.#hoverKey = this.#pickerKeyAt();
|
|
597
|
+
this.requestPaint();
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (mapped.event.kind === "release") {
|
|
601
|
+
const at = this.#pickerKeyAt();
|
|
602
|
+
const press = this.#pressKey;
|
|
603
|
+
if (press != null && at === press) {
|
|
604
|
+
this.#send({ kind: "picker_activate", key: press });
|
|
605
|
+
}
|
|
606
|
+
this.#pressKey = null;
|
|
607
|
+
this.#gestureOwner = null;
|
|
608
|
+
this.requestPaint();
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
// Press: drop prior selection + draft mark (browser model). Shift extends.
|
|
613
|
+
if (mapped.event.kind === "press") {
|
|
614
|
+
this.#mouseDragged = false;
|
|
615
|
+
if (!mapped.event.shift) {
|
|
616
|
+
const cur = this.#composerDraftState();
|
|
617
|
+
if (cur.mark !== null) {
|
|
618
|
+
this.#accept([composerCaretEvent(cur.cursor, null)]);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (mapped.event.kind === "drag")
|
|
623
|
+
this.#mouseDragged = true;
|
|
624
|
+
const out = this.#selection.handle(mapped.event);
|
|
625
|
+
if (mapped.event.kind === "release") {
|
|
626
|
+
this.#gestureOwner = null;
|
|
627
|
+
const osc = this.#selection.copyOscIfReady(out);
|
|
628
|
+
if (osc) {
|
|
629
|
+
this.#options.output.write(osc);
|
|
630
|
+
if (out.selection) {
|
|
631
|
+
const end = this.#selection.rangeEnd() ?? out.selection.active;
|
|
632
|
+
this.#showCopyToast(end, out.selection.anchor);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
// Drag: latest-wins present at SELECTION_PAINT_HZ. Press/release tick now
|
|
637
|
+
// (clear, copy-on-select, click) and cancel a trailing drag frame.
|
|
638
|
+
if (mapped.event.kind === "drag")
|
|
639
|
+
this.#paintSelectionSoon();
|
|
640
|
+
else {
|
|
641
|
+
this.#cancelSelectionPaintTimer();
|
|
642
|
+
this.tick();
|
|
643
|
+
}
|
|
644
|
+
if (mapped.event.kind === "release") {
|
|
645
|
+
// Drag-select in composer body → draft mark (textarea law); clear paint overlay.
|
|
646
|
+
const draftSel = this.#composerSelectionFromGesture();
|
|
647
|
+
if (draftSel) {
|
|
648
|
+
this.#selection.clear();
|
|
649
|
+
this.#accept([composerCaretEvent(draftSel.cursor, draftSel.mark)]);
|
|
650
|
+
this.tick();
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
// Bare click only — drag over non-selectable thought must not toggle collapsed.
|
|
654
|
+
if (!out.copyReady && !this.#selection.selection() && !this.#mouseDragged) {
|
|
655
|
+
const offset = this.#composerOffsetAt(ev.col - 1, ev.row - 1);
|
|
656
|
+
if (offset !== null) {
|
|
657
|
+
const cur = this.#composerDraftState();
|
|
658
|
+
const mark = mapped.event.shift ? (cur.mark ?? cur.cursor) : null;
|
|
659
|
+
this.#accept([composerCaretEvent(offset, mark)]);
|
|
660
|
+
this.tick();
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
this.onClick(ev.col, ev.row);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
#accept(events) {
|
|
668
|
+
if (events.length === 0)
|
|
669
|
+
return;
|
|
670
|
+
// Insert/backspace/delete/kill: the selected range is the entity to replace.
|
|
671
|
+
// Overlay (still dragging) or standing draft mark — not a collapsed caret.
|
|
672
|
+
let batch = events;
|
|
673
|
+
if (events.some((e) => isRangeEditKey(e))) {
|
|
674
|
+
const draftSel = this.#composerSelectionFromGesture() ?? standingDraftSelection(this.#composerDraftState());
|
|
675
|
+
if (draftSel) {
|
|
676
|
+
this.#selection.clear();
|
|
677
|
+
batch = [composerCaretEvent(draftSel.cursor, draftSel.mark), ...events];
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// Echo predicts from what is on screen NOW, before delivery.
|
|
681
|
+
// Catalog / non-interactive composer: keys still deliver; do not invent a draft.
|
|
682
|
+
if (this.#composerInteractive()) {
|
|
683
|
+
const composerOuter = this.#composerOuterWidth();
|
|
684
|
+
let current = this.#composerDraftState();
|
|
685
|
+
for (const event of batch) {
|
|
686
|
+
this.#echo = predict(this.#echo, current, event, {
|
|
687
|
+
composerOuterWidth: composerOuter,
|
|
688
|
+
});
|
|
689
|
+
current = this.#echo.predicted ?? current;
|
|
690
|
+
}
|
|
691
|
+
if (this.#committed !== null && this.#paintLocal !== null) {
|
|
692
|
+
writePaintEcho(this.#paintLocal, {
|
|
693
|
+
predicted: this.#echo.predicted,
|
|
694
|
+
misaligned: this.#echo.misaligned,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
this.#queue = offer(this.#queue, batch);
|
|
699
|
+
this.#drain();
|
|
700
|
+
this.requestPaint(); // speculative echo is on screen next macrotask
|
|
701
|
+
}
|
|
702
|
+
/** Body hit is the text column (chrome_frame). */
|
|
703
|
+
#composerOuterWidth() {
|
|
704
|
+
const hit = this.#frame?.byKey.get(this.#composerFrameKey)?.hitRect;
|
|
705
|
+
if (hit && hit.w > 0)
|
|
706
|
+
return hit.w + CHROME_FRAME_BODY_CHROME;
|
|
707
|
+
return Math.max(8, this.#geometry.cols);
|
|
708
|
+
}
|
|
709
|
+
#composerOffsetAt(col0, row0) {
|
|
710
|
+
const frame = this.#frame;
|
|
711
|
+
if (!frame)
|
|
712
|
+
return null;
|
|
713
|
+
const hit = frame.byKey.get(this.#composerFrameKey)?.hitRect;
|
|
714
|
+
if (!hit || hit.w <= 0 || hit.h <= 0)
|
|
715
|
+
return null;
|
|
716
|
+
if (col0 < hit.x || col0 >= hit.x + hit.w || row0 < hit.y || row0 >= hit.y + hit.h) {
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
const draft = this.#composerDraftState().draft;
|
|
720
|
+
return composerOffsetAtCell(draft, col0, row0, { col: hit.x, row: hit.y }, hit.w);
|
|
721
|
+
}
|
|
722
|
+
/** Visual selection entirely in composer body → draft {cursor, mark}. */
|
|
723
|
+
#composerSelectionFromGesture() {
|
|
724
|
+
const sel = this.#selection.selection();
|
|
725
|
+
if (!sel)
|
|
726
|
+
return null;
|
|
727
|
+
const frame = this.#frame;
|
|
728
|
+
if (!frame)
|
|
729
|
+
return null;
|
|
730
|
+
const hit = frame.byKey.get(this.#composerFrameKey)?.hitRect;
|
|
731
|
+
if (!hit || hit.w <= 0 || hit.h <= 0)
|
|
732
|
+
return null;
|
|
733
|
+
const inHit = (cell) => cell.col >= hit.x && cell.col < hit.x + hit.w && cell.row >= hit.y && cell.row < hit.y + hit.h;
|
|
734
|
+
if (!inHit(sel.anchor) || !inHit(sel.active))
|
|
735
|
+
return null;
|
|
736
|
+
return composerDraftSelectionFromCells(this.#composerDraftState().draft, sel.anchor, sel.active, { col: hit.x, row: hit.y }, hit.w);
|
|
737
|
+
}
|
|
738
|
+
#drain() {
|
|
739
|
+
const drained = deliver(this.#queue);
|
|
740
|
+
this.#queue = drained.queue;
|
|
741
|
+
if (drained.events.length > 0)
|
|
742
|
+
this.#send({ kind: "input_events", events: drained.events });
|
|
743
|
+
}
|
|
744
|
+
onResize(cols, rows) {
|
|
745
|
+
this.#geometry = { cols, rows };
|
|
746
|
+
this.#selection.setViewport(cols, rows);
|
|
747
|
+
this.#selection.clear(); // cells moved; selection geometry is stale
|
|
748
|
+
this.#previous = null; // resized buffer shares no rows with the old one
|
|
749
|
+
this.#structural = null;
|
|
750
|
+
this.#structuralKey = "";
|
|
751
|
+
this.#lastPrepared = null;
|
|
752
|
+
if (!this.#queue.ready) {
|
|
753
|
+
// Latest-wins during boot.
|
|
754
|
+
this.#pendingResize = { cols, rows };
|
|
755
|
+
this.requestPaint();
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
this.#send({ kind: "resize", cols, rows });
|
|
759
|
+
this.requestPaint();
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* 1-based terminal cell → hit-test against painted frame.
|
|
763
|
+
* Fold is host-local; `intent: "activate"` posts `activate{key}` (innermost).
|
|
764
|
+
*/
|
|
765
|
+
onClick(col, row) {
|
|
766
|
+
const frame = this.#frame;
|
|
767
|
+
if (frame === null || !this.#queue.ready)
|
|
768
|
+
return;
|
|
769
|
+
if (this.#agentsPreviewOpen() && !this.#pointerInPreview()) {
|
|
770
|
+
this.#send({ kind: "activate", key: `${APP_AGENTS_PREVIEW_OVERLAY_KEY}:backdrop` });
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
const hit = elementAt(frame, col - 1, row - 1);
|
|
774
|
+
if (hit === null)
|
|
775
|
+
return;
|
|
776
|
+
const toggle = toggleCollapsedFromHit(hit, frame.byKey);
|
|
777
|
+
if (toggle) {
|
|
778
|
+
const wasCollapsed = this.#thoughtCollapsed.get(toggle.blockId) ?? true;
|
|
779
|
+
this.#thoughtCollapsed.set(toggle.blockId, !wasCollapsed);
|
|
780
|
+
this.#paintSource?.toggleCollapsed?.(toggle.blockId);
|
|
781
|
+
this.tick();
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (this.#agentsPreviewOpen())
|
|
785
|
+
return;
|
|
786
|
+
const path = elementAtPath(frame, col - 1, row - 1);
|
|
787
|
+
if (path === null)
|
|
788
|
+
return;
|
|
789
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
790
|
+
const link = path[i];
|
|
791
|
+
if ((link.meta.intent === "activate" || link.meta.intent === "dismiss") && link.key) {
|
|
792
|
+
this.#send({ kind: "activate", key: link.key });
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
/** Apply a paint-lane batch from the worker. */
|
|
798
|
+
onPaintBatch(batch) {
|
|
799
|
+
let sawPaintInit = false;
|
|
800
|
+
const boxes = [];
|
|
801
|
+
for (const down of batch) {
|
|
802
|
+
if (down.kind === "paint_init") {
|
|
803
|
+
sawPaintInit = true;
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
boxes.push(down);
|
|
807
|
+
if (down.kind === "box_set") {
|
|
808
|
+
const incoming = readDraftState(down.value, this.#composerFrameKey);
|
|
809
|
+
this.#echo = reconcile(this.#echo, down.boxId, incoming);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const prevStructureGen = this.#mailbox.structureGen;
|
|
813
|
+
if (boxes.length > 0) {
|
|
814
|
+
this.#mailbox = applyBoxBusBatch(this.#mailbox, boxes);
|
|
815
|
+
this.#capturePickerFromMailbox();
|
|
816
|
+
}
|
|
817
|
+
// I7: structureGen reflow moves cells — clear here. box_set must not:
|
|
818
|
+
// selection_host content-stamp drops the extent only when serialize
|
|
819
|
+
// diverges. Clearing on every set would drop selection on every token.
|
|
820
|
+
if (this.#mailbox.structureGen !== prevStructureGen) {
|
|
821
|
+
this.#selection.clear();
|
|
822
|
+
}
|
|
823
|
+
if (sawPaintInit || boxes.some((down) => down.kind === "structure"))
|
|
824
|
+
this.#onStructure();
|
|
825
|
+
this.requestPaint();
|
|
826
|
+
}
|
|
827
|
+
/** New structure: relearn composer box; first time opens delivery gate. */
|
|
828
|
+
#onStructure() {
|
|
829
|
+
this.#echo = withComposerBox(this.#echo, this.#composerBoxId());
|
|
830
|
+
if (this.#queue.ready)
|
|
831
|
+
return;
|
|
832
|
+
this.#queue = markReady(this.#queue);
|
|
833
|
+
this.#options.onReady?.();
|
|
834
|
+
if (this.#pendingResize !== null) {
|
|
835
|
+
this.#send({ kind: "resize", cols: this.#pendingResize.cols, rows: this.#pendingResize.rows });
|
|
836
|
+
this.#pendingResize = null;
|
|
837
|
+
}
|
|
838
|
+
this.#drain();
|
|
839
|
+
}
|
|
840
|
+
#send(message) {
|
|
841
|
+
this.#options.port.post(message);
|
|
842
|
+
}
|
|
843
|
+
#cancelSelectionPaintTimer() {
|
|
844
|
+
if (this.#selectionPaintTimer === null)
|
|
845
|
+
return;
|
|
846
|
+
clearTimeout(this.#selectionPaintTimer);
|
|
847
|
+
this.#selectionPaintTimer = null;
|
|
848
|
+
}
|
|
849
|
+
#cancelCopyToastTimers() {
|
|
850
|
+
if (this.#copyToastArmTimer !== null) {
|
|
851
|
+
clearTimeout(this.#copyToastArmTimer);
|
|
852
|
+
this.#copyToastArmTimer = null;
|
|
853
|
+
}
|
|
854
|
+
if (this.#copyToastHoldTimer !== null) {
|
|
855
|
+
clearTimeout(this.#copyToastHoldTimer);
|
|
856
|
+
this.#copyToastHoldTimer = null;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
#showCopyToast(end, anchor) {
|
|
860
|
+
this.#cancelCopyToastTimers();
|
|
861
|
+
this.#copyToast = { end, anchor };
|
|
862
|
+
this.#copyToastArmed = false;
|
|
863
|
+
if (this.#stopped)
|
|
864
|
+
return;
|
|
865
|
+
this.#copyToastArmTimer = setTimeout(() => {
|
|
866
|
+
this.#copyToastArmTimer = null;
|
|
867
|
+
if (this.#stopped || this.#copyToast === null)
|
|
868
|
+
return;
|
|
869
|
+
this.#copyToastArmed = true;
|
|
870
|
+
this.#syncAnyMotion();
|
|
871
|
+
}, COPY_TOAST_ARM_MS);
|
|
872
|
+
this.#copyToastHoldTimer = setTimeout(() => {
|
|
873
|
+
this.#copyToastHoldTimer = null;
|
|
874
|
+
this.#dismissCopyToast(true);
|
|
875
|
+
}, COPY_TOAST_HOLD_MS);
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Press always dismisses. Move / input / scroll dismiss only after the 3s arm.
|
|
879
|
+
* Press does not paint — the caller ticks. Other kinds requestPaint.
|
|
880
|
+
*/
|
|
881
|
+
#copyToastNotice(kind) {
|
|
882
|
+
if (this.#copyToast === null)
|
|
883
|
+
return;
|
|
884
|
+
if (kind !== "press" && !this.#copyToastArmed)
|
|
885
|
+
return;
|
|
886
|
+
this.#dismissCopyToast(kind !== "press");
|
|
887
|
+
}
|
|
888
|
+
#dismissCopyToast(paint) {
|
|
889
|
+
if (this.#copyToast === null)
|
|
890
|
+
return;
|
|
891
|
+
this.#copyToast = null;
|
|
892
|
+
this.#copyToastArmed = false;
|
|
893
|
+
this.#cancelCopyToastTimers();
|
|
894
|
+
this.#syncAnyMotion();
|
|
895
|
+
// Timer-driven present — same as trailing drag. requestPaint is setImmediate
|
|
896
|
+
// coalesce; after copy the live loop is disarmed, so idle hold must tick now.
|
|
897
|
+
if (paint)
|
|
898
|
+
this.tick();
|
|
899
|
+
}
|
|
900
|
+
#syncAnyMotion() {
|
|
901
|
+
const pickerPresent = this.#frame?.byKey.has("slash-complete:chrome") === true;
|
|
902
|
+
const agentsPresent = this.#agentsViewport() !== null;
|
|
903
|
+
if (!agentsPresent) {
|
|
904
|
+
this.#clearAgentsHover();
|
|
905
|
+
}
|
|
906
|
+
const want = pickerPresent || agentsPresent || (this.#copyToast !== null && this.#copyToastArmed);
|
|
907
|
+
if (want === this.#anyMotion)
|
|
908
|
+
return;
|
|
909
|
+
this.#anyMotion = want;
|
|
910
|
+
this.#options.output.write(mouseAnyMotionBytes(want));
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Leading + trailing throttle for drag-select. First cell (or first after the
|
|
914
|
+
* gap) paints now; further SGR in the window only keep gesture `active`.
|
|
915
|
+
* One trailing timeout presents the latest cell when motion pauses.
|
|
916
|
+
*/
|
|
917
|
+
#paintSelectionSoon() {
|
|
918
|
+
if (this.#stopped)
|
|
919
|
+
return;
|
|
920
|
+
const wait = this.#selectionPaintAt + 1000 / SELECTION_PAINT_HZ - this.#now();
|
|
921
|
+
if (wait <= 0) {
|
|
922
|
+
this.#cancelSelectionPaintTimer();
|
|
923
|
+
this.#selectionPaintAt = this.#now();
|
|
924
|
+
this.tick();
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
if (this.#selectionPaintTimer !== null)
|
|
928
|
+
return;
|
|
929
|
+
this.#selectionPaintTimer = setTimeout(() => {
|
|
930
|
+
this.#selectionPaintTimer = null;
|
|
931
|
+
if (this.#stopped)
|
|
932
|
+
return;
|
|
933
|
+
this.#selectionPaintAt = this.#now();
|
|
934
|
+
this.tick();
|
|
935
|
+
}, wait);
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Paint one frame now. Reached only through a wake source (requestPaint,
|
|
939
|
+
* live timer, a mouse press/release, or a throttled drag-select present).
|
|
940
|
+
* Held ESC gets a belt-and-braces flush here; its primary path is the
|
|
941
|
+
* one-shot quiet timer.
|
|
942
|
+
*/
|
|
943
|
+
tick() {
|
|
944
|
+
if (this.#stopped)
|
|
945
|
+
return;
|
|
946
|
+
this.#maybeFlushEsc();
|
|
947
|
+
const tickStarted = this.#now();
|
|
948
|
+
const nowMs = this.#now();
|
|
949
|
+
if (this.#paintLocal !== null) {
|
|
950
|
+
const local = this.#paintLocal;
|
|
951
|
+
if (local.cols !== this.#geometry.cols || local.rows !== this.#geometry.rows) {
|
|
952
|
+
runInAction(() => {
|
|
953
|
+
local.cols = this.#geometry.cols;
|
|
954
|
+
local.rows = this.#geometry.rows;
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
const prepared = this.#paintSource?.snapshot ?? this.#paintSource?.prepare() ?? emptyPreparedPaint();
|
|
959
|
+
const retained = prepared.root !== null;
|
|
960
|
+
if (retained && !this.#queue.ready)
|
|
961
|
+
this.#onStructure();
|
|
962
|
+
let progressDisplay;
|
|
963
|
+
let birth;
|
|
964
|
+
let snapshot;
|
|
965
|
+
let echo = this.#echo;
|
|
966
|
+
if (retained) {
|
|
967
|
+
const identity = prepared.contentIdentity;
|
|
968
|
+
if (identity !== undefined && identity !== this.#contentIdentity) {
|
|
969
|
+
this.#selection.clear();
|
|
970
|
+
this.#contentIdentity = identity;
|
|
971
|
+
}
|
|
972
|
+
this.#committed = prepared;
|
|
973
|
+
progressDisplay = this.#stepProgress(nowMs, prepared.progressSites);
|
|
974
|
+
const birthInject = stepThoughtBirthFromSites(prepared.birthSites, this.#thoughtBirth, nowMs);
|
|
975
|
+
birth = new Map();
|
|
976
|
+
for (const [id, field] of birthInject)
|
|
977
|
+
birth.set(id, field.W);
|
|
978
|
+
if (prepared.picker)
|
|
979
|
+
this.#pickerLast = prepared.picker;
|
|
980
|
+
snapshot = {
|
|
981
|
+
tree: { root: prepared.root, boxes: {} },
|
|
982
|
+
getBox: () => undefined,
|
|
983
|
+
};
|
|
984
|
+
echo = {
|
|
985
|
+
boxId: null,
|
|
986
|
+
predicted: this.#paintLocal?.echo.predicted ?? null,
|
|
987
|
+
misaligned: this.#paintLocal?.echo.misaligned ?? false,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
else {
|
|
991
|
+
this.#committed = null;
|
|
992
|
+
progressDisplay = this.#stepProgress(nowMs);
|
|
993
|
+
const collapsed = paintSnapshotWithThoughtCollapsed(paintSnapshotFromMailbox(this.#mailbox), this.#thoughtCollapsed);
|
|
994
|
+
const birthInject = stepThoughtBirthFromSnapshot(collapsed, this.#thoughtBirth, nowMs);
|
|
995
|
+
birth = new Map();
|
|
996
|
+
for (const [id, field] of birthInject)
|
|
997
|
+
birth.set(id, field.W);
|
|
998
|
+
this.#capturePickerFromMailbox();
|
|
999
|
+
snapshot = collapsed;
|
|
1000
|
+
}
|
|
1001
|
+
const hoverProgress = this.#pointerOverProgress();
|
|
1002
|
+
const pickerChrome = this.#pickerLast || this.#hoverKey || this.#pressKey
|
|
1003
|
+
? { hoverKey: this.#hoverKey, pressKey: this.#pressKey }
|
|
1004
|
+
: undefined;
|
|
1005
|
+
const agentsChrome = this.#agentsHoverKey === null ? undefined : { hoverKey: this.#agentsHoverKey };
|
|
1006
|
+
const hostHotkeys = this.#quitArmed
|
|
1007
|
+
? [{ key: "Ctrl+C", action: this.#options.quitAction?.() || "exit" }]
|
|
1008
|
+
: undefined;
|
|
1009
|
+
const birthLive = anyThoughtBirthLive(this.#thoughtBirth);
|
|
1010
|
+
let progressLive = false;
|
|
1011
|
+
for (const anim of this.#progress.values()) {
|
|
1012
|
+
if (progressBarLive(anim)) {
|
|
1013
|
+
progressLive = true;
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
const spinnerHz = this.#frame?.liveDemand?.maxHz ?? 0;
|
|
1018
|
+
const extrasKey = [
|
|
1019
|
+
this.#geometry.cols,
|
|
1020
|
+
this.#geometry.rows,
|
|
1021
|
+
hostHotkeys?.map((h) => `${h.key}:${h.action}`).join("|") ?? "",
|
|
1022
|
+
this.#paintLocal?.modHeld === true ? "1" : "0",
|
|
1023
|
+
hoverProgress ? "1" : "0",
|
|
1024
|
+
pickerChrome?.hoverKey ?? "",
|
|
1025
|
+
pickerChrome?.pressKey ?? "",
|
|
1026
|
+
agentsChrome?.hoverKey ?? "",
|
|
1027
|
+
echo.predicted?.draft ?? "",
|
|
1028
|
+
echo.predicted?.cursor ?? "",
|
|
1029
|
+
echo.predicted?.mark ?? "",
|
|
1030
|
+
echo.misaligned ? "1" : "0",
|
|
1031
|
+
String(this.#themes.ground),
|
|
1032
|
+
this.#themes.depth,
|
|
1033
|
+
].join("\u001F");
|
|
1034
|
+
const cachedStructural = this.#structural;
|
|
1035
|
+
const cachedFrame = this.#frame;
|
|
1036
|
+
const reuseFrame = prepared === this.#lastPrepared && cachedFrame !== null && this.#structuralKey === extrasKey && !progressLive;
|
|
1037
|
+
const reuseBuffer = reuseFrame && cachedStructural !== null && !birthLive && spinnerHz === 0;
|
|
1038
|
+
let rendered;
|
|
1039
|
+
let reusedFrame = false;
|
|
1040
|
+
if (reuseBuffer && cachedStructural !== null && cachedFrame !== null) {
|
|
1041
|
+
rendered = { buffer: cloneBuffer(cachedStructural), frame: cachedFrame };
|
|
1042
|
+
reusedFrame = true;
|
|
1043
|
+
}
|
|
1044
|
+
else if (reuseFrame && cachedFrame !== null) {
|
|
1045
|
+
const buffer = createBuffer(this.#geometry.cols, this.#geometry.rows);
|
|
1046
|
+
paintFrame(cachedFrame, buffer, this.#themes.binding.paint, { nowMs, birth });
|
|
1047
|
+
rendered = { buffer, frame: cachedFrame };
|
|
1048
|
+
reusedFrame = true;
|
|
1049
|
+
}
|
|
1050
|
+
else {
|
|
1051
|
+
rendered = renderFrame({
|
|
1052
|
+
snapshot,
|
|
1053
|
+
geometry: this.#geometry,
|
|
1054
|
+
state: {
|
|
1055
|
+
nowMs,
|
|
1056
|
+
echo,
|
|
1057
|
+
theme: this.#themes.binding.paint,
|
|
1058
|
+
hostHotkeys,
|
|
1059
|
+
birth,
|
|
1060
|
+
progress: progressDisplay,
|
|
1061
|
+
hoverProgress,
|
|
1062
|
+
pickerChrome,
|
|
1063
|
+
agentsChrome,
|
|
1064
|
+
},
|
|
1065
|
+
});
|
|
1066
|
+
this.#frame = rendered.frame;
|
|
1067
|
+
this.#lastPrepared = prepared;
|
|
1068
|
+
this.#structuralKey = extrasKey;
|
|
1069
|
+
const spinnerNow = rendered.frame?.liveDemand?.maxHz ?? 0;
|
|
1070
|
+
this.#structural = !birthLive && !progressLive && spinnerNow === 0 ? cloneBuffer(rendered.buffer) : null;
|
|
1071
|
+
}
|
|
1072
|
+
this.#syncTapeFromFrame();
|
|
1073
|
+
this.#syncPreviewFromFrame();
|
|
1074
|
+
const hasFx = bufferHasBgFx(rendered.buffer);
|
|
1075
|
+
if (hasFx) {
|
|
1076
|
+
this.#armBgfx();
|
|
1077
|
+
if (this.#fxEpoch === null)
|
|
1078
|
+
this.#fxEpoch = nowMs;
|
|
1079
|
+
materializeBgFx(rendered.buffer, {
|
|
1080
|
+
t: (nowMs - this.#fxEpoch) / 1000,
|
|
1081
|
+
polarity: canvasPolarity(this.#themes.ground),
|
|
1082
|
+
resolution: { x: rendered.buffer.width, y: rendered.buffer.height },
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
// Selection recolor post-paint (same law as echo — never inside pure frame).
|
|
1086
|
+
if (rendered.frame !== null) {
|
|
1087
|
+
this.#selection.bindPaint(rendered.buffer, rendered.frame);
|
|
1088
|
+
this.#selection.recolor(rendered.buffer, rendered.frame, this.#themes.theme.ground);
|
|
1089
|
+
}
|
|
1090
|
+
// Copy-toast chrome on a clone — lastBuffer must stay free of plaque glyphs.
|
|
1091
|
+
let present = rendered.buffer;
|
|
1092
|
+
if (this.#copyToast !== null) {
|
|
1093
|
+
present = cloneBuffer(rendered.buffer);
|
|
1094
|
+
paintCopyToast(present, placeCopyToast({
|
|
1095
|
+
end: this.#copyToast.end,
|
|
1096
|
+
anchor: this.#copyToast.anchor,
|
|
1097
|
+
cols: present.width,
|
|
1098
|
+
rows: present.height,
|
|
1099
|
+
}), (style) => this.#themes.binding.paint.resolveStyle(style));
|
|
1100
|
+
}
|
|
1101
|
+
const emitted = emitAnsiDetailed(this.#previous, present, {
|
|
1102
|
+
forceFull: this.#previous === null,
|
|
1103
|
+
sync2026: this.#options.sync2026 === true,
|
|
1104
|
+
});
|
|
1105
|
+
this.#previous = cloneBuffer(present);
|
|
1106
|
+
if (emitted.bytes > 0) {
|
|
1107
|
+
this.#options.onPresent?.({ dirtyRows: emitted.dirtyRows, bytes: emitted.bytes, reusedFrame });
|
|
1108
|
+
}
|
|
1109
|
+
const tickMs = this.#now() - tickStarted;
|
|
1110
|
+
if (tickMs > 32 || emitted.dirtyRows > 0) {
|
|
1111
|
+
tracePresent(tickMs, emitted.dirtyRows, emitted.bytes, reusedFrame);
|
|
1112
|
+
}
|
|
1113
|
+
// Always re-park caret after paint. Enter-alt hid it (`?25l`).
|
|
1114
|
+
const draftState = this.#composerDraftState();
|
|
1115
|
+
const interactive = this.#composerInteractive();
|
|
1116
|
+
const park = parkComposerCaret({
|
|
1117
|
+
draft: draftState.draft,
|
|
1118
|
+
cursor: draftState.cursor,
|
|
1119
|
+
frame: rendered.frame,
|
|
1120
|
+
cols: this.#geometry.cols,
|
|
1121
|
+
rows: this.#geometry.rows,
|
|
1122
|
+
// interactive:false is unfocused for caret. OS focus alone would park a waiting prompt.
|
|
1123
|
+
termFocused: this.#termFocused && interactive,
|
|
1124
|
+
composerFrameKey: this.#composerFrameKey,
|
|
1125
|
+
});
|
|
1126
|
+
const caret = caretAnsi(park);
|
|
1127
|
+
if (emitted.ansi.length > 0 || caret.length > 0) {
|
|
1128
|
+
this.#options.output.write(emitted.ansi + caret);
|
|
1129
|
+
if (!this.#paintedOnce) {
|
|
1130
|
+
this.#paintedOnce = true;
|
|
1131
|
+
this.#options.onFirstPaint?.();
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
// fps-on-demand: keep a timer only while something on screen is alive.
|
|
1135
|
+
const cap = this.#options.targetFps ?? TARGET_FPS;
|
|
1136
|
+
const flourish = this.#options.flourishHz ?? DEFAULT_FLOURISH_HZ;
|
|
1137
|
+
const demandHz = Math.max(rendered.frame?.liveDemand?.maxHz ?? 0, birthLive ? flourish : 0, progressLive ? flourish : 0, hasFx ? flourish : 0);
|
|
1138
|
+
this.#setLiveHz(Math.min(demandHz, cap));
|
|
1139
|
+
this.#syncAnyMotion();
|
|
1140
|
+
this.#syncPointerShape();
|
|
1141
|
+
}
|
|
1142
|
+
#maybeFlushEsc() {
|
|
1143
|
+
if (this.#pendingBytes !== "\u001B")
|
|
1144
|
+
return;
|
|
1145
|
+
if (this.#now() - this.#lastByteAt < ESC_QUIET_MS)
|
|
1146
|
+
return;
|
|
1147
|
+
const flushed = flushPendingEsc(this.#pendingBytes);
|
|
1148
|
+
this.#pendingBytes = flushed.pending;
|
|
1149
|
+
this.#acceptDecoded(flushed.events);
|
|
1150
|
+
}
|
|
1151
|
+
#boxIdForKey(key) {
|
|
1152
|
+
const tree = this.#mailbox.tree;
|
|
1153
|
+
if (typeof tree !== "object" || tree === null)
|
|
1154
|
+
return null;
|
|
1155
|
+
const boxes = tree.boxes;
|
|
1156
|
+
const boxId = boxes?.[key];
|
|
1157
|
+
return typeof boxId === "number" ? boxId : null;
|
|
1158
|
+
}
|
|
1159
|
+
#composerBoxId() {
|
|
1160
|
+
return this.#boxIdForKey(this.#composerBoxKey);
|
|
1161
|
+
}
|
|
1162
|
+
/** Authoritative draft/caret/mark from mirror (or standing prediction). */
|
|
1163
|
+
#composerDraftState() {
|
|
1164
|
+
if (this.#committed !== null) {
|
|
1165
|
+
const predicted = this.#paintLocal?.echo.predicted;
|
|
1166
|
+
if (predicted)
|
|
1167
|
+
return predicted;
|
|
1168
|
+
return this.#committed.composer;
|
|
1169
|
+
}
|
|
1170
|
+
if (this.#echo.predicted)
|
|
1171
|
+
return this.#echo.predicted;
|
|
1172
|
+
const boxId = this.#echo.boxId ?? this.#composerBoxId();
|
|
1173
|
+
if (boxId === null)
|
|
1174
|
+
return { draft: "", cursor: 0, mark: null };
|
|
1175
|
+
const value = this.#mailbox.boxes.get(boxId);
|
|
1176
|
+
return readDraftState(value, this.#composerFrameKey);
|
|
1177
|
+
}
|
|
1178
|
+
#composerInteractive() {
|
|
1179
|
+
if (this.#committed !== null)
|
|
1180
|
+
return this.#committed.composer.interactive;
|
|
1181
|
+
const boxId = this.#echo.boxId ?? this.#composerBoxId();
|
|
1182
|
+
if (boxId === null)
|
|
1183
|
+
return true;
|
|
1184
|
+
return readComposerInteractive(this.#mailbox.boxes.get(boxId), this.#composerFrameKey);
|
|
1185
|
+
}
|
|
1186
|
+
#stepProgress(nowMs, preparedSites) {
|
|
1187
|
+
const sites = [];
|
|
1188
|
+
if (preparedSites === undefined) {
|
|
1189
|
+
for (const value of this.#mailbox.boxes.values()) {
|
|
1190
|
+
if (typeof value === "object" && value !== null && "type" in value) {
|
|
1191
|
+
sites.push(...collectProgressSites(value));
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1196
|
+
sites.push(...preparedSites);
|
|
1197
|
+
}
|
|
1198
|
+
const live = new Set();
|
|
1199
|
+
const display = new Map();
|
|
1200
|
+
for (const site of sites) {
|
|
1201
|
+
live.add(site.key);
|
|
1202
|
+
const next = stepProgressBar(this.#progress.get(site.key), site.progress, site.role, nowMs);
|
|
1203
|
+
this.#progress.set(site.key, next);
|
|
1204
|
+
display.set(site.key, {
|
|
1205
|
+
displayed: next.displayed,
|
|
1206
|
+
role: next.displayedRole,
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
for (const key of this.#progress.keys()) {
|
|
1210
|
+
if (!live.has(key))
|
|
1211
|
+
this.#progress.delete(key);
|
|
1212
|
+
}
|
|
1213
|
+
return display;
|
|
1214
|
+
}
|
|
1215
|
+
#pointerOverProgress() {
|
|
1216
|
+
const ptr = this.#pointer;
|
|
1217
|
+
const frame = this.#frame;
|
|
1218
|
+
if (!ptr || !frame)
|
|
1219
|
+
return false;
|
|
1220
|
+
const hit = frame.byKey.get(`${this.#composerFrameKey}:progress`)?.hitRect;
|
|
1221
|
+
if (!hit || hit.w <= 0 || hit.h <= 0)
|
|
1222
|
+
return false;
|
|
1223
|
+
return ptr.col >= hit.x && ptr.col < hit.x + hit.w && ptr.row >= hit.y && ptr.row < hit.y + hit.h;
|
|
1224
|
+
}
|
|
1225
|
+
#capturePickerFromMailbox() {
|
|
1226
|
+
const node = findPickerNode(this.#mailbox.tree);
|
|
1227
|
+
if (!node) {
|
|
1228
|
+
for (const value of this.#mailbox.boxes.values()) {
|
|
1229
|
+
const found = findPickerNode(value);
|
|
1230
|
+
if (found) {
|
|
1231
|
+
this.#pickerLast = pickerLastFromNode(found);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
this.#pickerLast = null;
|
|
1236
|
+
this.#hoverKey = null;
|
|
1237
|
+
this.#pressKey = null;
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
this.#pickerLast = pickerLastFromNode(node);
|
|
1241
|
+
}
|
|
1242
|
+
#tapeViewport() {
|
|
1243
|
+
const frame = this.#frame;
|
|
1244
|
+
if (frame === null)
|
|
1245
|
+
return null;
|
|
1246
|
+
return tapeViewportMetrics(frame.byKey);
|
|
1247
|
+
}
|
|
1248
|
+
#agentsViewport() {
|
|
1249
|
+
const frame = this.#frame;
|
|
1250
|
+
if (frame === null)
|
|
1251
|
+
return null;
|
|
1252
|
+
return agentsViewportMetrics(frame.byKey);
|
|
1253
|
+
}
|
|
1254
|
+
#previewViewport() {
|
|
1255
|
+
const frame = this.#frame;
|
|
1256
|
+
if (frame === null)
|
|
1257
|
+
return null;
|
|
1258
|
+
return agentsPreviewViewportMetrics(frame.byKey);
|
|
1259
|
+
}
|
|
1260
|
+
#pointerInTape() {
|
|
1261
|
+
const ptr = this.#pointer;
|
|
1262
|
+
const vp = this.#tapeViewport();
|
|
1263
|
+
if (!ptr || vp === null)
|
|
1264
|
+
return false;
|
|
1265
|
+
const r = vp.hitRect;
|
|
1266
|
+
return ptr.col >= r.x && ptr.col < r.x + r.w && ptr.row >= r.y && ptr.row < r.y + r.h;
|
|
1267
|
+
}
|
|
1268
|
+
#pointerInPreview() {
|
|
1269
|
+
const ptr = this.#pointer;
|
|
1270
|
+
const frame = this.#frame;
|
|
1271
|
+
if (!ptr || frame === null)
|
|
1272
|
+
return false;
|
|
1273
|
+
const modal = frame.byKey.get(APP_AGENTS_PREVIEW_KEY);
|
|
1274
|
+
if (modal === undefined)
|
|
1275
|
+
return false;
|
|
1276
|
+
const r = modal.hitRect;
|
|
1277
|
+
return ptr.col >= r.x && ptr.col < r.x + r.w && ptr.row >= r.y && ptr.row < r.y + r.h;
|
|
1278
|
+
}
|
|
1279
|
+
#toggleTapeExpanded() {
|
|
1280
|
+
const local = this.#paintLocal;
|
|
1281
|
+
if (local === null)
|
|
1282
|
+
return;
|
|
1283
|
+
writePaintTapeExpanded(local, !local.tapeExpanded);
|
|
1284
|
+
this.requestPaint();
|
|
1285
|
+
}
|
|
1286
|
+
#scrollTape(delta) {
|
|
1287
|
+
const local = this.#paintLocal;
|
|
1288
|
+
const vp = this.#tapeViewport();
|
|
1289
|
+
if (local === null || vp === null)
|
|
1290
|
+
return;
|
|
1291
|
+
const from = local.tapeAtBottom ? vp.maxScroll : local.tapeScroll;
|
|
1292
|
+
const next = nextTapeScroll(from, delta, vp.maxScroll);
|
|
1293
|
+
if (next.scroll === local.tapeScroll && next.atBottom === local.tapeAtBottom)
|
|
1294
|
+
return;
|
|
1295
|
+
writePaintTapeScroll(local, next.scroll, next.atBottom);
|
|
1296
|
+
this.requestPaint();
|
|
1297
|
+
}
|
|
1298
|
+
#scrollPreview(delta) {
|
|
1299
|
+
const local = this.#paintLocal;
|
|
1300
|
+
const vp = this.#previewViewport();
|
|
1301
|
+
if (local === null || vp === null)
|
|
1302
|
+
return;
|
|
1303
|
+
const from = local.previewAtBottom ? vp.maxScroll : local.previewScroll;
|
|
1304
|
+
const next = nextTapeScroll(from, delta, vp.maxScroll);
|
|
1305
|
+
if (next.scroll === local.previewScroll && next.atBottom === local.previewAtBottom)
|
|
1306
|
+
return;
|
|
1307
|
+
writePaintPreviewScroll(local, next.scroll, next.atBottom);
|
|
1308
|
+
this.requestPaint();
|
|
1309
|
+
}
|
|
1310
|
+
#syncTapeFromFrame() {
|
|
1311
|
+
const local = this.#paintLocal;
|
|
1312
|
+
const vp = this.#tapeViewport();
|
|
1313
|
+
if (local === null || vp === null)
|
|
1314
|
+
return;
|
|
1315
|
+
if (local.tapeAtBottom) {
|
|
1316
|
+
writePaintTapeScroll(local, vp.scrollY, true);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (local.tapeScroll > vp.maxScroll) {
|
|
1320
|
+
writePaintTapeScroll(local, vp.maxScroll, true);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
#syncPreviewFromFrame() {
|
|
1324
|
+
const local = this.#paintLocal;
|
|
1325
|
+
if (local === null)
|
|
1326
|
+
return;
|
|
1327
|
+
const vp = this.#previewViewport();
|
|
1328
|
+
if (vp === null) {
|
|
1329
|
+
writePaintPreviewScroll(local, 0, true);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (local.previewAtBottom) {
|
|
1333
|
+
writePaintPreviewScroll(local, vp.scrollY, true);
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
if (local.previewScroll > vp.maxScroll) {
|
|
1337
|
+
writePaintPreviewScroll(local, vp.maxScroll, true);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
#pointerInPickerChrome() {
|
|
1341
|
+
const ptr = this.#pointer;
|
|
1342
|
+
const frame = this.#frame;
|
|
1343
|
+
if (!ptr || !frame)
|
|
1344
|
+
return false;
|
|
1345
|
+
const chrome = frame.byKey.get("slash-complete:chrome");
|
|
1346
|
+
if (!chrome)
|
|
1347
|
+
return false;
|
|
1348
|
+
const r = chrome.hitRect;
|
|
1349
|
+
return ptr.col >= r.x && ptr.col < r.x + r.w && ptr.row >= r.y && ptr.row < r.y + r.h;
|
|
1350
|
+
}
|
|
1351
|
+
#pickerKeyAt() {
|
|
1352
|
+
const ptr = this.#pointer;
|
|
1353
|
+
const frame = this.#frame;
|
|
1354
|
+
const last = this.#pickerLast;
|
|
1355
|
+
if (!ptr || !frame || !last)
|
|
1356
|
+
return null;
|
|
1357
|
+
const chrome = frame.byKey.get("slash-complete:chrome");
|
|
1358
|
+
if (!chrome)
|
|
1359
|
+
return null;
|
|
1360
|
+
const r = chrome.hitRect;
|
|
1361
|
+
if (ptr.col < r.x || ptr.col >= r.x + r.w || ptr.row < r.y || ptr.row >= r.y + r.h) {
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
const bodyRow = ptr.row - (r.y + 1);
|
|
1365
|
+
return last.paintRowToKey[bodyRow] ?? null;
|
|
1366
|
+
}
|
|
1367
|
+
#pointerInAgentsPanel() {
|
|
1368
|
+
const ptr = this.#pointer;
|
|
1369
|
+
const frame = this.#frame;
|
|
1370
|
+
if (!ptr || !frame)
|
|
1371
|
+
return false;
|
|
1372
|
+
const panel = frame.byKey.get("agents:panel");
|
|
1373
|
+
if (panel === undefined)
|
|
1374
|
+
return false;
|
|
1375
|
+
const r = panel.hitRect;
|
|
1376
|
+
return ptr.col >= r.x && ptr.col < r.x + r.w && ptr.row >= r.y && ptr.row < r.y + r.h;
|
|
1377
|
+
}
|
|
1378
|
+
#rectContains(r, col, row) {
|
|
1379
|
+
return col >= r.x && col < r.x + r.w && row >= r.y && row < r.y + r.h;
|
|
1380
|
+
}
|
|
1381
|
+
#pointerInAgentsFurniture() {
|
|
1382
|
+
if (this.#pointerInAgentsPanel())
|
|
1383
|
+
return true;
|
|
1384
|
+
const ptr = this.#pointer;
|
|
1385
|
+
const frame = this.#frame;
|
|
1386
|
+
if (ptr == null || frame == null)
|
|
1387
|
+
return false;
|
|
1388
|
+
const peek = frame.byKey.get(APP_AGENTS_PEEK_KEY);
|
|
1389
|
+
if (peek !== undefined && this.#rectContains(peek.hitRect, ptr.col, ptr.row))
|
|
1390
|
+
return true;
|
|
1391
|
+
const panel = frame.byKey.get("agents:panel");
|
|
1392
|
+
if (panel === undefined || peek === undefined)
|
|
1393
|
+
return false;
|
|
1394
|
+
const left = panel.hitRect.x + panel.hitRect.w;
|
|
1395
|
+
const right = peek.hitRect.x;
|
|
1396
|
+
const top = Math.min(panel.hitRect.y, peek.hitRect.y);
|
|
1397
|
+
const bottom = Math.max(panel.hitRect.y + panel.hitRect.h, peek.hitRect.y + peek.hitRect.h);
|
|
1398
|
+
return ptr.col >= left && ptr.col < right && ptr.row >= top && ptr.row < bottom;
|
|
1399
|
+
}
|
|
1400
|
+
#agentsKeepAliveKeyAt() {
|
|
1401
|
+
const hoverable = this.#agentsHoverableKeyAt();
|
|
1402
|
+
if (hoverable != null)
|
|
1403
|
+
return hoverable;
|
|
1404
|
+
if (this.#pointerInAgentsFurniture())
|
|
1405
|
+
return this.#agentsHoverKey ?? this.#hoverSuppressKey;
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1408
|
+
#applyAgentsPointer() {
|
|
1409
|
+
const next = this.#agentsKeepAliveKeyAt();
|
|
1410
|
+
if (this.#hoverSuppressKey !== null) {
|
|
1411
|
+
if (next === this.#hoverSuppressKey)
|
|
1412
|
+
return;
|
|
1413
|
+
this.#hoverSuppressKey = null;
|
|
1414
|
+
}
|
|
1415
|
+
if (next === this.#agentsHoverKey)
|
|
1416
|
+
return;
|
|
1417
|
+
this.#agentsHoverKey = next;
|
|
1418
|
+
this.requestPaint();
|
|
1419
|
+
this.#send({ kind: "agents_hover", key: next });
|
|
1420
|
+
}
|
|
1421
|
+
#armHoverSuppress() {
|
|
1422
|
+
this.#hoverSuppressKey = this.#agentsKeepAliveKeyAt() ?? this.#agentsHoverKey;
|
|
1423
|
+
if (this.#agentsHoverKey !== null) {
|
|
1424
|
+
this.#agentsHoverKey = null;
|
|
1425
|
+
this.requestPaint();
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
#clearAgentsHover() {
|
|
1429
|
+
const had = this.#agentsHoverKey !== null || this.#hoverSuppressKey !== null;
|
|
1430
|
+
this.#hoverSuppressKey = null;
|
|
1431
|
+
if (this.#agentsHoverKey !== null) {
|
|
1432
|
+
this.#agentsHoverKey = null;
|
|
1433
|
+
this.requestPaint();
|
|
1434
|
+
}
|
|
1435
|
+
if (had)
|
|
1436
|
+
this.#send({ kind: "agents_hover", key: null });
|
|
1437
|
+
}
|
|
1438
|
+
#agentsPreviewOpen() {
|
|
1439
|
+
return this.#frame?.byKey.has(APP_AGENTS_PREVIEW_KEY) === true;
|
|
1440
|
+
}
|
|
1441
|
+
#agentsHoverableKeyAt() {
|
|
1442
|
+
const ptr = this.#pointer;
|
|
1443
|
+
const frame = this.#frame;
|
|
1444
|
+
if (!ptr || !frame || this.#agentsViewport() === null)
|
|
1445
|
+
return null;
|
|
1446
|
+
const path = elementAtPath(frame, ptr.col, ptr.row);
|
|
1447
|
+
if (path === null)
|
|
1448
|
+
return null;
|
|
1449
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
1450
|
+
const key = path[i].key;
|
|
1451
|
+
if (key === "")
|
|
1452
|
+
continue;
|
|
1453
|
+
const row = agentsRowKeyFromHit(key);
|
|
1454
|
+
if (row == null)
|
|
1455
|
+
continue;
|
|
1456
|
+
if (row.startsWith("session:") || row.startsWith("call:"))
|
|
1457
|
+
return row;
|
|
1458
|
+
}
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
#activateHit() {
|
|
1462
|
+
const ptr = this.#pointer;
|
|
1463
|
+
const frame = this.#frame;
|
|
1464
|
+
if (ptr == null || frame == null)
|
|
1465
|
+
return false;
|
|
1466
|
+
const path = elementAtPath(frame, ptr.col, ptr.row);
|
|
1467
|
+
if (path == null)
|
|
1468
|
+
return false;
|
|
1469
|
+
return path.some((link) => link.meta.intent === "activate");
|
|
1470
|
+
}
|
|
1471
|
+
#syncPointerShape() {
|
|
1472
|
+
const want = this.#activateHit() ? "pointer" : "default";
|
|
1473
|
+
if (want === this.#pointerShape)
|
|
1474
|
+
return;
|
|
1475
|
+
this.#pointerShape = want;
|
|
1476
|
+
this.#options.output.write(mousePointerBytes(want));
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
function findPickerNode(value) {
|
|
1480
|
+
if (typeof value !== "object" || value === null)
|
|
1481
|
+
return null;
|
|
1482
|
+
const rec = value;
|
|
1483
|
+
if (rec.type === "chrome_frame" && isPickerPayload(rec.picker)) {
|
|
1484
|
+
return { picker: rec.picker };
|
|
1485
|
+
}
|
|
1486
|
+
if (Array.isArray(rec.children)) {
|
|
1487
|
+
for (const child of rec.children) {
|
|
1488
|
+
const found = findPickerNode(child);
|
|
1489
|
+
if (found)
|
|
1490
|
+
return found;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
return null;
|
|
1494
|
+
}
|
|
1495
|
+
function isPickerPayload(value) {
|
|
1496
|
+
if (typeof value !== "object" || value === null)
|
|
1497
|
+
return false;
|
|
1498
|
+
const p = value;
|
|
1499
|
+
return Array.isArray(p.rows) && typeof p.scrollOffset === "number" && typeof p.viewport === "number";
|
|
1500
|
+
}
|
|
1501
|
+
function pickerLastFromNode(node) {
|
|
1502
|
+
const { rows, scrollOffset, viewport } = node.picker;
|
|
1503
|
+
const n = rows.length;
|
|
1504
|
+
const maxS = Math.max(0, n - viewport);
|
|
1505
|
+
const scroll = Math.min(maxS, Math.max(0, scrollOffset));
|
|
1506
|
+
const visible = rows.slice(scroll, scroll + viewport);
|
|
1507
|
+
const paintRowToKey = [];
|
|
1508
|
+
for (const row of visible) {
|
|
1509
|
+
const key = row.key ?? null;
|
|
1510
|
+
paintRowToKey.push(key);
|
|
1511
|
+
if ((row.href ?? "").trim().length > 0)
|
|
1512
|
+
paintRowToKey.push(key);
|
|
1513
|
+
}
|
|
1514
|
+
return { paintRowToKey, n, viewport, scrollOffset: scroll };
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Walk box value (stack or chrome_frame) for draft/cursor/mark.
|
|
1518
|
+
*
|
|
1519
|
+
* Slash mode stacks `slash-complete` (list panel with bodySegRows, empty draft)
|
|
1520
|
+
* above `composer:frame`. Returning the first chrome_frame always hit the empty
|
|
1521
|
+
* list panel — caret stayed at cell 0 while typing. Only the omnibox chrome_frame
|
|
1522
|
+
* is the draft surface.
|
|
1523
|
+
*/
|
|
1524
|
+
export function readComposerInteractive(value, composerFrameKey = "composer:frame") {
|
|
1525
|
+
if (typeof value !== "object" || value === null)
|
|
1526
|
+
return true;
|
|
1527
|
+
const rec = value;
|
|
1528
|
+
if (rec.type === "chrome_frame" && !Array.isArray(rec.bodySegRows)) {
|
|
1529
|
+
return rec.interactive !== false;
|
|
1530
|
+
}
|
|
1531
|
+
if (Array.isArray(rec.children)) {
|
|
1532
|
+
for (const child of rec.children) {
|
|
1533
|
+
if (typeof child === "object" && child !== null && child.key === composerFrameKey) {
|
|
1534
|
+
return readComposerInteractive(child, composerFrameKey);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
for (const child of rec.children) {
|
|
1538
|
+
if (typeof child === "object" &&
|
|
1539
|
+
child !== null &&
|
|
1540
|
+
child.type === "chrome_frame" &&
|
|
1541
|
+
!Array.isArray(child.bodySegRows)) {
|
|
1542
|
+
return readComposerInteractive(child, composerFrameKey);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
return true;
|
|
1547
|
+
}
|
|
1548
|
+
export function readDraftState(value, composerFrameKey = "composer:frame") {
|
|
1549
|
+
if (typeof value !== "object" || value === null) {
|
|
1550
|
+
return { draft: "", cursor: 0, mark: null };
|
|
1551
|
+
}
|
|
1552
|
+
const rec = value;
|
|
1553
|
+
// List/completion chrome uses bodySegRows — not a draft surface.
|
|
1554
|
+
if (rec.type === "chrome_frame" && !Array.isArray(rec.bodySegRows)) {
|
|
1555
|
+
const draft = typeof rec.draft === "string" ? rec.draft : "";
|
|
1556
|
+
const cursor = typeof rec.cursor === "number" ? Math.max(0, Math.min(draft.length, rec.cursor)) : draft.length;
|
|
1557
|
+
const mark = typeof rec.mark === "number" ? Math.max(0, Math.min(draft.length, rec.mark)) : null;
|
|
1558
|
+
return { draft, cursor, mark };
|
|
1559
|
+
}
|
|
1560
|
+
if (Array.isArray(rec.children)) {
|
|
1561
|
+
for (const child of rec.children) {
|
|
1562
|
+
if (typeof child === "object" && child !== null && child.key === composerFrameKey) {
|
|
1563
|
+
return readDraftState(child, composerFrameKey);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
let emptyComposer = null;
|
|
1567
|
+
for (const child of rec.children) {
|
|
1568
|
+
const found = readDraftState(child, composerFrameKey);
|
|
1569
|
+
if (found.draft.length > 0 || found.cursor > 0 || found.mark !== null) {
|
|
1570
|
+
return found;
|
|
1571
|
+
}
|
|
1572
|
+
if (typeof child === "object" &&
|
|
1573
|
+
child !== null &&
|
|
1574
|
+
child.type === "chrome_frame" &&
|
|
1575
|
+
!Array.isArray(child.bodySegRows)) {
|
|
1576
|
+
// Empty draft is still a valid omnibox (placeholder / idle).
|
|
1577
|
+
emptyComposer = found;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
if (emptyComposer)
|
|
1581
|
+
return emptyComposer;
|
|
1582
|
+
}
|
|
1583
|
+
return { draft: "", cursor: 0, mark: null };
|
|
1584
|
+
}
|
|
1585
|
+
/** Standing draft mark — the range insert/backspace/delete replace. */
|
|
1586
|
+
function tracePresent(tickMs, dirtyRows, bytes, reusedFrame) {
|
|
1587
|
+
const raw = process.env.DEBUG;
|
|
1588
|
+
if (raw === undefined || raw.trim() === "")
|
|
1589
|
+
return;
|
|
1590
|
+
const names = raw.split(/[\s,]+/).filter((part) => part !== "");
|
|
1591
|
+
const enabled = names.some((part) => {
|
|
1592
|
+
const name = part.startsWith("-") ? "" : part;
|
|
1593
|
+
return name === "*" || name === "quickdraw:present" || name === "quickdraw:*" || name.startsWith("quickdraw:");
|
|
1594
|
+
});
|
|
1595
|
+
if (!enabled)
|
|
1596
|
+
return;
|
|
1597
|
+
process.stderr.write(`${new Date().toISOString()} quickdraw:present pid=${process.pid} tickMs=${tickMs} dirtyRows=${dirtyRows} bytes=${bytes} reused=${reusedFrame}\n`);
|
|
1598
|
+
}
|
|
1599
|
+
function standingDraftSelection(state) {
|
|
1600
|
+
if (selectionRange(state) === null)
|
|
1601
|
+
return null;
|
|
1602
|
+
const mark = state.mark;
|
|
1603
|
+
if (mark === null)
|
|
1604
|
+
return null;
|
|
1605
|
+
return { cursor: state.cursor, mark };
|
|
1606
|
+
}
|
|
1607
|
+
//# sourceMappingURL=renderer.js.map
|