pi-crew 0.1.41 → 0.1.44
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/CHANGELOG.md +47 -0
- package/README.md +51 -0
- package/agents/analyst.md +11 -11
- package/agents/critic.md +11 -11
- package/agents/executor.md +11 -11
- package/agents/explorer.md +11 -11
- package/agents/planner.md +11 -11
- package/agents/reviewer.md +11 -11
- package/agents/security-reviewer.md +11 -11
- package/agents/test-engineer.md +11 -11
- package/agents/verifier.md +11 -11
- package/agents/writer.md +11 -11
- package/docs/refactor-tasks-phase3.md +394 -394
- package/docs/refactor-tasks-phase4.md +564 -564
- package/docs/refactor-tasks-phase5.md +402 -402
- package/docs/refactor-tasks-phase6.md +662 -662
- package/docs/research-extension-examples.md +297 -297
- package/docs/research-extension-system.md +324 -324
- package/docs/research-optimization-plan.md +548 -548
- package/docs/research-phase10-distillation.md +199 -0
- package/docs/research-phase11-distillation.md +201 -0
- package/docs/research-pi-coding-agent.md +357 -357
- package/docs/research-source-pi-crew-reference.md +174 -174
- package/docs/runtime-flow.md +148 -148
- package/docs/source-runtime-refactor-map.md +83 -83
- package/index.ts +6 -6
- package/package.json +1 -1
- package/src/agents/agent-serializer.ts +34 -34
- package/src/agents/discover-agents.ts +5 -4
- package/src/config/config.ts +28 -4
- package/src/extension/cross-extension-rpc.ts +82 -82
- package/src/extension/management.ts +37 -8
- package/src/extension/notification-router.ts +2 -2
- package/src/extension/register.ts +130 -8
- package/src/extension/registration/commands.ts +11 -9
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-tools.ts +28 -19
- package/src/extension/registration/team-tool.ts +2 -1
- package/src/extension/result-watcher.ts +4 -4
- package/src/extension/run-bundle-schema.ts +8 -4
- package/src/extension/run-import.ts +4 -0
- package/src/extension/run-index.ts +23 -1
- package/src/extension/run-maintenance.ts +43 -24
- package/src/extension/team-tool/api.ts +2 -2
- package/src/extension/team-tool/cancel.ts +76 -4
- package/src/extension/team-tool/context.ts +1 -0
- package/src/extension/team-tool/doctor.ts +8 -1
- package/src/extension/team-tool/handle-settings.ts +188 -0
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +79 -79
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +67 -0
- package/src/extension/team-tool/run.ts +6 -4
- package/src/extension/team-tool/status.ts +99 -93
- package/src/extension/team-tool-types.ts +4 -0
- package/src/extension/team-tool.ts +5 -1
- package/src/i18n.ts +184 -0
- package/src/observability/correlation.ts +2 -2
- package/src/observability/event-to-metric.ts +10 -3
- package/src/observability/exporters/adapter.ts +7 -1
- package/src/observability/exporters/otlp-exporter.ts +14 -2
- package/src/observability/exporters/prometheus-exporter.ts +9 -2
- package/src/observability/metric-registry.ts +18 -3
- package/src/observability/metric-retention.ts +11 -3
- package/src/observability/metric-sink.ts +9 -4
- package/src/observability/metrics-primitives.ts +4 -3
- package/src/prompt/prompt-runtime.ts +72 -68
- package/src/runtime/agent-control.ts +63 -63
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/attention-events.ts +28 -23
- package/src/runtime/background-runner.ts +53 -53
- package/src/runtime/child-pi.ts +4 -4
- package/src/runtime/completion-guard.ts +95 -4
- package/src/runtime/concurrency.ts +1 -1
- package/src/runtime/crash-recovery.ts +32 -1
- package/src/runtime/crew-agent-runtime.ts +59 -58
- package/src/runtime/deadletter.ts +14 -4
- package/src/runtime/delivery-coordinator.ts +143 -0
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +106 -106
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +48 -4
- package/src/runtime/live-agent-control.ts +87 -87
- package/src/runtime/live-agent-manager.ts +85 -85
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +305 -305
- package/src/runtime/manifest-cache.ts +2 -2
- package/src/runtime/model-fallback.ts +272 -261
- package/src/runtime/overflow-recovery.ts +157 -0
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +1 -1
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/policy-engine.ts +79 -78
- package/src/runtime/post-exit-stdio-guard.ts +2 -2
- package/src/runtime/process-status.ts +56 -56
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +5 -0
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/runtime-resolver.ts +1 -1
- package/src/runtime/session-resources.ts +25 -0
- package/src/runtime/session-snapshot.ts +59 -0
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/stale-reconciler.ts +179 -0
- package/src/runtime/subagent-manager.ts +3 -3
- package/src/runtime/supervisor-contact.ts +59 -0
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-output-context.ts +127 -127
- package/src/runtime/task-runner/live-executor.ts +101 -101
- package/src/runtime/task-runner/progress.ts +119 -111
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/task-runner.ts +14 -0
- package/src/runtime/team-runner.ts +9 -10
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/schema/config-schema.ts +2 -1
- package/src/schema/team-tool-schema.ts +115 -109
- package/src/state/artifact-store.ts +4 -2
- package/src/state/atomic-write.ts +12 -4
- package/src/state/contracts.ts +109 -105
- package/src/state/event-log.ts +3 -4
- package/src/state/jsonl-writer.ts +4 -1
- package/src/state/locks.ts +9 -1
- package/src/state/task-claims.ts +44 -42
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/discover-teams.ts +2 -2
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/crew-widget.ts +5 -4
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/live-run-sidebar.ts +1 -1
- package/src/ui/loaders.ts +158 -158
- package/src/ui/mascot.ts +3 -2
- package/src/ui/powerbar-publisher.ts +7 -6
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/render-scheduler.ts +54 -14
- package/src/ui/run-dashboard.ts +39 -11
- package/src/ui/run-snapshot-cache.ts +336 -36
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +58 -54
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/ui/theme-adapter.ts +1 -1
- package/src/ui/transcript-viewer.ts +7 -2
- package/src/utils/atomic-write.ts +33 -0
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/file-coalescer.ts +5 -3
- package/src/utils/frontmatter.ts +68 -36
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +12 -12
- package/src/utils/internal-error.ts +1 -1
- package/src/utils/names.ts +27 -26
- package/src/utils/paths.ts +1 -1
- package/src/utils/redaction.ts +44 -41
- package/src/utils/safe-paths.ts +47 -34
- package/src/utils/sleep.ts +2 -2
- package/src/utils/timings.ts +2 -0
- package/src/utils/visual.ts +9 -1
- package/src/workflows/discover-workflows.ts +4 -1
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/worktree-manager.ts +6 -1
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +29 -29
- package/workflows/fast-fix.workflow.md +22 -22
- package/workflows/implementation.workflow.md +38 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
|
@@ -22,15 +22,26 @@ const DEFAULT_EVENTS = [
|
|
|
22
22
|
"crew.mailbox.message",
|
|
23
23
|
];
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Coordinates UI renders with debounce + fallback polling.
|
|
27
|
+
*
|
|
28
|
+
* Critical: uses recursive setTimeout instead of setInterval + a rendering
|
|
29
|
+
* guard (`rendering` / `pendingRender`) so that when render() takes longer
|
|
30
|
+
* than the fallback interval, callbacks do NOT pile up and storm the event
|
|
31
|
+
* loop. Instead, overlapping schedules are collapsed into a single deferred
|
|
32
|
+
* re-render.
|
|
33
|
+
*/
|
|
25
34
|
export class RenderScheduler {
|
|
26
35
|
private readonly render: () => void;
|
|
27
36
|
private readonly onInvalidate?: (payload: unknown) => void;
|
|
28
37
|
private readonly debounceMs: number;
|
|
29
38
|
private readonly fallbackMs: number;
|
|
30
39
|
private debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
31
|
-
private fallbackTimer: ReturnType<typeof
|
|
40
|
+
private fallbackTimer: ReturnType<typeof setTimeout> | undefined;
|
|
32
41
|
private disposed = false;
|
|
33
42
|
private lastEventAt = 0;
|
|
43
|
+
private rendering = false;
|
|
44
|
+
private pendingRender = false;
|
|
34
45
|
private readonly unsubs: Array<() => void> = [];
|
|
35
46
|
|
|
36
47
|
constructor(events: RenderSchedulerEventBus | undefined, render: () => void, options: RenderSchedulerOptions = {}) {
|
|
@@ -39,8 +50,8 @@ export class RenderScheduler {
|
|
|
39
50
|
this.debounceMs = options.debounceMs ?? 75;
|
|
40
51
|
this.fallbackMs = options.fallbackMs ?? 750;
|
|
41
52
|
for (const event of options.events ?? DEFAULT_EVENTS) this.subscribe(events, event);
|
|
42
|
-
this.fallbackTimer =
|
|
43
|
-
this.fallbackTimer.unref
|
|
53
|
+
this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
|
|
54
|
+
this.fallbackTimer.unref();
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
private subscribe(events: RenderSchedulerEventBus | undefined, event: string): void {
|
|
@@ -54,10 +65,19 @@ export class RenderScheduler {
|
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
/** Recursive setTimeout — avoids setInterval timer storms. */
|
|
69
|
+
private fallbackLoop(): void {
|
|
58
70
|
if (this.disposed) return;
|
|
59
|
-
if (Date.now() - this.lastEventAt < this.fallbackMs)
|
|
60
|
-
|
|
71
|
+
if (Date.now() - this.lastEventAt < this.fallbackMs) {
|
|
72
|
+
if (this.disposed) return;
|
|
73
|
+
this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
|
|
74
|
+
this.fallbackTimer.unref();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.schedule();
|
|
78
|
+
if (this.disposed) return;
|
|
79
|
+
this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
|
|
80
|
+
this.fallbackTimer.unref();
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
schedule(payload?: unknown): void {
|
|
@@ -73,15 +93,39 @@ export class RenderScheduler {
|
|
|
73
93
|
this.debounceTimer = undefined;
|
|
74
94
|
this.flush();
|
|
75
95
|
}, this.debounceMs);
|
|
76
|
-
this.debounceTimer.unref
|
|
96
|
+
this.debounceTimer.unref();
|
|
77
97
|
}
|
|
78
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Flush a render. If a render is already in progress the request is
|
|
101
|
+
* collapsed: `pendingRender` is set and the caller that holds
|
|
102
|
+
* `rendering==true` will loop one more time after finishing.
|
|
103
|
+
*/
|
|
79
104
|
flush(): void {
|
|
80
105
|
if (this.disposed) return;
|
|
106
|
+
if (this.rendering) {
|
|
107
|
+
this.pendingRender = true;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.rendering = true;
|
|
111
|
+
this.pendingRender = false;
|
|
112
|
+
let iterations = 0;
|
|
81
113
|
try {
|
|
82
|
-
|
|
114
|
+
do {
|
|
115
|
+
this.pendingRender = false;
|
|
116
|
+
this.render();
|
|
117
|
+
iterations += 1;
|
|
118
|
+
// Safety valve: 5 re-renders max per flush to prevent infinite loops
|
|
119
|
+
// if render() itself calls flush() synchronously.
|
|
120
|
+
} while (this.pendingRender && !this.disposed && iterations < 5);
|
|
83
121
|
} catch (error) {
|
|
84
122
|
logInternalError("render-scheduler.render", error);
|
|
123
|
+
} finally {
|
|
124
|
+
this.rendering = false;
|
|
125
|
+
// If we hit the iteration cap, schedule one more render to drain.
|
|
126
|
+
if (iterations >= 5 && this.pendingRender && !this.disposed) {
|
|
127
|
+
this.schedule();
|
|
128
|
+
}
|
|
85
129
|
}
|
|
86
130
|
}
|
|
87
131
|
|
|
@@ -89,15 +133,11 @@ export class RenderScheduler {
|
|
|
89
133
|
if (this.disposed) return;
|
|
90
134
|
this.disposed = true;
|
|
91
135
|
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
92
|
-
if (this.fallbackTimer)
|
|
136
|
+
if (this.fallbackTimer) clearTimeout(this.fallbackTimer);
|
|
93
137
|
this.debounceTimer = undefined;
|
|
94
138
|
this.fallbackTimer = undefined;
|
|
95
139
|
for (const unsub of this.unsubs.splice(0)) {
|
|
96
|
-
try {
|
|
97
|
-
unsub();
|
|
98
|
-
} catch (error) {
|
|
99
|
-
logInternalError("render-scheduler.unsubscribe", error);
|
|
100
|
-
}
|
|
140
|
+
try { unsub(); } catch (error) { logInternalError("render-scheduler.unsubscribe", error); }
|
|
101
141
|
}
|
|
102
142
|
}
|
|
103
143
|
}
|
package/src/ui/run-dashboard.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { Box, Text } from "./layout-primitives.ts";
|
|
|
12
12
|
import { DynamicCrewBorder } from "./dynamic-border.ts";
|
|
13
13
|
import { CrewFooter } from "./crew-footer.ts";
|
|
14
14
|
import { aggregateUsage } from "../state/usage.ts";
|
|
15
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
15
16
|
import { renderAgentsPane } from "./dashboard-panes/agents-pane.ts";
|
|
16
17
|
import { renderMailboxPane } from "./dashboard-panes/mailbox-pane.ts";
|
|
17
18
|
import { renderProgressPane } from "./dashboard-panes/progress-pane.ts";
|
|
@@ -194,18 +195,38 @@ function runLabel(run: TeamRunManifest, selected: boolean, snapshotCache?: RunSn
|
|
|
194
195
|
const stale = isLikelyOrphanedActiveRun(run, agents);
|
|
195
196
|
const running = agents.find((agent) => agent.status === "running");
|
|
196
197
|
const queued = agents.find((agent) => agent.status === "queued");
|
|
197
|
-
const step = stale ? "orphaned queued run" : running ? `step ${running.taskId}` : queued ? `queued ${queued.taskId}` : `agents ${agents.length}`;
|
|
198
|
+
const step = stale ? "orphaned queued run" : running ? `step ${running.taskId.replace(/[\x00-\x1f\x7f-\x9f]/g, "")}` : queued ? `queued ${queued.taskId.replace(/[\x00-\x1f\x7f-\x9f]/g, "")}` : `agents ${agents.length}`;
|
|
198
199
|
const status: RunStatus = stale ? "stale" : (run.status as RunStatus);
|
|
199
200
|
const marker = selected ? "›" : " ";
|
|
200
201
|
return `${marker} ${iconForStatus(status, { runningGlyph: spinnerFrame(run.runId) })} ${run.runId.slice(-8)} ${status} | ${run.team}/${run.workflow ?? "none"} | ${step} | ${run.goal}`;
|
|
201
202
|
}
|
|
202
203
|
|
|
204
|
+
interface ResolvedRun {
|
|
205
|
+
manifest: TeamRunManifest;
|
|
206
|
+
snapshot: RunUiSnapshot | undefined;
|
|
207
|
+
agents: CrewAgentRecord[];
|
|
208
|
+
status: RunStatus;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function resolveRuns(runs: TeamRunManifest[], snapshotCache?: RunSnapshotCache): Map<string, ResolvedRun> {
|
|
212
|
+
const map = new Map<string, ResolvedRun>();
|
|
213
|
+
for (const run of runs) {
|
|
214
|
+
const snapshot = snapshotFor(run, snapshotCache);
|
|
215
|
+
const agents = snapshot?.agents ?? agentsFor(run, snapshotCache);
|
|
216
|
+
const displayRun = snapshot?.manifest ?? run;
|
|
217
|
+
const status: RunStatus = isLikelyOrphanedActiveRun(displayRun, agents) ? "stale" : (displayRun.status as RunStatus);
|
|
218
|
+
map.set(run.runId, { manifest: run, snapshot, agents, status });
|
|
219
|
+
}
|
|
220
|
+
return map;
|
|
221
|
+
}
|
|
222
|
+
|
|
203
223
|
function groupedRuns(runs: TeamRunManifest[], snapshotCache?: RunSnapshotCache): Array<{ label: string; run?: TeamRunManifest }> {
|
|
204
|
-
const
|
|
205
|
-
const recent = runs.filter((run) => !isDisplayActiveRun(snapshotFor(run, snapshotCache)?.manifest ?? run, agentsFor(run, snapshotCache)));
|
|
224
|
+
const resolved = resolveRuns(runs, snapshotCache);
|
|
206
225
|
const rows: Array<{ label: string; run?: TeamRunManifest }> = [];
|
|
226
|
+
const active = runs.filter((run) => isDisplayActiveRun(resolved.get(run.runId)?.snapshot?.manifest ?? run, resolved.get(run.runId)?.agents ?? []));
|
|
227
|
+
const rest = runs.filter((run) => !isDisplayActiveRun(resolved.get(run.runId)?.snapshot?.manifest ?? run, resolved.get(run.runId)?.agents ?? []));
|
|
207
228
|
if (active.length) rows.push({ label: "Active" }, ...active.map((run) => ({ label: run.runId, run })));
|
|
208
|
-
if (
|
|
229
|
+
if (rest.length) rows.push({ label: "Recent" }, ...rest.map((run) => ({ label: run.runId, run })));
|
|
209
230
|
return rows;
|
|
210
231
|
}
|
|
211
232
|
|
|
@@ -214,13 +235,9 @@ function selectedRunFromGrouped(runs: TeamRunManifest[], selected: number, snaps
|
|
|
214
235
|
}
|
|
215
236
|
|
|
216
237
|
function countByStatus(runs: TeamRunManifest[], snapshotCache?: RunSnapshotCache): string {
|
|
238
|
+
const resolved = resolveRuns(runs, snapshotCache);
|
|
217
239
|
const counts = new Map<RunStatus, number>();
|
|
218
|
-
for (const
|
|
219
|
-
const snapshot = snapshotFor(run, snapshotCache);
|
|
220
|
-
const displayRun = snapshot?.manifest ?? run;
|
|
221
|
-
const status: RunStatus = isLikelyOrphanedActiveRun(displayRun, snapshot?.agents ?? agentsFor(run, snapshotCache)) ? "stale" : (displayRun.status as RunStatus);
|
|
222
|
-
counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
223
|
-
}
|
|
240
|
+
for (const r of resolved.values()) counts.set(r.status, (counts.get(r.status) ?? 0) + 1);
|
|
224
241
|
return [...counts.entries()].map(([status, count]) => `${status}=${count}`).join(", ") || "none";
|
|
225
242
|
}
|
|
226
243
|
|
|
@@ -253,10 +270,12 @@ export class RunDashboard implements DashboardComponent {
|
|
|
253
270
|
private refreshRuns(): void {
|
|
254
271
|
if (!this.options.runProvider) return;
|
|
255
272
|
const selectedRunId = this.selectedRunId();
|
|
256
|
-
|
|
273
|
+
const next = this.options.runProvider();
|
|
274
|
+
this.runs = Array.isArray(next) ? next : this.runs;
|
|
257
275
|
if (selectedRunId) {
|
|
258
276
|
const nextIndex = groupedRuns(this.runs, this.options.snapshotCache).filter((row) => row.run).findIndex((row) => row.run?.runId === selectedRunId);
|
|
259
277
|
if (nextIndex >= 0) this.selected = nextIndex;
|
|
278
|
+
else this.selected = 0;
|
|
260
279
|
}
|
|
261
280
|
}
|
|
262
281
|
|
|
@@ -289,6 +308,15 @@ export class RunDashboard implements DashboardComponent {
|
|
|
289
308
|
}
|
|
290
309
|
|
|
291
310
|
render(width: number): string[] {
|
|
311
|
+
try {
|
|
312
|
+
return this.renderUnsafe(width);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
logInternalError("run-dashboard.render", error);
|
|
315
|
+
return renderLines(["Dashboard error — see logs for details."], width);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private renderUnsafe(width: number): string[] {
|
|
292
320
|
this.refreshRuns();
|
|
293
321
|
const signature = this.buildSignature();
|
|
294
322
|
if (signature !== this.cachedVersion || this.cachedWidth !== width) {
|