@typecaast/core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,14 +16,19 @@ interface SystemCard {
16
16
  actions?: {
17
17
  label: string;
18
18
  href?: string;
19
+ variant?: "primary" | "secondary";
19
20
  }[];
20
21
  }
21
22
  /** A reaction currently shown on a message. */
22
23
  interface RenderedReaction {
23
24
  emoji: string;
25
+ /** Emoji shortcode without colons (e.g. `"eyes"`), for skin tooltips. */
26
+ shortcode?: string;
24
27
  count: number;
25
28
  /** Participant ids who reacted (for grouping/tooltips). */
26
29
  by: string[];
30
+ /** Display names of the reactors, resolved from participants (for tooltips). */
31
+ byNames: string[];
27
32
  /** 0..1 pop-in progress. */
28
33
  progress: number;
29
34
  }
@@ -16,14 +16,19 @@ interface SystemCard {
16
16
  actions?: {
17
17
  label: string;
18
18
  href?: string;
19
+ variant?: "primary" | "secondary";
19
20
  }[];
20
21
  }
21
22
  /** A reaction currently shown on a message. */
22
23
  interface RenderedReaction {
23
24
  emoji: string;
25
+ /** Emoji shortcode without colons (e.g. `"eyes"`), for skin tooltips. */
26
+ shortcode?: string;
24
27
  count: number;
25
28
  /** Participant ids who reacted (for grouping/tooltips). */
26
29
  by: string[];
30
+ /** Display names of the reactors, resolved from participants (for tooltips). */
31
+ byNames: string[];
27
32
  /** 0..1 pop-in progress. */
28
33
  progress: number;
29
34
  }
package/dist/index.cjs CHANGED
@@ -49,6 +49,7 @@ var REVEAL_MS = 280;
49
49
  var SEND_REVEAL_MS = 180;
50
50
  var REACTION_POP_MS = 200;
51
51
  var DEFAULT_TYPING_MS = 1500;
52
+ var DEFAULT_REACTION_LAG_MS = 1e3;
52
53
  var TAIL_PAD_MS = 800;
53
54
  function inlineText(span) {
54
55
  switch (span.type) {
@@ -76,33 +77,64 @@ function compile(config) {
76
77
  const selfIds = new Set(
77
78
  config.participants.filter((p) => p.isSelf).map((p) => p.id)
78
79
  );
80
+ const nameById = new Map(config.participants.map((p) => [p.id, p.name]));
79
81
  const messages = [];
80
82
  const typings = [];
81
83
  const composers = [];
82
84
  const stepBoundaries = [];
83
85
  const byId = /* @__PURE__ */ new Map();
84
- let cursor = pacing.startDelayMs;
86
+ const firstStep = config.timeline[0];
87
+ const firstStepIsInstantStart = firstStep !== void 0 && (firstStep.type === "message" || firstStep.type === "system") && firstStep.instant === true;
88
+ let cursor = firstStepIsInstantStart ? 0 : pacing.startDelayMs;
85
89
  let lastMessage;
86
90
  let lastComposer;
87
91
  let autoId = 0;
88
92
  const nextId = (explicit) => explicit ?? `auto-${autoId++}`;
89
- const resolveTarget = (target) => target === "$prev" ? lastMessage : byId.get(target);
93
+ const resolveTarget = (target) => {
94
+ if (!target || target === "$prev") return lastMessage;
95
+ return byId.get(target);
96
+ };
90
97
  for (const step of config.timeline) {
91
98
  stepBoundaries.push(cursor);
92
99
  switch (step.type) {
93
100
  case "message":
94
101
  case "system": {
102
+ const isSelfMessage = step.type === "message" && selfIds.has(step.from) && !step.instant;
103
+ if (isSelfMessage) {
104
+ const text = plainText(schema.toContentNodes(step));
105
+ const typingDur = typingDurationMs(text, pacing.typingCps);
106
+ const sendAt = cursor + typingDur;
107
+ const id2 = nextId(step.id);
108
+ const comp = {
109
+ from: step.from,
110
+ text,
111
+ startMs: cursor,
112
+ endMs: sendAt,
113
+ sendMs: sendAt
114
+ };
115
+ composers.push(comp);
116
+ const msg2 = {
117
+ id: id2,
118
+ from: step.from,
119
+ isSelf: true,
120
+ variant: "message",
121
+ content: schema.toContentNodes(step),
122
+ appearMs: sendAt,
123
+ revealMs: SEND_REVEAL_MS,
124
+ atMs: sendAt,
125
+ reactions: []
126
+ };
127
+ messages.push(msg2);
128
+ byId.set(id2, msg2);
129
+ lastMessage = msg2;
130
+ lastComposer = void 0;
131
+ cursor = sendAt + SEND_REVEAL_MS;
132
+ break;
133
+ }
95
134
  let appearAt;
96
- if (step.delay != null) appearAt = cursor + step.delay;
97
- else if (step.instant) appearAt = cursor;
135
+ if (step.instant) appearAt = cursor;
98
136
  else {
99
- let gap = pacing.interMessageGapMs;
100
- if (lastMessage) {
101
- gap += readingDelayMs(
102
- plainText(lastMessage.content),
103
- pacing.readingWpm
104
- );
105
- }
137
+ const gap = lastMessage ? readingDelayMs(plainText(lastMessage.content), pacing.readingWpm) : 0;
106
138
  appearAt = cursor + withJitter(rng, gap, pacing.humanize);
107
139
  }
108
140
  if (step.type === "message" && step.typing) {
@@ -132,96 +164,90 @@ function compile(config) {
132
164
  messages.push(msg);
133
165
  byId.set(id, msg);
134
166
  lastMessage = msg;
135
- cursor = appearAt + reveal + (step.holdAfter ?? 0);
167
+ cursor = appearAt + reveal;
136
168
  break;
137
169
  }
138
170
  case "typing": {
139
- const start = cursor + (step.delay ?? 0);
140
171
  const dur = step.showTypingFor ?? withJitter(rng, DEFAULT_TYPING_MS, pacing.humanize);
141
- typings.push({ from: step.from, startMs: start, endMs: start + dur });
142
- cursor = start + dur + (step.holdAfter ?? 0);
172
+ typings.push({ from: step.from, startMs: cursor, endMs: cursor + dur });
173
+ cursor += dur;
143
174
  break;
144
175
  }
145
176
  case "composerType": {
146
- const start = cursor + (step.delay ?? 0);
147
177
  const dur = step.typingDuration ?? typingDurationMs(step.text, pacing.typingCps);
148
178
  const comp = {
149
179
  from: step.from,
150
180
  text: step.text,
151
- startMs: start,
152
- endMs: start + dur
181
+ startMs: cursor,
182
+ endMs: cursor + dur
153
183
  };
154
184
  composers.push(comp);
155
185
  lastComposer = comp;
156
- cursor = start + dur + (step.holdAfter ?? 0);
186
+ cursor += dur;
157
187
  break;
158
188
  }
159
189
  case "send": {
160
- const sendAt = cursor + (step.delay ?? 0);
161
190
  if (lastComposer) {
162
- lastComposer.sendMs = sendAt;
191
+ lastComposer.sendMs = cursor;
163
192
  const id = nextId(step.id);
164
- const sendFrom = step.from ?? lastComposer.from;
193
+ const sendFrom = lastComposer.from;
165
194
  const msg = {
166
195
  id,
167
196
  from: sendFrom,
168
197
  isSelf: selfIds.has(sendFrom),
169
198
  variant: "message",
170
199
  content: schema.toContentNodes({ text: lastComposer.text }),
171
- appearMs: sendAt,
200
+ appearMs: cursor,
172
201
  revealMs: SEND_REVEAL_MS,
173
- atMs: sendAt,
202
+ atMs: cursor,
174
203
  reactions: []
175
204
  };
176
205
  messages.push(msg);
177
206
  byId.set(id, msg);
178
207
  lastMessage = msg;
179
- cursor = sendAt + SEND_REVEAL_MS + (step.holdAfter ?? 0);
208
+ cursor += SEND_REVEAL_MS;
180
209
  lastComposer = void 0;
181
- } else {
182
- cursor = sendAt + (step.holdAfter ?? 0);
183
210
  }
184
211
  break;
185
212
  }
186
213
  case "reaction": {
187
214
  const target = resolveTarget(step.target);
188
215
  if (target) {
189
- const delay = step.delay ?? withJitter(rng, pacing.reactionDelayMs, pacing.humanize);
216
+ const delay = step.delay ?? withJitter(rng, DEFAULT_REACTION_LAG_MS, pacing.humanize);
190
217
  const appearAt = target.appearMs + target.revealMs + delay;
218
+ const by = step.from ? [step.from] : [];
191
219
  target.reactions.push({
192
220
  emoji: step.emoji,
193
- by: step.from ? [step.from] : [],
221
+ ...step.shortcode ? { shortcode: step.shortcode } : {},
222
+ by,
223
+ byNames: by.map((id) => nameById.get(id) ?? id),
194
224
  appearMs: appearAt,
195
225
  popMs: REACTION_POP_MS
196
226
  });
197
- cursor = Math.max(cursor, appearAt + REACTION_POP_MS) + (step.holdAfter ?? 0);
227
+ cursor = Math.max(cursor, appearAt + REACTION_POP_MS);
198
228
  }
199
229
  break;
200
230
  }
201
231
  case "edit": {
202
232
  const target = resolveTarget(step.target);
203
233
  if (target) {
204
- const editAt = cursor + (step.delay ?? pacing.interMessageGapMs);
205
- target.editedAtMs = editAt;
234
+ target.editedAtMs = cursor;
206
235
  target.editedContent = schema.toContentNodes(step);
207
- cursor = editAt + REVEAL_MS + (step.holdAfter ?? 0);
236
+ cursor += REVEAL_MS;
208
237
  }
209
238
  break;
210
239
  }
211
240
  case "delete": {
212
241
  const target = resolveTarget(step.target);
213
242
  if (target) {
214
- const delAt = cursor + (step.delay ?? pacing.interMessageGapMs);
215
- target.deletedAtMs = delAt;
216
- cursor = delAt + (step.holdAfter ?? 0);
243
+ target.deletedAtMs = cursor;
217
244
  }
218
245
  break;
219
246
  }
220
247
  case "readReceipt": {
221
- cursor += step.delay ?? 0;
222
248
  break;
223
249
  }
224
- case "beat": {
250
+ case "delay": {
225
251
  cursor += step.duration;
226
252
  break;
227
253
  }
@@ -255,8 +281,10 @@ function sampleState(compiled, t, theme) {
255
281
  if (r.appearMs > t) continue;
256
282
  reactions.push({
257
283
  emoji: r.emoji,
284
+ ...r.shortcode ? { shortcode: r.shortcode } : {},
258
285
  count: r.by.length || 1,
259
286
  by: r.by,
287
+ byNames: r.byNames,
260
288
  progress: r.popMs === 0 ? 1 : clamp01((t - r.appearMs) / r.popMs)
261
289
  });
262
290
  if (t - r.appearMs < SCROLL_FLAG_MS) reactionRecently = true;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine/rng.ts","../src/engine/pacing.ts","../src/engine/compile.ts","../src/engine/get-state-at.ts","../src/engine/capabilities.ts","../src/engine/create-player.ts","../src/engine/index.ts","../src/index.ts"],"names":["toContentNodes"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Deterministic PRNG. The engine must never call `Math.random()` (PLAN §3) —\n * all jitter flows from the config `seed` through this, so `compile` is a pure\n * function of (config) and the same seed always yields the same timeline.\n */\n\n/** Mulberry32 — small, fast, fully deterministic. Returns floats in [0, 1). */\nexport function createRng(seed: number): () => number {\n let a = seed >>> 0;\n return function next(): number {\n a |= 0;\n a = (a + 0x6d2b79f5) | 0;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Apply ±`fraction` jitter to a value, consuming one RNG draw. With\n * `fraction = 0` the value is returned unchanged (no draw consumed) so disabling\n * humanization is exact.\n */\nexport function withJitter(\n rng: () => number,\n value: number,\n fraction: number,\n): number {\n if (fraction <= 0) return value;\n const delta = (rng() * 2 - 1) * fraction;\n return value * (1 + delta);\n}\n","/**\n * Pacing helpers: convert text into typing/reading durations. Counting is by\n * **grapheme cluster** (not code unit) so emoji ZWJ sequences, combining marks,\n * and mixed scripts pace correctly (PLAN §20).\n */\n\ninterface SegmenterCtor {\n new (\n locales?: string,\n options?: { granularity?: \"grapheme\" | \"word\" | \"sentence\" },\n ): { segment(input: string): Iterable<unknown> };\n}\n\n/** Count grapheme clusters, preferring `Intl.Segmenter`, falling back to code points. */\nexport function graphemeCount(text: string): number {\n const Segmenter = (\n globalThis as unknown as { Intl?: { Segmenter?: SegmenterCtor } }\n ).Intl?.Segmenter;\n if (Segmenter) {\n let n = 0;\n for (const _ of new Segmenter(undefined, {\n granularity: \"grapheme\",\n }).segment(text)) {\n void _;\n n++;\n }\n return n;\n }\n return [...text].length;\n}\n\n/** ms to type `text` at `cps` chars/sec (min one grapheme of time). */\nexport function typingDurationMs(text: string, cps: number): number {\n const chars = Math.max(1, graphemeCount(text));\n return (chars / cps) * 1000;\n}\n\n/**\n * ms to \"read\" `text` at `wpm` words/min — used as the gap before an incoming\n * message ≈ reading time of the prior message. Words ≈ graphemes / 5.\n */\nexport function readingDelayMs(text: string, wpm: number): number {\n const words = Math.max(1, graphemeCount(text) / 5);\n return (words / wpm) * 60000;\n}\n","import {\n toContentNodes,\n type Config,\n type ContentNode,\n type InlineNode,\n type TextNode,\n} from \"@typecaast/schema\";\nimport { createRng, withJitter } from \"./rng.js\";\nimport { readingDelayMs, typingDurationMs } from \"./pacing.js\";\nimport type {\n CompiledComposer,\n CompiledMessage,\n CompiledTimeline,\n} from \"./compiled.js\";\n\n/** Reveal animation duration for a newly appearing message (ms). */\nconst REVEAL_MS = 280;\n/** Faster reveal when the self participant sends (commit feels snappy). */\nconst SEND_REVEAL_MS = 180;\nconst REACTION_POP_MS = 200;\nconst DEFAULT_TYPING_MS = 1500;\n/** Padding after the last event so the final frame holds (ms). */\nconst TAIL_PAD_MS = 800;\n\nfunction inlineText(span: InlineNode): string {\n switch (span.type) {\n case \"text\":\n case \"code\":\n case \"emoji\":\n return span.value;\n case \"mention\":\n return span.label;\n case \"link\":\n return span.label ?? span.href;\n }\n}\n\n/** Flatten content nodes to plain text for pacing math. */\nfunction plainText(content: ContentNode[]): string {\n return content\n .map((node) =>\n node.type === \"text\"\n ? (node as TextNode).spans.map(inlineText).join(\"\")\n : \"\",\n )\n .join(\" \")\n .trim();\n}\n\nconst cache = new WeakMap<Config, CompiledTimeline>();\n\n/**\n * Resolve an authored, auto-paced config into an absolute timeline (PLAN §5).\n * Pure and memoized by config reference. Overrides (`delay`, `instant`,\n * `typingDuration`, `showTypingFor`, `holdAfter`) win over computed values; all\n * jitter is seeded from `meta.seed`.\n */\nexport function compile(config: Config): CompiledTimeline {\n const cached = cache.get(config);\n if (cached) return cached;\n\n const pacing = config.pacing;\n const rng = createRng(config.meta.seed);\n const selfIds = new Set(\n config.participants.filter((p) => p.isSelf).map((p) => p.id),\n );\n const messages: CompiledMessage[] = [];\n const typings: CompiledTimeline[\"typings\"] = [];\n const composers: CompiledComposer[] = [];\n const stepBoundaries: number[] = [];\n const byId = new Map<string, CompiledMessage>();\n\n let cursor = pacing.startDelayMs;\n let lastMessage: CompiledMessage | undefined;\n let lastComposer: CompiledComposer | undefined;\n let autoId = 0;\n const nextId = (explicit?: string): string => explicit ?? `auto-${autoId++}`;\n const resolveTarget = (target: string): CompiledMessage | undefined =>\n target === \"$prev\" ? lastMessage : byId.get(target);\n\n for (const step of config.timeline) {\n stepBoundaries.push(cursor);\n\n switch (step.type) {\n case \"message\":\n case \"system\": {\n let appearAt: number;\n if (step.delay != null) appearAt = cursor + step.delay;\n else if (step.instant) appearAt = cursor;\n else {\n let gap = pacing.interMessageGapMs;\n if (lastMessage) {\n gap += readingDelayMs(\n plainText(lastMessage.content),\n pacing.readingWpm,\n );\n }\n appearAt = cursor + withJitter(rng, gap, pacing.humanize);\n }\n\n if (step.type === \"message\" && step.typing) {\n const showFor =\n (typeof step.typing === \"object\"\n ? step.typing.showTypingFor\n : undefined) ??\n typingDurationMs(plainText(toContentNodes(step)), pacing.typingCps);\n typings.push({\n from: step.from,\n startMs: appearAt,\n endMs: appearAt + showFor,\n });\n appearAt += showFor;\n }\n\n const id = nextId(step.id);\n const reveal = step.instant ? 0 : REVEAL_MS;\n const from =\n step.type === \"system\" ? (step.from ?? \"system\") : step.from;\n const msg: CompiledMessage = {\n id,\n from,\n isSelf: selfIds.has(from),\n variant: step.type === \"system\" ? \"system\" : \"message\",\n content: toContentNodes(step),\n appearMs: appearAt,\n revealMs: reveal,\n atMs: appearAt,\n reactions: [],\n ...(step.type === \"system\"\n ? { system: { card: step.card, actions: step.actions } }\n : {}),\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor = appearAt + reveal + (step.holdAfter ?? 0);\n break;\n }\n\n case \"typing\": {\n const start = cursor + (step.delay ?? 0);\n const dur =\n step.showTypingFor ??\n withJitter(rng, DEFAULT_TYPING_MS, pacing.humanize);\n typings.push({ from: step.from, startMs: start, endMs: start + dur });\n cursor = start + dur + (step.holdAfter ?? 0);\n break;\n }\n\n case \"composerType\": {\n const start = cursor + (step.delay ?? 0);\n const dur =\n step.typingDuration ?? typingDurationMs(step.text, pacing.typingCps);\n const comp: CompiledComposer = {\n from: step.from,\n text: step.text,\n startMs: start,\n endMs: start + dur,\n };\n composers.push(comp);\n lastComposer = comp;\n cursor = start + dur + (step.holdAfter ?? 0);\n break;\n }\n\n case \"send\": {\n const sendAt = cursor + (step.delay ?? 0);\n if (lastComposer) {\n lastComposer.sendMs = sendAt;\n const id = nextId(step.id);\n const sendFrom = step.from ?? lastComposer.from;\n const msg: CompiledMessage = {\n id,\n from: sendFrom,\n isSelf: selfIds.has(sendFrom),\n variant: \"message\",\n content: toContentNodes({ text: lastComposer.text }),\n appearMs: sendAt,\n revealMs: SEND_REVEAL_MS,\n atMs: sendAt,\n reactions: [],\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor = sendAt + SEND_REVEAL_MS + (step.holdAfter ?? 0);\n lastComposer = undefined;\n } else {\n cursor = sendAt + (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"reaction\": {\n const target = resolveTarget(step.target);\n if (target) {\n const delay =\n step.delay ??\n withJitter(rng, pacing.reactionDelayMs, pacing.humanize);\n const appearAt = target.appearMs + target.revealMs + delay;\n target.reactions.push({\n emoji: step.emoji,\n by: step.from ? [step.from] : [],\n appearMs: appearAt,\n popMs: REACTION_POP_MS,\n });\n cursor =\n Math.max(cursor, appearAt + REACTION_POP_MS) +\n (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"edit\": {\n const target = resolveTarget(step.target);\n if (target) {\n const editAt = cursor + (step.delay ?? pacing.interMessageGapMs);\n target.editedAtMs = editAt;\n target.editedContent = toContentNodes(step);\n cursor = editAt + REVEAL_MS + (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"delete\": {\n const target = resolveTarget(step.target);\n if (target) {\n const delAt = cursor + (step.delay ?? pacing.interMessageGapMs);\n target.deletedAtMs = delAt;\n cursor = delAt + (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"readReceipt\": {\n cursor += step.delay ?? 0;\n break;\n }\n\n case \"beat\": {\n cursor += step.duration;\n break;\n }\n }\n }\n\n const compiled: CompiledTimeline = {\n messages,\n typings,\n composers,\n durationMs: cursor + TAIL_PAD_MS,\n stepBoundaries,\n };\n cache.set(config, compiled);\n return compiled;\n}\n","import type { GetStateAt } from \"../player.js\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n TypingState,\n} from \"../sim-state.js\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Approximate row height used for the scroll target (skins reflow on top). */\nconst ROW_HEIGHT = 64;\n/** Window after an event during which the scroll reason flags it. */\nconst SCROLL_FLAG_MS = 300;\n\n/**\n * Sample the complete renderable state at time `t` from a compiled timeline —\n * the engine's pure function of time (PLAN §3). No `Date.now()`, no mutation;\n * the same `(compiled, t, theme)` always yields a deep-equal `SimState`.\n */\nexport function sampleState(\n compiled: CompiledTimeline,\n t: number,\n theme: ResolvedTheme,\n): SimState {\n const messages: RenderedMessage[] = [];\n let lastAppear = 0;\n let reactionRecently = false;\n\n for (const m of compiled.messages) {\n if (m.appearMs > t) continue;\n if (m.deletedAtMs !== undefined && t >= m.deletedAtMs) continue;\n\n const edited =\n m.editedAtMs !== undefined &&\n m.editedContent !== undefined &&\n t >= m.editedAtMs;\n\n const reactions: RenderedReaction[] = [];\n for (const r of m.reactions) {\n if (r.appearMs > t) continue;\n reactions.push({\n emoji: r.emoji,\n count: r.by.length || 1,\n by: r.by,\n progress: r.popMs === 0 ? 1 : clamp01((t - r.appearMs) / r.popMs),\n });\n if (t - r.appearMs < SCROLL_FLAG_MS) reactionRecently = true;\n }\n\n const previous = messages[messages.length - 1];\n messages.push({\n id: m.id,\n from: m.from,\n variant: m.variant,\n content: edited ? m.editedContent! : m.content,\n revealProgress:\n m.revealMs === 0 ? 1 : clamp01((t - m.appearMs) / m.revealMs),\n state: edited ? \"edited\" : \"sent\",\n reactions,\n isSelf: m.isSelf,\n isGrouped: previous !== undefined && previous.from === m.from,\n atMs: m.atMs,\n ...(m.system ? { system: m.system } : {}),\n });\n\n if (m.appearMs > lastAppear) lastAppear = m.appearMs;\n }\n\n const typingIndicators: TypingState[] = [];\n for (const ty of compiled.typings) {\n if (t >= ty.startMs && t < ty.endMs) {\n const span = ty.endMs - ty.startMs;\n typingIndicators.push({\n from: ty.from,\n progress: span <= 0 ? 1 : clamp01((t - ty.startMs) / span),\n });\n }\n }\n\n // Composer: the latest one whose window contains t and hasn't yet sent.\n let composer: SimState[\"composer\"] = {\n text: \"\",\n caret: 0,\n sending: false,\n };\n for (const c of compiled.composers) {\n const end = c.sendMs ?? Number.POSITIVE_INFINITY;\n if (t < c.startMs || t >= end) continue;\n const text =\n t >= c.endMs\n ? c.text\n : c.text.slice(\n 0,\n Math.round(\n clamp01((t - c.startMs) / Math.max(1, c.endMs - c.startMs)) *\n c.text.length,\n ),\n );\n composer = {\n from: c.from,\n text,\n caret: text.length,\n sending: c.sendMs !== undefined && t >= c.endMs,\n };\n }\n\n const reason: SimState[\"scroll\"][\"reason\"] =\n messages.length > 0 && t - lastAppear < SCROLL_FLAG_MS\n ? \"new-message\"\n : reactionRecently\n ? \"reaction\"\n : \"none\";\n\n return {\n messages,\n typingIndicators,\n composer,\n scroll: { targetOffset: messages.length * ROW_HEIGHT, reason },\n durationMs: compiled.durationMs,\n theme,\n };\n}\n\n/** Bind a compiled timeline + theme into a `GetStateAt` closure. */\nexport function createGetStateAt(\n compiled: CompiledTimeline,\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => sampleState(compiled, t, theme);\n}\n","import type { StepType } from \"@typecaast/schema\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\n/**\n * How a skin represents a given event type:\n * - `native`: a first-class affordance.\n * - `fallback`: a degraded but present form.\n * - `unsupported`: dropped from this skin's render (kept in the config).\n */\nexport type EventCapability = \"native\" | \"fallback\" | \"unsupported\";\n\n/** What a skin supports and how it represents each event/content type. */\nexport interface Capabilities {\n events: Partial<Record<StepType, EventCapability>>;\n /** Keyed by content node type (e.g. `image: true`, `videoEmbed: false`). */\n content: Partial<Record<string, boolean>>;\n reactions: boolean;\n threads: boolean;\n readReceipts: boolean;\n}\n\n/**\n * Apply a skin's capabilities to a compiled timeline: drop the events/content\n * the skin can't render, returning a new timeline (the original config is\n * untouched, so switching skins restores everything — PLAN §7). Timing is\n * preserved; only what's shown changes.\n */\nexport function resolveCapabilities(\n compiled: CompiledTimeline,\n caps: Capabilities,\n): CompiledTimeline {\n const ev = caps.events ?? {};\n const dropTyping = ev.typing === \"unsupported\";\n const dropReactions =\n caps.reactions === false || ev.reaction === \"unsupported\";\n const dropSystem = ev.system === \"unsupported\";\n const allowed = (type: string): boolean => caps.content?.[type] !== false;\n\n const messages = compiled.messages\n .filter((m) => !(dropSystem && m.variant === \"system\"))\n .map((m) => ({\n ...m,\n reactions: dropReactions ? [] : m.reactions,\n content: m.content.filter((n) => allowed(n.type)),\n ...(m.editedContent\n ? { editedContent: m.editedContent.filter((n) => allowed(n.type)) }\n : {}),\n }));\n\n return {\n ...compiled,\n messages,\n typings: dropTyping ? [] : compiled.typings,\n };\n}\n","import type {\n GetStateAt,\n Player,\n PlayerEvent,\n PlayerEventMap,\n} from \"../player.js\";\nimport type { SimState } from \"../sim-state.js\";\n\nexport interface PlayerOptions {\n durationMs: number;\n /** Step boundaries (ms) for stepNext/stepPrev. */\n steps?: number[];\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n}\n\n/** A monotonic time source; reading it doesn't affect `getStateAt` determinism. */\nfunction now(): number {\n const perf = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return perf ? perf.now() : Date.now();\n}\n\ntype FrameHandle = number;\n\ninterface SchedulerGlobals {\n requestAnimationFrame?: (cb: (time: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n setTimeout?: (cb: () => void, ms: number) => number;\n clearTimeout?: (handle: number) => void;\n}\n\nfunction schedule(cb: () => void): FrameHandle {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.requestAnimationFrame) return g.requestAnimationFrame(() => cb());\n return g.setTimeout ? g.setTimeout(cb, 16) : 0;\n}\nfunction unschedule(handle: FrameHandle): void {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.cancelAnimationFrame) g.cancelAnimationFrame(handle);\n else if (g.clearTimeout) g.clearTimeout(handle);\n}\n\n/**\n * The real-time `Player`: a thin clock wrapper around a pure `GetStateAt`\n * (PLAN §5). It owns the only wall-clock in the system (rAF in the browser, a\n * timeout fallback elsewhere) and samples the engine each tick. The same class\n * drove the UI over the mock and now drives it over the real engine — identical\n * surface, so swapping the engine changes nothing here.\n */\nexport class TimelinePlayer implements Player {\n private readonly getStateAt: GetStateAt;\n private readonly _durationMs: number;\n private readonly steps: number[];\n private _currentMs = 0;\n private _rate: number;\n private _loop: boolean;\n private _playing = false;\n private _state: SimState;\n private anchor = 0;\n private frame: FrameHandle | null = null;\n private listeners = new Map<PlayerEvent, Set<(payload: never) => void>>();\n\n constructor(getStateAt: GetStateAt, options: PlayerOptions) {\n this.getStateAt = getStateAt;\n this._durationMs = options.durationMs;\n this.steps = (options.steps ?? [0, options.durationMs])\n .slice()\n .sort((a, b) => a - b);\n this._rate = options.rate ?? 1;\n this._loop = options.loop ?? false;\n this._state = getStateAt(0);\n if (options.autoplay) this.play();\n }\n\n get state(): SimState {\n return this._state;\n }\n get durationMs(): number {\n return this._durationMs;\n }\n get currentMs(): number {\n return this._currentMs;\n }\n get rate(): number {\n return this._rate;\n }\n get playing(): boolean {\n return this._playing;\n }\n get loop(): boolean {\n return this._loop;\n }\n\n play(): void {\n if (this._playing) return;\n this._playing = true;\n this.anchor = now() - this._currentMs / this._rate;\n this.emit(\"play\", undefined);\n this.tick();\n }\n\n pause(): void {\n if (!this._playing) return;\n this._playing = false;\n if (this.frame !== null) {\n unschedule(this.frame);\n this.frame = null;\n }\n this.emit(\"pause\", undefined);\n }\n\n private tick = (): void => {\n if (!this._playing) return;\n const elapsed = (now() - this.anchor) * this._rate;\n if (elapsed >= this._durationMs) {\n if (this._loop) {\n this._currentMs = 0;\n this.anchor = now();\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n return;\n }\n this._currentMs = this._durationMs;\n this.sampleAndEmit();\n this.pause();\n this.emit(\"end\", undefined);\n return;\n }\n this._currentMs = elapsed;\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n };\n\n private sampleAndEmit(): void {\n this._state = this.getStateAt(this._currentMs);\n this.emit(\"tick\", this._state);\n }\n\n private clamp(t: number): number {\n return t < 0 ? 0 : t > this._durationMs ? this._durationMs : t;\n }\n\n seek(timeMs: number): void {\n this._currentMs = this.clamp(timeMs);\n this.anchor = now() - this._currentMs / this._rate;\n this.sampleAndEmit();\n this.emit(\"seek\", this._currentMs);\n }\n\n scrubTo(timeMs: number): void {\n this.seek(timeMs);\n }\n\n setRate(rate: number): void {\n this._rate = rate <= 0 ? 1 : rate;\n this.anchor = now() - this._currentMs / this._rate;\n }\n\n setLoop(loop: boolean): void {\n this._loop = loop;\n }\n\n stepNext(): void {\n const next = this.steps.find((s) => s > this._currentMs + 1e-6);\n this.seek(next ?? this._durationMs);\n }\n\n stepPrev(): void {\n const prev = [...this.steps]\n .reverse()\n .find((s) => s < this._currentMs - 1e-6);\n this.seek(prev ?? 0);\n }\n\n on<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as (payload: never) => void);\n return () => this.off(event, listener);\n }\n\n off<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): void {\n this.listeners.get(event)?.delete(listener as (payload: never) => void);\n }\n\n private emit<E extends PlayerEvent>(\n event: E,\n payload: PlayerEventMap[E],\n ): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set)\n (listener as (p: PlayerEventMap[E]) => void)(payload);\n }\n\n destroy(): void {\n this.pause();\n this.listeners.clear();\n }\n}\n\n/** Create a real-time player over a `GetStateAt`. */\nexport function createPlayer(\n getStateAt: GetStateAt,\n options: PlayerOptions,\n): Player {\n return new TimelinePlayer(getStateAt, options);\n}\n","import type { Config } from \"@typecaast/schema\";\nimport type { GetStateAt } from \"../player.js\";\nimport type { ResolvedTheme } from \"../sim-state.js\";\nimport type { Capabilities } from \"./capabilities.js\";\nimport { compile } from \"./compile.js\";\nimport { createGetStateAt } from \"./get-state-at.js\";\nimport { resolveCapabilities } from \"./capabilities.js\";\n\nexport { compile } from \"./compile.js\";\nexport { sampleState, createGetStateAt } from \"./get-state-at.js\";\nexport {\n createPlayer,\n TimelinePlayer,\n type PlayerOptions,\n} from \"./create-player.js\";\nexport { createRng, withJitter } from \"./rng.js\";\nexport { graphemeCount, typingDurationMs, readingDelayMs } from \"./pacing.js\";\nexport {\n resolveCapabilities,\n type Capabilities,\n type EventCapability,\n} from \"./capabilities.js\";\nexport type * from \"./compiled.js\";\n\n/** A ready-to-drive engine: a sampler plus what a player needs. */\nexport interface EngineHandle {\n getStateAt: GetStateAt;\n durationMs: number;\n /** Step boundaries for stepNext/stepPrev. */\n steps: number[];\n}\n\n/**\n * Compile a config and bind a theme into a ready engine — the one call a\n * renderer needs. `compile` is memoized, so re-creating an engine for the same\n * config (e.g. only the theme changed) is cheap.\n */\nexport function createEngine(\n config: Config,\n theme: ResolvedTheme = \"light\",\n capabilities?: Capabilities,\n): EngineHandle {\n let compiled = compile(config);\n if (capabilities) compiled = resolveCapabilities(compiled, capabilities);\n return {\n getStateAt: createGetStateAt(compiled, theme),\n durationMs: compiled.durationMs,\n steps: compiled.stepBoundaries,\n };\n}\n","/**\n * `@typecaast/core` — the framework-agnostic engine and, locked first, the\n * contracts the rest of the system builds against: `SimState` (the renderable\n * state), the skin-prop data types, and the `Player` interface.\n *\n * The engine implementation (`compile` + `getStateAt`) lands in M1-engine,\n * behind these same contracts.\n */\n\n/** Contract version for the SimState/Player/skin-prop surface. */\nexport const CORE_CONTRACT_VERSION = 1;\n\nexport type * from \"./sim-state.js\";\nexport type * from \"./skin-props.js\";\nexport type * from \"./player.js\";\n\n/** The engine: compile() + getStateAt() + createEngine(). */\nexport * from \"./engine/index.js\";\n"]}
1
+ {"version":3,"sources":["../src/engine/rng.ts","../src/engine/pacing.ts","../src/engine/compile.ts","../src/engine/get-state-at.ts","../src/engine/capabilities.ts","../src/engine/create-player.ts","../src/engine/index.ts","../src/index.ts"],"names":["toContentNodes","id","msg"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Deterministic PRNG. The engine must never call `Math.random()` (PLAN §3) —\n * all jitter flows from the config `seed` through this, so `compile` is a pure\n * function of (config) and the same seed always yields the same timeline.\n */\n\n/** Mulberry32 — small, fast, fully deterministic. Returns floats in [0, 1). */\nexport function createRng(seed: number): () => number {\n let a = seed >>> 0;\n return function next(): number {\n a |= 0;\n a = (a + 0x6d2b79f5) | 0;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Apply ±`fraction` jitter to a value, consuming one RNG draw. With\n * `fraction = 0` the value is returned unchanged (no draw consumed) so disabling\n * humanization is exact.\n */\nexport function withJitter(\n rng: () => number,\n value: number,\n fraction: number,\n): number {\n if (fraction <= 0) return value;\n const delta = (rng() * 2 - 1) * fraction;\n return value * (1 + delta);\n}\n","/**\n * Pacing helpers: convert text into typing/reading durations. Counting is by\n * **grapheme cluster** (not code unit) so emoji ZWJ sequences, combining marks,\n * and mixed scripts pace correctly (PLAN §20).\n */\n\ninterface SegmenterCtor {\n new (\n locales?: string,\n options?: { granularity?: \"grapheme\" | \"word\" | \"sentence\" },\n ): { segment(input: string): Iterable<unknown> };\n}\n\n/** Count grapheme clusters, preferring `Intl.Segmenter`, falling back to code points. */\nexport function graphemeCount(text: string): number {\n const Segmenter = (\n globalThis as unknown as { Intl?: { Segmenter?: SegmenterCtor } }\n ).Intl?.Segmenter;\n if (Segmenter) {\n let n = 0;\n for (const _ of new Segmenter(undefined, {\n granularity: \"grapheme\",\n }).segment(text)) {\n void _;\n n++;\n }\n return n;\n }\n return [...text].length;\n}\n\n/** ms to type `text` at `cps` chars/sec (min one grapheme of time). */\nexport function typingDurationMs(text: string, cps: number): number {\n const chars = Math.max(1, graphemeCount(text));\n return (chars / cps) * 1000;\n}\n\n/**\n * ms to \"read\" `text` at `wpm` words/min — used as the gap before an incoming\n * message ≈ reading time of the prior message. Words ≈ graphemes / 5.\n */\nexport function readingDelayMs(text: string, wpm: number): number {\n const words = Math.max(1, graphemeCount(text) / 5);\n return (words / wpm) * 60000;\n}\n","import {\n toContentNodes,\n type Config,\n type ContentNode,\n type InlineNode,\n type TextNode,\n} from \"@typecaast/schema\";\nimport { createRng, withJitter } from \"./rng.js\";\nimport { readingDelayMs, typingDurationMs } from \"./pacing.js\";\nimport type {\n CompiledComposer,\n CompiledMessage,\n CompiledTimeline,\n} from \"./compiled.js\";\n\n/** Reveal animation duration for a newly appearing message (ms). */\nconst REVEAL_MS = 280;\n/** Faster reveal when the self participant sends (commit feels snappy). */\nconst SEND_REVEAL_MS = 180;\nconst REACTION_POP_MS = 200;\nconst DEFAULT_TYPING_MS = 1500;\n/**\n * Default lag for a reaction whose `delay` is left blank — same beat the\n * builder auto-prepends between added steps (see `addStepAutoPaced`).\n */\nconst DEFAULT_REACTION_LAG_MS = 1000;\n/** Padding after the last event so the final frame holds (ms). */\nconst TAIL_PAD_MS = 800;\n\nfunction inlineText(span: InlineNode): string {\n switch (span.type) {\n case \"text\":\n case \"code\":\n case \"emoji\":\n return span.value;\n case \"mention\":\n return span.label;\n case \"link\":\n return span.label ?? span.href;\n }\n}\n\n/** Flatten content nodes to plain text for pacing math. */\nfunction plainText(content: ContentNode[]): string {\n return content\n .map((node) =>\n node.type === \"text\"\n ? (node as TextNode).spans.map(inlineText).join(\"\")\n : \"\",\n )\n .join(\" \")\n .trim();\n}\n\nconst cache = new WeakMap<Config, CompiledTimeline>();\n\n/**\n * Resolve an authored, auto-paced config into an absolute timeline (PLAN §5).\n * Pure and memoized by config reference. Overrides (`instant`,\n * `typingDuration`, `showTypingFor`) win over computed values; all jitter is\n * seeded from `meta.seed`. Use a `delay` step to insert an explicit pause.\n *\n * A plain `message` step from the self participant is auto-rendered through\n * the composer (type-then-send) just like an explicit `composerType` + `send`\n * pair. Use `instant: true` to skip the composer animation. Use the explicit\n * `composerType`/`send` primitives only when you need the type-pause-retype\n * choreography.\n */\nexport function compile(config: Config): CompiledTimeline {\n const cached = cache.get(config);\n if (cached) return cached;\n\n const pacing = config.pacing;\n const rng = createRng(config.meta.seed);\n const selfIds = new Set(\n config.participants.filter((p) => p.isSelf).map((p) => p.id),\n );\n const nameById = new Map(config.participants.map((p) => [p.id, p.name]));\n const messages: CompiledMessage[] = [];\n const typings: CompiledTimeline[\"typings\"] = [];\n const composers: CompiledComposer[] = [];\n const stepBoundaries: number[] = [];\n const byId = new Map<string, CompiledMessage>();\n\n // If the very first step is a message/system marked `instant`, the player\n // should open with that message already on screen — i.e. starting at t=0 —\n // rather than blank for `startDelayMs`.\n const firstStep = config.timeline[0];\n const firstStepIsInstantStart =\n firstStep !== undefined &&\n (firstStep.type === \"message\" || firstStep.type === \"system\") &&\n firstStep.instant === true;\n let cursor = firstStepIsInstantStart ? 0 : pacing.startDelayMs;\n let lastMessage: CompiledMessage | undefined;\n let lastComposer: CompiledComposer | undefined;\n let autoId = 0;\n const nextId = (explicit?: string): string => explicit ?? `auto-${autoId++}`;\n /**\n * Resolve a step's `target` to a compiled message. Blank/`undefined`/`$prev`\n * all mean \"the most-recent message\" so authors can leave the field empty in\n * the common case.\n */\n const resolveTarget = (target?: string): CompiledMessage | undefined => {\n if (!target || target === \"$prev\") return lastMessage;\n return byId.get(target);\n };\n\n for (const step of config.timeline) {\n stepBoundaries.push(cursor);\n\n switch (step.type) {\n case \"message\":\n case \"system\": {\n // Self messages are auto-rendered through the composer (type-then-send)\n // unless `instant` is set. This is pure sugar for an explicit\n // `composerType` + `send` pair, so timing matches: typing begins right\n // at the cursor with no auto-paced gap added on top. Keep the explicit\n // composerType+send pair available for the type-pause-retype case.\n const isSelfMessage =\n step.type === \"message\" && selfIds.has(step.from) && !step.instant;\n if (isSelfMessage) {\n const text = plainText(toContentNodes(step));\n const typingDur = typingDurationMs(text, pacing.typingCps);\n const sendAt = cursor + typingDur;\n const id = nextId(step.id);\n const comp: CompiledComposer = {\n from: step.from,\n text,\n startMs: cursor,\n endMs: sendAt,\n sendMs: sendAt,\n };\n composers.push(comp);\n const msg: CompiledMessage = {\n id,\n from: step.from,\n isSelf: true,\n variant: \"message\",\n content: toContentNodes(step),\n appearMs: sendAt,\n revealMs: SEND_REVEAL_MS,\n atMs: sendAt,\n reactions: [],\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n lastComposer = undefined;\n cursor = sendAt + SEND_REVEAL_MS;\n break;\n }\n\n let appearAt: number;\n if (step.instant) appearAt = cursor;\n else {\n // Auto-paced gap = simulated reading time of the prior message. For\n // explicit beats use a `delay` step.\n const gap = lastMessage\n ? readingDelayMs(plainText(lastMessage.content), pacing.readingWpm)\n : 0;\n appearAt = cursor + withJitter(rng, gap, pacing.humanize);\n }\n\n if (step.type === \"message\" && step.typing) {\n const showFor =\n (typeof step.typing === \"object\"\n ? step.typing.showTypingFor\n : undefined) ??\n typingDurationMs(plainText(toContentNodes(step)), pacing.typingCps);\n typings.push({\n from: step.from,\n startMs: appearAt,\n endMs: appearAt + showFor,\n });\n appearAt += showFor;\n }\n\n const id = nextId(step.id);\n const reveal = step.instant ? 0 : REVEAL_MS;\n const from =\n step.type === \"system\" ? (step.from ?? \"system\") : step.from;\n const msg: CompiledMessage = {\n id,\n from,\n isSelf: selfIds.has(from),\n variant: step.type === \"system\" ? \"system\" : \"message\",\n content: toContentNodes(step),\n appearMs: appearAt,\n revealMs: reveal,\n atMs: appearAt,\n reactions: [],\n ...(step.type === \"system\"\n ? { system: { card: step.card, actions: step.actions } }\n : {}),\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor = appearAt + reveal;\n break;\n }\n\n case \"typing\": {\n const dur =\n step.showTypingFor ??\n withJitter(rng, DEFAULT_TYPING_MS, pacing.humanize);\n typings.push({ from: step.from, startMs: cursor, endMs: cursor + dur });\n cursor += dur;\n break;\n }\n\n case \"composerType\": {\n const dur =\n step.typingDuration ?? typingDurationMs(step.text, pacing.typingCps);\n const comp: CompiledComposer = {\n from: step.from,\n text: step.text,\n startMs: cursor,\n endMs: cursor + dur,\n };\n composers.push(comp);\n lastComposer = comp;\n cursor += dur;\n break;\n }\n\n case \"send\": {\n if (lastComposer) {\n lastComposer.sendMs = cursor;\n const id = nextId(step.id);\n // A send commits whatever's in the composer, so the message is always\n // from whoever was typing — never the step's own `from` (a stray\n // self-default there used to mis-attribute the sent message).\n const sendFrom = lastComposer.from;\n const msg: CompiledMessage = {\n id,\n from: sendFrom,\n isSelf: selfIds.has(sendFrom),\n variant: \"message\",\n content: toContentNodes({ text: lastComposer.text }),\n appearMs: cursor,\n revealMs: SEND_REVEAL_MS,\n atMs: cursor,\n reactions: [],\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor += SEND_REVEAL_MS;\n lastComposer = undefined;\n }\n break;\n }\n\n case \"reaction\": {\n const target = resolveTarget(step.target);\n if (target) {\n // Default lag mirrors the builder's auto-prepended delay between\n // steps so reactions land with a natural beat unless overridden.\n const delay =\n step.delay ??\n withJitter(rng, DEFAULT_REACTION_LAG_MS, pacing.humanize);\n const appearAt = target.appearMs + target.revealMs + delay;\n const by = step.from ? [step.from] : [];\n target.reactions.push({\n emoji: step.emoji,\n ...(step.shortcode ? { shortcode: step.shortcode } : {}),\n by,\n byNames: by.map((id) => nameById.get(id) ?? id),\n appearMs: appearAt,\n popMs: REACTION_POP_MS,\n });\n cursor = Math.max(cursor, appearAt + REACTION_POP_MS);\n }\n break;\n }\n\n case \"edit\": {\n const target = resolveTarget(step.target);\n if (target) {\n // Edits/deletes happen at the cursor; insert a `delay` step before\n // them to add breathing room.\n target.editedAtMs = cursor;\n target.editedContent = toContentNodes(step);\n cursor += REVEAL_MS;\n }\n break;\n }\n\n case \"delete\": {\n const target = resolveTarget(step.target);\n if (target) {\n target.deletedAtMs = cursor;\n }\n break;\n }\n\n case \"readReceipt\": {\n // No timeline advancement; receipts are visual-only and overlay the\n // current frame.\n break;\n }\n\n case \"delay\": {\n cursor += step.duration;\n break;\n }\n }\n }\n\n const compiled: CompiledTimeline = {\n messages,\n typings,\n composers,\n durationMs: cursor + TAIL_PAD_MS,\n stepBoundaries,\n };\n cache.set(config, compiled);\n return compiled;\n}\n","import type { GetStateAt } from \"../player.js\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n TypingState,\n} from \"../sim-state.js\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Approximate row height used for the scroll target (skins reflow on top). */\nconst ROW_HEIGHT = 64;\n/** Window after an event during which the scroll reason flags it. */\nconst SCROLL_FLAG_MS = 300;\n\n/**\n * Sample the complete renderable state at time `t` from a compiled timeline —\n * the engine's pure function of time (PLAN §3). No `Date.now()`, no mutation;\n * the same `(compiled, t, theme)` always yields a deep-equal `SimState`.\n */\nexport function sampleState(\n compiled: CompiledTimeline,\n t: number,\n theme: ResolvedTheme,\n): SimState {\n const messages: RenderedMessage[] = [];\n let lastAppear = 0;\n let reactionRecently = false;\n\n for (const m of compiled.messages) {\n if (m.appearMs > t) continue;\n if (m.deletedAtMs !== undefined && t >= m.deletedAtMs) continue;\n\n const edited =\n m.editedAtMs !== undefined &&\n m.editedContent !== undefined &&\n t >= m.editedAtMs;\n\n const reactions: RenderedReaction[] = [];\n for (const r of m.reactions) {\n if (r.appearMs > t) continue;\n reactions.push({\n emoji: r.emoji,\n ...(r.shortcode ? { shortcode: r.shortcode } : {}),\n count: r.by.length || 1,\n by: r.by,\n byNames: r.byNames,\n progress: r.popMs === 0 ? 1 : clamp01((t - r.appearMs) / r.popMs),\n });\n if (t - r.appearMs < SCROLL_FLAG_MS) reactionRecently = true;\n }\n\n const previous = messages[messages.length - 1];\n messages.push({\n id: m.id,\n from: m.from,\n variant: m.variant,\n content: edited ? m.editedContent! : m.content,\n revealProgress:\n m.revealMs === 0 ? 1 : clamp01((t - m.appearMs) / m.revealMs),\n state: edited ? \"edited\" : \"sent\",\n reactions,\n isSelf: m.isSelf,\n isGrouped: previous !== undefined && previous.from === m.from,\n atMs: m.atMs,\n ...(m.system ? { system: m.system } : {}),\n });\n\n if (m.appearMs > lastAppear) lastAppear = m.appearMs;\n }\n\n const typingIndicators: TypingState[] = [];\n for (const ty of compiled.typings) {\n if (t >= ty.startMs && t < ty.endMs) {\n const span = ty.endMs - ty.startMs;\n typingIndicators.push({\n from: ty.from,\n progress: span <= 0 ? 1 : clamp01((t - ty.startMs) / span),\n });\n }\n }\n\n // Composer: the latest one whose window contains t and hasn't yet sent.\n let composer: SimState[\"composer\"] = {\n text: \"\",\n caret: 0,\n sending: false,\n };\n for (const c of compiled.composers) {\n const end = c.sendMs ?? Number.POSITIVE_INFINITY;\n if (t < c.startMs || t >= end) continue;\n const text =\n t >= c.endMs\n ? c.text\n : c.text.slice(\n 0,\n Math.round(\n clamp01((t - c.startMs) / Math.max(1, c.endMs - c.startMs)) *\n c.text.length,\n ),\n );\n composer = {\n from: c.from,\n text,\n caret: text.length,\n sending: c.sendMs !== undefined && t >= c.endMs,\n };\n }\n\n const reason: SimState[\"scroll\"][\"reason\"] =\n messages.length > 0 && t - lastAppear < SCROLL_FLAG_MS\n ? \"new-message\"\n : reactionRecently\n ? \"reaction\"\n : \"none\";\n\n return {\n messages,\n typingIndicators,\n composer,\n scroll: { targetOffset: messages.length * ROW_HEIGHT, reason },\n durationMs: compiled.durationMs,\n theme,\n };\n}\n\n/** Bind a compiled timeline + theme into a `GetStateAt` closure. */\nexport function createGetStateAt(\n compiled: CompiledTimeline,\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => sampleState(compiled, t, theme);\n}\n","import type { StepType } from \"@typecaast/schema\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\n/**\n * How a skin represents a given event type:\n * - `native`: a first-class affordance.\n * - `fallback`: a degraded but present form.\n * - `unsupported`: dropped from this skin's render (kept in the config).\n */\nexport type EventCapability = \"native\" | \"fallback\" | \"unsupported\";\n\n/** What a skin supports and how it represents each event/content type. */\nexport interface Capabilities {\n events: Partial<Record<StepType, EventCapability>>;\n /** Keyed by content node type (e.g. `image: true`, `videoEmbed: false`). */\n content: Partial<Record<string, boolean>>;\n reactions: boolean;\n threads: boolean;\n readReceipts: boolean;\n}\n\n/**\n * Apply a skin's capabilities to a compiled timeline: drop the events/content\n * the skin can't render, returning a new timeline (the original config is\n * untouched, so switching skins restores everything — PLAN §7). Timing is\n * preserved; only what's shown changes.\n */\nexport function resolveCapabilities(\n compiled: CompiledTimeline,\n caps: Capabilities,\n): CompiledTimeline {\n const ev = caps.events ?? {};\n const dropTyping = ev.typing === \"unsupported\";\n const dropReactions =\n caps.reactions === false || ev.reaction === \"unsupported\";\n const dropSystem = ev.system === \"unsupported\";\n const allowed = (type: string): boolean => caps.content?.[type] !== false;\n\n const messages = compiled.messages\n .filter((m) => !(dropSystem && m.variant === \"system\"))\n .map((m) => ({\n ...m,\n reactions: dropReactions ? [] : m.reactions,\n content: m.content.filter((n) => allowed(n.type)),\n ...(m.editedContent\n ? { editedContent: m.editedContent.filter((n) => allowed(n.type)) }\n : {}),\n }));\n\n return {\n ...compiled,\n messages,\n typings: dropTyping ? [] : compiled.typings,\n };\n}\n","import type {\n GetStateAt,\n Player,\n PlayerEvent,\n PlayerEventMap,\n} from \"../player.js\";\nimport type { SimState } from \"../sim-state.js\";\n\nexport interface PlayerOptions {\n durationMs: number;\n /** Step boundaries (ms) for stepNext/stepPrev. */\n steps?: number[];\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n}\n\n/** A monotonic time source; reading it doesn't affect `getStateAt` determinism. */\nfunction now(): number {\n const perf = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return perf ? perf.now() : Date.now();\n}\n\ntype FrameHandle = number;\n\ninterface SchedulerGlobals {\n requestAnimationFrame?: (cb: (time: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n setTimeout?: (cb: () => void, ms: number) => number;\n clearTimeout?: (handle: number) => void;\n}\n\nfunction schedule(cb: () => void): FrameHandle {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.requestAnimationFrame) return g.requestAnimationFrame(() => cb());\n return g.setTimeout ? g.setTimeout(cb, 16) : 0;\n}\nfunction unschedule(handle: FrameHandle): void {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.cancelAnimationFrame) g.cancelAnimationFrame(handle);\n else if (g.clearTimeout) g.clearTimeout(handle);\n}\n\n/**\n * The real-time `Player`: a thin clock wrapper around a pure `GetStateAt`\n * (PLAN §5). It owns the only wall-clock in the system (rAF in the browser, a\n * timeout fallback elsewhere) and samples the engine each tick. The same class\n * drove the UI over the mock and now drives it over the real engine — identical\n * surface, so swapping the engine changes nothing here.\n */\nexport class TimelinePlayer implements Player {\n private readonly getStateAt: GetStateAt;\n private readonly _durationMs: number;\n private readonly steps: number[];\n private _currentMs = 0;\n private _rate: number;\n private _loop: boolean;\n private _playing = false;\n private _state: SimState;\n private anchor = 0;\n private frame: FrameHandle | null = null;\n private listeners = new Map<PlayerEvent, Set<(payload: never) => void>>();\n\n constructor(getStateAt: GetStateAt, options: PlayerOptions) {\n this.getStateAt = getStateAt;\n this._durationMs = options.durationMs;\n this.steps = (options.steps ?? [0, options.durationMs])\n .slice()\n .sort((a, b) => a - b);\n this._rate = options.rate ?? 1;\n this._loop = options.loop ?? false;\n this._state = getStateAt(0);\n if (options.autoplay) this.play();\n }\n\n get state(): SimState {\n return this._state;\n }\n get durationMs(): number {\n return this._durationMs;\n }\n get currentMs(): number {\n return this._currentMs;\n }\n get rate(): number {\n return this._rate;\n }\n get playing(): boolean {\n return this._playing;\n }\n get loop(): boolean {\n return this._loop;\n }\n\n play(): void {\n if (this._playing) return;\n this._playing = true;\n this.anchor = now() - this._currentMs / this._rate;\n this.emit(\"play\", undefined);\n this.tick();\n }\n\n pause(): void {\n if (!this._playing) return;\n this._playing = false;\n if (this.frame !== null) {\n unschedule(this.frame);\n this.frame = null;\n }\n this.emit(\"pause\", undefined);\n }\n\n private tick = (): void => {\n if (!this._playing) return;\n const elapsed = (now() - this.anchor) * this._rate;\n if (elapsed >= this._durationMs) {\n if (this._loop) {\n this._currentMs = 0;\n this.anchor = now();\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n return;\n }\n this._currentMs = this._durationMs;\n this.sampleAndEmit();\n this.pause();\n this.emit(\"end\", undefined);\n return;\n }\n this._currentMs = elapsed;\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n };\n\n private sampleAndEmit(): void {\n this._state = this.getStateAt(this._currentMs);\n this.emit(\"tick\", this._state);\n }\n\n private clamp(t: number): number {\n return t < 0 ? 0 : t > this._durationMs ? this._durationMs : t;\n }\n\n seek(timeMs: number): void {\n this._currentMs = this.clamp(timeMs);\n this.anchor = now() - this._currentMs / this._rate;\n this.sampleAndEmit();\n this.emit(\"seek\", this._currentMs);\n }\n\n scrubTo(timeMs: number): void {\n this.seek(timeMs);\n }\n\n setRate(rate: number): void {\n this._rate = rate <= 0 ? 1 : rate;\n this.anchor = now() - this._currentMs / this._rate;\n }\n\n setLoop(loop: boolean): void {\n this._loop = loop;\n }\n\n stepNext(): void {\n const next = this.steps.find((s) => s > this._currentMs + 1e-6);\n this.seek(next ?? this._durationMs);\n }\n\n stepPrev(): void {\n const prev = [...this.steps]\n .reverse()\n .find((s) => s < this._currentMs - 1e-6);\n this.seek(prev ?? 0);\n }\n\n on<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as (payload: never) => void);\n return () => this.off(event, listener);\n }\n\n off<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): void {\n this.listeners.get(event)?.delete(listener as (payload: never) => void);\n }\n\n private emit<E extends PlayerEvent>(\n event: E,\n payload: PlayerEventMap[E],\n ): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set)\n (listener as (p: PlayerEventMap[E]) => void)(payload);\n }\n\n destroy(): void {\n this.pause();\n this.listeners.clear();\n }\n}\n\n/** Create a real-time player over a `GetStateAt`. */\nexport function createPlayer(\n getStateAt: GetStateAt,\n options: PlayerOptions,\n): Player {\n return new TimelinePlayer(getStateAt, options);\n}\n","import type { Config } from \"@typecaast/schema\";\nimport type { GetStateAt } from \"../player.js\";\nimport type { ResolvedTheme } from \"../sim-state.js\";\nimport type { Capabilities } from \"./capabilities.js\";\nimport { compile } from \"./compile.js\";\nimport { createGetStateAt } from \"./get-state-at.js\";\nimport { resolveCapabilities } from \"./capabilities.js\";\n\nexport { compile } from \"./compile.js\";\nexport { sampleState, createGetStateAt } from \"./get-state-at.js\";\nexport {\n createPlayer,\n TimelinePlayer,\n type PlayerOptions,\n} from \"./create-player.js\";\nexport { createRng, withJitter } from \"./rng.js\";\nexport { graphemeCount, typingDurationMs, readingDelayMs } from \"./pacing.js\";\nexport {\n resolveCapabilities,\n type Capabilities,\n type EventCapability,\n} from \"./capabilities.js\";\nexport type * from \"./compiled.js\";\n\n/** A ready-to-drive engine: a sampler plus what a player needs. */\nexport interface EngineHandle {\n getStateAt: GetStateAt;\n durationMs: number;\n /** Step boundaries for stepNext/stepPrev. */\n steps: number[];\n}\n\n/**\n * Compile a config and bind a theme into a ready engine — the one call a\n * renderer needs. `compile` is memoized, so re-creating an engine for the same\n * config (e.g. only the theme changed) is cheap.\n */\nexport function createEngine(\n config: Config,\n theme: ResolvedTheme = \"light\",\n capabilities?: Capabilities,\n): EngineHandle {\n let compiled = compile(config);\n if (capabilities) compiled = resolveCapabilities(compiled, capabilities);\n return {\n getStateAt: createGetStateAt(compiled, theme),\n durationMs: compiled.durationMs,\n steps: compiled.stepBoundaries,\n };\n}\n","/**\n * `@typecaast/core` — the framework-agnostic engine and, locked first, the\n * contracts the rest of the system builds against: `SimState` (the renderable\n * state), the skin-prop data types, and the `Player` interface.\n *\n * The engine implementation (`compile` + `getStateAt`) lands in M1-engine,\n * behind these same contracts.\n */\n\n/** Contract version for the SimState/Player/skin-prop surface. */\nexport const CORE_CONTRACT_VERSION = 1;\n\nexport type * from \"./sim-state.js\";\nexport type * from \"./skin-props.js\";\nexport type * from \"./player.js\";\n\n/** The engine: compile() + getStateAt() + createEngine(). */\nexport * from \"./engine/index.js\";\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as ResolvedTheme, C as ComposerState, a as RenderedMessage, b as RenderedReaction, T as TypingState, M as MessageVariant, S as SystemCard, G as GetStateAt, c as SimState } from './create-player-CEAzKJn1.cjs';
2
- export { d as MessageState, P as Player, e as PlayerEvent, f as PlayerEventMap, g as PlayerOptions, h as ScrollReason, i as ScrollState, j as TimelinePlayer, k as createPlayer } from './create-player-CEAzKJn1.cjs';
1
+ import { R as ResolvedTheme, C as ComposerState, a as RenderedMessage, b as RenderedReaction, T as TypingState, M as MessageVariant, S as SystemCard, G as GetStateAt, c as SimState } from './create-player-BUru5tb_.cjs';
2
+ export { d as MessageState, P as Player, e as PlayerEvent, f as PlayerEventMap, g as PlayerOptions, h as ScrollReason, i as ScrollState, j as TimelinePlayer, k as createPlayer } from './create-player-BUru5tb_.cjs';
3
3
  import { Participant, ContentNode, StepType, Config } from '@typecaast/schema';
4
4
 
5
5
  /**
@@ -54,7 +54,11 @@ interface AvatarProps {
54
54
  /** A reaction baked onto a message with absolute timing. */
55
55
  interface CompiledReaction {
56
56
  emoji: string;
57
+ /** Emoji shortcode (no colons), for tooltips. */
58
+ shortcode?: string;
57
59
  by: string[];
60
+ /** Reactor display names, resolved from participants at compile time. */
61
+ byNames: string[];
58
62
  appearMs: number;
59
63
  popMs: number;
60
64
  }
@@ -134,9 +138,15 @@ declare function resolveCapabilities(compiled: CompiledTimeline, caps: Capabilit
134
138
 
135
139
  /**
136
140
  * Resolve an authored, auto-paced config into an absolute timeline (PLAN §5).
137
- * Pure and memoized by config reference. Overrides (`delay`, `instant`,
138
- * `typingDuration`, `showTypingFor`, `holdAfter`) win over computed values; all
139
- * jitter is seeded from `meta.seed`.
141
+ * Pure and memoized by config reference. Overrides (`instant`,
142
+ * `typingDuration`, `showTypingFor`) win over computed values; all jitter is
143
+ * seeded from `meta.seed`. Use a `delay` step to insert an explicit pause.
144
+ *
145
+ * A plain `message` step from the self participant is auto-rendered through
146
+ * the composer (type-then-send) just like an explicit `composerType` + `send`
147
+ * pair. Use `instant: true` to skip the composer animation. Use the explicit
148
+ * `composerType`/`send` primitives only when you need the type-pause-retype
149
+ * choreography.
140
150
  */
141
151
  declare function compile(config: Config): CompiledTimeline;
142
152
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as ResolvedTheme, C as ComposerState, a as RenderedMessage, b as RenderedReaction, T as TypingState, M as MessageVariant, S as SystemCard, G as GetStateAt, c as SimState } from './create-player-CEAzKJn1.js';
2
- export { d as MessageState, P as Player, e as PlayerEvent, f as PlayerEventMap, g as PlayerOptions, h as ScrollReason, i as ScrollState, j as TimelinePlayer, k as createPlayer } from './create-player-CEAzKJn1.js';
1
+ import { R as ResolvedTheme, C as ComposerState, a as RenderedMessage, b as RenderedReaction, T as TypingState, M as MessageVariant, S as SystemCard, G as GetStateAt, c as SimState } from './create-player-BUru5tb_.js';
2
+ export { d as MessageState, P as Player, e as PlayerEvent, f as PlayerEventMap, g as PlayerOptions, h as ScrollReason, i as ScrollState, j as TimelinePlayer, k as createPlayer } from './create-player-BUru5tb_.js';
3
3
  import { Participant, ContentNode, StepType, Config } from '@typecaast/schema';
4
4
 
5
5
  /**
@@ -54,7 +54,11 @@ interface AvatarProps {
54
54
  /** A reaction baked onto a message with absolute timing. */
55
55
  interface CompiledReaction {
56
56
  emoji: string;
57
+ /** Emoji shortcode (no colons), for tooltips. */
58
+ shortcode?: string;
57
59
  by: string[];
60
+ /** Reactor display names, resolved from participants at compile time. */
61
+ byNames: string[];
58
62
  appearMs: number;
59
63
  popMs: number;
60
64
  }
@@ -134,9 +138,15 @@ declare function resolveCapabilities(compiled: CompiledTimeline, caps: Capabilit
134
138
 
135
139
  /**
136
140
  * Resolve an authored, auto-paced config into an absolute timeline (PLAN §5).
137
- * Pure and memoized by config reference. Overrides (`delay`, `instant`,
138
- * `typingDuration`, `showTypingFor`, `holdAfter`) win over computed values; all
139
- * jitter is seeded from `meta.seed`.
141
+ * Pure and memoized by config reference. Overrides (`instant`,
142
+ * `typingDuration`, `showTypingFor`) win over computed values; all jitter is
143
+ * seeded from `meta.seed`. Use a `delay` step to insert an explicit pause.
144
+ *
145
+ * A plain `message` step from the self participant is auto-rendered through
146
+ * the composer (type-then-send) just like an explicit `composerType` + `send`
147
+ * pair. Use `instant: true` to skip the composer animation. Use the explicit
148
+ * `composerType`/`send` primitives only when you need the type-pause-retype
149
+ * choreography.
140
150
  */
141
151
  declare function compile(config: Config): CompiledTimeline;
142
152
 
package/dist/index.js CHANGED
@@ -46,6 +46,7 @@ var REVEAL_MS = 280;
46
46
  var SEND_REVEAL_MS = 180;
47
47
  var REACTION_POP_MS = 200;
48
48
  var DEFAULT_TYPING_MS = 1500;
49
+ var DEFAULT_REACTION_LAG_MS = 1e3;
49
50
  var TAIL_PAD_MS = 800;
50
51
  function inlineText(span) {
51
52
  switch (span.type) {
@@ -73,33 +74,64 @@ function compile(config) {
73
74
  const selfIds = new Set(
74
75
  config.participants.filter((p) => p.isSelf).map((p) => p.id)
75
76
  );
77
+ const nameById = new Map(config.participants.map((p) => [p.id, p.name]));
76
78
  const messages = [];
77
79
  const typings = [];
78
80
  const composers = [];
79
81
  const stepBoundaries = [];
80
82
  const byId = /* @__PURE__ */ new Map();
81
- let cursor = pacing.startDelayMs;
83
+ const firstStep = config.timeline[0];
84
+ const firstStepIsInstantStart = firstStep !== void 0 && (firstStep.type === "message" || firstStep.type === "system") && firstStep.instant === true;
85
+ let cursor = firstStepIsInstantStart ? 0 : pacing.startDelayMs;
82
86
  let lastMessage;
83
87
  let lastComposer;
84
88
  let autoId = 0;
85
89
  const nextId = (explicit) => explicit ?? `auto-${autoId++}`;
86
- const resolveTarget = (target) => target === "$prev" ? lastMessage : byId.get(target);
90
+ const resolveTarget = (target) => {
91
+ if (!target || target === "$prev") return lastMessage;
92
+ return byId.get(target);
93
+ };
87
94
  for (const step of config.timeline) {
88
95
  stepBoundaries.push(cursor);
89
96
  switch (step.type) {
90
97
  case "message":
91
98
  case "system": {
99
+ const isSelfMessage = step.type === "message" && selfIds.has(step.from) && !step.instant;
100
+ if (isSelfMessage) {
101
+ const text = plainText(toContentNodes(step));
102
+ const typingDur = typingDurationMs(text, pacing.typingCps);
103
+ const sendAt = cursor + typingDur;
104
+ const id2 = nextId(step.id);
105
+ const comp = {
106
+ from: step.from,
107
+ text,
108
+ startMs: cursor,
109
+ endMs: sendAt,
110
+ sendMs: sendAt
111
+ };
112
+ composers.push(comp);
113
+ const msg2 = {
114
+ id: id2,
115
+ from: step.from,
116
+ isSelf: true,
117
+ variant: "message",
118
+ content: toContentNodes(step),
119
+ appearMs: sendAt,
120
+ revealMs: SEND_REVEAL_MS,
121
+ atMs: sendAt,
122
+ reactions: []
123
+ };
124
+ messages.push(msg2);
125
+ byId.set(id2, msg2);
126
+ lastMessage = msg2;
127
+ lastComposer = void 0;
128
+ cursor = sendAt + SEND_REVEAL_MS;
129
+ break;
130
+ }
92
131
  let appearAt;
93
- if (step.delay != null) appearAt = cursor + step.delay;
94
- else if (step.instant) appearAt = cursor;
132
+ if (step.instant) appearAt = cursor;
95
133
  else {
96
- let gap = pacing.interMessageGapMs;
97
- if (lastMessage) {
98
- gap += readingDelayMs(
99
- plainText(lastMessage.content),
100
- pacing.readingWpm
101
- );
102
- }
134
+ const gap = lastMessage ? readingDelayMs(plainText(lastMessage.content), pacing.readingWpm) : 0;
103
135
  appearAt = cursor + withJitter(rng, gap, pacing.humanize);
104
136
  }
105
137
  if (step.type === "message" && step.typing) {
@@ -129,96 +161,90 @@ function compile(config) {
129
161
  messages.push(msg);
130
162
  byId.set(id, msg);
131
163
  lastMessage = msg;
132
- cursor = appearAt + reveal + (step.holdAfter ?? 0);
164
+ cursor = appearAt + reveal;
133
165
  break;
134
166
  }
135
167
  case "typing": {
136
- const start = cursor + (step.delay ?? 0);
137
168
  const dur = step.showTypingFor ?? withJitter(rng, DEFAULT_TYPING_MS, pacing.humanize);
138
- typings.push({ from: step.from, startMs: start, endMs: start + dur });
139
- cursor = start + dur + (step.holdAfter ?? 0);
169
+ typings.push({ from: step.from, startMs: cursor, endMs: cursor + dur });
170
+ cursor += dur;
140
171
  break;
141
172
  }
142
173
  case "composerType": {
143
- const start = cursor + (step.delay ?? 0);
144
174
  const dur = step.typingDuration ?? typingDurationMs(step.text, pacing.typingCps);
145
175
  const comp = {
146
176
  from: step.from,
147
177
  text: step.text,
148
- startMs: start,
149
- endMs: start + dur
178
+ startMs: cursor,
179
+ endMs: cursor + dur
150
180
  };
151
181
  composers.push(comp);
152
182
  lastComposer = comp;
153
- cursor = start + dur + (step.holdAfter ?? 0);
183
+ cursor += dur;
154
184
  break;
155
185
  }
156
186
  case "send": {
157
- const sendAt = cursor + (step.delay ?? 0);
158
187
  if (lastComposer) {
159
- lastComposer.sendMs = sendAt;
188
+ lastComposer.sendMs = cursor;
160
189
  const id = nextId(step.id);
161
- const sendFrom = step.from ?? lastComposer.from;
190
+ const sendFrom = lastComposer.from;
162
191
  const msg = {
163
192
  id,
164
193
  from: sendFrom,
165
194
  isSelf: selfIds.has(sendFrom),
166
195
  variant: "message",
167
196
  content: toContentNodes({ text: lastComposer.text }),
168
- appearMs: sendAt,
197
+ appearMs: cursor,
169
198
  revealMs: SEND_REVEAL_MS,
170
- atMs: sendAt,
199
+ atMs: cursor,
171
200
  reactions: []
172
201
  };
173
202
  messages.push(msg);
174
203
  byId.set(id, msg);
175
204
  lastMessage = msg;
176
- cursor = sendAt + SEND_REVEAL_MS + (step.holdAfter ?? 0);
205
+ cursor += SEND_REVEAL_MS;
177
206
  lastComposer = void 0;
178
- } else {
179
- cursor = sendAt + (step.holdAfter ?? 0);
180
207
  }
181
208
  break;
182
209
  }
183
210
  case "reaction": {
184
211
  const target = resolveTarget(step.target);
185
212
  if (target) {
186
- const delay = step.delay ?? withJitter(rng, pacing.reactionDelayMs, pacing.humanize);
213
+ const delay = step.delay ?? withJitter(rng, DEFAULT_REACTION_LAG_MS, pacing.humanize);
187
214
  const appearAt = target.appearMs + target.revealMs + delay;
215
+ const by = step.from ? [step.from] : [];
188
216
  target.reactions.push({
189
217
  emoji: step.emoji,
190
- by: step.from ? [step.from] : [],
218
+ ...step.shortcode ? { shortcode: step.shortcode } : {},
219
+ by,
220
+ byNames: by.map((id) => nameById.get(id) ?? id),
191
221
  appearMs: appearAt,
192
222
  popMs: REACTION_POP_MS
193
223
  });
194
- cursor = Math.max(cursor, appearAt + REACTION_POP_MS) + (step.holdAfter ?? 0);
224
+ cursor = Math.max(cursor, appearAt + REACTION_POP_MS);
195
225
  }
196
226
  break;
197
227
  }
198
228
  case "edit": {
199
229
  const target = resolveTarget(step.target);
200
230
  if (target) {
201
- const editAt = cursor + (step.delay ?? pacing.interMessageGapMs);
202
- target.editedAtMs = editAt;
231
+ target.editedAtMs = cursor;
203
232
  target.editedContent = toContentNodes(step);
204
- cursor = editAt + REVEAL_MS + (step.holdAfter ?? 0);
233
+ cursor += REVEAL_MS;
205
234
  }
206
235
  break;
207
236
  }
208
237
  case "delete": {
209
238
  const target = resolveTarget(step.target);
210
239
  if (target) {
211
- const delAt = cursor + (step.delay ?? pacing.interMessageGapMs);
212
- target.deletedAtMs = delAt;
213
- cursor = delAt + (step.holdAfter ?? 0);
240
+ target.deletedAtMs = cursor;
214
241
  }
215
242
  break;
216
243
  }
217
244
  case "readReceipt": {
218
- cursor += step.delay ?? 0;
219
245
  break;
220
246
  }
221
- case "beat": {
247
+ case "delay": {
222
248
  cursor += step.duration;
223
249
  break;
224
250
  }
@@ -252,8 +278,10 @@ function sampleState(compiled, t, theme) {
252
278
  if (r.appearMs > t) continue;
253
279
  reactions.push({
254
280
  emoji: r.emoji,
281
+ ...r.shortcode ? { shortcode: r.shortcode } : {},
255
282
  count: r.by.length || 1,
256
283
  by: r.by,
284
+ byNames: r.byNames,
257
285
  progress: r.popMs === 0 ? 1 : clamp01((t - r.appearMs) / r.popMs)
258
286
  });
259
287
  if (t - r.appearMs < SCROLL_FLAG_MS) reactionRecently = true;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine/rng.ts","../src/engine/pacing.ts","../src/engine/compile.ts","../src/engine/get-state-at.ts","../src/engine/capabilities.ts","../src/engine/index.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Deterministic PRNG. The engine must never call `Math.random()` (PLAN §3) —\n * all jitter flows from the config `seed` through this, so `compile` is a pure\n * function of (config) and the same seed always yields the same timeline.\n */\n\n/** Mulberry32 — small, fast, fully deterministic. Returns floats in [0, 1). */\nexport function createRng(seed: number): () => number {\n let a = seed >>> 0;\n return function next(): number {\n a |= 0;\n a = (a + 0x6d2b79f5) | 0;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Apply ±`fraction` jitter to a value, consuming one RNG draw. With\n * `fraction = 0` the value is returned unchanged (no draw consumed) so disabling\n * humanization is exact.\n */\nexport function withJitter(\n rng: () => number,\n value: number,\n fraction: number,\n): number {\n if (fraction <= 0) return value;\n const delta = (rng() * 2 - 1) * fraction;\n return value * (1 + delta);\n}\n","/**\n * Pacing helpers: convert text into typing/reading durations. Counting is by\n * **grapheme cluster** (not code unit) so emoji ZWJ sequences, combining marks,\n * and mixed scripts pace correctly (PLAN §20).\n */\n\ninterface SegmenterCtor {\n new (\n locales?: string,\n options?: { granularity?: \"grapheme\" | \"word\" | \"sentence\" },\n ): { segment(input: string): Iterable<unknown> };\n}\n\n/** Count grapheme clusters, preferring `Intl.Segmenter`, falling back to code points. */\nexport function graphemeCount(text: string): number {\n const Segmenter = (\n globalThis as unknown as { Intl?: { Segmenter?: SegmenterCtor } }\n ).Intl?.Segmenter;\n if (Segmenter) {\n let n = 0;\n for (const _ of new Segmenter(undefined, {\n granularity: \"grapheme\",\n }).segment(text)) {\n void _;\n n++;\n }\n return n;\n }\n return [...text].length;\n}\n\n/** ms to type `text` at `cps` chars/sec (min one grapheme of time). */\nexport function typingDurationMs(text: string, cps: number): number {\n const chars = Math.max(1, graphemeCount(text));\n return (chars / cps) * 1000;\n}\n\n/**\n * ms to \"read\" `text` at `wpm` words/min — used as the gap before an incoming\n * message ≈ reading time of the prior message. Words ≈ graphemes / 5.\n */\nexport function readingDelayMs(text: string, wpm: number): number {\n const words = Math.max(1, graphemeCount(text) / 5);\n return (words / wpm) * 60000;\n}\n","import {\n toContentNodes,\n type Config,\n type ContentNode,\n type InlineNode,\n type TextNode,\n} from \"@typecaast/schema\";\nimport { createRng, withJitter } from \"./rng.js\";\nimport { readingDelayMs, typingDurationMs } from \"./pacing.js\";\nimport type {\n CompiledComposer,\n CompiledMessage,\n CompiledTimeline,\n} from \"./compiled.js\";\n\n/** Reveal animation duration for a newly appearing message (ms). */\nconst REVEAL_MS = 280;\n/** Faster reveal when the self participant sends (commit feels snappy). */\nconst SEND_REVEAL_MS = 180;\nconst REACTION_POP_MS = 200;\nconst DEFAULT_TYPING_MS = 1500;\n/** Padding after the last event so the final frame holds (ms). */\nconst TAIL_PAD_MS = 800;\n\nfunction inlineText(span: InlineNode): string {\n switch (span.type) {\n case \"text\":\n case \"code\":\n case \"emoji\":\n return span.value;\n case \"mention\":\n return span.label;\n case \"link\":\n return span.label ?? span.href;\n }\n}\n\n/** Flatten content nodes to plain text for pacing math. */\nfunction plainText(content: ContentNode[]): string {\n return content\n .map((node) =>\n node.type === \"text\"\n ? (node as TextNode).spans.map(inlineText).join(\"\")\n : \"\",\n )\n .join(\" \")\n .trim();\n}\n\nconst cache = new WeakMap<Config, CompiledTimeline>();\n\n/**\n * Resolve an authored, auto-paced config into an absolute timeline (PLAN §5).\n * Pure and memoized by config reference. Overrides (`delay`, `instant`,\n * `typingDuration`, `showTypingFor`, `holdAfter`) win over computed values; all\n * jitter is seeded from `meta.seed`.\n */\nexport function compile(config: Config): CompiledTimeline {\n const cached = cache.get(config);\n if (cached) return cached;\n\n const pacing = config.pacing;\n const rng = createRng(config.meta.seed);\n const selfIds = new Set(\n config.participants.filter((p) => p.isSelf).map((p) => p.id),\n );\n const messages: CompiledMessage[] = [];\n const typings: CompiledTimeline[\"typings\"] = [];\n const composers: CompiledComposer[] = [];\n const stepBoundaries: number[] = [];\n const byId = new Map<string, CompiledMessage>();\n\n let cursor = pacing.startDelayMs;\n let lastMessage: CompiledMessage | undefined;\n let lastComposer: CompiledComposer | undefined;\n let autoId = 0;\n const nextId = (explicit?: string): string => explicit ?? `auto-${autoId++}`;\n const resolveTarget = (target: string): CompiledMessage | undefined =>\n target === \"$prev\" ? lastMessage : byId.get(target);\n\n for (const step of config.timeline) {\n stepBoundaries.push(cursor);\n\n switch (step.type) {\n case \"message\":\n case \"system\": {\n let appearAt: number;\n if (step.delay != null) appearAt = cursor + step.delay;\n else if (step.instant) appearAt = cursor;\n else {\n let gap = pacing.interMessageGapMs;\n if (lastMessage) {\n gap += readingDelayMs(\n plainText(lastMessage.content),\n pacing.readingWpm,\n );\n }\n appearAt = cursor + withJitter(rng, gap, pacing.humanize);\n }\n\n if (step.type === \"message\" && step.typing) {\n const showFor =\n (typeof step.typing === \"object\"\n ? step.typing.showTypingFor\n : undefined) ??\n typingDurationMs(plainText(toContentNodes(step)), pacing.typingCps);\n typings.push({\n from: step.from,\n startMs: appearAt,\n endMs: appearAt + showFor,\n });\n appearAt += showFor;\n }\n\n const id = nextId(step.id);\n const reveal = step.instant ? 0 : REVEAL_MS;\n const from =\n step.type === \"system\" ? (step.from ?? \"system\") : step.from;\n const msg: CompiledMessage = {\n id,\n from,\n isSelf: selfIds.has(from),\n variant: step.type === \"system\" ? \"system\" : \"message\",\n content: toContentNodes(step),\n appearMs: appearAt,\n revealMs: reveal,\n atMs: appearAt,\n reactions: [],\n ...(step.type === \"system\"\n ? { system: { card: step.card, actions: step.actions } }\n : {}),\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor = appearAt + reveal + (step.holdAfter ?? 0);\n break;\n }\n\n case \"typing\": {\n const start = cursor + (step.delay ?? 0);\n const dur =\n step.showTypingFor ??\n withJitter(rng, DEFAULT_TYPING_MS, pacing.humanize);\n typings.push({ from: step.from, startMs: start, endMs: start + dur });\n cursor = start + dur + (step.holdAfter ?? 0);\n break;\n }\n\n case \"composerType\": {\n const start = cursor + (step.delay ?? 0);\n const dur =\n step.typingDuration ?? typingDurationMs(step.text, pacing.typingCps);\n const comp: CompiledComposer = {\n from: step.from,\n text: step.text,\n startMs: start,\n endMs: start + dur,\n };\n composers.push(comp);\n lastComposer = comp;\n cursor = start + dur + (step.holdAfter ?? 0);\n break;\n }\n\n case \"send\": {\n const sendAt = cursor + (step.delay ?? 0);\n if (lastComposer) {\n lastComposer.sendMs = sendAt;\n const id = nextId(step.id);\n const sendFrom = step.from ?? lastComposer.from;\n const msg: CompiledMessage = {\n id,\n from: sendFrom,\n isSelf: selfIds.has(sendFrom),\n variant: \"message\",\n content: toContentNodes({ text: lastComposer.text }),\n appearMs: sendAt,\n revealMs: SEND_REVEAL_MS,\n atMs: sendAt,\n reactions: [],\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor = sendAt + SEND_REVEAL_MS + (step.holdAfter ?? 0);\n lastComposer = undefined;\n } else {\n cursor = sendAt + (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"reaction\": {\n const target = resolveTarget(step.target);\n if (target) {\n const delay =\n step.delay ??\n withJitter(rng, pacing.reactionDelayMs, pacing.humanize);\n const appearAt = target.appearMs + target.revealMs + delay;\n target.reactions.push({\n emoji: step.emoji,\n by: step.from ? [step.from] : [],\n appearMs: appearAt,\n popMs: REACTION_POP_MS,\n });\n cursor =\n Math.max(cursor, appearAt + REACTION_POP_MS) +\n (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"edit\": {\n const target = resolveTarget(step.target);\n if (target) {\n const editAt = cursor + (step.delay ?? pacing.interMessageGapMs);\n target.editedAtMs = editAt;\n target.editedContent = toContentNodes(step);\n cursor = editAt + REVEAL_MS + (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"delete\": {\n const target = resolveTarget(step.target);\n if (target) {\n const delAt = cursor + (step.delay ?? pacing.interMessageGapMs);\n target.deletedAtMs = delAt;\n cursor = delAt + (step.holdAfter ?? 0);\n }\n break;\n }\n\n case \"readReceipt\": {\n cursor += step.delay ?? 0;\n break;\n }\n\n case \"beat\": {\n cursor += step.duration;\n break;\n }\n }\n }\n\n const compiled: CompiledTimeline = {\n messages,\n typings,\n composers,\n durationMs: cursor + TAIL_PAD_MS,\n stepBoundaries,\n };\n cache.set(config, compiled);\n return compiled;\n}\n","import type { GetStateAt } from \"../player.js\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n TypingState,\n} from \"../sim-state.js\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Approximate row height used for the scroll target (skins reflow on top). */\nconst ROW_HEIGHT = 64;\n/** Window after an event during which the scroll reason flags it. */\nconst SCROLL_FLAG_MS = 300;\n\n/**\n * Sample the complete renderable state at time `t` from a compiled timeline —\n * the engine's pure function of time (PLAN §3). No `Date.now()`, no mutation;\n * the same `(compiled, t, theme)` always yields a deep-equal `SimState`.\n */\nexport function sampleState(\n compiled: CompiledTimeline,\n t: number,\n theme: ResolvedTheme,\n): SimState {\n const messages: RenderedMessage[] = [];\n let lastAppear = 0;\n let reactionRecently = false;\n\n for (const m of compiled.messages) {\n if (m.appearMs > t) continue;\n if (m.deletedAtMs !== undefined && t >= m.deletedAtMs) continue;\n\n const edited =\n m.editedAtMs !== undefined &&\n m.editedContent !== undefined &&\n t >= m.editedAtMs;\n\n const reactions: RenderedReaction[] = [];\n for (const r of m.reactions) {\n if (r.appearMs > t) continue;\n reactions.push({\n emoji: r.emoji,\n count: r.by.length || 1,\n by: r.by,\n progress: r.popMs === 0 ? 1 : clamp01((t - r.appearMs) / r.popMs),\n });\n if (t - r.appearMs < SCROLL_FLAG_MS) reactionRecently = true;\n }\n\n const previous = messages[messages.length - 1];\n messages.push({\n id: m.id,\n from: m.from,\n variant: m.variant,\n content: edited ? m.editedContent! : m.content,\n revealProgress:\n m.revealMs === 0 ? 1 : clamp01((t - m.appearMs) / m.revealMs),\n state: edited ? \"edited\" : \"sent\",\n reactions,\n isSelf: m.isSelf,\n isGrouped: previous !== undefined && previous.from === m.from,\n atMs: m.atMs,\n ...(m.system ? { system: m.system } : {}),\n });\n\n if (m.appearMs > lastAppear) lastAppear = m.appearMs;\n }\n\n const typingIndicators: TypingState[] = [];\n for (const ty of compiled.typings) {\n if (t >= ty.startMs && t < ty.endMs) {\n const span = ty.endMs - ty.startMs;\n typingIndicators.push({\n from: ty.from,\n progress: span <= 0 ? 1 : clamp01((t - ty.startMs) / span),\n });\n }\n }\n\n // Composer: the latest one whose window contains t and hasn't yet sent.\n let composer: SimState[\"composer\"] = {\n text: \"\",\n caret: 0,\n sending: false,\n };\n for (const c of compiled.composers) {\n const end = c.sendMs ?? Number.POSITIVE_INFINITY;\n if (t < c.startMs || t >= end) continue;\n const text =\n t >= c.endMs\n ? c.text\n : c.text.slice(\n 0,\n Math.round(\n clamp01((t - c.startMs) / Math.max(1, c.endMs - c.startMs)) *\n c.text.length,\n ),\n );\n composer = {\n from: c.from,\n text,\n caret: text.length,\n sending: c.sendMs !== undefined && t >= c.endMs,\n };\n }\n\n const reason: SimState[\"scroll\"][\"reason\"] =\n messages.length > 0 && t - lastAppear < SCROLL_FLAG_MS\n ? \"new-message\"\n : reactionRecently\n ? \"reaction\"\n : \"none\";\n\n return {\n messages,\n typingIndicators,\n composer,\n scroll: { targetOffset: messages.length * ROW_HEIGHT, reason },\n durationMs: compiled.durationMs,\n theme,\n };\n}\n\n/** Bind a compiled timeline + theme into a `GetStateAt` closure. */\nexport function createGetStateAt(\n compiled: CompiledTimeline,\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => sampleState(compiled, t, theme);\n}\n","import type { StepType } from \"@typecaast/schema\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\n/**\n * How a skin represents a given event type:\n * - `native`: a first-class affordance.\n * - `fallback`: a degraded but present form.\n * - `unsupported`: dropped from this skin's render (kept in the config).\n */\nexport type EventCapability = \"native\" | \"fallback\" | \"unsupported\";\n\n/** What a skin supports and how it represents each event/content type. */\nexport interface Capabilities {\n events: Partial<Record<StepType, EventCapability>>;\n /** Keyed by content node type (e.g. `image: true`, `videoEmbed: false`). */\n content: Partial<Record<string, boolean>>;\n reactions: boolean;\n threads: boolean;\n readReceipts: boolean;\n}\n\n/**\n * Apply a skin's capabilities to a compiled timeline: drop the events/content\n * the skin can't render, returning a new timeline (the original config is\n * untouched, so switching skins restores everything — PLAN §7). Timing is\n * preserved; only what's shown changes.\n */\nexport function resolveCapabilities(\n compiled: CompiledTimeline,\n caps: Capabilities,\n): CompiledTimeline {\n const ev = caps.events ?? {};\n const dropTyping = ev.typing === \"unsupported\";\n const dropReactions =\n caps.reactions === false || ev.reaction === \"unsupported\";\n const dropSystem = ev.system === \"unsupported\";\n const allowed = (type: string): boolean => caps.content?.[type] !== false;\n\n const messages = compiled.messages\n .filter((m) => !(dropSystem && m.variant === \"system\"))\n .map((m) => ({\n ...m,\n reactions: dropReactions ? [] : m.reactions,\n content: m.content.filter((n) => allowed(n.type)),\n ...(m.editedContent\n ? { editedContent: m.editedContent.filter((n) => allowed(n.type)) }\n : {}),\n }));\n\n return {\n ...compiled,\n messages,\n typings: dropTyping ? [] : compiled.typings,\n };\n}\n","import type { Config } from \"@typecaast/schema\";\nimport type { GetStateAt } from \"../player.js\";\nimport type { ResolvedTheme } from \"../sim-state.js\";\nimport type { Capabilities } from \"./capabilities.js\";\nimport { compile } from \"./compile.js\";\nimport { createGetStateAt } from \"./get-state-at.js\";\nimport { resolveCapabilities } from \"./capabilities.js\";\n\nexport { compile } from \"./compile.js\";\nexport { sampleState, createGetStateAt } from \"./get-state-at.js\";\nexport {\n createPlayer,\n TimelinePlayer,\n type PlayerOptions,\n} from \"./create-player.js\";\nexport { createRng, withJitter } from \"./rng.js\";\nexport { graphemeCount, typingDurationMs, readingDelayMs } from \"./pacing.js\";\nexport {\n resolveCapabilities,\n type Capabilities,\n type EventCapability,\n} from \"./capabilities.js\";\nexport type * from \"./compiled.js\";\n\n/** A ready-to-drive engine: a sampler plus what a player needs. */\nexport interface EngineHandle {\n getStateAt: GetStateAt;\n durationMs: number;\n /** Step boundaries for stepNext/stepPrev. */\n steps: number[];\n}\n\n/**\n * Compile a config and bind a theme into a ready engine — the one call a\n * renderer needs. `compile` is memoized, so re-creating an engine for the same\n * config (e.g. only the theme changed) is cheap.\n */\nexport function createEngine(\n config: Config,\n theme: ResolvedTheme = \"light\",\n capabilities?: Capabilities,\n): EngineHandle {\n let compiled = compile(config);\n if (capabilities) compiled = resolveCapabilities(compiled, capabilities);\n return {\n getStateAt: createGetStateAt(compiled, theme),\n durationMs: compiled.durationMs,\n steps: compiled.stepBoundaries,\n };\n}\n","/**\n * `@typecaast/core` — the framework-agnostic engine and, locked first, the\n * contracts the rest of the system builds against: `SimState` (the renderable\n * state), the skin-prop data types, and the `Player` interface.\n *\n * The engine implementation (`compile` + `getStateAt`) lands in M1-engine,\n * behind these same contracts.\n */\n\n/** Contract version for the SimState/Player/skin-prop surface. */\nexport const CORE_CONTRACT_VERSION = 1;\n\nexport type * from \"./sim-state.js\";\nexport type * from \"./skin-props.js\";\nexport type * from \"./player.js\";\n\n/** The engine: compile() + getStateAt() + createEngine(). */\nexport * from \"./engine/index.js\";\n"]}
1
+ {"version":3,"sources":["../src/engine/rng.ts","../src/engine/pacing.ts","../src/engine/compile.ts","../src/engine/get-state-at.ts","../src/engine/capabilities.ts","../src/engine/index.ts","../src/index.ts"],"names":["id","msg"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Deterministic PRNG. The engine must never call `Math.random()` (PLAN §3) —\n * all jitter flows from the config `seed` through this, so `compile` is a pure\n * function of (config) and the same seed always yields the same timeline.\n */\n\n/** Mulberry32 — small, fast, fully deterministic. Returns floats in [0, 1). */\nexport function createRng(seed: number): () => number {\n let a = seed >>> 0;\n return function next(): number {\n a |= 0;\n a = (a + 0x6d2b79f5) | 0;\n let t = Math.imul(a ^ (a >>> 15), 1 | a);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Apply ±`fraction` jitter to a value, consuming one RNG draw. With\n * `fraction = 0` the value is returned unchanged (no draw consumed) so disabling\n * humanization is exact.\n */\nexport function withJitter(\n rng: () => number,\n value: number,\n fraction: number,\n): number {\n if (fraction <= 0) return value;\n const delta = (rng() * 2 - 1) * fraction;\n return value * (1 + delta);\n}\n","/**\n * Pacing helpers: convert text into typing/reading durations. Counting is by\n * **grapheme cluster** (not code unit) so emoji ZWJ sequences, combining marks,\n * and mixed scripts pace correctly (PLAN §20).\n */\n\ninterface SegmenterCtor {\n new (\n locales?: string,\n options?: { granularity?: \"grapheme\" | \"word\" | \"sentence\" },\n ): { segment(input: string): Iterable<unknown> };\n}\n\n/** Count grapheme clusters, preferring `Intl.Segmenter`, falling back to code points. */\nexport function graphemeCount(text: string): number {\n const Segmenter = (\n globalThis as unknown as { Intl?: { Segmenter?: SegmenterCtor } }\n ).Intl?.Segmenter;\n if (Segmenter) {\n let n = 0;\n for (const _ of new Segmenter(undefined, {\n granularity: \"grapheme\",\n }).segment(text)) {\n void _;\n n++;\n }\n return n;\n }\n return [...text].length;\n}\n\n/** ms to type `text` at `cps` chars/sec (min one grapheme of time). */\nexport function typingDurationMs(text: string, cps: number): number {\n const chars = Math.max(1, graphemeCount(text));\n return (chars / cps) * 1000;\n}\n\n/**\n * ms to \"read\" `text` at `wpm` words/min — used as the gap before an incoming\n * message ≈ reading time of the prior message. Words ≈ graphemes / 5.\n */\nexport function readingDelayMs(text: string, wpm: number): number {\n const words = Math.max(1, graphemeCount(text) / 5);\n return (words / wpm) * 60000;\n}\n","import {\n toContentNodes,\n type Config,\n type ContentNode,\n type InlineNode,\n type TextNode,\n} from \"@typecaast/schema\";\nimport { createRng, withJitter } from \"./rng.js\";\nimport { readingDelayMs, typingDurationMs } from \"./pacing.js\";\nimport type {\n CompiledComposer,\n CompiledMessage,\n CompiledTimeline,\n} from \"./compiled.js\";\n\n/** Reveal animation duration for a newly appearing message (ms). */\nconst REVEAL_MS = 280;\n/** Faster reveal when the self participant sends (commit feels snappy). */\nconst SEND_REVEAL_MS = 180;\nconst REACTION_POP_MS = 200;\nconst DEFAULT_TYPING_MS = 1500;\n/**\n * Default lag for a reaction whose `delay` is left blank — same beat the\n * builder auto-prepends between added steps (see `addStepAutoPaced`).\n */\nconst DEFAULT_REACTION_LAG_MS = 1000;\n/** Padding after the last event so the final frame holds (ms). */\nconst TAIL_PAD_MS = 800;\n\nfunction inlineText(span: InlineNode): string {\n switch (span.type) {\n case \"text\":\n case \"code\":\n case \"emoji\":\n return span.value;\n case \"mention\":\n return span.label;\n case \"link\":\n return span.label ?? span.href;\n }\n}\n\n/** Flatten content nodes to plain text for pacing math. */\nfunction plainText(content: ContentNode[]): string {\n return content\n .map((node) =>\n node.type === \"text\"\n ? (node as TextNode).spans.map(inlineText).join(\"\")\n : \"\",\n )\n .join(\" \")\n .trim();\n}\n\nconst cache = new WeakMap<Config, CompiledTimeline>();\n\n/**\n * Resolve an authored, auto-paced config into an absolute timeline (PLAN §5).\n * Pure and memoized by config reference. Overrides (`instant`,\n * `typingDuration`, `showTypingFor`) win over computed values; all jitter is\n * seeded from `meta.seed`. Use a `delay` step to insert an explicit pause.\n *\n * A plain `message` step from the self participant is auto-rendered through\n * the composer (type-then-send) just like an explicit `composerType` + `send`\n * pair. Use `instant: true` to skip the composer animation. Use the explicit\n * `composerType`/`send` primitives only when you need the type-pause-retype\n * choreography.\n */\nexport function compile(config: Config): CompiledTimeline {\n const cached = cache.get(config);\n if (cached) return cached;\n\n const pacing = config.pacing;\n const rng = createRng(config.meta.seed);\n const selfIds = new Set(\n config.participants.filter((p) => p.isSelf).map((p) => p.id),\n );\n const nameById = new Map(config.participants.map((p) => [p.id, p.name]));\n const messages: CompiledMessage[] = [];\n const typings: CompiledTimeline[\"typings\"] = [];\n const composers: CompiledComposer[] = [];\n const stepBoundaries: number[] = [];\n const byId = new Map<string, CompiledMessage>();\n\n // If the very first step is a message/system marked `instant`, the player\n // should open with that message already on screen — i.e. starting at t=0 —\n // rather than blank for `startDelayMs`.\n const firstStep = config.timeline[0];\n const firstStepIsInstantStart =\n firstStep !== undefined &&\n (firstStep.type === \"message\" || firstStep.type === \"system\") &&\n firstStep.instant === true;\n let cursor = firstStepIsInstantStart ? 0 : pacing.startDelayMs;\n let lastMessage: CompiledMessage | undefined;\n let lastComposer: CompiledComposer | undefined;\n let autoId = 0;\n const nextId = (explicit?: string): string => explicit ?? `auto-${autoId++}`;\n /**\n * Resolve a step's `target` to a compiled message. Blank/`undefined`/`$prev`\n * all mean \"the most-recent message\" so authors can leave the field empty in\n * the common case.\n */\n const resolveTarget = (target?: string): CompiledMessage | undefined => {\n if (!target || target === \"$prev\") return lastMessage;\n return byId.get(target);\n };\n\n for (const step of config.timeline) {\n stepBoundaries.push(cursor);\n\n switch (step.type) {\n case \"message\":\n case \"system\": {\n // Self messages are auto-rendered through the composer (type-then-send)\n // unless `instant` is set. This is pure sugar for an explicit\n // `composerType` + `send` pair, so timing matches: typing begins right\n // at the cursor with no auto-paced gap added on top. Keep the explicit\n // composerType+send pair available for the type-pause-retype case.\n const isSelfMessage =\n step.type === \"message\" && selfIds.has(step.from) && !step.instant;\n if (isSelfMessage) {\n const text = plainText(toContentNodes(step));\n const typingDur = typingDurationMs(text, pacing.typingCps);\n const sendAt = cursor + typingDur;\n const id = nextId(step.id);\n const comp: CompiledComposer = {\n from: step.from,\n text,\n startMs: cursor,\n endMs: sendAt,\n sendMs: sendAt,\n };\n composers.push(comp);\n const msg: CompiledMessage = {\n id,\n from: step.from,\n isSelf: true,\n variant: \"message\",\n content: toContentNodes(step),\n appearMs: sendAt,\n revealMs: SEND_REVEAL_MS,\n atMs: sendAt,\n reactions: [],\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n lastComposer = undefined;\n cursor = sendAt + SEND_REVEAL_MS;\n break;\n }\n\n let appearAt: number;\n if (step.instant) appearAt = cursor;\n else {\n // Auto-paced gap = simulated reading time of the prior message. For\n // explicit beats use a `delay` step.\n const gap = lastMessage\n ? readingDelayMs(plainText(lastMessage.content), pacing.readingWpm)\n : 0;\n appearAt = cursor + withJitter(rng, gap, pacing.humanize);\n }\n\n if (step.type === \"message\" && step.typing) {\n const showFor =\n (typeof step.typing === \"object\"\n ? step.typing.showTypingFor\n : undefined) ??\n typingDurationMs(plainText(toContentNodes(step)), pacing.typingCps);\n typings.push({\n from: step.from,\n startMs: appearAt,\n endMs: appearAt + showFor,\n });\n appearAt += showFor;\n }\n\n const id = nextId(step.id);\n const reveal = step.instant ? 0 : REVEAL_MS;\n const from =\n step.type === \"system\" ? (step.from ?? \"system\") : step.from;\n const msg: CompiledMessage = {\n id,\n from,\n isSelf: selfIds.has(from),\n variant: step.type === \"system\" ? \"system\" : \"message\",\n content: toContentNodes(step),\n appearMs: appearAt,\n revealMs: reveal,\n atMs: appearAt,\n reactions: [],\n ...(step.type === \"system\"\n ? { system: { card: step.card, actions: step.actions } }\n : {}),\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor = appearAt + reveal;\n break;\n }\n\n case \"typing\": {\n const dur =\n step.showTypingFor ??\n withJitter(rng, DEFAULT_TYPING_MS, pacing.humanize);\n typings.push({ from: step.from, startMs: cursor, endMs: cursor + dur });\n cursor += dur;\n break;\n }\n\n case \"composerType\": {\n const dur =\n step.typingDuration ?? typingDurationMs(step.text, pacing.typingCps);\n const comp: CompiledComposer = {\n from: step.from,\n text: step.text,\n startMs: cursor,\n endMs: cursor + dur,\n };\n composers.push(comp);\n lastComposer = comp;\n cursor += dur;\n break;\n }\n\n case \"send\": {\n if (lastComposer) {\n lastComposer.sendMs = cursor;\n const id = nextId(step.id);\n // A send commits whatever's in the composer, so the message is always\n // from whoever was typing — never the step's own `from` (a stray\n // self-default there used to mis-attribute the sent message).\n const sendFrom = lastComposer.from;\n const msg: CompiledMessage = {\n id,\n from: sendFrom,\n isSelf: selfIds.has(sendFrom),\n variant: \"message\",\n content: toContentNodes({ text: lastComposer.text }),\n appearMs: cursor,\n revealMs: SEND_REVEAL_MS,\n atMs: cursor,\n reactions: [],\n };\n messages.push(msg);\n byId.set(id, msg);\n lastMessage = msg;\n cursor += SEND_REVEAL_MS;\n lastComposer = undefined;\n }\n break;\n }\n\n case \"reaction\": {\n const target = resolveTarget(step.target);\n if (target) {\n // Default lag mirrors the builder's auto-prepended delay between\n // steps so reactions land with a natural beat unless overridden.\n const delay =\n step.delay ??\n withJitter(rng, DEFAULT_REACTION_LAG_MS, pacing.humanize);\n const appearAt = target.appearMs + target.revealMs + delay;\n const by = step.from ? [step.from] : [];\n target.reactions.push({\n emoji: step.emoji,\n ...(step.shortcode ? { shortcode: step.shortcode } : {}),\n by,\n byNames: by.map((id) => nameById.get(id) ?? id),\n appearMs: appearAt,\n popMs: REACTION_POP_MS,\n });\n cursor = Math.max(cursor, appearAt + REACTION_POP_MS);\n }\n break;\n }\n\n case \"edit\": {\n const target = resolveTarget(step.target);\n if (target) {\n // Edits/deletes happen at the cursor; insert a `delay` step before\n // them to add breathing room.\n target.editedAtMs = cursor;\n target.editedContent = toContentNodes(step);\n cursor += REVEAL_MS;\n }\n break;\n }\n\n case \"delete\": {\n const target = resolveTarget(step.target);\n if (target) {\n target.deletedAtMs = cursor;\n }\n break;\n }\n\n case \"readReceipt\": {\n // No timeline advancement; receipts are visual-only and overlay the\n // current frame.\n break;\n }\n\n case \"delay\": {\n cursor += step.duration;\n break;\n }\n }\n }\n\n const compiled: CompiledTimeline = {\n messages,\n typings,\n composers,\n durationMs: cursor + TAIL_PAD_MS,\n stepBoundaries,\n };\n cache.set(config, compiled);\n return compiled;\n}\n","import type { GetStateAt } from \"../player.js\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n TypingState,\n} from \"../sim-state.js\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** Approximate row height used for the scroll target (skins reflow on top). */\nconst ROW_HEIGHT = 64;\n/** Window after an event during which the scroll reason flags it. */\nconst SCROLL_FLAG_MS = 300;\n\n/**\n * Sample the complete renderable state at time `t` from a compiled timeline —\n * the engine's pure function of time (PLAN §3). No `Date.now()`, no mutation;\n * the same `(compiled, t, theme)` always yields a deep-equal `SimState`.\n */\nexport function sampleState(\n compiled: CompiledTimeline,\n t: number,\n theme: ResolvedTheme,\n): SimState {\n const messages: RenderedMessage[] = [];\n let lastAppear = 0;\n let reactionRecently = false;\n\n for (const m of compiled.messages) {\n if (m.appearMs > t) continue;\n if (m.deletedAtMs !== undefined && t >= m.deletedAtMs) continue;\n\n const edited =\n m.editedAtMs !== undefined &&\n m.editedContent !== undefined &&\n t >= m.editedAtMs;\n\n const reactions: RenderedReaction[] = [];\n for (const r of m.reactions) {\n if (r.appearMs > t) continue;\n reactions.push({\n emoji: r.emoji,\n ...(r.shortcode ? { shortcode: r.shortcode } : {}),\n count: r.by.length || 1,\n by: r.by,\n byNames: r.byNames,\n progress: r.popMs === 0 ? 1 : clamp01((t - r.appearMs) / r.popMs),\n });\n if (t - r.appearMs < SCROLL_FLAG_MS) reactionRecently = true;\n }\n\n const previous = messages[messages.length - 1];\n messages.push({\n id: m.id,\n from: m.from,\n variant: m.variant,\n content: edited ? m.editedContent! : m.content,\n revealProgress:\n m.revealMs === 0 ? 1 : clamp01((t - m.appearMs) / m.revealMs),\n state: edited ? \"edited\" : \"sent\",\n reactions,\n isSelf: m.isSelf,\n isGrouped: previous !== undefined && previous.from === m.from,\n atMs: m.atMs,\n ...(m.system ? { system: m.system } : {}),\n });\n\n if (m.appearMs > lastAppear) lastAppear = m.appearMs;\n }\n\n const typingIndicators: TypingState[] = [];\n for (const ty of compiled.typings) {\n if (t >= ty.startMs && t < ty.endMs) {\n const span = ty.endMs - ty.startMs;\n typingIndicators.push({\n from: ty.from,\n progress: span <= 0 ? 1 : clamp01((t - ty.startMs) / span),\n });\n }\n }\n\n // Composer: the latest one whose window contains t and hasn't yet sent.\n let composer: SimState[\"composer\"] = {\n text: \"\",\n caret: 0,\n sending: false,\n };\n for (const c of compiled.composers) {\n const end = c.sendMs ?? Number.POSITIVE_INFINITY;\n if (t < c.startMs || t >= end) continue;\n const text =\n t >= c.endMs\n ? c.text\n : c.text.slice(\n 0,\n Math.round(\n clamp01((t - c.startMs) / Math.max(1, c.endMs - c.startMs)) *\n c.text.length,\n ),\n );\n composer = {\n from: c.from,\n text,\n caret: text.length,\n sending: c.sendMs !== undefined && t >= c.endMs,\n };\n }\n\n const reason: SimState[\"scroll\"][\"reason\"] =\n messages.length > 0 && t - lastAppear < SCROLL_FLAG_MS\n ? \"new-message\"\n : reactionRecently\n ? \"reaction\"\n : \"none\";\n\n return {\n messages,\n typingIndicators,\n composer,\n scroll: { targetOffset: messages.length * ROW_HEIGHT, reason },\n durationMs: compiled.durationMs,\n theme,\n };\n}\n\n/** Bind a compiled timeline + theme into a `GetStateAt` closure. */\nexport function createGetStateAt(\n compiled: CompiledTimeline,\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => sampleState(compiled, t, theme);\n}\n","import type { StepType } from \"@typecaast/schema\";\nimport type { CompiledTimeline } from \"./compiled.js\";\n\n/**\n * How a skin represents a given event type:\n * - `native`: a first-class affordance.\n * - `fallback`: a degraded but present form.\n * - `unsupported`: dropped from this skin's render (kept in the config).\n */\nexport type EventCapability = \"native\" | \"fallback\" | \"unsupported\";\n\n/** What a skin supports and how it represents each event/content type. */\nexport interface Capabilities {\n events: Partial<Record<StepType, EventCapability>>;\n /** Keyed by content node type (e.g. `image: true`, `videoEmbed: false`). */\n content: Partial<Record<string, boolean>>;\n reactions: boolean;\n threads: boolean;\n readReceipts: boolean;\n}\n\n/**\n * Apply a skin's capabilities to a compiled timeline: drop the events/content\n * the skin can't render, returning a new timeline (the original config is\n * untouched, so switching skins restores everything — PLAN §7). Timing is\n * preserved; only what's shown changes.\n */\nexport function resolveCapabilities(\n compiled: CompiledTimeline,\n caps: Capabilities,\n): CompiledTimeline {\n const ev = caps.events ?? {};\n const dropTyping = ev.typing === \"unsupported\";\n const dropReactions =\n caps.reactions === false || ev.reaction === \"unsupported\";\n const dropSystem = ev.system === \"unsupported\";\n const allowed = (type: string): boolean => caps.content?.[type] !== false;\n\n const messages = compiled.messages\n .filter((m) => !(dropSystem && m.variant === \"system\"))\n .map((m) => ({\n ...m,\n reactions: dropReactions ? [] : m.reactions,\n content: m.content.filter((n) => allowed(n.type)),\n ...(m.editedContent\n ? { editedContent: m.editedContent.filter((n) => allowed(n.type)) }\n : {}),\n }));\n\n return {\n ...compiled,\n messages,\n typings: dropTyping ? [] : compiled.typings,\n };\n}\n","import type { Config } from \"@typecaast/schema\";\nimport type { GetStateAt } from \"../player.js\";\nimport type { ResolvedTheme } from \"../sim-state.js\";\nimport type { Capabilities } from \"./capabilities.js\";\nimport { compile } from \"./compile.js\";\nimport { createGetStateAt } from \"./get-state-at.js\";\nimport { resolveCapabilities } from \"./capabilities.js\";\n\nexport { compile } from \"./compile.js\";\nexport { sampleState, createGetStateAt } from \"./get-state-at.js\";\nexport {\n createPlayer,\n TimelinePlayer,\n type PlayerOptions,\n} from \"./create-player.js\";\nexport { createRng, withJitter } from \"./rng.js\";\nexport { graphemeCount, typingDurationMs, readingDelayMs } from \"./pacing.js\";\nexport {\n resolveCapabilities,\n type Capabilities,\n type EventCapability,\n} from \"./capabilities.js\";\nexport type * from \"./compiled.js\";\n\n/** A ready-to-drive engine: a sampler plus what a player needs. */\nexport interface EngineHandle {\n getStateAt: GetStateAt;\n durationMs: number;\n /** Step boundaries for stepNext/stepPrev. */\n steps: number[];\n}\n\n/**\n * Compile a config and bind a theme into a ready engine — the one call a\n * renderer needs. `compile` is memoized, so re-creating an engine for the same\n * config (e.g. only the theme changed) is cheap.\n */\nexport function createEngine(\n config: Config,\n theme: ResolvedTheme = \"light\",\n capabilities?: Capabilities,\n): EngineHandle {\n let compiled = compile(config);\n if (capabilities) compiled = resolveCapabilities(compiled, capabilities);\n return {\n getStateAt: createGetStateAt(compiled, theme),\n durationMs: compiled.durationMs,\n steps: compiled.stepBoundaries,\n };\n}\n","/**\n * `@typecaast/core` — the framework-agnostic engine and, locked first, the\n * contracts the rest of the system builds against: `SimState` (the renderable\n * state), the skin-prop data types, and the `Player` interface.\n *\n * The engine implementation (`compile` + `getStateAt`) lands in M1-engine,\n * behind these same contracts.\n */\n\n/** Contract version for the SimState/Player/skin-prop surface. */\nexport const CORE_CONTRACT_VERSION = 1;\n\nexport type * from \"./sim-state.js\";\nexport type * from \"./skin-props.js\";\nexport type * from \"./player.js\";\n\n/** The engine: compile() + getStateAt() + createEngine(). */\nexport * from \"./engine/index.js\";\n"]}
@@ -81,6 +81,7 @@ function reactionsFor(targetId, t) {
81
81
  emoji: r.emoji,
82
82
  count: r.by.length,
83
83
  by: r.by,
84
+ byNames: r.by,
84
85
  progress: clamp01((t - r.start) / REACTION_MS)
85
86
  }));
86
87
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mocks/billing-toast.ts","../../src/engine/create-player.ts"],"names":["toContentNodes"],"mappings":"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","file":"index.cjs","sourcesContent":["import { toContentNodes, type Participant } from \"@typecaast/schema\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n} from \"../sim-state.js\";\nimport type { GetStateAt } from \"../player.js\";\n\n/**\n * A hand-authored, faked playback of the spec's Slack billing-toast thread —\n * **no engine**. It exists so the UI (skins, player, builder) can be built and\n * validated against the locked `SimState` contract before `compile`/\n * `getStateAt` exist (UI-first, PLAN §14). Building it by hand is also how we\n * pressure-test what the real engine + schema must produce.\n *\n * `buildMockBillingToastState(t)` is a pure function of time — same `t` always\n * yields a deep-equal state — so it stands in for the engine's determinism too.\n */\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** ms a message takes to reveal (fade/slide-in) once it appears. */\nconst REVEAL_MS = 280;\n/** ms a reaction takes to pop in. */\nconst REACTION_MS = 200;\n\nexport const mockParticipants: Participant[] = [\n { id: \"cory\", name: \"Cory Watilo\", isSelf: true, kind: \"person\" },\n { id: \"paul\", name: \"Paul D'Ambra\", color: \"#5b3a8e\", kind: \"person\" },\n { id: \"posthog-bot\", name: \"PostHog\", kind: \"app\" },\n];\n\nconst COMPOSER_TEXT =\n \"Let me check how exceptions are captured in the frontend.\";\n\ninterface MockMessageEvent {\n kind: \"message\" | \"system\";\n id: string;\n from: string;\n start: number;\n content: ReturnType<typeof toContentNodes>;\n card?: string;\n actions?: { label: string; href?: string }[];\n}\n\ninterface MockReactionEvent {\n target: string;\n emoji: string;\n by: string[];\n start: number;\n}\n\ninterface MockTypingEvent {\n from: string;\n start: number;\n end: number;\n}\n\ninterface MockComposerEvent {\n from: string;\n text: string;\n start: number;\n end: number;\n}\n\nconst messageEvents: MockMessageEvent[] = [\n {\n kind: \"message\",\n id: \"m1\",\n from: \"cory\",\n start: 600,\n content: toContentNodes({\n text: \"i got a billing toast error on the dashboard but i think it's a bug?\",\n }),\n },\n {\n kind: \"message\",\n id: \"m2\",\n from: \"paul\",\n start: 4300,\n content: toContentNodes({\n text: \"@PostHog the billing/spend API call shouldn't show an error toast to the user…\",\n }),\n },\n {\n kind: \"message\",\n id: \"m3\",\n from: \"cory\",\n start: 6000,\n content: toContentNodes({\n text: \"here's the toast:\",\n images: [{ src: \"./toast.png\", alt: \"billing error toast\", width: 320 }],\n }),\n },\n {\n kind: \"system\",\n id: \"s1\",\n from: \"posthog-bot\",\n start: 7500,\n content: toContentNodes({ text: \"Pull request opened.\" }),\n card: \"pr-opened\",\n actions: [{ label: \"View PR\" }, { label: \"Open in PostHog Code\" }],\n },\n {\n kind: \"message\",\n id: \"m4\",\n from: \"cory\",\n start: 11200,\n content: toContentNodes({ text: COMPOSER_TEXT }),\n },\n];\n\nconst reactionEvents: MockReactionEvent[] = [\n { target: \"m1\", emoji: \"🦔\", by: [\"paul\"], start: 2000 },\n];\n\nconst typingEvents: MockTypingEvent[] = [\n { from: \"paul\", start: 2600, end: 4300 },\n];\n\nconst composerEvents: MockComposerEvent[] = [\n { from: \"cory\", text: COMPOSER_TEXT, start: 8500, end: 11000 },\n];\n\n/** Total faked timeline length. */\nexport const MOCK_BILLING_TOAST_DURATION_MS = 12000;\n\n/** Step boundaries (event start times) for stepNext/stepPrev in the player. */\nexport const MOCK_BILLING_TOAST_STEPS: number[] = [\n 0,\n ...messageEvents.map((e) => e.start),\n ...reactionEvents.map((e) => e.start),\n ...typingEvents.map((e) => e.start),\n ...composerEvents.map((e) => e.start),\n MOCK_BILLING_TOAST_DURATION_MS,\n]\n .sort((a, b) => a - b)\n .filter((t, i, arr) => arr.indexOf(t) === i);\n\nfunction reactionsFor(targetId: string, t: number): RenderedReaction[] {\n return reactionEvents\n .filter((r) => r.target === targetId && r.start <= t)\n .map((r) => ({\n emoji: r.emoji,\n count: r.by.length,\n by: r.by,\n progress: clamp01((t - r.start) / REACTION_MS),\n }));\n}\n\n/** Build the complete faked state at time `t` (pure). */\nexport function buildMockBillingToastState(\n t: number,\n theme: ResolvedTheme = \"light\",\n): SimState {\n // Composer: typing in progress until a send commits the message.\n const composerEvent = composerEvents[0];\n const sendEvent = messageEvents.find((e) => e.id === \"m4\");\n let composerText = \"\";\n let composerCaret = 0;\n let sending = false;\n let composerFrom: string | undefined;\n if (composerEvent && sendEvent && t < sendEvent.start) {\n if (t >= composerEvent.start) {\n composerFrom = composerEvent.from;\n const frac = clamp01(\n (t - composerEvent.start) / (composerEvent.end - composerEvent.start),\n );\n const chars = Math.round(frac * composerEvent.text.length);\n composerText = composerEvent.text.slice(0, chars);\n composerCaret = composerText.length;\n sending = t >= composerEvent.end;\n }\n }\n\n const visible = messageEvents.filter((e) => e.start <= t);\n const messages: RenderedMessage[] = visible.map((e, i) => {\n const previous = visible[i - 1];\n return {\n id: e.id,\n from: e.from,\n variant: e.kind,\n content: e.content,\n revealProgress: clamp01((t - e.start) / REVEAL_MS),\n state: \"sent\",\n reactions: reactionsFor(e.id, t),\n isSelf: e.from === \"cory\",\n isGrouped: previous !== undefined && previous.from === e.from,\n atMs: e.start,\n ...(e.kind === \"system\"\n ? { system: { card: e.card, actions: e.actions } }\n : {}),\n };\n });\n\n const typingIndicators = typingEvents\n .filter((e) => t >= e.start && t < e.end)\n .map((e) => ({\n from: e.from,\n progress: clamp01((t - e.start) / (e.end - e.start)),\n }));\n\n // Scroll: target grows with the thread; flag a reason when something landed recently.\n const lastAppear = Math.max(0, ...visible.map((e) => e.start));\n const recentReaction = reactionEvents.some(\n (r) => r.start <= t && t - r.start < 300,\n );\n const scroll = {\n targetOffset: messages.length * 64,\n reason: (t - lastAppear < 300 && messages.length > 0\n ? \"new-message\"\n : recentReaction\n ? \"reaction\"\n : \"none\") as SimState[\"scroll\"][\"reason\"],\n };\n\n return {\n messages,\n typingIndicators,\n composer: {\n from: composerFrom,\n text: composerText,\n caret: composerCaret,\n sending,\n },\n scroll,\n durationMs: MOCK_BILLING_TOAST_DURATION_MS,\n theme,\n };\n}\n\n/** A `GetStateAt` over the faked billing-toast thread, fixed to one theme. */\nexport function createMockBillingToastGetStateAt(\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => buildMockBillingToastState(t, theme);\n}\n\n/** Hand-picked snapshots at representative moments (for stories/tests). */\nexport const mockBillingToastSnapshots = {\n empty: buildMockBillingToastState(0),\n firstMessage: buildMockBillingToastState(900),\n paulTyping: buildMockBillingToastState(3000),\n withSystemCard: buildMockBillingToastState(7800),\n composerTyping: buildMockBillingToastState(9800),\n complete: buildMockBillingToastState(MOCK_BILLING_TOAST_DURATION_MS),\n} satisfies Record<string, SimState>;\n","import type {\n GetStateAt,\n Player,\n PlayerEvent,\n PlayerEventMap,\n} from \"../player.js\";\nimport type { SimState } from \"../sim-state.js\";\n\nexport interface PlayerOptions {\n durationMs: number;\n /** Step boundaries (ms) for stepNext/stepPrev. */\n steps?: number[];\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n}\n\n/** A monotonic time source; reading it doesn't affect `getStateAt` determinism. */\nfunction now(): number {\n const perf = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return perf ? perf.now() : Date.now();\n}\n\ntype FrameHandle = number;\n\ninterface SchedulerGlobals {\n requestAnimationFrame?: (cb: (time: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n setTimeout?: (cb: () => void, ms: number) => number;\n clearTimeout?: (handle: number) => void;\n}\n\nfunction schedule(cb: () => void): FrameHandle {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.requestAnimationFrame) return g.requestAnimationFrame(() => cb());\n return g.setTimeout ? g.setTimeout(cb, 16) : 0;\n}\nfunction unschedule(handle: FrameHandle): void {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.cancelAnimationFrame) g.cancelAnimationFrame(handle);\n else if (g.clearTimeout) g.clearTimeout(handle);\n}\n\n/**\n * The real-time `Player`: a thin clock wrapper around a pure `GetStateAt`\n * (PLAN §5). It owns the only wall-clock in the system (rAF in the browser, a\n * timeout fallback elsewhere) and samples the engine each tick. The same class\n * drove the UI over the mock and now drives it over the real engine — identical\n * surface, so swapping the engine changes nothing here.\n */\nexport class TimelinePlayer implements Player {\n private readonly getStateAt: GetStateAt;\n private readonly _durationMs: number;\n private readonly steps: number[];\n private _currentMs = 0;\n private _rate: number;\n private _loop: boolean;\n private _playing = false;\n private _state: SimState;\n private anchor = 0;\n private frame: FrameHandle | null = null;\n private listeners = new Map<PlayerEvent, Set<(payload: never) => void>>();\n\n constructor(getStateAt: GetStateAt, options: PlayerOptions) {\n this.getStateAt = getStateAt;\n this._durationMs = options.durationMs;\n this.steps = (options.steps ?? [0, options.durationMs])\n .slice()\n .sort((a, b) => a - b);\n this._rate = options.rate ?? 1;\n this._loop = options.loop ?? false;\n this._state = getStateAt(0);\n if (options.autoplay) this.play();\n }\n\n get state(): SimState {\n return this._state;\n }\n get durationMs(): number {\n return this._durationMs;\n }\n get currentMs(): number {\n return this._currentMs;\n }\n get rate(): number {\n return this._rate;\n }\n get playing(): boolean {\n return this._playing;\n }\n get loop(): boolean {\n return this._loop;\n }\n\n play(): void {\n if (this._playing) return;\n this._playing = true;\n this.anchor = now() - this._currentMs / this._rate;\n this.emit(\"play\", undefined);\n this.tick();\n }\n\n pause(): void {\n if (!this._playing) return;\n this._playing = false;\n if (this.frame !== null) {\n unschedule(this.frame);\n this.frame = null;\n }\n this.emit(\"pause\", undefined);\n }\n\n private tick = (): void => {\n if (!this._playing) return;\n const elapsed = (now() - this.anchor) * this._rate;\n if (elapsed >= this._durationMs) {\n if (this._loop) {\n this._currentMs = 0;\n this.anchor = now();\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n return;\n }\n this._currentMs = this._durationMs;\n this.sampleAndEmit();\n this.pause();\n this.emit(\"end\", undefined);\n return;\n }\n this._currentMs = elapsed;\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n };\n\n private sampleAndEmit(): void {\n this._state = this.getStateAt(this._currentMs);\n this.emit(\"tick\", this._state);\n }\n\n private clamp(t: number): number {\n return t < 0 ? 0 : t > this._durationMs ? this._durationMs : t;\n }\n\n seek(timeMs: number): void {\n this._currentMs = this.clamp(timeMs);\n this.anchor = now() - this._currentMs / this._rate;\n this.sampleAndEmit();\n this.emit(\"seek\", this._currentMs);\n }\n\n scrubTo(timeMs: number): void {\n this.seek(timeMs);\n }\n\n setRate(rate: number): void {\n this._rate = rate <= 0 ? 1 : rate;\n this.anchor = now() - this._currentMs / this._rate;\n }\n\n setLoop(loop: boolean): void {\n this._loop = loop;\n }\n\n stepNext(): void {\n const next = this.steps.find((s) => s > this._currentMs + 1e-6);\n this.seek(next ?? this._durationMs);\n }\n\n stepPrev(): void {\n const prev = [...this.steps]\n .reverse()\n .find((s) => s < this._currentMs - 1e-6);\n this.seek(prev ?? 0);\n }\n\n on<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as (payload: never) => void);\n return () => this.off(event, listener);\n }\n\n off<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): void {\n this.listeners.get(event)?.delete(listener as (payload: never) => void);\n }\n\n private emit<E extends PlayerEvent>(\n event: E,\n payload: PlayerEventMap[E],\n ): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set)\n (listener as (p: PlayerEventMap[E]) => void)(payload);\n }\n\n destroy(): void {\n this.pause();\n this.listeners.clear();\n }\n}\n\n/** Create a real-time player over a `GetStateAt`. */\nexport function createPlayer(\n getStateAt: GetStateAt,\n options: PlayerOptions,\n): Player {\n return new TimelinePlayer(getStateAt, options);\n}\n"]}
1
+ {"version":3,"sources":["../../src/mocks/billing-toast.ts","../../src/engine/create-player.ts"],"names":["toContentNodes"],"mappings":"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","file":"index.cjs","sourcesContent":["import { toContentNodes, type Participant } from \"@typecaast/schema\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n} from \"../sim-state.js\";\nimport type { GetStateAt } from \"../player.js\";\n\n/**\n * A hand-authored, faked playback of the spec's Slack billing-toast thread —\n * **no engine**. It exists so the UI (skins, player, builder) can be built and\n * validated against the locked `SimState` contract before `compile`/\n * `getStateAt` exist (UI-first, PLAN §14). Building it by hand is also how we\n * pressure-test what the real engine + schema must produce.\n *\n * `buildMockBillingToastState(t)` is a pure function of time — same `t` always\n * yields a deep-equal state — so it stands in for the engine's determinism too.\n */\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** ms a message takes to reveal (fade/slide-in) once it appears. */\nconst REVEAL_MS = 280;\n/** ms a reaction takes to pop in. */\nconst REACTION_MS = 200;\n\nexport const mockParticipants: Participant[] = [\n { id: \"cory\", name: \"Cory Watilo\", isSelf: true, kind: \"person\" },\n { id: \"paul\", name: \"Paul D'Ambra\", color: \"#5b3a8e\", kind: \"person\" },\n { id: \"posthog-bot\", name: \"PostHog\", kind: \"app\" },\n];\n\nconst COMPOSER_TEXT =\n \"Let me check how exceptions are captured in the frontend.\";\n\ninterface MockMessageEvent {\n kind: \"message\" | \"system\";\n id: string;\n from: string;\n start: number;\n content: ReturnType<typeof toContentNodes>;\n card?: string;\n actions?: {\n label: string;\n href?: string;\n variant?: \"primary\" | \"secondary\";\n }[];\n}\n\ninterface MockReactionEvent {\n target: string;\n emoji: string;\n by: string[];\n start: number;\n}\n\ninterface MockTypingEvent {\n from: string;\n start: number;\n end: number;\n}\n\ninterface MockComposerEvent {\n from: string;\n text: string;\n start: number;\n end: number;\n}\n\nconst messageEvents: MockMessageEvent[] = [\n {\n kind: \"message\",\n id: \"m1\",\n from: \"cory\",\n start: 600,\n content: toContentNodes({\n text: \"i got a billing toast error on the dashboard but i think it's a bug?\",\n }),\n },\n {\n kind: \"message\",\n id: \"m2\",\n from: \"paul\",\n start: 4300,\n content: toContentNodes({\n text: \"@PostHog the billing/spend API call shouldn't show an error toast to the user…\",\n }),\n },\n {\n kind: \"message\",\n id: \"m3\",\n from: \"cory\",\n start: 6000,\n content: toContentNodes({\n text: \"here's the toast:\",\n images: [{ src: \"./toast.png\", alt: \"billing error toast\", width: 320 }],\n }),\n },\n {\n kind: \"system\",\n id: \"s1\",\n from: \"posthog-bot\",\n start: 7500,\n content: toContentNodes({ text: \"Pull request opened.\" }),\n card: \"pr-opened\",\n actions: [{ label: \"View PR\" }, { label: \"Open in PostHog Code\" }],\n },\n {\n kind: \"message\",\n id: \"m4\",\n from: \"cory\",\n start: 11200,\n content: toContentNodes({ text: COMPOSER_TEXT }),\n },\n];\n\nconst reactionEvents: MockReactionEvent[] = [\n { target: \"m1\", emoji: \"🦔\", by: [\"paul\"], start: 2000 },\n];\n\nconst typingEvents: MockTypingEvent[] = [\n { from: \"paul\", start: 2600, end: 4300 },\n];\n\nconst composerEvents: MockComposerEvent[] = [\n { from: \"cory\", text: COMPOSER_TEXT, start: 8500, end: 11000 },\n];\n\n/** Total faked timeline length. */\nexport const MOCK_BILLING_TOAST_DURATION_MS = 12000;\n\n/** Step boundaries (event start times) for stepNext/stepPrev in the player. */\nexport const MOCK_BILLING_TOAST_STEPS: number[] = [\n 0,\n ...messageEvents.map((e) => e.start),\n ...reactionEvents.map((e) => e.start),\n ...typingEvents.map((e) => e.start),\n ...composerEvents.map((e) => e.start),\n MOCK_BILLING_TOAST_DURATION_MS,\n]\n .sort((a, b) => a - b)\n .filter((t, i, arr) => arr.indexOf(t) === i);\n\nfunction reactionsFor(targetId: string, t: number): RenderedReaction[] {\n return reactionEvents\n .filter((r) => r.target === targetId && r.start <= t)\n .map((r) => ({\n emoji: r.emoji,\n count: r.by.length,\n by: r.by,\n byNames: r.by,\n progress: clamp01((t - r.start) / REACTION_MS),\n }));\n}\n\n/** Build the complete faked state at time `t` (pure). */\nexport function buildMockBillingToastState(\n t: number,\n theme: ResolvedTheme = \"light\",\n): SimState {\n // Composer: typing in progress until a send commits the message.\n const composerEvent = composerEvents[0];\n const sendEvent = messageEvents.find((e) => e.id === \"m4\");\n let composerText = \"\";\n let composerCaret = 0;\n let sending = false;\n let composerFrom: string | undefined;\n if (composerEvent && sendEvent && t < sendEvent.start) {\n if (t >= composerEvent.start) {\n composerFrom = composerEvent.from;\n const frac = clamp01(\n (t - composerEvent.start) / (composerEvent.end - composerEvent.start),\n );\n const chars = Math.round(frac * composerEvent.text.length);\n composerText = composerEvent.text.slice(0, chars);\n composerCaret = composerText.length;\n sending = t >= composerEvent.end;\n }\n }\n\n const visible = messageEvents.filter((e) => e.start <= t);\n const messages: RenderedMessage[] = visible.map((e, i) => {\n const previous = visible[i - 1];\n return {\n id: e.id,\n from: e.from,\n variant: e.kind,\n content: e.content,\n revealProgress: clamp01((t - e.start) / REVEAL_MS),\n state: \"sent\",\n reactions: reactionsFor(e.id, t),\n isSelf: e.from === \"cory\",\n isGrouped: previous !== undefined && previous.from === e.from,\n atMs: e.start,\n ...(e.kind === \"system\"\n ? { system: { card: e.card, actions: e.actions } }\n : {}),\n };\n });\n\n const typingIndicators = typingEvents\n .filter((e) => t >= e.start && t < e.end)\n .map((e) => ({\n from: e.from,\n progress: clamp01((t - e.start) / (e.end - e.start)),\n }));\n\n // Scroll: target grows with the thread; flag a reason when something landed recently.\n const lastAppear = Math.max(0, ...visible.map((e) => e.start));\n const recentReaction = reactionEvents.some(\n (r) => r.start <= t && t - r.start < 300,\n );\n const scroll = {\n targetOffset: messages.length * 64,\n reason: (t - lastAppear < 300 && messages.length > 0\n ? \"new-message\"\n : recentReaction\n ? \"reaction\"\n : \"none\") as SimState[\"scroll\"][\"reason\"],\n };\n\n return {\n messages,\n typingIndicators,\n composer: {\n from: composerFrom,\n text: composerText,\n caret: composerCaret,\n sending,\n },\n scroll,\n durationMs: MOCK_BILLING_TOAST_DURATION_MS,\n theme,\n };\n}\n\n/** A `GetStateAt` over the faked billing-toast thread, fixed to one theme. */\nexport function createMockBillingToastGetStateAt(\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => buildMockBillingToastState(t, theme);\n}\n\n/** Hand-picked snapshots at representative moments (for stories/tests). */\nexport const mockBillingToastSnapshots = {\n empty: buildMockBillingToastState(0),\n firstMessage: buildMockBillingToastState(900),\n paulTyping: buildMockBillingToastState(3000),\n withSystemCard: buildMockBillingToastState(7800),\n composerTyping: buildMockBillingToastState(9800),\n complete: buildMockBillingToastState(MOCK_BILLING_TOAST_DURATION_MS),\n} satisfies Record<string, SimState>;\n","import type {\n GetStateAt,\n Player,\n PlayerEvent,\n PlayerEventMap,\n} from \"../player.js\";\nimport type { SimState } from \"../sim-state.js\";\n\nexport interface PlayerOptions {\n durationMs: number;\n /** Step boundaries (ms) for stepNext/stepPrev. */\n steps?: number[];\n autoplay?: boolean;\n loop?: boolean;\n rate?: number;\n}\n\n/** A monotonic time source; reading it doesn't affect `getStateAt` determinism. */\nfunction now(): number {\n const perf = (globalThis as unknown as { performance?: { now(): number } })\n .performance;\n return perf ? perf.now() : Date.now();\n}\n\ntype FrameHandle = number;\n\ninterface SchedulerGlobals {\n requestAnimationFrame?: (cb: (time: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n setTimeout?: (cb: () => void, ms: number) => number;\n clearTimeout?: (handle: number) => void;\n}\n\nfunction schedule(cb: () => void): FrameHandle {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.requestAnimationFrame) return g.requestAnimationFrame(() => cb());\n return g.setTimeout ? g.setTimeout(cb, 16) : 0;\n}\nfunction unschedule(handle: FrameHandle): void {\n const g = globalThis as unknown as SchedulerGlobals;\n if (g.cancelAnimationFrame) g.cancelAnimationFrame(handle);\n else if (g.clearTimeout) g.clearTimeout(handle);\n}\n\n/**\n * The real-time `Player`: a thin clock wrapper around a pure `GetStateAt`\n * (PLAN §5). It owns the only wall-clock in the system (rAF in the browser, a\n * timeout fallback elsewhere) and samples the engine each tick. The same class\n * drove the UI over the mock and now drives it over the real engine — identical\n * surface, so swapping the engine changes nothing here.\n */\nexport class TimelinePlayer implements Player {\n private readonly getStateAt: GetStateAt;\n private readonly _durationMs: number;\n private readonly steps: number[];\n private _currentMs = 0;\n private _rate: number;\n private _loop: boolean;\n private _playing = false;\n private _state: SimState;\n private anchor = 0;\n private frame: FrameHandle | null = null;\n private listeners = new Map<PlayerEvent, Set<(payload: never) => void>>();\n\n constructor(getStateAt: GetStateAt, options: PlayerOptions) {\n this.getStateAt = getStateAt;\n this._durationMs = options.durationMs;\n this.steps = (options.steps ?? [0, options.durationMs])\n .slice()\n .sort((a, b) => a - b);\n this._rate = options.rate ?? 1;\n this._loop = options.loop ?? false;\n this._state = getStateAt(0);\n if (options.autoplay) this.play();\n }\n\n get state(): SimState {\n return this._state;\n }\n get durationMs(): number {\n return this._durationMs;\n }\n get currentMs(): number {\n return this._currentMs;\n }\n get rate(): number {\n return this._rate;\n }\n get playing(): boolean {\n return this._playing;\n }\n get loop(): boolean {\n return this._loop;\n }\n\n play(): void {\n if (this._playing) return;\n this._playing = true;\n this.anchor = now() - this._currentMs / this._rate;\n this.emit(\"play\", undefined);\n this.tick();\n }\n\n pause(): void {\n if (!this._playing) return;\n this._playing = false;\n if (this.frame !== null) {\n unschedule(this.frame);\n this.frame = null;\n }\n this.emit(\"pause\", undefined);\n }\n\n private tick = (): void => {\n if (!this._playing) return;\n const elapsed = (now() - this.anchor) * this._rate;\n if (elapsed >= this._durationMs) {\n if (this._loop) {\n this._currentMs = 0;\n this.anchor = now();\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n return;\n }\n this._currentMs = this._durationMs;\n this.sampleAndEmit();\n this.pause();\n this.emit(\"end\", undefined);\n return;\n }\n this._currentMs = elapsed;\n this.sampleAndEmit();\n this.frame = schedule(this.tick);\n };\n\n private sampleAndEmit(): void {\n this._state = this.getStateAt(this._currentMs);\n this.emit(\"tick\", this._state);\n }\n\n private clamp(t: number): number {\n return t < 0 ? 0 : t > this._durationMs ? this._durationMs : t;\n }\n\n seek(timeMs: number): void {\n this._currentMs = this.clamp(timeMs);\n this.anchor = now() - this._currentMs / this._rate;\n this.sampleAndEmit();\n this.emit(\"seek\", this._currentMs);\n }\n\n scrubTo(timeMs: number): void {\n this.seek(timeMs);\n }\n\n setRate(rate: number): void {\n this._rate = rate <= 0 ? 1 : rate;\n this.anchor = now() - this._currentMs / this._rate;\n }\n\n setLoop(loop: boolean): void {\n this._loop = loop;\n }\n\n stepNext(): void {\n const next = this.steps.find((s) => s > this._currentMs + 1e-6);\n this.seek(next ?? this._durationMs);\n }\n\n stepPrev(): void {\n const prev = [...this.steps]\n .reverse()\n .find((s) => s < this._currentMs - 1e-6);\n this.seek(prev ?? 0);\n }\n\n on<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener as (payload: never) => void);\n return () => this.off(event, listener);\n }\n\n off<E extends PlayerEvent>(\n event: E,\n listener: (payload: PlayerEventMap[E]) => void,\n ): void {\n this.listeners.get(event)?.delete(listener as (payload: never) => void);\n }\n\n private emit<E extends PlayerEvent>(\n event: E,\n payload: PlayerEventMap[E],\n ): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set)\n (listener as (p: PlayerEventMap[E]) => void)(payload);\n }\n\n destroy(): void {\n this.pause();\n this.listeners.clear();\n }\n}\n\n/** Create a real-time player over a `GetStateAt`. */\nexport function createPlayer(\n getStateAt: GetStateAt,\n options: PlayerOptions,\n): Player {\n return new TimelinePlayer(getStateAt, options);\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { Participant } from '@typecaast/schema';
2
- import { R as ResolvedTheme, c as SimState, G as GetStateAt } from '../create-player-CEAzKJn1.cjs';
3
- export { j as MockPlayer, g as MockPlayerOptions, k as createMockPlayer } from '../create-player-CEAzKJn1.cjs';
2
+ import { R as ResolvedTheme, c as SimState, G as GetStateAt } from '../create-player-BUru5tb_.cjs';
3
+ export { j as MockPlayer, g as MockPlayerOptions, k as createMockPlayer } from '../create-player-BUru5tb_.cjs';
4
4
 
5
5
  declare const mockParticipants: Participant[];
6
6
  /** Total faked timeline length. */
@@ -1,6 +1,6 @@
1
1
  import { Participant } from '@typecaast/schema';
2
- import { R as ResolvedTheme, c as SimState, G as GetStateAt } from '../create-player-CEAzKJn1.js';
3
- export { j as MockPlayer, g as MockPlayerOptions, k as createMockPlayer } from '../create-player-CEAzKJn1.js';
2
+ import { R as ResolvedTheme, c as SimState, G as GetStateAt } from '../create-player-BUru5tb_.js';
3
+ export { j as MockPlayer, g as MockPlayerOptions, k as createMockPlayer } from '../create-player-BUru5tb_.js';
4
4
 
5
5
  declare const mockParticipants: Participant[];
6
6
  /** Total faked timeline length. */
@@ -79,6 +79,7 @@ function reactionsFor(targetId, t) {
79
79
  emoji: r.emoji,
80
80
  count: r.by.length,
81
81
  by: r.by,
82
+ byNames: r.by,
82
83
  progress: clamp01((t - r.start) / REACTION_MS)
83
84
  }));
84
85
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mocks/billing-toast.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { toContentNodes, type Participant } from \"@typecaast/schema\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n} from \"../sim-state.js\";\nimport type { GetStateAt } from \"../player.js\";\n\n/**\n * A hand-authored, faked playback of the spec's Slack billing-toast thread —\n * **no engine**. It exists so the UI (skins, player, builder) can be built and\n * validated against the locked `SimState` contract before `compile`/\n * `getStateAt` exist (UI-first, PLAN §14). Building it by hand is also how we\n * pressure-test what the real engine + schema must produce.\n *\n * `buildMockBillingToastState(t)` is a pure function of time — same `t` always\n * yields a deep-equal state — so it stands in for the engine's determinism too.\n */\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** ms a message takes to reveal (fade/slide-in) once it appears. */\nconst REVEAL_MS = 280;\n/** ms a reaction takes to pop in. */\nconst REACTION_MS = 200;\n\nexport const mockParticipants: Participant[] = [\n { id: \"cory\", name: \"Cory Watilo\", isSelf: true, kind: \"person\" },\n { id: \"paul\", name: \"Paul D'Ambra\", color: \"#5b3a8e\", kind: \"person\" },\n { id: \"posthog-bot\", name: \"PostHog\", kind: \"app\" },\n];\n\nconst COMPOSER_TEXT =\n \"Let me check how exceptions are captured in the frontend.\";\n\ninterface MockMessageEvent {\n kind: \"message\" | \"system\";\n id: string;\n from: string;\n start: number;\n content: ReturnType<typeof toContentNodes>;\n card?: string;\n actions?: { label: string; href?: string }[];\n}\n\ninterface MockReactionEvent {\n target: string;\n emoji: string;\n by: string[];\n start: number;\n}\n\ninterface MockTypingEvent {\n from: string;\n start: number;\n end: number;\n}\n\ninterface MockComposerEvent {\n from: string;\n text: string;\n start: number;\n end: number;\n}\n\nconst messageEvents: MockMessageEvent[] = [\n {\n kind: \"message\",\n id: \"m1\",\n from: \"cory\",\n start: 600,\n content: toContentNodes({\n text: \"i got a billing toast error on the dashboard but i think it's a bug?\",\n }),\n },\n {\n kind: \"message\",\n id: \"m2\",\n from: \"paul\",\n start: 4300,\n content: toContentNodes({\n text: \"@PostHog the billing/spend API call shouldn't show an error toast to the user…\",\n }),\n },\n {\n kind: \"message\",\n id: \"m3\",\n from: \"cory\",\n start: 6000,\n content: toContentNodes({\n text: \"here's the toast:\",\n images: [{ src: \"./toast.png\", alt: \"billing error toast\", width: 320 }],\n }),\n },\n {\n kind: \"system\",\n id: \"s1\",\n from: \"posthog-bot\",\n start: 7500,\n content: toContentNodes({ text: \"Pull request opened.\" }),\n card: \"pr-opened\",\n actions: [{ label: \"View PR\" }, { label: \"Open in PostHog Code\" }],\n },\n {\n kind: \"message\",\n id: \"m4\",\n from: \"cory\",\n start: 11200,\n content: toContentNodes({ text: COMPOSER_TEXT }),\n },\n];\n\nconst reactionEvents: MockReactionEvent[] = [\n { target: \"m1\", emoji: \"🦔\", by: [\"paul\"], start: 2000 },\n];\n\nconst typingEvents: MockTypingEvent[] = [\n { from: \"paul\", start: 2600, end: 4300 },\n];\n\nconst composerEvents: MockComposerEvent[] = [\n { from: \"cory\", text: COMPOSER_TEXT, start: 8500, end: 11000 },\n];\n\n/** Total faked timeline length. */\nexport const MOCK_BILLING_TOAST_DURATION_MS = 12000;\n\n/** Step boundaries (event start times) for stepNext/stepPrev in the player. */\nexport const MOCK_BILLING_TOAST_STEPS: number[] = [\n 0,\n ...messageEvents.map((e) => e.start),\n ...reactionEvents.map((e) => e.start),\n ...typingEvents.map((e) => e.start),\n ...composerEvents.map((e) => e.start),\n MOCK_BILLING_TOAST_DURATION_MS,\n]\n .sort((a, b) => a - b)\n .filter((t, i, arr) => arr.indexOf(t) === i);\n\nfunction reactionsFor(targetId: string, t: number): RenderedReaction[] {\n return reactionEvents\n .filter((r) => r.target === targetId && r.start <= t)\n .map((r) => ({\n emoji: r.emoji,\n count: r.by.length,\n by: r.by,\n progress: clamp01((t - r.start) / REACTION_MS),\n }));\n}\n\n/** Build the complete faked state at time `t` (pure). */\nexport function buildMockBillingToastState(\n t: number,\n theme: ResolvedTheme = \"light\",\n): SimState {\n // Composer: typing in progress until a send commits the message.\n const composerEvent = composerEvents[0];\n const sendEvent = messageEvents.find((e) => e.id === \"m4\");\n let composerText = \"\";\n let composerCaret = 0;\n let sending = false;\n let composerFrom: string | undefined;\n if (composerEvent && sendEvent && t < sendEvent.start) {\n if (t >= composerEvent.start) {\n composerFrom = composerEvent.from;\n const frac = clamp01(\n (t - composerEvent.start) / (composerEvent.end - composerEvent.start),\n );\n const chars = Math.round(frac * composerEvent.text.length);\n composerText = composerEvent.text.slice(0, chars);\n composerCaret = composerText.length;\n sending = t >= composerEvent.end;\n }\n }\n\n const visible = messageEvents.filter((e) => e.start <= t);\n const messages: RenderedMessage[] = visible.map((e, i) => {\n const previous = visible[i - 1];\n return {\n id: e.id,\n from: e.from,\n variant: e.kind,\n content: e.content,\n revealProgress: clamp01((t - e.start) / REVEAL_MS),\n state: \"sent\",\n reactions: reactionsFor(e.id, t),\n isSelf: e.from === \"cory\",\n isGrouped: previous !== undefined && previous.from === e.from,\n atMs: e.start,\n ...(e.kind === \"system\"\n ? { system: { card: e.card, actions: e.actions } }\n : {}),\n };\n });\n\n const typingIndicators = typingEvents\n .filter((e) => t >= e.start && t < e.end)\n .map((e) => ({\n from: e.from,\n progress: clamp01((t - e.start) / (e.end - e.start)),\n }));\n\n // Scroll: target grows with the thread; flag a reason when something landed recently.\n const lastAppear = Math.max(0, ...visible.map((e) => e.start));\n const recentReaction = reactionEvents.some(\n (r) => r.start <= t && t - r.start < 300,\n );\n const scroll = {\n targetOffset: messages.length * 64,\n reason: (t - lastAppear < 300 && messages.length > 0\n ? \"new-message\"\n : recentReaction\n ? \"reaction\"\n : \"none\") as SimState[\"scroll\"][\"reason\"],\n };\n\n return {\n messages,\n typingIndicators,\n composer: {\n from: composerFrom,\n text: composerText,\n caret: composerCaret,\n sending,\n },\n scroll,\n durationMs: MOCK_BILLING_TOAST_DURATION_MS,\n theme,\n };\n}\n\n/** A `GetStateAt` over the faked billing-toast thread, fixed to one theme. */\nexport function createMockBillingToastGetStateAt(\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => buildMockBillingToastState(t, theme);\n}\n\n/** Hand-picked snapshots at representative moments (for stories/tests). */\nexport const mockBillingToastSnapshots = {\n empty: buildMockBillingToastState(0),\n firstMessage: buildMockBillingToastState(900),\n paulTyping: buildMockBillingToastState(3000),\n withSystemCard: buildMockBillingToastState(7800),\n composerTyping: buildMockBillingToastState(9800),\n complete: buildMockBillingToastState(MOCK_BILLING_TOAST_DURATION_MS),\n} satisfies Record<string, SimState>;\n"]}
1
+ {"version":3,"sources":["../../src/mocks/billing-toast.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { toContentNodes, type Participant } from \"@typecaast/schema\";\nimport type {\n RenderedMessage,\n RenderedReaction,\n ResolvedTheme,\n SimState,\n} from \"../sim-state.js\";\nimport type { GetStateAt } from \"../player.js\";\n\n/**\n * A hand-authored, faked playback of the spec's Slack billing-toast thread —\n * **no engine**. It exists so the UI (skins, player, builder) can be built and\n * validated against the locked `SimState` contract before `compile`/\n * `getStateAt` exist (UI-first, PLAN §14). Building it by hand is also how we\n * pressure-test what the real engine + schema must produce.\n *\n * `buildMockBillingToastState(t)` is a pure function of time — same `t` always\n * yields a deep-equal state — so it stands in for the engine's determinism too.\n */\n\nconst clamp01 = (x: number): number => (x < 0 ? 0 : x > 1 ? 1 : x);\n\n/** ms a message takes to reveal (fade/slide-in) once it appears. */\nconst REVEAL_MS = 280;\n/** ms a reaction takes to pop in. */\nconst REACTION_MS = 200;\n\nexport const mockParticipants: Participant[] = [\n { id: \"cory\", name: \"Cory Watilo\", isSelf: true, kind: \"person\" },\n { id: \"paul\", name: \"Paul D'Ambra\", color: \"#5b3a8e\", kind: \"person\" },\n { id: \"posthog-bot\", name: \"PostHog\", kind: \"app\" },\n];\n\nconst COMPOSER_TEXT =\n \"Let me check how exceptions are captured in the frontend.\";\n\ninterface MockMessageEvent {\n kind: \"message\" | \"system\";\n id: string;\n from: string;\n start: number;\n content: ReturnType<typeof toContentNodes>;\n card?: string;\n actions?: {\n label: string;\n href?: string;\n variant?: \"primary\" | \"secondary\";\n }[];\n}\n\ninterface MockReactionEvent {\n target: string;\n emoji: string;\n by: string[];\n start: number;\n}\n\ninterface MockTypingEvent {\n from: string;\n start: number;\n end: number;\n}\n\ninterface MockComposerEvent {\n from: string;\n text: string;\n start: number;\n end: number;\n}\n\nconst messageEvents: MockMessageEvent[] = [\n {\n kind: \"message\",\n id: \"m1\",\n from: \"cory\",\n start: 600,\n content: toContentNodes({\n text: \"i got a billing toast error on the dashboard but i think it's a bug?\",\n }),\n },\n {\n kind: \"message\",\n id: \"m2\",\n from: \"paul\",\n start: 4300,\n content: toContentNodes({\n text: \"@PostHog the billing/spend API call shouldn't show an error toast to the user…\",\n }),\n },\n {\n kind: \"message\",\n id: \"m3\",\n from: \"cory\",\n start: 6000,\n content: toContentNodes({\n text: \"here's the toast:\",\n images: [{ src: \"./toast.png\", alt: \"billing error toast\", width: 320 }],\n }),\n },\n {\n kind: \"system\",\n id: \"s1\",\n from: \"posthog-bot\",\n start: 7500,\n content: toContentNodes({ text: \"Pull request opened.\" }),\n card: \"pr-opened\",\n actions: [{ label: \"View PR\" }, { label: \"Open in PostHog Code\" }],\n },\n {\n kind: \"message\",\n id: \"m4\",\n from: \"cory\",\n start: 11200,\n content: toContentNodes({ text: COMPOSER_TEXT }),\n },\n];\n\nconst reactionEvents: MockReactionEvent[] = [\n { target: \"m1\", emoji: \"🦔\", by: [\"paul\"], start: 2000 },\n];\n\nconst typingEvents: MockTypingEvent[] = [\n { from: \"paul\", start: 2600, end: 4300 },\n];\n\nconst composerEvents: MockComposerEvent[] = [\n { from: \"cory\", text: COMPOSER_TEXT, start: 8500, end: 11000 },\n];\n\n/** Total faked timeline length. */\nexport const MOCK_BILLING_TOAST_DURATION_MS = 12000;\n\n/** Step boundaries (event start times) for stepNext/stepPrev in the player. */\nexport const MOCK_BILLING_TOAST_STEPS: number[] = [\n 0,\n ...messageEvents.map((e) => e.start),\n ...reactionEvents.map((e) => e.start),\n ...typingEvents.map((e) => e.start),\n ...composerEvents.map((e) => e.start),\n MOCK_BILLING_TOAST_DURATION_MS,\n]\n .sort((a, b) => a - b)\n .filter((t, i, arr) => arr.indexOf(t) === i);\n\nfunction reactionsFor(targetId: string, t: number): RenderedReaction[] {\n return reactionEvents\n .filter((r) => r.target === targetId && r.start <= t)\n .map((r) => ({\n emoji: r.emoji,\n count: r.by.length,\n by: r.by,\n byNames: r.by,\n progress: clamp01((t - r.start) / REACTION_MS),\n }));\n}\n\n/** Build the complete faked state at time `t` (pure). */\nexport function buildMockBillingToastState(\n t: number,\n theme: ResolvedTheme = \"light\",\n): SimState {\n // Composer: typing in progress until a send commits the message.\n const composerEvent = composerEvents[0];\n const sendEvent = messageEvents.find((e) => e.id === \"m4\");\n let composerText = \"\";\n let composerCaret = 0;\n let sending = false;\n let composerFrom: string | undefined;\n if (composerEvent && sendEvent && t < sendEvent.start) {\n if (t >= composerEvent.start) {\n composerFrom = composerEvent.from;\n const frac = clamp01(\n (t - composerEvent.start) / (composerEvent.end - composerEvent.start),\n );\n const chars = Math.round(frac * composerEvent.text.length);\n composerText = composerEvent.text.slice(0, chars);\n composerCaret = composerText.length;\n sending = t >= composerEvent.end;\n }\n }\n\n const visible = messageEvents.filter((e) => e.start <= t);\n const messages: RenderedMessage[] = visible.map((e, i) => {\n const previous = visible[i - 1];\n return {\n id: e.id,\n from: e.from,\n variant: e.kind,\n content: e.content,\n revealProgress: clamp01((t - e.start) / REVEAL_MS),\n state: \"sent\",\n reactions: reactionsFor(e.id, t),\n isSelf: e.from === \"cory\",\n isGrouped: previous !== undefined && previous.from === e.from,\n atMs: e.start,\n ...(e.kind === \"system\"\n ? { system: { card: e.card, actions: e.actions } }\n : {}),\n };\n });\n\n const typingIndicators = typingEvents\n .filter((e) => t >= e.start && t < e.end)\n .map((e) => ({\n from: e.from,\n progress: clamp01((t - e.start) / (e.end - e.start)),\n }));\n\n // Scroll: target grows with the thread; flag a reason when something landed recently.\n const lastAppear = Math.max(0, ...visible.map((e) => e.start));\n const recentReaction = reactionEvents.some(\n (r) => r.start <= t && t - r.start < 300,\n );\n const scroll = {\n targetOffset: messages.length * 64,\n reason: (t - lastAppear < 300 && messages.length > 0\n ? \"new-message\"\n : recentReaction\n ? \"reaction\"\n : \"none\") as SimState[\"scroll\"][\"reason\"],\n };\n\n return {\n messages,\n typingIndicators,\n composer: {\n from: composerFrom,\n text: composerText,\n caret: composerCaret,\n sending,\n },\n scroll,\n durationMs: MOCK_BILLING_TOAST_DURATION_MS,\n theme,\n };\n}\n\n/** A `GetStateAt` over the faked billing-toast thread, fixed to one theme. */\nexport function createMockBillingToastGetStateAt(\n theme: ResolvedTheme = \"light\",\n): GetStateAt {\n return (t: number) => buildMockBillingToastState(t, theme);\n}\n\n/** Hand-picked snapshots at representative moments (for stories/tests). */\nexport const mockBillingToastSnapshots = {\n empty: buildMockBillingToastState(0),\n firstMessage: buildMockBillingToastState(900),\n paulTyping: buildMockBillingToastState(3000),\n withSystemCard: buildMockBillingToastState(7800),\n composerTyping: buildMockBillingToastState(9800),\n complete: buildMockBillingToastState(MOCK_BILLING_TOAST_DURATION_MS),\n} satisfies Record<string, SimState>;\n"]}
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@typecaast/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-agnostic Typecaast engine + the SimState/Player/skin-prop contracts.",
5
5
  "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/corywatilo/typecaast.git",
9
+ "directory": "packages/core"
10
+ },
6
11
  "type": "module",
7
12
  "sideEffects": false,
8
13
  "files": [
@@ -24,7 +29,7 @@
24
29
  }
25
30
  },
26
31
  "dependencies": {
27
- "@typecaast/schema": "0.1.0"
32
+ "@typecaast/schema": "0.2.0"
28
33
  },
29
34
  "scripts": {
30
35
  "build": "tsup",
@@ -32,6 +37,7 @@
32
37
  "typecheck": "tsc --noEmit",
33
38
  "lint": "eslint src",
34
39
  "test": "vitest run",
40
+ "bench": "vitest bench --run",
35
41
  "clean": "rm -rf dist .turbo"
36
42
  }
37
43
  }