openhorse 0.2.21 → 0.2.23
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/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +189 -49
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/parser.d.ts.map +1 -1
- package/dist/commands/parser.js +8 -3
- package/dist/commands/parser.js.map +1 -1
- package/dist/commands/types.d.ts +2 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/core/cost-tracker.d.ts +45 -5
- package/dist/core/cost-tracker.d.ts.map +1 -1
- package/dist/core/cost-tracker.js +78 -13
- package/dist/core/cost-tracker.js.map +1 -1
- package/dist/framework/index.d.ts +1 -1
- package/dist/framework/index.d.ts.map +1 -1
- package/dist/framework/index.js.map +1 -1
- package/dist/framework/query.d.ts +25 -0
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +196 -49
- package/dist/framework/query.js.map +1 -1
- package/dist/framework/store.d.ts +6 -1
- package/dist/framework/store.d.ts.map +1 -1
- package/dist/framework/store.js +10 -1
- package/dist/framework/store.js.map +1 -1
- package/dist/harness/capsule.d.ts +1 -0
- package/dist/harness/capsule.d.ts.map +1 -1
- package/dist/harness/capsule.js +38 -9
- package/dist/harness/capsule.js.map +1 -1
- package/dist/harness/contract.d.ts +2 -0
- package/dist/harness/contract.d.ts.map +1 -1
- package/dist/harness/contract.js +78 -14
- package/dist/harness/contract.js.map +1 -1
- package/dist/harness/index.d.ts +2 -2
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +4 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/ledger.d.ts.map +1 -1
- package/dist/harness/ledger.js +6 -2
- package/dist/harness/ledger.js.map +1 -1
- package/dist/harness/state.d.ts.map +1 -1
- package/dist/harness/state.js +37 -7
- package/dist/harness/state.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
- package/dist/ink-ui/components/StatusLine.js +0 -2
- package/dist/ink-ui/components/StatusLine.js.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.js +12 -5
- package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
- package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-controller.js +11 -1
- package/dist/runtime/agent-runtime-controller.js.map +1 -1
- package/dist/runtime/aggregate-tool-presenter.d.ts +13 -0
- package/dist/runtime/aggregate-tool-presenter.d.ts.map +1 -0
- package/dist/runtime/aggregate-tool-presenter.js +44 -0
- package/dist/runtime/aggregate-tool-presenter.js.map +1 -0
- package/dist/runtime/chat-controller.d.ts +9 -2
- package/dist/runtime/chat-controller.d.ts.map +1 -1
- package/dist/runtime/chat-controller.js +345 -90
- package/dist/runtime/chat-controller.js.map +1 -1
- package/dist/runtime/rich-text/ansi-parser.d.ts +1 -1
- package/dist/runtime/rich-text/ansi-parser.d.ts.map +1 -1
- package/dist/runtime/rich-text/ansi-parser.js +93 -55
- package/dist/runtime/rich-text/ansi-parser.js.map +1 -1
- package/dist/runtime/rich-text/layout.d.ts +2 -11
- package/dist/runtime/rich-text/layout.d.ts.map +1 -1
- package/dist/runtime/rich-text/layout.js +280 -245
- package/dist/runtime/rich-text/layout.js.map +1 -1
- package/dist/runtime/rich-text/markdown-parser.js +31 -24
- package/dist/runtime/rich-text/markdown-parser.js.map +1 -1
- package/dist/runtime/rich-text/types.d.ts +1 -1
- package/dist/runtime/rich-text/types.d.ts.map +1 -1
- package/dist/runtime/subagents/production.d.ts.map +1 -1
- package/dist/runtime/subagents/production.js +28 -3
- package/dist/runtime/subagents/production.js.map +1 -1
- package/dist/runtime/subagents/result-parser.d.ts.map +1 -1
- package/dist/runtime/subagents/result-parser.js +54 -40
- package/dist/runtime/subagents/result-parser.js.map +1 -1
- package/dist/runtime/subagents/types.d.ts +2 -0
- package/dist/runtime/subagents/types.d.ts.map +1 -1
- package/dist/runtime/subagents/types.js +5 -1
- package/dist/runtime/subagents/types.js.map +1 -1
- package/dist/runtime/tool-detail-repository.d.ts +45 -0
- package/dist/runtime/tool-detail-repository.d.ts.map +1 -0
- package/dist/runtime/tool-detail-repository.js +131 -0
- package/dist/runtime/tool-detail-repository.js.map +1 -0
- package/dist/runtime/tool-output-presentation.d.ts +89 -0
- package/dist/runtime/tool-output-presentation.d.ts.map +1 -0
- package/dist/runtime/tool-output-presentation.js +329 -0
- package/dist/runtime/tool-output-presentation.js.map +1 -0
- package/dist/runtime/ui-events.d.ts +17 -0
- package/dist/runtime/ui-events.d.ts.map +1 -1
- package/dist/runtime/ui-events.js.map +1 -1
- package/dist/runtime/ui-view-model.d.ts +9 -0
- package/dist/runtime/ui-view-model.d.ts.map +1 -1
- package/dist/runtime/ui-view-model.js +59 -37
- package/dist/runtime/ui-view-model.js.map +1 -1
- package/dist/services/compact/auto-compact.d.ts +15 -2
- package/dist/services/compact/auto-compact.d.ts.map +1 -1
- package/dist/services/compact/auto-compact.js +56 -24
- package/dist/services/compact/auto-compact.js.map +1 -1
- package/dist/services/compact/compact.d.ts +2 -0
- package/dist/services/compact/compact.d.ts.map +1 -1
- package/dist/services/compact/compact.js +22 -5
- package/dist/services/compact/compact.js.map +1 -1
- package/dist/services/compact/coordinator.d.ts +26 -0
- package/dist/services/compact/coordinator.d.ts.map +1 -0
- package/dist/services/compact/coordinator.js +44 -0
- package/dist/services/compact/coordinator.js.map +1 -0
- package/dist/services/compact/index.d.ts +1 -0
- package/dist/services/compact/index.d.ts.map +1 -1
- package/dist/services/compact/index.js +3 -1
- package/dist/services/compact/index.js.map +1 -1
- package/dist/services/compact/summary-generator.d.ts +6 -2
- package/dist/services/compact/summary-generator.d.ts.map +1 -1
- package/dist/services/compact/summary-generator.js +73 -31
- package/dist/services/compact/summary-generator.js.map +1 -1
- package/dist/services/config-dir.d.ts +11 -1
- package/dist/services/config-dir.d.ts.map +1 -1
- package/dist/services/config-dir.js +19 -2
- package/dist/services/config-dir.js.map +1 -1
- package/dist/services/config.d.ts +4 -2
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +40 -0
- package/dist/services/config.js.map +1 -1
- package/dist/services/doctor.d.ts +2 -0
- package/dist/services/doctor.d.ts.map +1 -1
- package/dist/services/doctor.js +8 -1
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/global-config.d.ts +10 -0
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/global-config.js.map +1 -1
- package/dist/services/llm.d.ts +21 -4
- package/dist/services/llm.d.ts.map +1 -1
- package/dist/services/llm.js +66 -8
- package/dist/services/llm.js.map +1 -1
- package/dist/services/model-context.d.ts +36 -0
- package/dist/services/model-context.d.ts.map +1 -1
- package/dist/services/model-context.js +245 -32
- package/dist/services/model-context.js.map +1 -1
- package/dist/services/redaction.js +1 -1
- package/dist/services/redaction.js.map +1 -1
- package/dist/services/session-storage.d.ts +42 -1
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +175 -54
- package/dist/services/session-storage.js.map +1 -1
- package/dist/services/storage-maintenance.d.ts.map +1 -1
- package/dist/services/storage-maintenance.js +3 -2
- package/dist/services/storage-maintenance.js.map +1 -1
- package/dist/services/usage-state.d.ts +55 -5
- package/dist/services/usage-state.d.ts.map +1 -1
- package/dist/services/usage-state.js +185 -59
- package/dist/services/usage-state.js.map +1 -1
- package/dist/services/verification-profile.d.ts.map +1 -1
- package/dist/services/verification-profile.js +3 -2
- package/dist/services/verification-profile.js.map +1 -1
- package/dist/services/workspace-diff.d.ts.map +1 -1
- package/dist/services/workspace-diff.js +34 -3
- package/dist/services/workspace-diff.js.map +1 -1
- package/dist/services/yaml-config.js +1 -1
- package/dist/skills/runtime.d.ts +9 -5
- package/dist/skills/runtime.d.ts.map +1 -1
- package/dist/skills/runtime.js +157 -12
- package/dist/skills/runtime.js.map +1 -1
- package/dist/terminal-ui/editor.d.ts.map +1 -1
- package/dist/terminal-ui/editor.js +14 -3
- package/dist/terminal-ui/editor.js.map +1 -1
- package/dist/terminal-ui/launch.d.ts +21 -0
- package/dist/terminal-ui/launch.d.ts.map +1 -1
- package/dist/terminal-ui/launch.js +203 -45
- package/dist/terminal-ui/launch.js.map +1 -1
- package/dist/terminal-ui/output-queue.d.ts +43 -0
- package/dist/terminal-ui/output-queue.d.ts.map +1 -0
- package/dist/terminal-ui/output-queue.js +139 -0
- package/dist/terminal-ui/output-queue.js.map +1 -0
- package/dist/terminal-ui/raw-editor.d.ts +42 -0
- package/dist/terminal-ui/raw-editor.d.ts.map +1 -1
- package/dist/terminal-ui/raw-editor.js +337 -41
- package/dist/terminal-ui/raw-editor.js.map +1 -1
- package/dist/tools/bash_security.d.ts.map +1 -1
- package/dist/tools/bash_security.js +14 -5
- package/dist/tools/bash_security.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +176 -73
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/plan.d.ts.map +1 -1
- package/dist/tools/plan.js +7 -0
- package/dist/tools/plan.js.map +1 -1
- package/dist/tools/web.d.ts +10 -0
- package/dist/tools/web.d.ts.map +1 -1
- package/dist/tools/web.js +81 -1
- package/dist/tools/web.js.map +1 -1
- package/dist/tui-core/input-parser.d.ts +9 -2
- package/dist/tui-core/input-parser.d.ts.map +1 -1
- package/dist/tui-core/input-parser.js +57 -3
- package/dist/tui-core/input-parser.js.map +1 -1
- package/dist/tui-core/style.d.ts +16 -0
- package/dist/tui-core/style.d.ts.map +1 -1
- package/dist/tui-core/style.js +22 -1
- package/dist/tui-core/style.js.map +1 -1
- package/dist/tui-ui/inline-surface.d.ts +69 -12
- package/dist/tui-ui/inline-surface.d.ts.map +1 -1
- package/dist/tui-ui/inline-surface.js +366 -94
- package/dist/tui-ui/inline-surface.js.map +1 -1
- package/dist/tui-ui/input-ownership.d.ts +46 -0
- package/dist/tui-ui/input-ownership.d.ts.map +1 -0
- package/dist/tui-ui/input-ownership.js +74 -0
- package/dist/tui-ui/input-ownership.js.map +1 -0
- package/dist/tui-ui/launch.d.ts +2 -0
- package/dist/tui-ui/launch.d.ts.map +1 -1
- package/dist/tui-ui/launch.js +289 -43
- package/dist/tui-ui/launch.js.map +1 -1
- package/dist/tui-ui/layout.d.ts +52 -2
- package/dist/tui-ui/layout.d.ts.map +1 -1
- package/dist/tui-ui/layout.js +230 -76
- package/dist/tui-ui/layout.js.map +1 -1
- package/dist/tui-ui/runner.d.ts +101 -8
- package/dist/tui-ui/runner.d.ts.map +1 -1
- package/dist/tui-ui/runner.js +843 -51
- package/dist/tui-ui/runner.js.map +1 -1
- package/dist/tui-ui/state.d.ts +70 -4
- package/dist/tui-ui/state.d.ts.map +1 -1
- package/dist/tui-ui/state.js +251 -20
- package/dist/tui-ui/state.js.map +1 -1
- package/dist/tui-ui/theme-profile.d.ts +25 -0
- package/dist/tui-ui/theme-profile.d.ts.map +1 -0
- package/dist/tui-ui/theme-profile.js +130 -0
- package/dist/tui-ui/theme-profile.js.map +1 -0
- package/dist/tui-ui/theme.d.ts +13 -0
- package/dist/tui-ui/theme.d.ts.map +1 -0
- package/dist/tui-ui/theme.js +20 -0
- package/dist/tui-ui/theme.js.map +1 -0
- package/dist/tui-ui/transcript-cache.d.ts +5 -3
- package/dist/tui-ui/transcript-cache.d.ts.map +1 -1
- package/dist/tui-ui/transcript-cache.js +16 -9
- package/dist/tui-ui/transcript-cache.js.map +1 -1
- package/dist/tui-ui/transcript-inspector-layout.d.ts +9 -0
- package/dist/tui-ui/transcript-inspector-layout.d.ts.map +1 -0
- package/dist/tui-ui/transcript-inspector-layout.js +97 -0
- package/dist/tui-ui/transcript-inspector-layout.js.map +1 -0
- package/dist/tui-ui/transcript-inspector-surface.d.ts +18 -0
- package/dist/tui-ui/transcript-inspector-surface.d.ts.map +1 -0
- package/dist/tui-ui/transcript-inspector-surface.js +88 -0
- package/dist/tui-ui/transcript-inspector-surface.js.map +1 -0
- package/dist/tui-ui/transcript-inspector.d.ts +35 -0
- package/dist/tui-ui/transcript-inspector.d.ts.map +1 -0
- package/dist/tui-ui/transcript-inspector.js +128 -0
- package/dist/tui-ui/transcript-inspector.js.map +1 -0
- package/dist/tui-ui/transcript-layout.d.ts +22 -0
- package/dist/tui-ui/transcript-layout.d.ts.map +1 -0
- package/dist/tui-ui/transcript-layout.js +439 -0
- package/dist/tui-ui/transcript-layout.js.map +1 -0
- package/dist/ui/status-bar.d.ts +1 -1
- package/dist/ui/status-bar.d.ts.map +1 -1
- package/dist/ui/status-bar.js +2 -15
- package/dist/ui/status-bar.js.map +1 -1
- package/dist/ui-v2/components/shell.d.ts.map +1 -1
- package/dist/ui-v2/components/shell.js +0 -8
- package/dist/ui-v2/components/shell.js.map +1 -1
- package/package.json +1 -1
|
@@ -38,6 +38,8 @@ const SHOW_CURSOR = '\x1b[?25h';
|
|
|
38
38
|
const HIDE_CURSOR = '\x1b[?25l';
|
|
39
39
|
const DISABLE_AUTOWRAP = '\x1b[?7l';
|
|
40
40
|
const ENABLE_AUTOWRAP = '\x1b[?7h';
|
|
41
|
+
/** Fraction of the terminal height occupied by the bottom live band. */
|
|
42
|
+
const LIVE_BAND_RATIO = 0.75;
|
|
41
43
|
function cursorUp(n) {
|
|
42
44
|
return n > 0 ? `\x1b[${n}A` : '';
|
|
43
45
|
}
|
|
@@ -49,10 +51,17 @@ class InlineTerminalSurface {
|
|
|
49
51
|
this.phase = 'idle';
|
|
50
52
|
this.width = 0;
|
|
51
53
|
this.height = 0;
|
|
54
|
+
/** Height of the bottom live band (status + prompt + live tail). */
|
|
55
|
+
this.liveBandRows = 0;
|
|
52
56
|
this.liveRegionCapacity = 0;
|
|
53
57
|
this.cursorRow = 0;
|
|
54
58
|
this.cursorColumn = 0;
|
|
55
59
|
this.previousFrame = null;
|
|
60
|
+
/** Suppress stale-width live paints between SIGWINCH and the resize repaint. */
|
|
61
|
+
this.resizePending = false;
|
|
62
|
+
this.pendingResizeWidth = null;
|
|
63
|
+
this.resizeGeneration = 0;
|
|
64
|
+
this.resizeWaiters = [];
|
|
56
65
|
/** Serialized FIFO queue of pending operations. */
|
|
57
66
|
this.queue = [];
|
|
58
67
|
this.processing = false;
|
|
@@ -60,17 +69,50 @@ class InlineTerminalSurface {
|
|
|
60
69
|
this.now = options.now ?? (() => Date.now());
|
|
61
70
|
this.suppressColor = (0, style_1.shouldSuppressColor)();
|
|
62
71
|
}
|
|
72
|
+
/** Compute the live band height (~75% of screen, min 8, max height-1). */
|
|
73
|
+
static computeBandRows(height) {
|
|
74
|
+
const h = Math.max(1, Math.floor(height));
|
|
75
|
+
const minBand = 8;
|
|
76
|
+
const maxBand = Math.max(minBand, h - 1); // leave >=1 history row when possible
|
|
77
|
+
const desired = Math.round(h * LIVE_BAND_RATIO);
|
|
78
|
+
return Math.max(minBand, Math.min(maxBand, desired));
|
|
79
|
+
}
|
|
80
|
+
/** Live band height for the runner to size its live frame. */
|
|
81
|
+
getLiveBandRows() {
|
|
82
|
+
return this.liveBandRows;
|
|
83
|
+
}
|
|
84
|
+
/** History area rows above the band (recently scrolled committed lines). */
|
|
85
|
+
get historyAreaRows() {
|
|
86
|
+
return Math.max(0, this.height - this.liveBandRows);
|
|
87
|
+
}
|
|
63
88
|
getState() {
|
|
64
89
|
return {
|
|
65
90
|
phase: this.phase,
|
|
66
91
|
width: this.width,
|
|
67
92
|
height: this.height,
|
|
93
|
+
liveBandRows: this.liveBandRows,
|
|
68
94
|
liveRegionCapacity: this.liveRegionCapacity,
|
|
69
95
|
cursorRow: this.cursorRow,
|
|
70
96
|
cursorColumn: this.cursorColumn,
|
|
71
97
|
previousFrame: this.previousFrame,
|
|
72
98
|
};
|
|
73
99
|
}
|
|
100
|
+
/** Mark the terminal as resizing before the debounced layout pass runs. */
|
|
101
|
+
beginResize(width = this.width) {
|
|
102
|
+
this.resizeGeneration += 1;
|
|
103
|
+
this.resizePending = true;
|
|
104
|
+
this.pendingResizeWidth = Math.max(1, Math.floor(width));
|
|
105
|
+
return this.resizeGeneration;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Await completion of all queued operations.
|
|
109
|
+
* Because the queue is strictly FIFO, enqueueing a no-op and awaiting it
|
|
110
|
+
* guarantees every operation enqueued before this call has finished. Used by
|
|
111
|
+
* tests to deterministically observe the terminal after a burst of renders.
|
|
112
|
+
*/
|
|
113
|
+
whenIdle() {
|
|
114
|
+
return this.enqueue(async () => { });
|
|
115
|
+
}
|
|
74
116
|
/** Safe content width: never write the last column (avoid pending-wrap). */
|
|
75
117
|
get safeContentWidth() {
|
|
76
118
|
return Math.max(1, this.width - 1);
|
|
@@ -112,121 +154,246 @@ class InlineTerminalSurface {
|
|
|
112
154
|
return;
|
|
113
155
|
this.width = Math.max(1, Math.floor(width));
|
|
114
156
|
this.height = Math.max(1, Math.floor(height));
|
|
157
|
+
this.liveBandRows = InlineTerminalSurface.computeBandRows(this.height);
|
|
115
158
|
this.phase = 'mounted';
|
|
116
159
|
this.liveRegionCapacity = 0;
|
|
117
160
|
this.cursorRow = 0;
|
|
118
161
|
this.cursorColumn = 0;
|
|
119
|
-
this.writeRaw(`${ENABLE_BRACKETED_PASTE}${HIDE_CURSOR}`);
|
|
162
|
+
await this.writeRaw(`${ENABLE_BRACKETED_PASTE}${HIDE_CURSOR}`);
|
|
120
163
|
});
|
|
121
164
|
}
|
|
122
165
|
/**
|
|
123
|
-
* Commit finalized transcript entries to scrollback
|
|
124
|
-
*
|
|
166
|
+
* Commit finalized transcript entries to native scrollback.
|
|
167
|
+
*
|
|
168
|
+
* Protocol:
|
|
169
|
+
* 1. Erase only the live rows owned by this surface and return to their top.
|
|
170
|
+
* 2. Release that block and write finalized rows as ordinary line-oriented
|
|
171
|
+
* shell output. The terminal decides when those lines enter scrollback.
|
|
172
|
+
* 3. Allocate a fresh live block from the resulting cursor and repaint the
|
|
173
|
+
* latest live frame below the committed output.
|
|
125
174
|
*/
|
|
126
175
|
async commit(batch, getLatestLiveFrame) {
|
|
127
176
|
let output = '';
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
177
|
+
for (;;) {
|
|
178
|
+
let retryAfterResize = false;
|
|
179
|
+
await this.enqueue(async () => {
|
|
180
|
+
if (this.resizePending) {
|
|
181
|
+
retryAfterResize = true;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// Committed output behaves like ordinary shell output. Keeping
|
|
185
|
+
// autowrap enabled preserves an already-queued old-width batch if the
|
|
186
|
+
// terminal changes size immediately before this operation executes.
|
|
187
|
+
const chunks = [this.clearOwnedLiveRegion(), ENABLE_AUTOWRAP];
|
|
188
|
+
this.releaseLiveRegion();
|
|
189
|
+
for (const entry of batch.entries) {
|
|
190
|
+
for (const row of entry.rows) {
|
|
191
|
+
chunks.push(CR);
|
|
192
|
+
for (const [index, span] of row.entries()) {
|
|
193
|
+
if (index > 0)
|
|
194
|
+
chunks.push(style_1.SGR_RESET);
|
|
195
|
+
if (span.style) {
|
|
196
|
+
const sgr = (0, style_1.encodeStyleToSgr)(span.style, this.suppressColor);
|
|
197
|
+
if (sgr)
|
|
198
|
+
chunks.push(sgr);
|
|
199
|
+
}
|
|
200
|
+
chunks.push(span.text);
|
|
140
201
|
}
|
|
141
|
-
chunks.push(
|
|
202
|
+
chunks.push(style_1.SGR_RESET);
|
|
203
|
+
chunks.push('\n');
|
|
142
204
|
}
|
|
143
|
-
chunks.push(style_1.SGR_RESET);
|
|
144
|
-
chunks.push('\n'); // hard line boundary, never rely on pending wrap
|
|
145
205
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
this.
|
|
156
|
-
}
|
|
157
|
-
return {
|
|
206
|
+
const liveFrame = getLatestLiveFrame();
|
|
207
|
+
if (liveFrame) {
|
|
208
|
+
chunks.push(this.renderLiveInternal(liveFrame));
|
|
209
|
+
}
|
|
210
|
+
output = chunks.join('');
|
|
211
|
+
await this.writeRaw(output);
|
|
212
|
+
});
|
|
213
|
+
if (!retryAfterResize)
|
|
214
|
+
break;
|
|
215
|
+
await this.waitForResizeCompletion();
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
output,
|
|
219
|
+
committedEntries: batch.entries.length,
|
|
220
|
+
batchId: batch.batchId,
|
|
221
|
+
generation: batch.generation,
|
|
222
|
+
displayKeys: batch.entries.map(entry => entry.displayKey),
|
|
223
|
+
};
|
|
158
224
|
}
|
|
159
225
|
/** Render the live region frame (relative addressing, changed-row diff). */
|
|
160
226
|
async renderLive(frame) {
|
|
227
|
+
let output = '';
|
|
228
|
+
const requestedDuringResize = this.resizePending;
|
|
229
|
+
await this.enqueue(async () => {
|
|
230
|
+
if (requestedDuringResize || this.resizePending)
|
|
231
|
+
return;
|
|
232
|
+
output = this.renderLiveInternal(frame);
|
|
233
|
+
await this.writeRaw(output);
|
|
234
|
+
});
|
|
235
|
+
return output;
|
|
236
|
+
}
|
|
237
|
+
/** Forget the diff baseline and repaint only the currently owned live region. */
|
|
238
|
+
async forceRedraw(frame) {
|
|
161
239
|
let output = '';
|
|
162
240
|
await this.enqueue(async () => {
|
|
241
|
+
if (this.phase !== 'mounted' || this.resizePending)
|
|
242
|
+
return;
|
|
243
|
+
this.previousFrame = null;
|
|
163
244
|
output = this.renderLiveInternal(frame);
|
|
164
|
-
this.writeRaw(output);
|
|
245
|
+
await this.writeRaw(output);
|
|
165
246
|
});
|
|
166
247
|
return output;
|
|
167
248
|
}
|
|
168
249
|
renderLiveInternal(frame) {
|
|
169
250
|
const chunks = [];
|
|
170
|
-
const requiredRows = Math.min(frame.height, Math.max(1, this.
|
|
171
|
-
//
|
|
251
|
+
const requiredRows = Math.min(frame.height, Math.max(1, this.liveBandRows));
|
|
252
|
+
// Allocate or resize the cursor-owned live block from the current cursor.
|
|
172
253
|
chunks.push(this.ensureCapacity(requiredRows));
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
254
|
+
// Disable autowrap while painting band rows: a row that fills the last
|
|
255
|
+
// column (e.g. the full-width prompt border ┌─...─┐) would otherwise leave
|
|
256
|
+
// the terminal in a pending-wrap state that corrupts the next row's
|
|
257
|
+
// repaint. Re-enabled at the end of the batch.
|
|
258
|
+
chunks.push(DISABLE_AUTOWRAP);
|
|
259
|
+
const capacity = this.liveRegionCapacity;
|
|
260
|
+
const prev = this.previousFrame;
|
|
261
|
+
let row = this.cursorRow;
|
|
262
|
+
// Only diff when the previous frame shares the same geometry (height and
|
|
263
|
+
// capacity). Any geometry change (resize, first frame) triggers a full
|
|
264
|
+
// repaint so we never leave stale rows behind.
|
|
265
|
+
const canDiff = prev !== null && prev.height === capacity && frame.height === capacity;
|
|
266
|
+
if (!canDiff) {
|
|
267
|
+
// Full repaint: move to band top, clear every band row, then write every
|
|
268
|
+
// frame row. Track the real cursor row in `row` so the final positioning
|
|
269
|
+
// block below is exact.
|
|
270
|
+
chunks.push(cursorUp(row));
|
|
271
|
+
for (let i = 0; i < capacity; i++) {
|
|
272
|
+
chunks.push(CR, EL2);
|
|
273
|
+
if (i < capacity - 1) {
|
|
274
|
+
chunks.push(cursorDown(1));
|
|
275
|
+
row += 1;
|
|
193
276
|
}
|
|
194
|
-
chunks.push(span.text);
|
|
195
277
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
278
|
+
chunks.push(cursorUp(capacity - 1));
|
|
279
|
+
row = 0;
|
|
280
|
+
const frameRows = Math.min(frame.height, capacity);
|
|
281
|
+
for (let r = 0; r < frameRows; r++) {
|
|
282
|
+
chunks.push(...this.renderRowChunks(frame.rows[r] ?? []));
|
|
283
|
+
if (r < frameRows - 1) {
|
|
284
|
+
chunks.push(CR, cursorDown(1));
|
|
285
|
+
row += 1;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// Changed-row diff: only repaint rows whose styled content differs from
|
|
291
|
+
// the previous frame. Navigation moves the real cursor to the changed
|
|
292
|
+
// row, clears it, and redraws — avoiding a full-band flicker on every
|
|
293
|
+
// tick (e.g. while a tool is running and only the status line changes).
|
|
294
|
+
for (let r = 0; r < capacity; r++) {
|
|
295
|
+
const newRow = r < frame.height ? frame.rows[r] : [];
|
|
296
|
+
const oldRow = r < prev.height ? prev.rows[r] : [];
|
|
297
|
+
if (this.renderRowString(newRow) === this.renderRowString(oldRow))
|
|
298
|
+
continue;
|
|
299
|
+
if (r < row) {
|
|
300
|
+
chunks.push(cursorUp(row - r));
|
|
301
|
+
row = r;
|
|
302
|
+
}
|
|
303
|
+
else if (r > row) {
|
|
304
|
+
chunks.push(cursorDown(r - row));
|
|
305
|
+
row = r;
|
|
306
|
+
}
|
|
307
|
+
chunks.push(CR, EL2);
|
|
308
|
+
chunks.push(...this.renderRowChunks(newRow));
|
|
200
309
|
}
|
|
201
310
|
}
|
|
202
|
-
// Position cursor at frame cursor
|
|
311
|
+
// Position cursor at frame cursor using the precisely-tracked `row`.
|
|
203
312
|
const targetRow = Math.min(frame.cursor.row, this.liveRegionCapacity - 1);
|
|
204
313
|
const targetCol = Math.min(frame.cursor.column, Math.max(0, this.width - 1));
|
|
205
|
-
|
|
206
|
-
|
|
314
|
+
if (targetRow < row) {
|
|
315
|
+
chunks.push(cursorUp(row - targetRow));
|
|
316
|
+
row = targetRow;
|
|
317
|
+
}
|
|
318
|
+
else if (targetRow > row) {
|
|
319
|
+
chunks.push(cursorDown(targetRow - row));
|
|
320
|
+
row = targetRow;
|
|
321
|
+
}
|
|
207
322
|
chunks.push(CR);
|
|
208
323
|
if (targetCol > 0)
|
|
209
324
|
chunks.push(`\x1b[${targetCol}C`);
|
|
210
|
-
this.cursorRow =
|
|
325
|
+
this.cursorRow = row;
|
|
211
326
|
this.cursorColumn = targetCol;
|
|
212
327
|
chunks.push(frame.cursor.visible ? SHOW_CURSOR : HIDE_CURSOR);
|
|
213
328
|
chunks.push(ENABLE_AUTOWRAP);
|
|
214
329
|
this.previousFrame = frame;
|
|
215
330
|
return chunks.join('');
|
|
216
331
|
}
|
|
217
|
-
/**
|
|
332
|
+
/** Render a single styled frame row to ANSI chunks (text + SGR + reset). */
|
|
333
|
+
renderRowChunks(row) {
|
|
334
|
+
const chunks = [];
|
|
335
|
+
const spans = (0, frame_1.renderStyledFrameRow)(this.trimDefaultTrailingCells(row));
|
|
336
|
+
let emittedSgr = false;
|
|
337
|
+
for (const [index, span] of spans.entries()) {
|
|
338
|
+
if (index > 0)
|
|
339
|
+
chunks.push(style_1.SGR_RESET);
|
|
340
|
+
const sgr = (0, style_1.encodeStyleToSgr)(span.style, this.suppressColor);
|
|
341
|
+
if (sgr) {
|
|
342
|
+
chunks.push(sgr);
|
|
343
|
+
emittedSgr = true;
|
|
344
|
+
}
|
|
345
|
+
chunks.push(span.text);
|
|
346
|
+
}
|
|
347
|
+
if (emittedSgr || spans.length > 1)
|
|
348
|
+
chunks.push(style_1.SGR_RESET);
|
|
349
|
+
return chunks;
|
|
350
|
+
}
|
|
351
|
+
/** Stable string form of a styled row, used for changed-row comparison. */
|
|
352
|
+
renderRowString(row) {
|
|
353
|
+
return this.renderRowChunks(row).join('');
|
|
354
|
+
}
|
|
355
|
+
trimDefaultTrailingCells(row) {
|
|
356
|
+
let end = row.length;
|
|
357
|
+
while (end > 0) {
|
|
358
|
+
const cell = row[end - 1];
|
|
359
|
+
if (cell.char !== ' ' || (0, style_1.styleKey)(cell.style) !== '')
|
|
360
|
+
break;
|
|
361
|
+
end -= 1;
|
|
362
|
+
}
|
|
363
|
+
return row.slice(0, end);
|
|
364
|
+
}
|
|
365
|
+
renderedRowWidth(row) {
|
|
366
|
+
return this.trimDefaultTrailingCells(row)
|
|
367
|
+
.reduce((total, cell) => total + cell.width, 0);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Ensure the surface owns exactly `requiredRows` rows starting at the current
|
|
371
|
+
* cursor. Allocation uses ordinary newlines, so existing shell output is
|
|
372
|
+
* preserved and may naturally move into terminal scrollback.
|
|
373
|
+
*/
|
|
218
374
|
ensureCapacity(requiredRows) {
|
|
219
|
-
const target = Math.min(requiredRows, Math.max(1, this.
|
|
375
|
+
const target = Math.min(requiredRows, Math.max(1, this.liveBandRows));
|
|
220
376
|
const chunks = [];
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
377
|
+
if (this.liveRegionCapacity !== target) {
|
|
378
|
+
if (this.liveRegionCapacity > 0) {
|
|
379
|
+
chunks.push(this.clearLiveRegion());
|
|
380
|
+
this.releaseLiveRegion();
|
|
381
|
+
}
|
|
382
|
+
chunks.push(CR);
|
|
383
|
+
for (let i = 1; i < target; i++)
|
|
384
|
+
chunks.push('\n');
|
|
385
|
+
chunks.push(cursorUp(target - 1), CR);
|
|
386
|
+
this.liveRegionCapacity = target;
|
|
387
|
+
this.cursorRow = 0;
|
|
388
|
+
this.cursorColumn = 0;
|
|
389
|
+
this.previousFrame = null;
|
|
225
390
|
}
|
|
226
391
|
return chunks.join('');
|
|
227
392
|
}
|
|
228
|
-
/** Clear the live
|
|
393
|
+
/** Clear the live band rows (relative, no absolute addressing). */
|
|
229
394
|
clearLiveRegion() {
|
|
395
|
+
if (this.liveRegionCapacity === 0)
|
|
396
|
+
return '';
|
|
230
397
|
const chunks = [];
|
|
231
398
|
chunks.push(cursorUp(this.cursorRow));
|
|
232
399
|
for (let i = 0; i < this.liveRegionCapacity; i++) {
|
|
@@ -236,21 +403,83 @@ class InlineTerminalSurface {
|
|
|
236
403
|
}
|
|
237
404
|
chunks.push(cursorUp(this.liveRegionCapacity - 1));
|
|
238
405
|
this.cursorRow = 0;
|
|
406
|
+
this.cursorColumn = 0;
|
|
407
|
+
return chunks.join('');
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Clear the old live block after the terminal has already adopted a new
|
|
411
|
+
* width. A previously full row can occupy multiple physical rows after
|
|
412
|
+
* reflow, so tracked logical rows are not sufficient during resize.
|
|
413
|
+
*/
|
|
414
|
+
clearReflowedLiveRegion(nextWidth) {
|
|
415
|
+
if (this.liveRegionCapacity === 0)
|
|
416
|
+
return '';
|
|
417
|
+
if (!this.previousFrame)
|
|
418
|
+
return this.clearLiveRegion();
|
|
419
|
+
const physicalWidth = Math.max(1, Math.floor(nextWidth));
|
|
420
|
+
const frame = this.previousFrame;
|
|
421
|
+
const rowPhysicalHeights = Array.from({ length: this.liveRegionCapacity }, (_, row) => {
|
|
422
|
+
const cells = frame.rows[row] ?? [];
|
|
423
|
+
const writtenWidth = this.renderedRowWidth(cells);
|
|
424
|
+
return Math.max(1, Math.ceil(Math.max(1, writtenWidth) / physicalWidth));
|
|
425
|
+
});
|
|
426
|
+
const logicalCursorRow = Math.min(this.cursorRow, rowPhysicalHeights.length - 1);
|
|
427
|
+
const cursorPhysicalRow = rowPhysicalHeights
|
|
428
|
+
.slice(0, logicalCursorRow)
|
|
429
|
+
.reduce((total, rows) => total + rows, 0)
|
|
430
|
+
+ Math.floor(Math.max(0, this.cursorColumn) / physicalWidth);
|
|
431
|
+
const physicalRows = rowPhysicalHeights.reduce((total, rows) => total + rows, 0);
|
|
432
|
+
const chunks = [cursorUp(cursorPhysicalRow)];
|
|
433
|
+
for (let row = 0; row < physicalRows; row += 1) {
|
|
434
|
+
chunks.push(CR, EL2);
|
|
435
|
+
if (row < physicalRows - 1)
|
|
436
|
+
chunks.push(cursorDown(1));
|
|
437
|
+
}
|
|
438
|
+
chunks.push(cursorUp(physicalRows - 1));
|
|
439
|
+
this.cursorRow = 0;
|
|
440
|
+
this.cursorColumn = 0;
|
|
239
441
|
return chunks.join('');
|
|
240
442
|
}
|
|
241
|
-
|
|
242
|
-
|
|
443
|
+
clearOwnedLiveRegion() {
|
|
444
|
+
return this.resizePending && this.pendingResizeWidth !== null
|
|
445
|
+
? this.clearReflowedLiveRegion(this.pendingResizeWidth)
|
|
446
|
+
: this.clearLiveRegion();
|
|
447
|
+
}
|
|
448
|
+
waitForResizeCompletion() {
|
|
449
|
+
if (!this.resizePending)
|
|
450
|
+
return Promise.resolve();
|
|
451
|
+
return new Promise(resolve => this.resizeWaiters.push(resolve));
|
|
452
|
+
}
|
|
453
|
+
completeResize() {
|
|
454
|
+
this.resizePending = false;
|
|
455
|
+
this.pendingResizeWidth = null;
|
|
456
|
+
const waiters = this.resizeWaiters.splice(0);
|
|
457
|
+
for (const resolve of waiters)
|
|
458
|
+
resolve();
|
|
459
|
+
}
|
|
460
|
+
releaseLiveRegion() {
|
|
461
|
+
this.liveRegionCapacity = 0;
|
|
462
|
+
this.cursorRow = 0;
|
|
463
|
+
this.cursorColumn = 0;
|
|
464
|
+
this.previousFrame = null;
|
|
465
|
+
}
|
|
466
|
+
/** Resize: erase only the owned block, then rebuild from the current cursor. */
|
|
467
|
+
async resize(width, height, getLatestLiveFrame, generation = this.resizePending ? this.resizeGeneration : this.beginResize(width)) {
|
|
243
468
|
await this.enqueue(async () => {
|
|
469
|
+
if (generation !== this.resizeGeneration)
|
|
470
|
+
return;
|
|
471
|
+
const chunks = [this.clearReflowedLiveRegion(width)];
|
|
472
|
+
this.releaseLiveRegion();
|
|
244
473
|
this.width = Math.max(1, Math.floor(width));
|
|
245
474
|
this.height = Math.max(1, Math.floor(height));
|
|
246
|
-
|
|
247
|
-
this.liveRegionCapacity = 0;
|
|
248
|
-
this.cursorRow = 0;
|
|
249
|
-
this.previousFrame = null;
|
|
475
|
+
this.liveBandRows = InlineTerminalSurface.computeBandRows(this.height);
|
|
250
476
|
const liveFrame = getLatestLiveFrame();
|
|
251
477
|
if (liveFrame) {
|
|
252
|
-
|
|
478
|
+
chunks.push(this.renderLiveInternal(liveFrame));
|
|
253
479
|
}
|
|
480
|
+
await this.writeRaw(chunks.join(''));
|
|
481
|
+
if (generation === this.resizeGeneration)
|
|
482
|
+
this.completeResize();
|
|
254
483
|
});
|
|
255
484
|
}
|
|
256
485
|
/** Suspend for child process: clear live region, disable bracketed paste, show cursor. */
|
|
@@ -258,24 +487,27 @@ class InlineTerminalSurface {
|
|
|
258
487
|
await this.enqueue(async () => {
|
|
259
488
|
if (this.phase !== 'mounted')
|
|
260
489
|
return;
|
|
261
|
-
this.writeRaw(this.
|
|
262
|
-
this.writeRaw(`${style_1.SGR_RESET}${SHOW_CURSOR}${DISABLE_BRACKETED_PASTE}${ENABLE_AUTOWRAP}`);
|
|
490
|
+
await this.writeRaw(`${this.clearOwnedLiveRegion()}${style_1.SGR_RESET}${SHOW_CURSOR}${DISABLE_BRACKETED_PASTE}${ENABLE_AUTOWRAP}`);
|
|
263
491
|
this.phase = 'suspended';
|
|
264
|
-
this.
|
|
265
|
-
this.
|
|
492
|
+
this.releaseLiveRegion();
|
|
493
|
+
this.completeResize();
|
|
266
494
|
});
|
|
267
495
|
}
|
|
268
496
|
/** Restore after child process: re-enable bracketed paste, rebuild live frame. */
|
|
269
|
-
async restore(getLatestLiveFrame) {
|
|
497
|
+
async restore(getLatestLiveFrame, width = this.width, height = this.height) {
|
|
270
498
|
await this.enqueue(async () => {
|
|
271
499
|
if (this.phase !== 'suspended')
|
|
272
500
|
return;
|
|
501
|
+
this.width = Math.max(1, Math.floor(width));
|
|
502
|
+
this.height = Math.max(1, Math.floor(height));
|
|
503
|
+
this.liveBandRows = InlineTerminalSurface.computeBandRows(this.height);
|
|
273
504
|
this.phase = 'mounted';
|
|
274
|
-
|
|
505
|
+
const chunks = [`${ENABLE_BRACKETED_PASTE}${HIDE_CURSOR}`];
|
|
275
506
|
const liveFrame = getLatestLiveFrame();
|
|
276
507
|
if (liveFrame) {
|
|
277
|
-
|
|
508
|
+
chunks.push(this.renderLiveInternal(liveFrame));
|
|
278
509
|
}
|
|
510
|
+
await this.writeRaw(chunks.join(''));
|
|
279
511
|
});
|
|
280
512
|
}
|
|
281
513
|
/** Unmount: clear live region, restore terminal state. Does NOT erase scrollback. */
|
|
@@ -283,13 +515,10 @@ class InlineTerminalSurface {
|
|
|
283
515
|
await this.enqueue(async () => {
|
|
284
516
|
if (this.phase === 'unmounted')
|
|
285
517
|
return;
|
|
286
|
-
this.writeRaw(this.
|
|
287
|
-
this.writeRaw(`${style_1.SGR_RESET}${SHOW_CURSOR}${DISABLE_BRACKETED_PASTE}${ENABLE_AUTOWRAP}`);
|
|
288
|
-
// Final newline so shell prompt starts on a clean line.
|
|
289
|
-
this.writeRaw('\n');
|
|
518
|
+
await this.writeRaw(`${this.clearOwnedLiveRegion()}${style_1.SGR_RESET}${SHOW_CURSOR}${DISABLE_BRACKETED_PASTE}${ENABLE_AUTOWRAP}\n`);
|
|
290
519
|
this.phase = 'unmounted';
|
|
291
|
-
this.
|
|
292
|
-
this.
|
|
520
|
+
this.releaseLiveRegion();
|
|
521
|
+
this.completeResize();
|
|
293
522
|
});
|
|
294
523
|
}
|
|
295
524
|
/** Flush: wait for queue to drain. Yields to I/O between checks. */
|
|
@@ -298,23 +527,66 @@ class InlineTerminalSurface {
|
|
|
298
527
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
299
528
|
}
|
|
300
529
|
}
|
|
301
|
-
writeRaw(chunk) {
|
|
302
|
-
if (
|
|
530
|
+
async writeRaw(chunk) {
|
|
531
|
+
if (!chunk)
|
|
303
532
|
return;
|
|
533
|
+
if (this.output.writable === false) {
|
|
534
|
+
throw new Error('terminal output is not writable');
|
|
535
|
+
}
|
|
536
|
+
let accepted;
|
|
304
537
|
try {
|
|
305
|
-
this.output.write(chunk);
|
|
538
|
+
accepted = this.output.write(chunk);
|
|
306
539
|
}
|
|
307
|
-
catch {
|
|
308
|
-
|
|
540
|
+
catch (error) {
|
|
541
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
309
542
|
}
|
|
543
|
+
// Node streams return a boolean. A few compatible/test streams return
|
|
544
|
+
// void; only an explicit false means backpressure.
|
|
545
|
+
if (accepted !== false)
|
|
546
|
+
return;
|
|
547
|
+
await new Promise((resolve, reject) => {
|
|
548
|
+
let settled = false;
|
|
549
|
+
const cleanup = () => {
|
|
550
|
+
this.output.off('drain', onDrain);
|
|
551
|
+
this.output.off('error', onError);
|
|
552
|
+
this.output.off('close', onClose);
|
|
553
|
+
};
|
|
554
|
+
const settle = (error) => {
|
|
555
|
+
if (settled)
|
|
556
|
+
return;
|
|
557
|
+
settled = true;
|
|
558
|
+
cleanup();
|
|
559
|
+
if (error)
|
|
560
|
+
reject(error);
|
|
561
|
+
else
|
|
562
|
+
resolve();
|
|
563
|
+
};
|
|
564
|
+
const onDrain = () => {
|
|
565
|
+
if (this.output.writable === false) {
|
|
566
|
+
settle(new Error('terminal output closed before drain'));
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
settle();
|
|
570
|
+
};
|
|
571
|
+
const onError = (error) => {
|
|
572
|
+
settle(error instanceof Error ? error : new Error(String(error ?? 'terminal output error')));
|
|
573
|
+
};
|
|
574
|
+
const onClose = () => settle(new Error('terminal output closed before drain'));
|
|
575
|
+
this.output.on('drain', onDrain);
|
|
576
|
+
this.output.on('error', onError);
|
|
577
|
+
this.output.on('close', onClose);
|
|
578
|
+
});
|
|
310
579
|
}
|
|
311
580
|
/** Reset for tests. */
|
|
312
581
|
reset() {
|
|
313
582
|
this.phase = 'idle';
|
|
583
|
+
this.liveBandRows = 0;
|
|
314
584
|
this.liveRegionCapacity = 0;
|
|
315
585
|
this.cursorRow = 0;
|
|
316
586
|
this.cursorColumn = 0;
|
|
317
587
|
this.previousFrame = null;
|
|
588
|
+
this.completeResize();
|
|
589
|
+
this.resizeGeneration = 0;
|
|
318
590
|
this.queue = [];
|
|
319
591
|
this.processing = false;
|
|
320
592
|
}
|