rovecode 0.4.0-beta.3 → 0.4.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/README.md +57 -72
- package/THIRD_PARTY_NOTICES.md +0 -44
- package/bin/rovecode.ts +21 -0
- package/package.json +16 -38
- package/src/account/keys.ts +97 -0
- package/src/account/login.ts +158 -0
- package/src/account/provision.ts +47 -0
- package/src/account/store.ts +63 -0
- package/src/acp/server.ts +373 -0
- package/src/cli/account-cmd.ts +116 -0
- package/src/cli/connect.ts +244 -0
- package/src/cli/context-cmd.ts +199 -0
- package/src/cli/dispatch.ts +109 -0
- package/src/cli/doctor.ts +324 -0
- package/src/cli/export.ts +278 -0
- package/src/cli/help.ts +240 -0
- package/src/cli/is-tui-invocation.ts +8 -0
- package/src/cli/main.ts +599 -0
- package/src/cli/market-cmd.ts +658 -0
- package/src/cli/mcp-market-cmd.ts +299 -0
- package/src/cli/output.ts +382 -0
- package/src/cli/repl.ts +172 -0
- package/src/cli/resume.ts +32 -0
- package/src/cli/run-limits.ts +78 -0
- package/src/cli/runtime.ts +792 -0
- package/src/cli/setup.ts +187 -0
- package/src/cli/update-cmd.ts +78 -0
- package/src/cli/workflow-cmd.ts +100 -0
- package/src/coding/checkpoints.ts +270 -0
- package/src/coding/diff.ts +136 -0
- package/src/coding/files.ts +339 -0
- package/src/coding/hashline.ts +319 -0
- package/src/coding/lsp.ts +406 -0
- package/src/coding/repomap-cache.ts +99 -0
- package/src/coding/repomap-files.ts +110 -0
- package/src/coding/repomap.ts +392 -0
- package/src/core/compaction.ts +399 -0
- package/src/core/config.ts +289 -0
- package/src/core/context-report.ts +228 -0
- package/src/core/context.ts +60 -0
- package/src/core/count-remote.ts +107 -0
- package/src/core/execpolicy-rules.ts +196 -0
- package/src/core/execpolicy.ts +385 -0
- package/src/core/executor.ts +397 -0
- package/src/core/guardrails.ts +400 -0
- package/src/core/hooks.ts +398 -0
- package/src/core/images.ts +230 -0
- package/src/core/intro.ts +236 -0
- package/src/core/loop.ts +621 -0
- package/src/core/modes.ts +372 -0
- package/src/core/orchestrator.ts +207 -0
- package/src/core/reflection.ts +165 -0
- package/src/core/sandbox-config.ts +167 -0
- package/src/core/session-images.ts +73 -0
- package/src/core/session.ts +398 -0
- package/src/core/settings.ts +98 -0
- package/src/core/stuck-detector.ts +273 -0
- package/src/core/tasks.ts +374 -0
- package/src/core/token-scale.ts +108 -0
- package/src/core/tool-output-budget.ts +166 -0
- package/src/core/tools.ts +288 -0
- package/src/core/types.ts +330 -0
- package/src/core/update-check.ts +171 -0
- package/src/core/update.ts +158 -0
- package/src/core/usage.ts +204 -0
- package/src/core/validate.ts +121 -0
- package/src/core/verify-gate.ts +159 -0
- package/src/core/verify.ts +237 -0
- package/src/core/voice.ts +158 -0
- package/src/core/win-job.ts +183 -0
- package/src/design/audit.ts +797 -0
- package/src/design/direction.ts +190 -0
- package/src/design/rules.ts +157 -0
- package/src/eval/bench.ts +150 -0
- package/src/eval/gauntlet-runner.ts +218 -0
- package/src/eval/gauntlet.ts +226 -0
- package/src/eval/grader.ts +186 -0
- package/src/eval/record.ts +202 -0
- package/src/eval/redact.ts +141 -0
- package/src/eval/replay.ts +147 -0
- package/src/eval/trajectory.ts +373 -0
- package/src/index.ts +17 -0
- package/src/market/catalogs/mcp-docs.json +111 -0
- package/src/market/catalogs/plugins.json +111 -0
- package/src/market/catalogs/skills.json +478 -0
- package/src/market/clone.ts +72 -0
- package/src/market/context-cost.ts +121 -0
- package/src/market/digest.ts +106 -0
- package/src/market/index.ts +22 -0
- package/src/market/install.ts +578 -0
- package/src/market/manifest.ts +187 -0
- package/src/market/prereq.ts +145 -0
- package/src/market/registry.ts +363 -0
- package/src/market/resolve.ts +111 -0
- package/src/market/types.ts +236 -0
- package/src/market/validate.ts +227 -0
- package/src/mcp/client.ts +431 -0
- package/src/mcp/config.ts +239 -0
- package/src/mcp/local-package.ts +211 -0
- package/src/mcp/market-catalog.ts +84 -0
- package/src/mcp/market-install.ts +289 -0
- package/src/mcp/market.ts +0 -0
- package/src/mcp/tools.ts +131 -0
- package/src/mcp/trust.ts +49 -0
- package/src/memory/blocks.ts +175 -0
- package/src/memory/recall.ts +355 -0
- package/src/memory/store.ts +105 -0
- package/src/memory/tools.ts +99 -0
- package/src/plugins/cli.ts +123 -0
- package/src/plugins/discover.ts +108 -0
- package/src/plugins/index.ts +50 -0
- package/src/plugins/init.ts +140 -0
- package/src/plugins/install.ts +184 -0
- package/src/plugins/load.ts +149 -0
- package/src/plugins/manifest.ts +106 -0
- package/src/plugins/state.ts +83 -0
- package/src/providers/auth.ts +293 -0
- package/src/providers/cache.ts +223 -0
- package/src/providers/catalog-local.ts +160 -0
- package/src/providers/catalog.ts +408 -0
- package/src/providers/middleware-context.ts +86 -0
- package/src/providers/middleware.ts +373 -0
- package/src/providers/profile-glm53.ts +111 -0
- package/src/providers/profile-sonnet5-persona.ts +65 -0
- package/src/providers/profile-sonnet5-voice.ts +23 -0
- package/src/providers/profiles.ts +156 -0
- package/src/providers/provider-config.ts +311 -0
- package/src/providers/registry.ts +302 -0
- package/src/providers/response-validation.ts +80 -0
- package/src/providers/retry.ts +234 -0
- package/src/providers/router.ts +294 -0
- package/src/providers/sse.ts +26 -0
- package/src/providers/stream-errors.ts +117 -0
- package/src/providers/stream.ts +569 -0
- package/src/providers/thinking.ts +189 -0
- package/src/providers/wire-messages.ts +129 -0
- package/src/sdk/client.ts +225 -0
- package/src/sdk/index.ts +3 -0
- package/src/server/dashboard.ts +144 -0
- package/src/server/http.ts +343 -0
- package/src/server/openapi.ts +246 -0
- package/src/sextant/card-hits.ts +102 -0
- package/src/sextant/card-keys.ts +55 -0
- package/src/sextant/context-source.ts +157 -0
- package/src/sextant/draw-agents.ts +273 -0
- package/src/sextant/draw-code.ts +388 -0
- package/src/sextant/draw-context.ts +222 -0
- package/src/sextant/draw-frame.ts +164 -0
- package/src/sextant/draw-market.ts +573 -0
- package/src/sextant/draw-messages.ts +386 -0
- package/src/sextant/draw-pet.ts +230 -0
- package/src/sextant/draw-plan.ts +159 -0
- package/src/sextant/draw-tabs.ts +85 -0
- package/src/sextant/draw-util.ts +65 -0
- package/src/sextant/engine.ts +230 -0
- package/src/sextant/frame-hits.ts +25 -0
- package/src/sextant/frame.ts +101 -0
- package/src/sextant/git-status.ts +197 -0
- package/src/sextant/grid.ts +59 -0
- package/src/sextant/input.ts +119 -0
- package/src/sextant/keys.ts +488 -0
- package/src/sextant/layout.ts +86 -0
- package/src/sextant/local-commands.ts +156 -0
- package/src/sextant/market-source.ts +287 -0
- package/src/sextant/mentions.ts +141 -0
- package/src/sextant/message-hits.ts +26 -0
- package/src/sextant/model.ts +387 -0
- package/src/sextant/overlays.ts +451 -0
- package/src/sextant/panel-hits.ts +38 -0
- package/src/sextant/pet.ts +399 -0
- package/src/sextant/screen.ts +324 -0
- package/src/sextant/scroll-hits.ts +66 -0
- package/src/sextant/scrollbar.ts +82 -0
- package/src/sextant/selection.ts +123 -0
- package/src/sextant/sextant-bridge.ts +174 -0
- package/src/sextant/sextant-cards.ts +142 -0
- package/src/sextant/sextant-diff-base.ts +63 -0
- package/src/sextant/sextant-files.ts +154 -0
- package/src/sextant/sextant-frame-loop.ts +314 -0
- package/src/sextant/sextant-renderer.ts +478 -0
- package/src/sextant/sextant-repo.ts +131 -0
- package/src/sextant/theme.ts +66 -0
- package/src/sextant/tool-rows.ts +189 -0
- package/src/sextant/types.ts +473 -0
- package/src/skills/index.ts +306 -0
- package/src/skills/tools.ts +69 -0
- package/src/skills/versioned.ts +227 -0
- package/src/telemetry/otel.ts +353 -0
- package/src/telemetry/otlp.ts +68 -0
- package/src/tools/ask-user.ts +156 -0
- package/src/tools/design.ts +151 -0
- package/src/tools/evalcell.ts +338 -0
- package/src/tools/html-text.ts +139 -0
- package/src/tools/provider.ts +149 -0
- package/src/tools/task.ts +216 -0
- package/src/tools/todo.ts +320 -0
- package/src/tools/webfetch.ts +331 -0
- package/src/tui/app.ts +608 -0
- package/src/tui/attach.ts +127 -0
- package/src/tui/checkpoints-cmd.ts +70 -0
- package/src/tui/clipboard-image.ts +81 -0
- package/src/tui/commands.ts +277 -0
- package/src/tui/cost.ts +108 -0
- package/src/tui/info-cmd.ts +144 -0
- package/src/tui/mcp-cmd.ts +128 -0
- package/src/tui/modes-cmd.ts +45 -0
- package/src/tui/overlays.ts +97 -0
- package/src/tui/pi-renderer.ts +424 -0
- package/src/tui/providers-cmd.ts +366 -0
- package/src/tui/renderer.ts +101 -0
- package/src/tui/replay-marker.ts +29 -0
- package/src/tui/session-cmd.ts +146 -0
- package/src/tui/sextant-attach.ts +68 -0
- package/src/tui/sextant-io.ts +184 -0
- package/src/tui/sextant-smoke.ts +110 -0
- package/src/tui/smoke.ts +72 -0
- package/src/tui/theme.ts +59 -0
- package/src/tui/todo-label.ts +7 -0
- package/src/workflow/engine.ts +266 -0
- package/tsconfig.json +30 -0
- package/vendor/pi-tui/LICENSE +21 -0
- package/vendor/pi-tui/PATCHES.md +12 -0
- package/vendor/pi-tui/PROVENANCE.md +12 -0
- package/vendor/pi-tui/README.upstream.md +854 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/src/alt-screen-search.ts +158 -0
- package/vendor/pi-tui/src/autocomplete.ts +827 -0
- package/vendor/pi-tui/src/components/alt-screen-flash.ts +52 -0
- package/vendor/pi-tui/src/components/box.ts +138 -0
- package/vendor/pi-tui/src/components/cancellable-loader.ts +41 -0
- package/vendor/pi-tui/src/components/editor.ts +2364 -0
- package/vendor/pi-tui/src/components/h-stack.ts +45 -0
- package/vendor/pi-tui/src/components/image.ts +128 -0
- package/vendor/pi-tui/src/components/input.ts +448 -0
- package/vendor/pi-tui/src/components/loader.ts +93 -0
- package/vendor/pi-tui/src/components/markdown.ts +1016 -0
- package/vendor/pi-tui/src/components/scroll-view.ts +217 -0
- package/vendor/pi-tui/src/components/select-list.ts +230 -0
- package/vendor/pi-tui/src/components/settings-list.ts +277 -0
- package/vendor/pi-tui/src/components/spacer.ts +29 -0
- package/vendor/pi-tui/src/components/stack.ts +155 -0
- package/vendor/pi-tui/src/components/text.ts +108 -0
- package/vendor/pi-tui/src/components/truncated-text.ts +66 -0
- package/vendor/pi-tui/src/components/v-stack.ts +34 -0
- package/vendor/pi-tui/src/editor-component.ts +75 -0
- package/vendor/pi-tui/src/fuzzy.ts +138 -0
- package/vendor/pi-tui/src/index.ts +149 -0
- package/vendor/pi-tui/src/keybindings.ts +321 -0
- package/vendor/pi-tui/src/keys.ts +1402 -0
- package/vendor/pi-tui/src/kill-ring.ts +47 -0
- package/vendor/pi-tui/src/latex.ts +1381 -0
- package/vendor/pi-tui/src/layout-node.ts +52 -0
- package/vendor/pi-tui/src/layout.ts +411 -0
- package/vendor/pi-tui/src/native-modifiers.ts +60 -0
- package/vendor/pi-tui/src/native-module-path.ts +32 -0
- package/vendor/pi-tui/src/stdin-buffer.ts +445 -0
- package/vendor/pi-tui/src/terminal-colors.ts +74 -0
- package/vendor/pi-tui/src/terminal-image.ts +701 -0
- package/vendor/pi-tui/src/terminal.ts +554 -0
- package/vendor/pi-tui/src/tui-alt-screen.ts +1379 -0
- package/vendor/pi-tui/src/tui-main-screen.ts +655 -0
- package/vendor/pi-tui/src/tui.ts +1264 -0
- package/vendor/pi-tui/src/undo-stack.ts +29 -0
- package/vendor/pi-tui/src/utils.ts +1327 -0
- package/vendor/pi-tui/src/word-navigation.ts +118 -0
- package/vendor/pi-tui/test/test-themes.ts +39 -0
- package/vendor/pi-tui/test/virtual-terminal.ts +219 -0
- package/CHANGELOG.md +0 -527
- package/bin/rovecode.js +0 -24
- package/dist/cli/app-j6gn14w3.js +0 -2
- package/dist/cli/ask-user-cwstt8fz.js +0 -2
- package/dist/cli/auth-login-9bbp9915.js +0 -2
- package/dist/cli/auth-m8p9grty.js +0 -2
- package/dist/cli/bench-16zqdms5.js +0 -9
- package/dist/cli/catalog-1xchffa4.js +0 -2
- package/dist/cli/cli-1n1zb64f.js +0 -2
- package/dist/cli/client-2t9gjkck.js +0 -2
- package/dist/cli/commands-exafvm2b.js +0 -2
- package/dist/cli/connect-6zde0kn3.js +0 -2
- package/dist/cli/context-cmd-5t43wgqt.js +0 -2
- package/dist/cli/context-report-kt01pw8y.js +0 -2
- package/dist/cli/count-remote-ap7x3vh6.js +0 -2
- package/dist/cli/design-ne5zszyh.js +0 -2
- package/dist/cli/dispatch-2r5myxye.js +0 -2
- package/dist/cli/doctor-ws4fh4tn.js +0 -3
- package/dist/cli/executor-bdrjn634.js +0 -2
- package/dist/cli/export-1mxb9g5p.js +0 -2
- package/dist/cli/files-g104xghh.js +0 -2
- package/dist/cli/gauntlet-07xrjpj7.js +0 -2
- package/dist/cli/gauntlet-runner-xvy64436.js +0 -10
- package/dist/cli/gauntlet-wave3-jm91yt5w.js +0 -5
- package/dist/cli/gauntlet-wave4-r13py7p1.js +0 -14
- package/dist/cli/hashline-znvrat11.js +0 -2
- package/dist/cli/http-xafw6fsh.js +0 -143
- package/dist/cli/index-1sgjm25y.js +0 -2
- package/dist/cli/init-g2m0tn4m.js +0 -51
- package/dist/cli/install-avaqjjqq.js +0 -2
- package/dist/cli/loop-mmpfft01.js +0 -2
- package/dist/cli/main-0904f6ps.js +0 -5
- package/dist/cli/main-0ab9fc26.js +0 -9
- package/dist/cli/main-0jys2ccn.js +0 -3
- package/dist/cli/main-0mtcdbs7.js +0 -3
- package/dist/cli/main-0z1w2zsg.js +0 -3
- package/dist/cli/main-1dchs7xv.js +0 -18
- package/dist/cli/main-1ereejm1.js +0 -3
- package/dist/cli/main-1k1kw6b5.js +0 -3
- package/dist/cli/main-27y4sm2k.js +0 -38
- package/dist/cli/main-2wwjex5j.js +0 -58
- package/dist/cli/main-2yeveeve.js +0 -6
- package/dist/cli/main-2yfck9b5.js +0 -3
- package/dist/cli/main-2zmzgkwh.js +0 -3
- package/dist/cli/main-351pz3z7.js +0 -7
- package/dist/cli/main-3gjqfh7a.js +0 -6
- package/dist/cli/main-3nf3kgve.js +0 -3
- package/dist/cli/main-3pjrb2hd.js +0 -3
- package/dist/cli/main-3rxcvgna.js +0 -19
- package/dist/cli/main-4b3jgy66.js +0 -19
- package/dist/cli/main-4wndhjdc.js +0 -7
- package/dist/cli/main-4xcmvxnk.js +0 -3
- package/dist/cli/main-5tbz0wbz.js +0 -4
- package/dist/cli/main-5ywnwthm.js +0 -3
- package/dist/cli/main-6b62vkz0.js +0 -14
- package/dist/cli/main-6dnk69vp.js +0 -3
- package/dist/cli/main-6genrmhs.js +0 -136
- package/dist/cli/main-73g7eff4.js +0 -15
- package/dist/cli/main-7c5thhjd.js +0 -5
- package/dist/cli/main-7rn6bqje.js +0 -3
- package/dist/cli/main-80haw7qk.js +0 -4
- package/dist/cli/main-875s60s2.js +0 -4
- package/dist/cli/main-8kjxbpw4.js +0 -8
- package/dist/cli/main-90ds1z4e.js +0 -10
- package/dist/cli/main-9etavkew.js +0 -3
- package/dist/cli/main-a9njrkk1.js +0 -3
- package/dist/cli/main-aecrjq2d.js +0 -12
- package/dist/cli/main-ck9asesq.js +0 -9
- package/dist/cli/main-cta9racd.js +0 -4
- package/dist/cli/main-ddv7j2ag.js +0 -3
- package/dist/cli/main-dfreez27.js +0 -10
- package/dist/cli/main-f7rw7des.js +0 -3
- package/dist/cli/main-ggcn7rd7.js +0 -5
- package/dist/cli/main-gzkmycnv.js +0 -3
- package/dist/cli/main-hq51jg8v.js +0 -18
- package/dist/cli/main-jft389w9.js +0 -8
- package/dist/cli/main-k1eqkg83.js +0 -3
- package/dist/cli/main-k2y8a2aw.js +0 -9
- package/dist/cli/main-kcpbykxz.js +0 -4
- package/dist/cli/main-kd488vje.js +0 -22
- package/dist/cli/main-kh32yvgk.js +0 -5
- package/dist/cli/main-kqxnqjnv.js +0 -25
- package/dist/cli/main-kyn0xnsg.js +0 -3
- package/dist/cli/main-m1kk6fp5.js +0 -21
- package/dist/cli/main-mv40pcr2.js +0 -4
- package/dist/cli/main-n0t3973w.js +0 -3
- package/dist/cli/main-nqveez48.js +0 -4
- package/dist/cli/main-pknhvrmj.js +0 -3
- package/dist/cli/main-pn1w7a7j.js +0 -3
- package/dist/cli/main-prxxs70n.js +0 -4
- package/dist/cli/main-q3vsesf9.js +0 -3
- package/dist/cli/main-qsevpgsv.js +0 -3
- package/dist/cli/main-rdgdw24b.js +0 -25
- package/dist/cli/main-rfth4tbm.js +0 -16
- package/dist/cli/main-rg0wn0xf.js +0 -5
- package/dist/cli/main-sdmxhtv8.js +0 -4
- package/dist/cli/main-skbp13js.js +0 -18
- package/dist/cli/main-t4xnd213.js +0 -7
- package/dist/cli/main-vqak588n.js +0 -4
- package/dist/cli/main-w2n1303f.js +0 -9
- package/dist/cli/main-wbrdspr2.js +0 -5
- package/dist/cli/main-wsrg79c1.js +0 -7
- package/dist/cli/main-x4r0fne4.js +0 -5
- package/dist/cli/main-xea2f3tn.js +0 -6
- package/dist/cli/main-xg704a3c.js +0 -3
- package/dist/cli/main-xvnrabfp.js +0 -16
- package/dist/cli/main-xy53xf0r.js +0 -4
- package/dist/cli/main-y1fqy60y.js +0 -3
- package/dist/cli/main-yn8cd281.js +0 -34
- package/dist/cli/main-yr0ksc0h.js +0 -4
- package/dist/cli/main-z2ex2vyf.js +0 -4
- package/dist/cli/main-z3aayzvq.js +0 -3
- package/dist/cli/main-zaqh35jg.js +0 -3
- package/dist/cli/main-zc2e8e46.js +0 -4
- package/dist/cli/main-zzrfw6cf.js +0 -13
- package/dist/cli/main.js +0 -280
- package/dist/cli/market-cmd-e14kmx9n.js +0 -5
- package/dist/cli/mcp-login-wq7ktdek.js +0 -2
- package/dist/cli/mcp-market-cmd-9mg3jecy.js +0 -2
- package/dist/cli/notify-b7qc0cjb.js +0 -2
- package/dist/cli/oauth-z8whcgfx.js +0 -2
- package/dist/cli/output-b3ewj3ps.js +0 -16
- package/dist/cli/profiles-6mr5he5e.js +0 -2
- package/dist/cli/provider-config-g7j42q8x.js +0 -2
- package/dist/cli/provider-jr1y8vvm.js +0 -2
- package/dist/cli/registry-s8yk86g0.js +0 -2
- package/dist/cli/registry-t6p8d4mn.js +0 -2
- package/dist/cli/repl-bajwe1mh.js +0 -11
- package/dist/cli/resume-rwn9nz7y.js +0 -2
- package/dist/cli/run-flags-nah7ndpt.js +0 -2
- package/dist/cli/runtime-n7gafzhb.js +0 -2
- package/dist/cli/sandbox-config-emdy18x4.js +0 -2
- package/dist/cli/server-b0nvs2bn.js +0 -5
- package/dist/cli/session-arg-y75wd4kj.js +0 -2
- package/dist/cli/session-j62evmjq.js +0 -2
- package/dist/cli/sessions-cmd-tsnwz0ns.js +0 -7
- package/dist/cli/settings-df10wfez.js +0 -2
- package/dist/cli/setup-jzvv72fg.js +0 -2
- package/dist/cli/sextant-smoke-37m81ke6.js +0 -5
- package/dist/cli/skills-cmd-gjxnxnhx.js +0 -2
- package/dist/cli/smoke-p7748apt.js +0 -8
- package/dist/cli/start-chat-s4st3mm0.js +0 -12
- package/dist/cli/stream-gmeyewds.js +0 -2
- package/dist/cli/task-gh0kkp3n.js +0 -2
- package/dist/cli/tasks-z1kfpe8e.js +0 -2
- package/dist/cli/thinking-0eqkrz6t.js +0 -2
- package/dist/cli/todo-5brcrt9m.js +0 -2
- package/dist/cli/tools-7pzm0vj9.js +0 -2
- package/dist/cli/tools-s635p6s8.js +0 -2
- package/dist/cli/trust-cmd-cjav8zgm.js +0 -2
- package/dist/cli/update-check-pt31bm2f.js +0 -2
- package/dist/cli/update-cmd-tk131s9t.js +0 -2
- package/dist/cli/voice-56nabd8d.js +0 -2
- package/dist/cli/webfetch-xd8q596m.js +0 -2
- package/dist/cli/websearch-5hkf98k1.js +0 -2
- package/dist/cli/workflow-cmd-cy3cvzjp.js +0 -4
- package/dist/cli/workspace-q10g5z3e.js +0 -2
- package/dist/lib/index.js +0 -62
- package/dist/lib/models-index.json +0 -1
- package/dist/lib/plugins.js +0 -55
- package/dist/lib/providers.js +0 -17
- package/dist/lib/public-api.js +0 -20
- package/dist/lib/sdk.js +0 -360
- /package/{dist/cli → src/providers}/models-index.json +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/** Sextant frame loop (port #44): the ONE timer of the surface. Owns the Screen (#40) over the
|
|
2
|
+
* TerminalIO, the layout, the per-frame hit zones, the painters wiring (#41 frame/files/plan/usage,
|
|
3
|
+
* #42 code/messages, #45 pet, #43 suggest/palette/help overlays), the scroll write-backs the pure
|
|
4
|
+
* #42 painters cannot do, the cursor placement and the input pipeline (parseInput → keys.handleInput).
|
|
5
|
+
* Ported from the user's sextant v0.4.0 app.js:1599-1632 (the boot: 40 ms tick, paint only when
|
|
6
|
+
* dirty / animating / every 170 ms, input and resize handlers; idle, the timer now sleeps until the next ambient change — see the class note). No git/fs here — the renderer
|
|
7
|
+
* schedules its I/O from `onTick`, off the paint path. `clock` is injected: tests drive time. */
|
|
8
|
+
|
|
9
|
+
import { agentsScrollTop, laneCells } from "./draw-agents.ts";
|
|
10
|
+
import { drawCode, codeScrollTop } from "./draw-code.ts";
|
|
11
|
+
import { drawMessages, messagesScroll, promptCursor } from "./draw-messages.ts";
|
|
12
|
+
import { drawPet, SWAY_MS } from "./draw-pet.ts";
|
|
13
|
+
import { fuzzy, tokenize } from "./engine.ts";
|
|
14
|
+
import { renderFrame } from "./frame.ts";
|
|
15
|
+
import { parseInput } from "./input.ts";
|
|
16
|
+
import { handleInput, type KeyCtx } from "./keys.ts";
|
|
17
|
+
import { layout as layoutFn } from "./layout.ts";
|
|
18
|
+
import { treeRows } from "./model.ts";
|
|
19
|
+
import { drawHelp, drawPalette, drawSuggest, suggestions } from "./overlays.ts";
|
|
20
|
+
import { drawMarket } from "./draw-market.ts";
|
|
21
|
+
import { drawContext } from "./draw-context.ts";
|
|
22
|
+
import { petEnabled, type Pet } from "./pet.ts";
|
|
23
|
+
import { Screen } from "./screen.ts";
|
|
24
|
+
import { cardHits } from "./card-hits.ts";
|
|
25
|
+
import { fileRowHits } from "./panel-hits.ts";
|
|
26
|
+
import { followTailIfAtEnd, scrollThumbHits } from "./scroll-hits.ts";
|
|
27
|
+
import { frameHits } from "./frame-hits.ts";
|
|
28
|
+
import { messageRowHits } from "./message-hits.ts";
|
|
29
|
+
import { drawTabs, mainPage } from "./draw-tabs.ts";
|
|
30
|
+
import type { HitZone, InputEvent, Layout, SextantState, TerminalIO, Theme, TreeRow } from "./types.ts";
|
|
31
|
+
|
|
32
|
+
/** the frame period while something moves (app.js: setInterval(tick, 40)) */
|
|
33
|
+
export const FRAME_MS = 40;
|
|
34
|
+
/** a tick repaints a frame older than this even when nothing asked (tests drive the clock and tick by hand) */
|
|
35
|
+
export const IDLE_REPAINT_MS = 170;
|
|
36
|
+
/** the loop never sleeps longer than this: the repo's idle scan and the pet's due hum ride on the tick */
|
|
37
|
+
export const IDLE_TICK_MS = 2000;
|
|
38
|
+
/** the idle header glyph steps through its four colours every 500 ms (draw-frame.ts activityGlyph) */
|
|
39
|
+
export const PULSE_MS = 500;
|
|
40
|
+
/** the suggestion box shows at most this many rows (#43 fix-wave cap) */
|
|
41
|
+
export const MAX_SUGGESTIONS = 8;
|
|
42
|
+
/** the boot reveal animates for this long after bootAt (5 × 90 ms steps + slack) */
|
|
43
|
+
const REVEAL_MS = 600;
|
|
44
|
+
/** a chunk that ENDS in a lone ESC is held this long: parseInput reports a lone ESC as Escape at once
|
|
45
|
+
* (the esc-esc arm needs that), so a sequence split right after its ESC byte would otherwise be typed
|
|
46
|
+
* as text — the next chunk (or the timer) completes it */
|
|
47
|
+
export const ESC_HOLD_MS = 20;
|
|
48
|
+
|
|
49
|
+
export interface FrameLoopDeps {
|
|
50
|
+
io: TerminalIO;
|
|
51
|
+
clock: () => number;
|
|
52
|
+
state: SextantState;
|
|
53
|
+
theme: () => Theme;
|
|
54
|
+
pet: Pet;
|
|
55
|
+
truecolor: boolean;
|
|
56
|
+
/** RendererHooks + the renderer-owned local effects keys.ts needs */
|
|
57
|
+
keyCtx: () => Pick<KeyCtx, "hooks" | "local">;
|
|
58
|
+
/** renderer-level intercept BEFORE keys.handleInput (picker Enter/Esc, the ⌃c arm); true = swallowed */
|
|
59
|
+
beforeInput?: (ev: InputEvent, now: number) => boolean;
|
|
60
|
+
/** after keys.handleInput ran for `ev` */
|
|
61
|
+
afterInput?: (ev: InputEvent, now: number) => void;
|
|
62
|
+
/** every tick, BEFORE the paint decision — the renderer reloads the code panel's file and schedules
|
|
63
|
+
* its git/fs work here, so a reload an event asked for lands in the same frame (never a one-frame
|
|
64
|
+
* `cannot read <file>` between an edit's end and the next tick) */
|
|
65
|
+
onTick?: (now: number) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The loop is one re-armed setTimeout, not a setInterval. Its period is FRAME_MS while something MOVES
|
|
69
|
+
* (a spinner, a storm, the boot reveal: a new picture every frame). Otherwise the surface is only
|
|
70
|
+
* AMBIENT — the header glyph steps colour every 500 ms, the pet sways every 1.8 s, a quip or a toast
|
|
71
|
+
* expires at a known time — and the loop sleeps until the earliest of those instants (nextAmbient), so an
|
|
72
|
+
* idle session paints ~2 frames a second instead of 6 and wakes ~2 times instead of 25; after a run,
|
|
73
|
+
* when the glyph holds still, well under one a second. Nothing waits for the sleeping timer: a keystroke
|
|
74
|
+
* paints in parse(), and every state change goes through markDirty() (dispatch, resize, the renderer),
|
|
75
|
+
* which pulls the next tick forward to one frame from now (wake()). */
|
|
76
|
+
export class FrameLoop {
|
|
77
|
+
private screen: Screen | null = null;
|
|
78
|
+
private timer: ReturnType<typeof setTimeout> | null = null;
|
|
79
|
+
/** performance.now() at which `timer` fires (wake() only re-arms when that is further than a frame away) */
|
|
80
|
+
private dueAt = 0;
|
|
81
|
+
/** the delay the last tick chose for the next one (fire() arms it) */
|
|
82
|
+
private _pace = FRAME_MS;
|
|
83
|
+
/** clock time of the next ambient change the last tick saw (a tick at or past it paints) */
|
|
84
|
+
private ambientDue = -Infinity;
|
|
85
|
+
private unsubs: (() => void)[] = [];
|
|
86
|
+
private carry = "";
|
|
87
|
+
private held = "";
|
|
88
|
+
private holdTimer: ReturnType<typeof setTimeout> | null = null;
|
|
89
|
+
private dirty = true;
|
|
90
|
+
private lastRender = -Infinity;
|
|
91
|
+
private L: Layout;
|
|
92
|
+
private hits: HitZone[] = [];
|
|
93
|
+
/** the grabbed zone (a scrollbar thumb) and the row it was pressed on; outlives the per-frame hits */
|
|
94
|
+
private readonly drag = { zone: null as HitZone | null, y0: 0 };
|
|
95
|
+
private rows: TreeRow[] = [];
|
|
96
|
+
/** frames painted (tests: "a render happened") */
|
|
97
|
+
frames = 0;
|
|
98
|
+
|
|
99
|
+
constructor(private readonly d: FrameLoopDeps) {
|
|
100
|
+
const { cols, rows } = d.io.size();
|
|
101
|
+
this.L = layoutFn(cols, rows, this.layoutOpts());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private layoutOpts(): { pet: boolean } { return { pet: petEnabled(this.d.io.env) }; }
|
|
105
|
+
/** the interval is live */
|
|
106
|
+
get active(): boolean { return this.timer !== null; }
|
|
107
|
+
/** the layout of the last frame (before the first frame: computed from the io size) */
|
|
108
|
+
get layout(): Layout { return this.L; }
|
|
109
|
+
/** the delay between the last tick and the next (tests: the idle backoff) */
|
|
110
|
+
get pace(): number { return this._pace; }
|
|
111
|
+
markDirty(): void { this.dirty = true; this.wake(); }
|
|
112
|
+
/** the last painted frame as text (tests/smoke); "" before start() */
|
|
113
|
+
frameText(): string { return this.screen?.toText() ?? ""; }
|
|
114
|
+
|
|
115
|
+
/** build the Screen, subscribe input/resize, paint the first frame, start the interval */
|
|
116
|
+
start(): void {
|
|
117
|
+
if (this.timer) return;
|
|
118
|
+
const { cols, rows } = this.d.io.size();
|
|
119
|
+
this.screen = new Screen(this.d.io, cols, rows, { truecolor: this.d.truecolor });
|
|
120
|
+
this.unsubs.push(this.d.io.onInput((chunk) => this.feed(chunk)));
|
|
121
|
+
this.unsubs.push(this.d.io.onResize((c, r) => { this.screen?.resize(c, r); this.markDirty(); }));
|
|
122
|
+
this._pace = FRAME_MS;
|
|
123
|
+
this.arm(FRAME_MS);
|
|
124
|
+
this.render(this.d.clock());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** (re)arm the one timer */
|
|
128
|
+
private arm(ms: number): void {
|
|
129
|
+
if (this.timer !== null) clearTimeout(this.timer);
|
|
130
|
+
this.dueAt = performance.now() + ms;
|
|
131
|
+
this.timer = setTimeout(() => this.fire(), ms);
|
|
132
|
+
}
|
|
133
|
+
/** the timer fired: tick, then arm the pace the tick chose — unless the tick stopped the loop (timer
|
|
134
|
+
* gone) or a wake() inside it already re-armed (timer replaced): then it is no longer ours to arm */
|
|
135
|
+
private fire(): void {
|
|
136
|
+
const fired = this.timer;
|
|
137
|
+
this.tick();
|
|
138
|
+
if (this.timer === fired) { this.timer = null; this.arm(this._pace); }
|
|
139
|
+
}
|
|
140
|
+
/** something changed: if the loop is asleep, pull the next tick forward to one frame from now */
|
|
141
|
+
private wake(): void {
|
|
142
|
+
if (this.timer !== null && this.dueAt - performance.now() > FRAME_MS) this.arm(FRAME_MS);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** clear the frame timer and the ESC-hold timer, unsubscribe; the screen keeps its last frame for frameText() */
|
|
146
|
+
stop(): void {
|
|
147
|
+
if (this.timer) { clearTimeout(this.timer); this.timer = null; }
|
|
148
|
+
if (this.holdTimer) { clearTimeout(this.holdTimer); this.holdTimer = null; }
|
|
149
|
+
this.held = "";
|
|
150
|
+
for (const u of this.unsubs) u();
|
|
151
|
+
this.unsubs = [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** raw bytes → events: a chunk ending in a lone ESC waits ESC_HOLD_MS for its continuation; an
|
|
155
|
+
* incomplete CSI/paste is carried by parseInput into the next chunk */
|
|
156
|
+
feed(chunk: string): void {
|
|
157
|
+
if (this.holdTimer) { clearTimeout(this.holdTimer); this.holdTimer = null; }
|
|
158
|
+
const data = this.held + chunk;
|
|
159
|
+
this.held = "";
|
|
160
|
+
if (data.endsWith("\x1b")) {
|
|
161
|
+
this.held = data;
|
|
162
|
+
this.holdTimer = setTimeout(() => { this.holdTimer = null; this.flushInput(); }, ESC_HOLD_MS);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
this.parse(data);
|
|
166
|
+
}
|
|
167
|
+
/** parse the held bytes now (the hold timer fired, or a test wants determinism) */
|
|
168
|
+
flushInput(): void {
|
|
169
|
+
if (this.holdTimer) { clearTimeout(this.holdTimer); this.holdTimer = null; }
|
|
170
|
+
const data = this.held;
|
|
171
|
+
this.held = "";
|
|
172
|
+
if (data) this.parse(data);
|
|
173
|
+
}
|
|
174
|
+
private parse(data: string): void {
|
|
175
|
+
const r = parseInput(data, this.carry);
|
|
176
|
+
this.carry = r.rest;
|
|
177
|
+
for (const ev of r.events) this.dispatch(ev);
|
|
178
|
+
// Paint the result of this chunk NOW rather than waiting for the next 40 ms tick. A keystroke was
|
|
179
|
+
// always handled immediately, but its echo waited for the timer — invisible on an idle machine, and
|
|
180
|
+
// on a loaded one, where setInterval is starved, it is the difference between a responsive prompt
|
|
181
|
+
// and one that appears to have stopped accepting input. This costs no extra frame: the tick would
|
|
182
|
+
// have painted the same state a moment later, and it clears `dirty` so the tick then skips.
|
|
183
|
+
// Once per CHUNK, not once per event, so a paste of two hundred characters still paints once.
|
|
184
|
+
// `this.timer` is the guard, not `this.screen`: a key can END the session — ⌃c, /exit, a click on
|
|
185
|
+
// the exit row — and by the time this line runs the loop has been stopped and the terminal handed
|
|
186
|
+
// back. Painting then writes a frame over the shell the user has just been returned to. The screen
|
|
187
|
+
// object is still there; the loop is what says whether it is ours to paint on.
|
|
188
|
+
if (this.timer !== null && r.events.length > 0) this.render(this.d.clock());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
dispatch(ev: InputEvent): void {
|
|
192
|
+
const now = this.d.clock();
|
|
193
|
+
this.markDirty(); // and wake: the tick that follows this key (onTick: file reload) must come in one frame, not one sleep
|
|
194
|
+
if (this.d.beforeInput?.(ev, now)) return;
|
|
195
|
+
const kc = this.d.keyCtx();
|
|
196
|
+
const ctx: KeyCtx = { layout: this.L, hooks: kc.hooks, local: kc.local, hits: this.hits, rows: this.rows, fuzzy, drag: this.drag };
|
|
197
|
+
handleInput(this.d.state, ev, ctx, now);
|
|
198
|
+
this.d.afterInput?.(ev, now);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** a new picture EVERY frame: a run's spinners, a waiting card's blink, touched-file spinners, the pet's
|
|
202
|
+
* storms and effects, the boot reveal. These hold the loop at FRAME_MS. */
|
|
203
|
+
moving(now: number): boolean {
|
|
204
|
+
const s = this.d.state, P = this.d.pet.state;
|
|
205
|
+
return s.running || s.card !== null || s.files.touched.size > 0 || now - s.bootAt < REVEAL_MS
|
|
206
|
+
|| now < P.stormUntil || P.fx.some((f) => f.until > now);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** the clock time the picture next changes on its own while nothing moves — Infinity when it never will:
|
|
210
|
+
* the idle header glyph's next colour step (only while the activity state is IDLE and no card is up —
|
|
211
|
+
* draw-frame.ts modeOf; after a run the glyph holds), the pet's next sway step (only while it is on
|
|
212
|
+
* screen — the pet also hums from drawPet, so its due hum lands on the sway wake), and the instants a
|
|
213
|
+
* quip, a glance, a toast or the esc arm expire */
|
|
214
|
+
nextAmbient(now: number): number {
|
|
215
|
+
const s = this.d.state, P = this.d.pet.state;
|
|
216
|
+
let due = Infinity;
|
|
217
|
+
if (s.card === null && s.activity.state === "IDLE") due = Math.min(due, (Math.floor(now / PULSE_MS) + 1) * PULSE_MS);
|
|
218
|
+
if (this.L.pet) due = Math.min(due, (Math.floor(now / SWAY_MS) + 1) * SWAY_MS);
|
|
219
|
+
if (P.quip && P.quip.until > now) due = Math.min(due, P.quip.until);
|
|
220
|
+
if (P.glance && P.glance.until > now) due = Math.min(due, P.glance.until);
|
|
221
|
+
for (const t of s.toasts) if (t.until > now) due = Math.min(due, t.until);
|
|
222
|
+
if (now < s.escUntil) due = Math.min(due, s.escUntil);
|
|
223
|
+
return due;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
tick(): void {
|
|
227
|
+
const now = this.d.clock();
|
|
228
|
+
this.d.onTick?.(now); // first: a reload marks dirty and paints below, in this very frame
|
|
229
|
+
if (this.dirty || this.moving(now) || now >= this.ambientDue || now - this.lastRender >= IDLE_REPAINT_MS) this.render(now);
|
|
230
|
+
this._pace = this.nextPace(now);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** the delay to the next tick, decided AFTER the paint (a painter can mark dirty or start a quip): a
|
|
234
|
+
* frame while anything moves or is dirty, else the time to the next ambient change, never under a frame
|
|
235
|
+
* (a timer that fires a millisecond early must not spin) and never over IDLE_TICK_MS */
|
|
236
|
+
private nextPace(now: number): number {
|
|
237
|
+
if (this.dirty || this.moving(now)) { this.ambientDue = -Infinity; return FRAME_MS; }
|
|
238
|
+
this.ambientDue = this.nextAmbient(now);
|
|
239
|
+
return Math.min(IDLE_TICK_MS, Math.max(FRAME_MS, this.ambientDue - now));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** paint one frame: panels (renderFrame) → suggestion box → palette → help → scroll write-backs → cursor → flush */
|
|
243
|
+
render(now: number): void {
|
|
244
|
+
const scr = this.screen;
|
|
245
|
+
if (!scr) return;
|
|
246
|
+
const s = this.d.state, theme = this.d.theme(), pet = this.d.pet;
|
|
247
|
+
scr.begin(theme.bg);
|
|
248
|
+
this.rows = treeRows(s, now); // the same (version, now) key drawFiles uses → one build per frame (model.ts cache)
|
|
249
|
+
const hits: HitZone[] = [];
|
|
250
|
+
const L = renderFrame(scr, s, theme, now, {
|
|
251
|
+
layout: layoutFn,
|
|
252
|
+
layoutOpts: this.layoutOpts(),
|
|
253
|
+
painters: {
|
|
254
|
+
code: (g, r, st, th, t) => drawCode(g, r, st, th, t, { tokenize }),
|
|
255
|
+
messages: drawMessages,
|
|
256
|
+
pet: (g, r, st, th, t) => drawPet(g, r, pet, st, th, t),
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
this.L = L;
|
|
260
|
+
if (L.pet) hits.push({ rect: L.pet, onClick: () => pet.poke(this.d.clock()) });
|
|
261
|
+
// the frame's border rows (frame-hits.ts): unread badge → notices, theme name → next theme, effort → /effort
|
|
262
|
+
for (const z of frameHits(L.frame, s, theme, now)) hits.push(z);
|
|
263
|
+
// paging (draw-tabs.ts): on a narrow terminal the main slot may be showing files or plan instead
|
|
264
|
+
// of code; the strip is painted over the slot's top border and each tab is a click zone
|
|
265
|
+
const main = mainPage(L, s);
|
|
266
|
+
for (const t of drawTabs(scr, L, s, theme)) {
|
|
267
|
+
hits.push({ rect: t.rect, onClick: () => { s.page = t.page; s.focus = t.page === "files" ? "files" : "code"; } });
|
|
268
|
+
}
|
|
269
|
+
// the body rect drawCode hands its mode painter (inner rect minus the 3-wide rail and its gutter)
|
|
270
|
+
const body = { x: L.code.x + 2, y: L.code.y + 1, w: L.code.w - 9, h: L.code.h - 2 };
|
|
271
|
+
if (main === "code" && s.code.mode === "agents" && !s.code.laneOpen) {
|
|
272
|
+
for (const { index, rect } of laneCells(body, s).cells) hits.push({ rect, onClick: () => { s.code.lane = index; s.focus = "code"; } });
|
|
273
|
+
}
|
|
274
|
+
// the files tree rows (panel-hits.ts): click = select + Enter, i.e. fold a dir / open a file.
|
|
275
|
+
// The rows live in the files panel, or in the main slot when files is paged in there.
|
|
276
|
+
const filesRect = L.files ?? (main === "files" ? L.code : null);
|
|
277
|
+
for (const hit of fileRowHits(filesRect, s, this.rows)) {
|
|
278
|
+
hits.push({
|
|
279
|
+
rect: hit.rect,
|
|
280
|
+
onClick: () => { s.focus = "files"; s.files.cursor = hit.index; },
|
|
281
|
+
key: { type: "key", name: "enter" },
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// scrollbar thumbs (scroll-hits.ts): grab + drag scrolls; after the row zones so the thumb column wins
|
|
285
|
+
for (const hit of scrollThumbHits(L, main, s, theme, now, this.rows)) hits.push(hit);
|
|
286
|
+
followTailIfAtEnd(L, s, theme, now); // scrolled back to the end → follow new text again
|
|
287
|
+
// tool rows that name a file (message-hits.ts): click = open it in the code panel, like /open
|
|
288
|
+
for (const hit of messageRowHits(L.messages, s, theme, now)) hits.push({ rect: hit.rect, onClick: () => this.d.keyCtx().local.openFile(hit.path) });
|
|
289
|
+
// the modal card's buttons (card-hits.ts). Registered BEFORE the palette/help so those overlays,
|
|
290
|
+
// which draw over the card, still win the last-registered-wins walk in keys.ts.
|
|
291
|
+
for (const hit of cardHits(L.messages, s, theme)) {
|
|
292
|
+
hits.push({
|
|
293
|
+
rect: hit.rect,
|
|
294
|
+
onClick: () => { if (s.card) s.card.selected = hit.index; },
|
|
295
|
+
// a click on a labelled button IS the decision; the free-text row only takes the caret
|
|
296
|
+
...(hit.confirm ? { key: { type: "key" as const, name: "enter" } } : {}),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
drawSuggest(scr, L.messages, s, suggestions(s, s.files.paths, fuzzy).slice(0, MAX_SUGGESTIONS), theme, hits);
|
|
300
|
+
const paletteCursor = drawPalette(scr, L, theme, s, fuzzy, hits);
|
|
301
|
+
const marketCursor = drawMarket(scr, L, theme, s, fuzzy, hits);
|
|
302
|
+
const contextCursor = drawContext(scr, L, theme, s, hits);
|
|
303
|
+
if (s.help) drawHelp(scr, L, theme, s, hits);
|
|
304
|
+
this.hits = hits;
|
|
305
|
+
// the #42/#46 seams: the painters are pure, so the effective scroll positions are written back here
|
|
306
|
+
// (codeScrollTop counts 0 rows in agents mode and would pin the open lane to its top)
|
|
307
|
+
s.code.scroll = s.code.mode === "agents" ? agentsScrollTop(body, s) : codeScrollTop(L.code, s);
|
|
308
|
+
s.msgScroll = messagesScroll(L.messages, s, theme, now).offset;
|
|
309
|
+
scr.flush(s.palette ? paletteCursor : s.market ? marketCursor : s.context ? contextCursor : s.help ? null : promptCursor(L.messages, s));
|
|
310
|
+
this.dirty = false;
|
|
311
|
+
this.lastRender = now;
|
|
312
|
+
this.frames++;
|
|
313
|
+
}
|
|
314
|
+
}
|