writetrack 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/pipes.js CHANGED
@@ -1 +1 @@
1
- function u(n){let{url:o,headers:a={},retries:t=0,backoffMs:e=100,maxBackoffMs:i=3e4,keepalive:s=!1}=n;return{async send(p){let m=null;for(let c=0;c<=t;c++){if(c>0){let r=Math.min(e*Math.pow(2,c-1),i);await new Promise(d=>setTimeout(d,r))}try{let r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",...a},body:JSON.stringify(p),keepalive:s});if(r.ok)return;m=new Error(`Webhook failed: ${r.status} ${r.statusText}`)}catch(r){m=r instanceof Error?r:new Error(String(r))}}throw m}}}function l(n){let{client:o,actionName:a="writetrack_session",tags:t={}}=n;return{async send(e){let i={...t,duration:e.metadata.duration,keystrokeCount:e.session.events.length,qualityLevel:e.quality.qualityLevel,targetElement:e.metadata.targetElement,schemaVersion:e.version};e.metadata.userId&&(i.userId=e.metadata.userId),e.metadata.contentId&&(i.contentId=e.metadata.contentId),e.metadata.custom&&(i.custom=e.metadata.custom);try{o.addAction(a,i)}catch(s){throw new Error(`Datadog addAction failed: ${s instanceof Error?s.message:String(s)}`)}}}}function k(n){let{client:o,eventName:a="WriteTrack Session"}=n;return{async send(t){let e={duration:t.metadata.duration,keystrokeCount:t.session.events.length,qualityLevel:t.quality.qualityLevel,targetElement:t.metadata.targetElement,schemaVersion:t.version};t.metadata.userId&&(e.userId=t.metadata.userId),t.metadata.contentId&&(e.contentId=t.metadata.contentId),t.metadata.custom&&(e.custom=t.metadata.custom),o.track(a,e)}}}function f(n){let{tracer:o,spanName:a="writetrack.session"}=n;return{async send(t){let e=o.startSpan(a);try{e.setAttribute("writetrack.duration",t.metadata.duration),e.setAttribute("writetrack.keystroke_count",t.session.events.length),e.setAttribute("writetrack.quality_level",t.quality.qualityLevel),e.setAttribute("writetrack.target_element",t.metadata.targetElement),e.setAttribute("writetrack.schema_version",t.version),t.metadata.userId&&e.setAttribute("writetrack.user_id",t.metadata.userId),t.metadata.contentId&&e.setAttribute("writetrack.content_id",t.metadata.contentId),e.setStatus({code:1})}finally{e.end()}}}}export{l as datadog,f as opentelemetry,k as segment,u as webhook};
1
+ function p(n){let{url:o,headers:i={},retries:t=0,backoffMs:e=100,maxBackoffMs:a=3e4,keepalive:c=!1}=n;return{async send(f){let u=null;for(let s=0;s<=t;s++){if(s>0){let r=Math.min(e*Math.pow(2,s-1),a);await new Promise(l=>setTimeout(l,r))}try{let r=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",...i},body:JSON.stringify(f),keepalive:c});if(r.ok)return;u=new Error(`Webhook failed: ${r.status} ${r.statusText}`)}catch(r){u=r instanceof Error?r:new Error(String(r))}}throw u}}}function m(n){return n instanceof Error?n.message:String(n)}function d(n){let{client:o,actionName:i="writetrack_session",tags:t={}}=n;return{async send(e){let a={...t,duration:e.metadata.duration,keystrokeCount:e.session.events.length,qualityLevel:e.quality.qualityLevel,targetElement:e.metadata.targetElement,schemaVersion:e.version};e.metadata.userId&&(a.userId=e.metadata.userId),e.metadata.contentId&&(a.contentId=e.metadata.contentId),e.metadata.custom&&(a.custom=e.metadata.custom);try{o.addAction(i,a)}catch(c){throw new Error(`Datadog addAction failed: ${m(c)}`)}}}}function g(n){let{client:o,eventName:i="WriteTrack Session"}=n;return{async send(t){let e={duration:t.metadata.duration,keystrokeCount:t.session.events.length,qualityLevel:t.quality.qualityLevel,targetElement:t.metadata.targetElement,schemaVersion:t.version};t.metadata.userId&&(e.userId=t.metadata.userId),t.metadata.contentId&&(e.contentId=t.metadata.contentId),t.metadata.custom&&(e.custom=t.metadata.custom),o.track(i,e)}}}function v(n){let{tracer:o,spanName:i="writetrack.session"}=n;return{async send(t){let e=o.startSpan(i);try{e.setAttribute("writetrack.duration",t.metadata.duration),e.setAttribute("writetrack.keystroke_count",t.session.events.length),e.setAttribute("writetrack.quality_level",t.quality.qualityLevel),e.setAttribute("writetrack.target_element",t.metadata.targetElement),e.setAttribute("writetrack.schema_version",t.version),t.metadata.userId&&e.setAttribute("writetrack.user_id",t.metadata.userId),t.metadata.contentId&&e.setAttribute("writetrack.content_id",t.metadata.contentId),e.setStatus({code:1})}finally{e.end()}}}}export{d as datadog,v as opentelemetry,g as segment,p as webhook};
@@ -1,4 +1,4 @@
1
- import { S as SessionAnalysis } from './analysis-types-zqWj2pHM.js';
1
+ import { S as SessionAnalysis } from './analysis-types-DvrvSEh0.js';
2
2
  import './index-Cy91q_po.js';
3
3
 
4
4
  /**
package/dist/esm/viz.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { W as WriteTrackDataSchema } from './index-Cy91q_po.js';
2
- import { S as SessionAnalysis, a as SessionReport, d as IntegrityProof } from './analysis-types-zqWj2pHM.js';
2
+ import { S as SessionAnalysis, a as SessionReport, d as IntegrityProof } from './analysis-types-DvrvSEh0.js';
3
3
 
4
4
  /**
5
5
  * BaseChart — abstract web component base class for WriteTrack visualizations.
@@ -16,10 +16,20 @@ declare abstract class BaseChart<T = unknown> extends HTMLElement {
16
16
  private _resizeObserver;
17
17
  private _resizeRaf;
18
18
  private _themeObserver;
19
- private _initialResize;
19
+ private _lastWidth;
20
20
  private _rendered;
21
21
  private _internalTheme;
22
22
  static get observedAttributes(): string[];
23
+ /** Whether to render in compact/sparkline mode (no axes, tight margins). */
24
+ get compact(): boolean;
25
+ /** Get the chart width — reads --wt-chart-width CSS var (set by scorecard
26
+ * bridge), then container/host clientWidth, then fallback. The CSS var
27
+ * ensures the chart renders at the correct width even before layout. */
28
+ protected getChartWidth(fallback: number): number;
29
+ /** Get the chart height — reads --wt-chart-height CSS var, then explicit
30
+ * inline height, then fallback. Avoids reading content-derived clientHeight
31
+ * to prevent a resize feedback loop. */
32
+ protected getChartHeight(fallback: number): number;
23
33
  constructor();
24
34
  /** Override in subclasses to provide component-specific CSS */
25
35
  protected getStylesheet(): string;
@@ -40,6 +50,8 @@ declare abstract class BaseChart<T = unknown> extends HTMLElement {
40
50
  destroy(): void;
41
51
  /** Called on container resize. Override in subclasses that shouldn't full re-render. */
42
52
  protected onResize(): void;
53
+ /** Wrap render() in try/catch so subclasses don't need individual error handling */
54
+ private safeRender;
43
55
  /** Subclasses implement this to draw their visualization */
44
56
  protected abstract render(): void;
45
57
  /** Register the custom element if not already defined */
@@ -98,34 +110,6 @@ declare class SpeedTimeline extends BaseChart<SessionAnalysis> {
98
110
  protected render(): void;
99
111
  }
100
112
 
101
- /**
102
- * CompositionTimeline — session activity segments over time.
103
- *
104
- * Shows typing/paste/pause/tabAway segments as colored horizontal bars
105
- * and paste events as dots scaled by character count.
106
- */
107
-
108
- declare const SEGMENT_COLORS: Record<string, string>;
109
- interface SegmentDatum {
110
- start: number;
111
- end: number;
112
- type: string;
113
- color: string;
114
- }
115
- interface PasteDatum {
116
- time: number;
117
- chars: number;
118
- source: string;
119
- }
120
- /** Extract activity segments from analysis, converting ms to seconds. */
121
- declare function extractSegments(analysis: SessionAnalysis): SegmentDatum[];
122
- /** Extract paste event markers from analysis. */
123
- declare function extractPasteMarkers(analysis: SessionAnalysis): PasteDatum[];
124
- declare class CompositionTimeline extends BaseChart<SessionAnalysis> {
125
- static tagName: string;
126
- protected render(): void;
127
- }
128
-
129
113
  /**
130
114
  * RhythmHeatmap — dwell time vs flight time scatter plot.
131
115
  *
@@ -182,26 +166,47 @@ interface GrowthPoint {
182
166
  declare function extractGrowthData(data: WriteTrackDataSchema): GrowthPoint[];
183
167
  declare class DocumentGrowth extends BaseChart<WriteTrackDataSchema> {
184
168
  static tagName: string;
169
+ /** Optional change point time (seconds) — renders as a dashed vertical rule. */
170
+ private _changePointTime;
171
+ /** Set a change point time (in seconds) to display as a vertical marker. */
172
+ setChangePoint(timeSec: number | null): void;
185
173
  protected render(): void;
186
174
  }
187
175
 
188
176
  /**
189
- * DocumentRibbon — keystroke ribbon colored by inter-key gap.
177
+ * EditBeeswarmper-keystroke beeswarm visualization.
190
178
  *
191
- * Each bar represents one keystroke. Yellow = short gaps (fast), red = long gaps (slow).
179
+ * Each dot is a single edit action. Inserts, deletes, pastes, and cuts
180
+ * are color-coded and dodged vertically by time using Plot.dodgeY.
192
181
  * Consumes raw WriteTrackDataSchema (not WASM analysis output).
193
182
  */
194
183
 
195
- interface RibbonDatum {
196
- start: number;
197
- end: number;
198
- flight: number;
184
+ type BeeswarmType = 'insert' | 'delete' | 'paste' | 'cut';
185
+ interface BeeswarmDatum {
186
+ time: number;
187
+ type: BeeswarmType;
188
+ chars: number;
189
+ key: string;
199
190
  }
200
- /** Extract ribbon data from raw keydown events. */
201
- declare function extractRibbonData(data: WriteTrackDataSchema): RibbonDatum[];
202
- declare class DocumentRibbon extends BaseChart<WriteTrackDataSchema> {
191
+ /** Extract per-keystroke beeswarm data from raw session events. */
192
+ declare function extractBeeswarmData(data: WriteTrackDataSchema): BeeswarmDatum[];
193
+ declare class EditBeeswarm extends BaseChart<WriteTrackDataSchema> {
203
194
  static tagName: string;
195
+ static get observedAttributes(): string[];
196
+ /** Whether to render typed characters as letter glyphs instead of dots.
197
+ * Internal/experimental — not part of the public API. */
198
+ get glyphs(): boolean;
199
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
204
200
  protected render(): void;
201
+ /** Compact render: binned data, no axes, no tooltips, tight margins. */
202
+ private renderCompact;
203
+ /** Dot marks for all event types (default mode). */
204
+ private buildDotMarks;
205
+ /** Glyph marks: single text mark so everything dodges together.
206
+ * Typed chars as letters, deletes as ×, pastes as 📋, cuts as ✂. */
207
+ private buildGlyphMarks;
208
+ /** Crop the SVG viewBox top to fit content, preserving the bottom (axis). */
209
+ private cropViewBox;
205
210
  }
206
211
 
207
212
  /**
@@ -241,23 +246,49 @@ interface BubbleDatum {
241
246
  time: number;
242
247
  chars: number;
243
248
  type: EditType;
244
- size: number;
245
249
  }
246
250
  /** Group consecutive keystrokes into edit runs and add paste/cut events. */
247
251
  declare function extractCorrectionBubbles(data: WriteTrackDataSchema): BubbleDatum[];
248
252
  declare class CorrectionsBubble extends BaseChart<WriteTrackDataSchema> {
249
253
  static tagName: string;
250
254
  protected render(): void;
255
+ /** Compact render: 2 lanes if no clipboard events, time-binned if clipboard present. */
256
+ private renderCompact;
257
+ }
258
+
259
+ /**
260
+ * OriginBar — content origin proportion as a horizontal stacked bar.
261
+ *
262
+ * Shows typed / pasted / autocompleted as percentages of all content created.
263
+ */
264
+
265
+ interface OriginDatum {
266
+ category: 'origin';
267
+ segment: string;
268
+ value: number;
269
+ }
270
+ interface OriginBarColors {
271
+ typed: string;
272
+ pasted: string;
273
+ autocompleted: string;
274
+ }
275
+ /** Extract origin proportions from analysis, filtering out 0% segments. */
276
+ declare function extractOriginData(analysis: SessionAnalysis): OriginDatum[];
277
+ /** Render a horizontal stacked bar showing content origin proportions. */
278
+ declare function renderOriginBar(data: OriginDatum[], width: number, colors?: OriginBarColors): SVGSVGElement | null;
279
+ declare class WtOriginBar extends BaseChart<SessionAnalysis> {
280
+ static tagName: string;
281
+ protected render(): void;
251
282
  }
252
283
 
253
284
  declare class WtScorecard extends BaseChart<SessionReport> {
254
285
  static tagName: string;
255
- private _activeDetail;
256
- constructor();
286
+ static get observedAttributes(): string[];
287
+ private _activeCategory;
257
288
  /**
258
289
  * Override onResize to only re-render charts instead of full re-render.
259
- * A full re-render replaces container content changes height
260
- * triggers ResizeObserver again infinite loop.
290
+ * A full re-render replaces container content -> changes height ->
291
+ * triggers ResizeObserver again -> infinite loop.
261
292
  */
262
293
  protected onResize(): void;
263
294
  protected getStylesheet(): string;
@@ -265,30 +296,27 @@ declare class WtScorecard extends BaseChart<SessionReport> {
265
296
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
266
297
  protected render(): void;
267
298
  private buildHeader;
268
- private buildSummary;
269
- private buildTierLabel;
299
+ private buildFlags;
300
+ private buildIntegrityDetails;
270
301
  private buildGrid;
271
302
  private buildCell;
272
303
  private buildCellMetrics;
273
304
  private buildDetailPanel;
274
305
  private selectCell;
306
+ private populateAndRenderDetail;
275
307
  private populateDetail;
276
- private closeDetail;
277
- private expandPanel;
278
- private collapsePanel;
279
308
  private getIndicatorCode;
280
309
  private getComputedColors;
281
- private getChartWidth;
282
- private plotOpts;
283
- private plotCategory;
310
+ /** Render the origin proportion bar into #origin-bar if it exists and data is available. */
311
+ private renderOriginBarIfPresent;
312
+ /**
313
+ * Create a standalone chart component.
314
+ * Returns { el, init } — append el to DOM first, then call init()
315
+ * so the chart can measure its real container width.
316
+ */
317
+ private createDetailChart;
284
318
  private renderAllCharts;
285
319
  private rerenderAllCharts;
286
- private plotContentOrigin;
287
- private plotTimingAuthenticity;
288
- private plotRevisionBehavior;
289
- private plotSessionContinuity;
290
- private plotPhysicalPlausibility;
291
- private plotTemporalPatterns;
292
320
  }
293
321
 
294
322
  interface IntegrityFooterData {
@@ -314,8 +342,8 @@ declare function isPass(code: string): boolean;
314
342
  declare function getStatus(code: string | undefined): 'pass' | 'flag' | 'unknown';
315
343
  /** Display names for SessionAnalysis category fields. */
316
344
  declare const CATEGORY_NAMES: Record<string, string>;
317
- /** All 6 analysis category keys in display order. */
318
- declare const CATEGORY_KEYS: readonly ["contentOrigin", "timingAuthenticity", "revisionBehavior", "sessionContinuity", "physicalPlausibility", "temporalPatterns"];
345
+ /** All 7 analysis category keys in display order. */
346
+ declare const CATEGORY_KEYS: readonly ["contentOrigin", "timingAuthenticity", "revisionBehavior", "sessionContinuity", "physicalPlausibility", "temporalPatterns", "writingProcess"];
319
347
  type CategoryKey = (typeof CATEGORY_KEYS)[number];
320
348
  /** Tier 1 categories are expanded by default. */
321
349
  declare const TIER1_CATEGORIES: CategoryKey[];
@@ -357,4 +385,36 @@ declare const GLOSSARY: Record<string, GlossaryEntry>;
357
385
  */
358
386
  declare function wrapTerms(text: string, baseUrl?: string): string;
359
387
 
360
- export { BaseChart, type BubbleDatum, CATEGORY_NAMES, type CategoryKey, CompositionTimeline, CorrectionsBubble, DocumentGrowth, DocumentRibbon, type EditType, EditWaterfall, GLOSSARY, type GlossaryEntry, type GrowthPoint, IntegrityFooter, type PasteDatum, type PauseBin, PauseDistribution, RhythmHeatmap, type RhythmPoint, type RibbonDatum, SEGMENT_COLORS, type ScorecardMetric, type SegmentDatum, Sparkline, type SpeedPoint, SpeedTimeline, TIER1_CATEGORIES, TIER2_CATEGORIES, type WaterfallData, type WaterfallPoint, WtScorecard, extractCorrectionBubbles, extractGrowthData, extractPasteMarkers, extractPauseHistogram, extractRhythmPairs, extractRibbonData, extractSegments, extractSeries, extractSpeedData, extractWaterfallData, formatDuration, generateCaption, generateSummary, getMetrics, getStatus, isPass, wrapTerms };
388
+ /**
389
+ * StatBadge — compact pill/lozenge badges for at-a-glance session stats.
390
+ *
391
+ * Renders a row of inline badges with label + color-coded value.
392
+ */
393
+
394
+ type BadgeData = SessionAnalysis;
395
+ type BadgeStatus = 'clear' | 'flag' | 'neutral';
396
+ interface StatBadge {
397
+ label: string;
398
+ value: string;
399
+ status: BadgeStatus;
400
+ metric: string;
401
+ }
402
+ /** Extract headline stat badges from analysis data. */
403
+ declare function extractStatBadges(analysis: SessionAnalysis): StatBadge[];
404
+ /** Render a row of stat badge pills as a DOM element. */
405
+ declare function renderStatBadges(badges: StatBadge[]): HTMLElement;
406
+ declare class WtBadge extends BaseChart<BadgeData> {
407
+ static tagName: string;
408
+ static get observedAttributes(): string[];
409
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
410
+ protected getStylesheet(): string;
411
+ protected render(): void;
412
+ }
413
+
414
+ /**
415
+ * Generate a plain-English summary of a writing session from analysis data.
416
+ * Returns null if there is insufficient data to summarise.
417
+ */
418
+ declare function generateWritingSummary(analysis: SessionAnalysis): string | null;
419
+
420
+ export { type BadgeData, type BadgeStatus, BaseChart, type BeeswarmDatum, type BeeswarmType, type BubbleDatum, CATEGORY_NAMES, type CategoryKey, CorrectionsBubble, DocumentGrowth, EditBeeswarm, type EditType, EditWaterfall, GLOSSARY, type GlossaryEntry, type GrowthPoint, IntegrityFooter, type OriginBarColors, type OriginDatum, type PauseBin, PauseDistribution, RhythmHeatmap, type RhythmPoint, type ScorecardMetric, Sparkline, type SpeedPoint, SpeedTimeline, type StatBadge, TIER1_CATEGORIES, TIER2_CATEGORIES, type WaterfallData, type WaterfallPoint, WtBadge, WtOriginBar, WtScorecard, extractBeeswarmData, extractCorrectionBubbles, extractGrowthData, extractOriginData, extractPauseHistogram, extractRhythmPairs, extractSeries, extractSpeedData, extractStatBadges, extractWaterfallData, formatDuration, generateCaption, generateSummary, generateWritingSummary, getMetrics, getStatus, isPass, renderOriginBar, renderStatBadges, wrapTerms };