termcut 0.5.0 → 0.6.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/src/timeline.ts CHANGED
@@ -2,7 +2,7 @@ import { MARKER } from "./cast";
2
2
  import type { CastEvent } from "./types";
3
3
 
4
4
  export interface TimedEvent {
5
- /** Time on the visible (hide-collapsed, speed-adjusted) timeline, seconds. */
5
+ /** Time on the visible (hide-collapsed, speed-adjusted, idle-compressed) timeline, seconds. */
6
6
  vt: number;
7
7
  type: CastEvent[1];
8
8
  data: string;
@@ -13,16 +13,22 @@ export interface Timeline {
13
13
  duration: number;
14
14
  }
15
15
 
16
+ export interface TimelineOptions {
17
+ /** Keep `i` (input) events; the renderer needs them for the key overlay. Default false. */
18
+ keepInput?: boolean;
19
+ /** Cap any gap between consecutive events to this many seconds (idle compression). */
20
+ maxPause?: number;
21
+ }
22
+
16
23
  /**
17
- * Collapse hidden intervals and apply playback speed. Hidden events keep their relative order but all land
18
- * on the instant the hide started, so the first visible frame after `show` reflects their combined effect.
19
- * Input (`i`) events are dropped: the PTY already echoed them.
24
+ * Collapse hidden intervals, apply playback speed, optionally cap idle gaps. Hidden events keep their relative
25
+ * order but all land on the instant the hide started, so the first visible frame after `show` reflects their
26
+ * combined effect. Input (`i`) events are dropped unless `keepInput`: the PTY already echoed them.
20
27
  */
21
- export function buildTimeline(events: CastEvent[], playbackSpeed: number): Timeline {
28
+ export function buildTimeline(events: CastEvent[], playbackSpeed: number, opts: TimelineOptions = {}): Timeline {
22
29
  const out: TimedEvent[] = [];
23
30
  let hiddenSince: number | null = null;
24
31
  let removed = 0;
25
- let duration = 0;
26
32
 
27
33
  for (const [t, type, data] of events) {
28
34
  if (type === "m" && data === MARKER.hide) {
@@ -36,12 +42,25 @@ export function buildTimeline(events: CastEvent[], playbackSpeed: number): Timel
36
42
  }
37
43
  continue;
38
44
  }
39
- if (type === "i") continue;
45
+ if (type === "i" && !opts.keepInput) continue;
40
46
  const visible = hiddenSince === null ? t - removed : hiddenSince - removed;
41
- const vt = visible / playbackSpeed;
42
- out.push({ vt, type, data });
43
- if (vt > duration) duration = vt;
47
+ out.push({ vt: visible / playbackSpeed, type, data });
48
+ }
49
+
50
+ if (opts.maxPause !== undefined && opts.maxPause >= 0) {
51
+ // Walk forward; whenever the next event is further away than maxPause, pull everything after it closer.
52
+ let shift = 0;
53
+ let prev: number | null = null;
54
+ for (const e of out) {
55
+ const original = e.vt;
56
+ if (prev !== null && original - prev > opts.maxPause) shift += original - prev - opts.maxPause;
57
+ prev = original;
58
+ e.vt = original - shift;
59
+ }
44
60
  }
61
+
62
+ let duration = 0;
63
+ for (const e of out) if (e.vt > duration) duration = e.vt;
45
64
  return { events: out, duration };
46
65
  }
47
66
 
package/src/types.ts CHANGED
@@ -48,6 +48,26 @@ export interface FontConfig {
48
48
  letterSpacing?: number;
49
49
  }
50
50
 
51
+ export interface KeysConfig {
52
+ position?: "bottom" | "top";
53
+ /** How long a chip stays visible, e.g. "1.2s". */
54
+ ttl?: Duration;
55
+ /** Printable keys pressed within this window merge into one chip. Default "350ms". */
56
+ merge?: Duration;
57
+ }
58
+
59
+ /** A region of the terminal grid to magnify. */
60
+ export interface ZoomRegion {
61
+ /** Inclusive row range, 0-based. Default: all rows. */
62
+ rows?: [number, number];
63
+ /** Inclusive column range, 0-based. Default: all columns. */
64
+ cols?: [number, number];
65
+ /** Animation length on the render clock. Default "400ms". */
66
+ duration?: Duration;
67
+ /** Inner padding around the region, in cells. Default 1. */
68
+ padding?: number;
69
+ }
70
+
51
71
  /** A browser window recorded next to the terminal (Bun.WebView). */
52
72
  export interface BrowserConfig {
53
73
  /** Page to open when recording starts (may also be opened later with `t.browser.goto`). */
@@ -115,6 +135,12 @@ export interface VideoConfig {
115
135
  height?: number;
116
136
  /** Where looping outputs (GIF, WebP) start: a frame number or a percentage like "50%". */
117
137
  loopOffset?: number | string;
138
+ /** Idle compression: at render time, gaps between events longer than this are shortened to this. */
139
+ maxPause?: Duration;
140
+ /** Show recent key presses as chips. `true` = bottom centre, 1.2 s. */
141
+ keys?: boolean | KeysConfig;
142
+ /** A named bundle of defaults applied under explicit settings: readme | x | youtube | square. */
143
+ preset?: "readme" | "x" | "youtube" | "square";
118
144
 
119
145
  /** Frames per second of the output. Default 60. */
120
146
  fps?: number;
@@ -169,6 +195,8 @@ export interface ResolvedConfig {
169
195
  width?: number;
170
196
  height?: number;
171
197
  loopOffset?: number | string;
198
+ maxPause?: number;
199
+ keys?: Required<KeysConfig> & { ttl: number; merge: number };
172
200
  fps: number;
173
201
  typingSpeed: number;
174
202
  typingJitter: number;
@@ -278,6 +306,18 @@ export interface TerminalSession {
278
306
  resize(cols: number, rows: number): Promise<void>;
279
307
  /** Shorthand for `run("clear")`. */
280
308
  clear(): Promise<void>;
309
+ /**
310
+ * Show Markdown in the terminal as a caption (rendered to ANSI by @wterm/markdown). It is written into the
311
+ * recording and the screen model only, never sent to the shell; afterwards the shell is asked for a fresh prompt.
312
+ * Use at a shell prompt, not inside a full-screen program.
313
+ */
314
+ print(markdown: string): Promise<void>;
315
+ /** A title card: big heading + rule, then a pause (default "1.5s"). */
316
+ title(text: string, opts?: { pause?: Duration }): Promise<void>;
317
+ /** Magnify a region of the terminal (animated at render time); `zoom(null)` resets. */
318
+ zoom(region: ZoomRegion | null): Promise<void>;
319
+ /** Named chapter: becomes mp4 chapter metadata and shows up in `--json` output. */
320
+ chapter(name: string): Promise<void>;
281
321
  /** The recorded browser window; throws if `browser` is not configured. */
282
322
  readonly browser: BrowserSession;
283
323
  /** Overlay layout: bring the terminal or the browser window to the front (recorded as a marker). */