Skip to content

Comments

docs: improve JSDoc coverage across keet public APIs#145

Merged
ysdede merged 3 commits intomasterfrom
chore/jsdoc-keet
Feb 18, 2026
Merged

docs: improve JSDoc coverage across keet public APIs#145
ysdede merged 3 commits intomasterfrom
chore/jsdoc-keet

Conversation

@ysdede
Copy link
Owner

@ysdede ysdede commented Feb 18, 2026

Summary

  • Increase JSDoc/TSDoc coverage for exported/public APIs in components, lib modules, stores, and shared types.
  • Standardize Keet file header version tags to Keet v1.1.
  • Keep changes documentation-only (no runtime behavior changes).

Validation

  • npm test
  • npm run build

Summary by Sourcery

Improve documentation coverage and consistency across public transcription APIs and UI components.

Documentation

  • Add or expand JSDoc/TSDoc comments for public types, component props, services, and worker clients throughout the audio, transcription, and UI layers.
  • Document utility helpers (e.g., duration formatting) to clarify usage and return formats.
  • Standardize Keet source file headers to the v1.1 version tag across core modules.

Summary by CodeRabbit

Release Notes

  • New Features

    • Model selection interface with progress tracking and backend selection (WebGPU/WASM).
    • Enhanced waveform visualizer with recording state indicator and compact variant.
    • Time duration formatter for improved display formatting.
    • Expanded transcription display with sentence history and confidence indicators.
  • Documentation

    • Added comprehensive JSDoc comments throughout components and utility modules.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 18, 2026

Reviewer's Guide

Documentation-only update that standardizes Keet header version tags to v1.1 and adds/expands JSDoc/TSDoc comments for public types, component props, services, workers, and utility APIs across the app, without changing runtime behavior.

File-Level Changes

Change Details Files
Standardize Keet file header version tags to v1.1 across core modules.
  • Update header version tag comments from v2.0/v3.0/v4.0 to v1.1 in shared types, transcription modules, workers, app store, and entry point files
  • Ensure version comment consistency for public-facing modules exported from the transcription index
src/types/index.ts
src/lib/transcription/types.ts
src/stores/appStore.ts
src/lib/transcription/ModelManager.ts
src/lib/transcription/TranscriptionService.ts
src/lib/transcription/TokenStreamTranscriber.ts
src/lib/transcription/TranscriptionWorkerClient.ts
src/index.tsx
src/lib/transcription/index.ts
src/lib/transcription/transcription.worker.ts
Improve JSDoc coverage and clarity for shared types and worker/buffer contracts.
  • Add descriptive JSDoc to audio, VAD, transcription, and app state types used across the UI and pipeline
  • Document buffer worker, TEN-VAD worker, and mel worker request/response unions, configs, and result payloads
  • Clarify semantics of debug-related types such as VADState, V4MergerStats, MergeInfo, and DebugToken
src/types/index.ts
src/lib/buffer/types.ts
src/lib/audio/MelWorkerClient.ts
src/lib/vad/TenVADWorkerClient.ts
src/stores/appStore.ts
src/lib/transcription/SentenceBoundaryDetector.ts
src/lib/transcription/UtteranceBasedMerger.ts
src/lib/transcription/WindowBuilder.ts
src/lib/buffer/BufferWorkerClient.ts
Add JSDoc to public component props and exported UI helpers to better describe external API surfaces.
  • Annotate props interfaces for components used by consumers (overlays, sidebars, displays, settings, waveform/visualizers, debug tools) with parameter semantics and value ranges
  • Document exported helper constants and functions such as MODELS, getModelDisplayName, SPECTRUM_BAR_COUNT, CompactWaveform, and signals exposed from App
  • Clarify behavior of top-level UI elements like ContextPanel, DebugPanel, EnergyMeter, BufferVisualizer, PrivacyBadge, StatusBar, LayeredBufferVisualizer, and ModelLoadingOverlay
src/components/ModelLoadingOverlay.tsx
src/components/Sidebar.tsx
src/components/TranscriptionDisplay.tsx
src/components/SettingsPanel.tsx
src/components/LayeredBufferVisualizer.tsx
src/components/Waveform.tsx
src/components/ContextPanel.tsx
src/components/DebugPanel.tsx
src/components/EnergyMeter.tsx
src/components/BufferVisualizer.tsx
src/components/PrivacyBadge.tsx
src/components/StatusBar.tsx
src/App.tsx
Document high-level transcription services and workers to clarify responsibilities and callbacks.
  • Add class-level JSDoc for ModelManager, TranscriptionService, TokenStreamTranscriber, and TranscriptionWorkerClient that explain lifecycle responsibilities and streaming behavior
  • Document configuration and callback interfaces for token stream and transcription service types, including timing, merging, and diagnostic hooks
  • Clarify the role of settings and progress types such as TranscriptionServiceConfig, TranscriptionCallbacks, TokenStreamConfig, TokenStreamCallbacks, and TokenStreamResult
src/lib/transcription/types.ts
src/lib/transcription/ModelManager.ts
src/lib/transcription/TranscriptionService.ts
src/lib/transcription/TokenStreamTranscriber.ts
src/lib/transcription/TranscriptionWorkerClient.ts
src/lib/transcription/transcription.worker.ts
src/lib/transcription/index.ts
Add JSDoc to shared utilities and status helpers.
  • Document time formatting helper to define input units and output format
  • Clarify UI-facing signals and status bar responsibilities for consumers and internal use
src/utils/time.ts
src/App.tsx
src/components/StatusBar.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • Many of the new JSDoc descriptions duplicate the existing inline comments (e.g., in AudioConfig, HasSpeechResult); consider removing redundant line comments to avoid drift and keep a single source of truth for field semantics.
  • You’ve standardized headers to Keet v1.1 even for files that previously declared higher major versions (e.g., v3/v4 workers and transcribers); if those version tags are intended to reflect feature generation rather than current app version, it may be worth double-checking that this downgrade won’t confuse consumers of these APIs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Many of the new JSDoc descriptions duplicate the existing inline comments (e.g., in `AudioConfig`, `HasSpeechResult`); consider removing redundant line comments to avoid drift and keep a single source of truth for field semantics.
- You’ve standardized headers to `Keet v1.1` even for files that previously declared higher major versions (e.g., v3/v4 workers and transcribers); if those version tags are intended to reflect feature generation rather than current app version, it may be worth double-checking that this downgrade won’t confuse consumers of these APIs.

## Individual Comments

### Comment 1
<location> `src/types/index.ts:80-81` </location>
<code_context>
   startTime: number;
+  /** End timestamp in seconds. */
   endTime: number;
+  /** Optional confidence score in the range 0-1. */
   confidence: number;
 }

</code_context>

<issue_to_address>
**issue:** Docstring marks `confidence` as optional, but the type is required.

The JSDoc marks `confidence` as optional but the type is required. If callers may omit this field, change it to `confidence?: number;`. If it must always be provided, update the JSDoc to remove "Optional" for consistency.
</issue_to_address>

### Comment 2
<location> `src/components/ModelLoadingOverlay.tsx:34-41` </location>
<code_context>
     { id: 'parakeet-tdt-0.6b-v3', name: 'Parakeet v3', desc: 'Multilingual Streaming' },
 ];

+/**
+ * Resolves a human-readable model label.
+ * @param id - Internal model ID.
+ * @returns Display name if known, otherwise the raw ID.
+ */
 export function getModelDisplayName(id: string): string {
     return (MODELS.find((m) => m.id === id)?.name ?? id) || 'Unknown model';
 }
</code_context>

<issue_to_address>
**suggestion:** Behavior of `getModelDisplayName` can diverge from the documented fallback.

Because of `(… ?? id) || 'Unknown model'`, an empty-string `id` (or other falsy IDs) will return `'Unknown model'` instead of the raw ID. If you need the documented behavior, consider removing the `|| 'Unknown model'` or updating the JSDoc to mention the `'Unknown model'` fallback explicitly.

```suggestion
/**
 * Resolves a human-readable model label.
 * @param id - Internal model ID.
 * @returns Display name if known, otherwise the raw ID.
 */
export function getModelDisplayName(id: string): string {
    return MODELS.find((m) => m.id === id)?.name ?? id;
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Repository owner deleted a comment from chatgpt-codex-connector bot Feb 18, 2026
Repository owner deleted a comment from coderabbitai bot Feb 18, 2026
Repository owner deleted a comment from coderabbitai bot Feb 18, 2026
@ysdede
Copy link
Owner Author

ysdede commented Feb 18, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

The PR introduces reactive signals for AudioEngine and MelWorkerClient accessibility, expands component and worker API surfaces with new optional props and type definitions, adds comprehensive JSDoc documentation across modules, and standardizes version headers from v2.0/v3.0/v4.0 to v1.1 throughout the codebase.

Changes

Cohort / File(s) Summary
App Initialization & Signals
src/App.tsx, src/index.tsx
Introduces audioEngineSignal/setAudioEngineSignal and melClientSignal/setMelClientSignal for reactive component access; updates version header to v1.1.
Component Props Expansion
src/components/ModelLoadingOverlay.tsx, src/components/SettingsPanel.tsx, src/components/TranscriptionDisplay.tsx, src/components/Waveform.tsx, src/components/ContextPanel.tsx, src/components/DebugPanel.tsx, src/components/LayeredBufferVisualizer.tsx
Expands public component interfaces with new optional/required props: ModelLoadingOverlay adds isVisible, progress, message, state, backend, MODELS constant, and getModelDisplayName function; SettingsPanel, TranscriptionDisplay, and Waveform add callback and control props with documentation.
Component Documentation
src/components/BufferVisualizer.tsx, src/components/EnergyMeter.tsx, src/components/PrivacyBadge.tsx, src/components/Sidebar.tsx, src/components/StatusBar.tsx
Adds JSDoc comments describing component purpose and props; no behavioral changes.
Worker Communication Types
src/lib/buffer/types.ts, src/lib/audio/MelWorkerClient.ts, src/lib/buffer/BufferWorkerClient.ts, src/lib/vad/TenVADWorkerClient.ts
Expands worker request/response message types; introduces RangeResult, VadSummaryResult, MelFeatures, and TenVADResultCallback; enriches TenVADConfig and TenVADResult with threshold, wasmPath, flags, globalSampleOffset, hopCount, processingTimeMs.
Transcription Infrastructure
src/lib/transcription/types.ts, src/lib/transcription/TokenStreamTranscriber.ts, src/lib/transcription/... (6 other transcription modules)
Introduces TokenStreamConfig, TokenStreamCallbacks, TokenStreamResult interfaces; adds ModelState, BackendType, ModelConfig, ModelProgress, TranscriptionWord, TranscriptionServiceConfig types; expands TranscriptionResult with words array; updates version headers to v1.1.
Utilities & Store
src/utils/time.ts, src/stores/appStore.ts, src/types/index.ts
Adds formatDuration utility function; augments appStore with JSDoc field documentation; expands types/index.ts with comprehensive interface documentation (no signature changes).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • ysdede/keet#143: Implements the BufferWorkerClient.getVadSummary handler and worker logic that corresponds to the GET_VAD_SUMMARY types and VadSummaryResult introduced in this PR.
  • ysdede/keet#106: Directly overlaps with audioEngineSignal and setAudioEngineSignal additions in src/App.tsx.

Poem

🐰 Whiskers twitching with documentation glee,
Signals now flow through reactivity,
Props expand wide, types shine clear,
Version headers corrected, v1.1's here!
With docs and with types, the code's dressed so neat,
This refactor's quite thorough—no half-hearted feat!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'docs: improve JSDoc coverage across keet public APIs' accurately captures the main objective of the changeset: adding and improving JSDoc documentation across public APIs, with standardized version tags as secondary changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/jsdoc-keet

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/utils/time.ts (1)

9-10: ⚠️ Potential issue | 🟡 Minor

s is not floored — floating-point seconds produce a malformed output string.

seconds % 60 retains any fractional part. (30.5).toString() is "30.5", and .padStart(2, '0') only left-pads; it does not truncate. For an input of 90.5 the output is "01:30.5" instead of "01:30". Because seconds is typed as number and audio timestamps from HTMLMediaElement.duration are routinely floats, this will manifest in real usage.

🐛 Proposed fix
-  const s = seconds % 60;
+  const s = Math.floor(seconds % 60);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/time.ts` around lines 9 - 10, The formatted time uses a fractional
seconds value (const s = seconds % 60) which yields strings like "30.5"; fix by
computing an integer totalSeconds = Math.floor(seconds) (or Math.trunc) and
derive h/m/s from that so s is an integer before padding; update uses of seconds
-> totalSeconds in the h/m/s calculations (look for the const s = ... and the
return formatting expression) so formatted output becomes whole seconds.
src/lib/transcription/transcription.worker.ts (1)

1-10: ⚠️ Potential issue | 🟡 Minor

Disambiguate the "Keet v1.1" header from the "v3/v4 mode" labels in the same block

After standardizing the header to Keet v1.1, the adjacent comment block now places two unrelated versioning schemes side-by-side with no label to distinguish them:

 * Keet v1.1 - Transcription Web Worker   ← application/package version
 * ...
 * - v3 token-stream mode …               ← internal pipeline mode label
 * - v4 utterance mode …                  ← internal pipeline mode label

A reader unfamiliar with the project could infer that "v1.1" is simply an earlier generation than the "v3/v4" modes documented below it. A brief qualifier on the mode lines (e.g., "pipeline v3 token-stream mode …") would eliminate the ambiguity.

📝 Suggested wording
 /**
  * Keet v1.1 - Transcription Web Worker
  * 
  * Runs heavy AI inference and text merging in a background thread
  * to prevent UI stuttering on the main thread.
  *
  * Supports both:
- * - v3 token-stream mode (LCSPTFAMerger, fixed-window)
- * - v4 utterance mode (UtteranceBasedMerger, cursor-based windowing)
+ * - pipeline v3 token-stream mode (LCSPTFAMerger, fixed-window)
+ * - pipeline v4 utterance mode (UtteranceBasedMerger, cursor-based windowing)
  */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/transcription.worker.ts` around lines 1 - 10, Update
the top-of-file comment in transcription.worker.ts to disambiguate the
application version from internal pipeline modes: keep "Keet v1.1 -
Transcription Web Worker" as the package/app header and change the two mode
lines to explicitly label them as pipeline modes, e.g. "pipeline v3 token-stream
mode (LCSPTFAMerger, fixed-window)" and "pipeline v4 utterance mode
(UtteranceBasedMerger, cursor-based windowing)"; locate the block containing
"Keet v1.1 - Transcription Web Worker" and the lines "v3 token-stream mode
(LCSPTFAMerger, fixed-window)" and "v4 utterance mode (UtteranceBasedMerger,
cursor-based windowing)" and edit those lines only to add the qualifier.
src/lib/transcription/SentenceBoundaryDetector.ts (1)

423-432: ⚠️ Potential issue | 🟠 Major

Hash collision in generateCacheKey can silently return wrong cached sentence boundaries.

A 32-bit polynomial hash has a non-trivial collision probability for real-world transcription texts (birthday paradox: ~50 % chance of at least one collision after ~65 000 distinct cache keys). When two different texts hash to the same key, performNLP returns the cached result of the first text for the second without any detection. The bug is silent — no error is raised and the caller receives incorrect sentence boundaries.

Fix: either append the text length to the key (cheap partial disambiguation), use a cryptographic hash (e.g. a SubtleCrypto SHA-256 digest — async but collision-resistant), or simply store the original text as the cache key (usually fine given cacheSize: 100 default and typical sentence-level granularity).

🔑 Minimal fix — key by length-prefixed hash
 private generateCacheKey(text: string): string {
     let hash = 0;
-    if (text.length === 0) return hash.toString();
+    if (text.length === 0) return '0:0';
     for (let i = 0; i < text.length; i++) {
         const char = text.charCodeAt(i);
         hash = ((hash << 5) - hash) + char;
         hash = hash & hash;
     }
-    return hash.toString();
+    return `${text.length}:${hash >>> 0}`;  // include length as cheap disambiguator
 }

Or, for a small cache (≤100 entries), use the text itself as the key:

 private generateCacheKey(text: string): string {
-    let hash = 0;
-    if (text.length === 0) return hash.toString();
-    for (let i = 0; i < text.length; i++) {
-        const char = text.charCodeAt(i);
-        hash = ((hash << 5) - hash) + char;
-        hash = hash & hash;
-    }
-    return hash.toString();
+    return text;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/SentenceBoundaryDetector.ts` around lines 423 - 432,
The current generateCacheKey implementation can collide and cause performNLP to
return wrong cached boundaries; replace it so the cache key is unambiguous
(e.g., use the full input text as the key or at minimum prefix the 32-bit hash
with the text length) and update any references to the cache lookup accordingly
(see generateCacheKey and where performNLP accesses the cache/Map). For the
small default cacheSize (100) using the raw text as the key is simplest and
safest—change generateCacheKey to return the input text (or "len:hash" if you
prefer a compact key) so cached entries map uniquely to their original text.
🧹 Nitpick comments (8)
src/lib/transcription/ModelManager.ts (1)

236-237: Import formatting is inconsistent with the identical import on line 100.

Line 100 keeps the import on a single line; the reformatted version places the module string on a new line but attaches the closing ) to it, which is an unusual style. Consider either reverting to one line (matching line 100) or placing the closing ) on its own line.

♻️ Align with line 100 style
-      const { ParakeetModel } = await import(
-        'parakeet.js');
+      const { ParakeetModel } = await import('parakeet.js');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/ModelManager.ts` around lines 236 - 237, The dynamic
import of ParakeetModel in ModelManager.ts is formatted differently from the
identical import at line 100; change the import to match line 100 by putting it
on a single line (e.g., const { ParakeetModel } = await import('parakeet.js');)
so the style is consistent, or alternatively move the closing parenthesis to its
own line if you prefer multiline—update the import expression that references
ParakeetModel to follow the same formatting as the earlier import.
src/utils/time.ts (1)

6-10: Consider guarding against negative and non-finite inputs.

A negative or NaN/Infinity value produces garbage output (e.g., Math.floor(-0.001) = -1). For a public duration-formatting utility, a short guard makes the contract explicit and prevents silent mis-rendering.

🛡️ Proposed guard
 export function formatDuration(seconds: number): string {
+  if (!isFinite(seconds) || seconds < 0) return '00:00';
   const h = Math.floor(seconds / 3600);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/time.ts` around lines 6 - 10, The formatDuration function currently
assumes seconds is a non-negative finite number; add a guard at the start of
formatDuration to validate the input (check typeof seconds === 'number' and
Number.isFinite(seconds') and treat negative values or non-finite/NaN as 0 or
return a clear default string (e.g., "00:00" or "0:00:00" depending on existing
behavior). Update formatDuration to clamp seconds to Math.max(0,
Math.floor(seconds)) after validation so h/m/s calculations never see negative
or non-finite values; keep the rest of the formatting logic unchanged.
src/lib/transcription/WindowBuilder.ts (2)

85-148: Optional: add @param / @returns tags to public methods.

All methods have a prose description, but none carry @param or @returns tags. For a documentation-coverage PR, adding them for the non-trivial public methods would give IDE tooling (hover hints, signature help) their full value.

Example for markSentenceEnd and the cursor helpers:

📝 Example `@param` / `@returns` additions
-    /**
-     * Record the end frame of a fully finalized sentence.
-     */
-    markSentenceEnd(frameIdx: number): void {
+    /**
+     * Record the end frame of a fully finalized sentence.
+     * `@param` frameIdx - Global frame index of the sentence boundary.
+     */
+    markSentenceEnd(frameIdx: number): void {
-    /**
-     * Advance the mature cursor to a finalized sentence boundary.
-     * The mature cursor marks where transcription is considered stable.
-     */
-    advanceMatureCursor(frameIdx: number): void {
+    /**
+     * Advance the mature cursor to a finalized sentence boundary.
+     * The mature cursor marks where transcription is considered stable.
+     * `@param` frameIdx - Global frame index to advance the cursor to; ignored if not strictly greater than the current position.
+     */
+    advanceMatureCursor(frameIdx: number): void {
-    /**
-     * Advance the mature cursor using a time value (seconds).
-     * Converts to frame offset based on sample rate.
-     */
-    advanceMatureCursorByTime(timeSec: number): void {
+    /**
+     * Advance the mature cursor using a time value (seconds).
+     * Converts to frame offset based on sample rate.
+     * `@param` timeSec - Time in seconds; converted to a frame index via `Math.round(timeSec * sampleRate)`.
+     */
+    advanceMatureCursorByTime(timeSec: number): void {
-    /**
-     * Get current mature cursor position in frames.
-     */
-    getMatureCursorFrame(): number {
+    /**
+     * Get current mature cursor position in frames.
+     * `@returns` Global frame index of the mature cursor (0 until first sentence).
+     */
+    getMatureCursorFrame(): number {
-    /**
-     * Get current mature cursor position in seconds.
-     */
-    getMatureCursorTime(): number {
+    /**
+     * Get current mature cursor position in seconds.
+     * `@returns` Cursor position in seconds (0 until first sentence).
+     */
+    getMatureCursorTime(): number {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/WindowBuilder.ts` around lines 85 - 148, Add JSDoc
`@param` and `@returns` tags to the public methods markSentenceEnd,
advanceMatureCursor, advanceMatureCursorByTime, getMatureCursorFrame, and
getMatureCursorTime: document the parameter names and types (e.g., frameIdx:
number, timeSec: number) with `@param` and document return types for the getters
and advanceMatureCursorByTime using `@returns` (e.g., number or void) so IDE
hover/signature help shows explicit param and return info; keep the existing
prose summary and debug notes, only augment each method's comment block with the
appropriate `@param/`@returns annotations.

62-81: Optional: document the constructor parameters.

Since the PR is specifically targeting JSDoc coverage, the constructor is the one public-facing entry point still missing documentation. The three parameters (ringBuffer, vadBuffer, config) have non-obvious semantics (e.g., vadBuffer = null disables VAD entirely).

📝 Proposed constructor JSDoc
+    /**
+     * `@param` ringBuffer - The audio ring buffer supplying frame data.
+     * `@param` vadBuffer  - Optional VAD ring buffer; pass `null` to disable VAD boundary refinement.
+     * `@param` config     - Partial configuration overrides; unset keys fall back to built-in defaults.
+     */
     constructor(
         ringBuffer: IRingBuffer,
         vadBuffer: VADRingBuffer | null = null,
         config: Partial<WindowBuilderConfig> = {}
     ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/WindowBuilder.ts` around lines 62 - 81, Add a JSDoc
block to the WindowBuilder constructor documenting each parameter and the
defaults/semantics: describe ringBuffer (IRingBuffer) as required input audio
buffer, vadBuffer (VADRingBuffer | null) where null disables VAD behavior, and
config (Partial<WindowBuilderConfig>) with its default values and meanings
(sampleRate, minDurationSec, maxDurationSec, minInitialDurationSec,
maxSentences, useVadBoundaries, vadSilenceThreshold, debug). Mention that config
overrides defaults and that useVadBoundaries=true depends on a non-null
vadBuffer, and note any units (seconds) for duration fields to clarify usage;
attach this JSDoc immediately above the constructor declaration.
src/types/index.ts (1)

61-70: TranscriptionResult in src/types/index.ts is unused dead code and should be removed or its purpose clarified.

This file defines TranscriptionResult with fields text, tokens: Token[], isFinal, and timestamp, but no code in the repository imports it from here. The actual runtime code imports TranscriptionResult from either src/lib/transcription/types.ts (chunkText, text, words, totalDuration, isFinal) or src/lib/model/types.ts (text, words?). Since src/types/index.ts appears to serve as a public API facade (evidenced by appStore.ts importing RecordingState, ModelState, and BackendType from there), either remove this unused definition or confirm it represents an intended public API and align it with the actual runtime interface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/index.ts` around lines 61 - 70, Remove or reconcile the unused
TranscriptionResult type: either delete the TranscriptionResult interface (and
any unused Token type) from src/types/index.ts if it's dead, or update it to
match the actual runtime shape used elsewhere (e.g., align fields to
chunkText/text/words/totalDuration/isFinal as in lib/transcription/types.ts and
optional words as in lib/model/types.ts) and then export that canonical version
so other modules import the correct public API; update any re-exports in the
module to ensure RecordingState/ModelState/BackendType exports remain intact and
run a repo-wide search for TranscriptionResult to confirm no stale imports
remain.
src/components/ContextPanel.tsx (1)

14-15: New optional prop onDeviceSelect goes beyond documentation.

The PR description states "documentation-only; no runtime behavior changes intended," but this adds a new callback prop to ContextPanelProps. While it's optional and won't break existing callers, it is a public API surface change — worth noting in the PR description for accuracy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ContextPanel.tsx` around lines 14 - 15, The PR added a new
optional prop onDeviceSelect to ContextPanelProps in ContextPanel.tsx which is a
public API surface change; update the PR description to explicitly mention this
new callback prop (onDeviceSelect) and that it was added to ContextPanelProps so
reviewers/users are aware of the API change (or alternatively mark it
internal/undocumented if it wasn’t intended as a public API change).
src/lib/transcription/SentenceBoundaryDetector.ts (2)

287-301: O(n²) in heuristic path — drop the superfluous indexOf.

The _idx parameter in .map((word, _idx) => …) is deliberately discarded, then words.indexOf(word) does a full O(n) linear scan for every filtered word. The index is already available from a single reduce/flatMap pass over the original array.

♻️ O(n) rewrite
 private detectSentenceEndingsHeuristic(words: DetectorWord[]): SentenceEndingWord[] {
-    return words
-        .filter(word => /[.?!]$/.test(word.text))
-        .map((word, _idx) => {
-            const wordIndex = words.indexOf(word);
-            return {
-                ...word,
-                wordIndex,
-                sentenceMetadata: {
-                    sentenceText: word.text,
-                    detectionMethod: 'heuristic' as const,
-                },
-            };
-        });
+    return words.reduce<SentenceEndingWord[]>((acc, word, wordIndex) => {
+        if (/[.?!]$/.test(word.text)) {
+            acc.push({
+                ...word,
+                wordIndex,
+                sentenceMetadata: {
+                    sentenceText: word.text,
+                    detectionMethod: 'heuristic' as const,
+                },
+            });
+        }
+        return acc;
+    }, []);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/SentenceBoundaryDetector.ts` around lines 287 - 301,
The heuristic path in detectSentenceEndingsHeuristic currently calls
words.indexOf(word) inside a .map, causing O(n²); instead preserve the original
index by iterating with the index from the source array (e.g., use words.reduce
or words.forEach or words.flatMap with the (word, idx) callback) and when you
detect /[.?!]$/.test(word.text) push the SentenceEndingWord with wordIndex set
to idx and sentenceMetadata set as before; update detectSentenceEndingsHeuristic
to return a SentenceEndingWord[] built in O(n) without using indexOf.

65-65: Import WinkMethods from wink-nlp and use it for proper type safety.

Replace private nlp: any | null = null; with private nlp: WinkMethods | null = null; by adding WinkMethods to the import statement: import winkNLP, { type WinkMethods } from 'wink-nlp';

Using any loses all type safety for every this.nlp.readDoc() and this.nlp.sentences() call site. winkNLP() returns a properly typed WinkMethods object.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/SentenceBoundaryDetector.ts` at line 65, Update the
SentenceBoundaryDetector class to use the proper Wink NLP types: change the type
of the private field nlp from any to WinkMethods (i.e., replace "private nlp:
any | null = null" with "private nlp: WinkMethods | null = null") and add the
type import to the top-level import for wink-nlp (import winkNLP, { type
WinkMethods } from 'wink-nlp'); this will give correct typing for calls like
this.nlp.readDoc() and this.nlp.sentences().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Sidebar.tsx`:
- Around line 4-25: The SidebarProps type declares audioLevel and the Sidebar
component never reads or uses props.audioLevel; either remove audioLevel from
SidebarProps or surface it in the UI—e.g., read audioLevel inside the Sidebar
component (where onToggleRecording/RecordButton and the record button area are
implemented) and render a simple level indicator or pass it down to the existing
RecordButton/record area so the prop is actually consumed; update SidebarProps
and any callsites accordingly to keep types consistent (modify
SidebarProps.audioLevel or the RecordButton props depending on where you wire
the value).

In `@src/lib/transcription/ModelManager.ts`:
- Line 2: Update the stale "v2.0" version reference in the internal comment
inside ModelManager (the comment mentioning "parakeet.js/IndexedDB cache") so it
matches the file header "Keet v1.1" or remove the version token entirely; locate
the comment within the ModelManager class (the block that begins "// In v2.0 we
rely on parakeet.js/IndexedDB cache, but we can do a quick check") and either
change "v2.0" to "v1.1" or rewrite it to avoid a specific version mention while
preserving the intent about checking the parakeet.js/IndexedDB cache.

In `@src/lib/transcription/types.ts`:
- Around line 5-8: The BackendType and ModelState type definitions are
duplicated; move the single source-of-truth for these types into a canonical
shared types module (the central types index module), export BackendType and
ModelState from there, remove the duplicate type declarations in transcription
types and model types and the inline literal types inside ModelLoadingOverlay,
and update those modules to import/re-export the canonical types (ensure names
remain BackendType and ModelState so references compile).

---

Outside diff comments:
In `@src/lib/transcription/SentenceBoundaryDetector.ts`:
- Around line 423-432: The current generateCacheKey implementation can collide
and cause performNLP to return wrong cached boundaries; replace it so the cache
key is unambiguous (e.g., use the full input text as the key or at minimum
prefix the 32-bit hash with the text length) and update any references to the
cache lookup accordingly (see generateCacheKey and where performNLP accesses the
cache/Map). For the small default cacheSize (100) using the raw text as the key
is simplest and safest—change generateCacheKey to return the input text (or
"len:hash" if you prefer a compact key) so cached entries map uniquely to their
original text.

In `@src/lib/transcription/transcription.worker.ts`:
- Around line 1-10: Update the top-of-file comment in transcription.worker.ts to
disambiguate the application version from internal pipeline modes: keep "Keet
v1.1 - Transcription Web Worker" as the package/app header and change the two
mode lines to explicitly label them as pipeline modes, e.g. "pipeline v3
token-stream mode (LCSPTFAMerger, fixed-window)" and "pipeline v4 utterance mode
(UtteranceBasedMerger, cursor-based windowing)"; locate the block containing
"Keet v1.1 - Transcription Web Worker" and the lines "v3 token-stream mode
(LCSPTFAMerger, fixed-window)" and "v4 utterance mode (UtteranceBasedMerger,
cursor-based windowing)" and edit those lines only to add the qualifier.

In `@src/utils/time.ts`:
- Around line 9-10: The formatted time uses a fractional seconds value (const s
= seconds % 60) which yields strings like "30.5"; fix by computing an integer
totalSeconds = Math.floor(seconds) (or Math.trunc) and derive h/m/s from that so
s is an integer before padding; update uses of seconds -> totalSeconds in the
h/m/s calculations (look for the const s = ... and the return formatting
expression) so formatted output becomes whole seconds.

---

Nitpick comments:
In `@src/components/ContextPanel.tsx`:
- Around line 14-15: The PR added a new optional prop onDeviceSelect to
ContextPanelProps in ContextPanel.tsx which is a public API surface change;
update the PR description to explicitly mention this new callback prop
(onDeviceSelect) and that it was added to ContextPanelProps so reviewers/users
are aware of the API change (or alternatively mark it internal/undocumented if
it wasn’t intended as a public API change).

In `@src/lib/transcription/ModelManager.ts`:
- Around line 236-237: The dynamic import of ParakeetModel in ModelManager.ts is
formatted differently from the identical import at line 100; change the import
to match line 100 by putting it on a single line (e.g., const { ParakeetModel }
= await import('parakeet.js');) so the style is consistent, or alternatively
move the closing parenthesis to its own line if you prefer multiline—update the
import expression that references ParakeetModel to follow the same formatting as
the earlier import.

In `@src/lib/transcription/SentenceBoundaryDetector.ts`:
- Around line 287-301: The heuristic path in detectSentenceEndingsHeuristic
currently calls words.indexOf(word) inside a .map, causing O(n²); instead
preserve the original index by iterating with the index from the source array
(e.g., use words.reduce or words.forEach or words.flatMap with the (word, idx)
callback) and when you detect /[.?!]$/.test(word.text) push the
SentenceEndingWord with wordIndex set to idx and sentenceMetadata set as before;
update detectSentenceEndingsHeuristic to return a SentenceEndingWord[] built in
O(n) without using indexOf.
- Line 65: Update the SentenceBoundaryDetector class to use the proper Wink NLP
types: change the type of the private field nlp from any to WinkMethods (i.e.,
replace "private nlp: any | null = null" with "private nlp: WinkMethods | null =
null") and add the type import to the top-level import for wink-nlp (import
winkNLP, { type WinkMethods } from 'wink-nlp'); this will give correct typing
for calls like this.nlp.readDoc() and this.nlp.sentences().

In `@src/lib/transcription/WindowBuilder.ts`:
- Around line 85-148: Add JSDoc `@param` and `@returns` tags to the public methods
markSentenceEnd, advanceMatureCursor, advanceMatureCursorByTime,
getMatureCursorFrame, and getMatureCursorTime: document the parameter names and
types (e.g., frameIdx: number, timeSec: number) with `@param` and document return
types for the getters and advanceMatureCursorByTime using `@returns` (e.g., number
or void) so IDE hover/signature help shows explicit param and return info; keep
the existing prose summary and debug notes, only augment each method's comment
block with the appropriate `@param/`@returns annotations.
- Around line 62-81: Add a JSDoc block to the WindowBuilder constructor
documenting each parameter and the defaults/semantics: describe ringBuffer
(IRingBuffer) as required input audio buffer, vadBuffer (VADRingBuffer | null)
where null disables VAD behavior, and config (Partial<WindowBuilderConfig>) with
its default values and meanings (sampleRate, minDurationSec, maxDurationSec,
minInitialDurationSec, maxSentences, useVadBoundaries, vadSilenceThreshold,
debug). Mention that config overrides defaults and that useVadBoundaries=true
depends on a non-null vadBuffer, and note any units (seconds) for duration
fields to clarify usage; attach this JSDoc immediately above the constructor
declaration.

In `@src/types/index.ts`:
- Around line 61-70: Remove or reconcile the unused TranscriptionResult type:
either delete the TranscriptionResult interface (and any unused Token type) from
src/types/index.ts if it's dead, or update it to match the actual runtime shape
used elsewhere (e.g., align fields to chunkText/text/words/totalDuration/isFinal
as in lib/transcription/types.ts and optional words as in lib/model/types.ts)
and then export that canonical version so other modules import the correct
public API; update any re-exports in the module to ensure
RecordingState/ModelState/BackendType exports remain intact and run a repo-wide
search for TranscriptionResult to confirm no stale imports remain.

In `@src/utils/time.ts`:
- Around line 6-10: The formatDuration function currently assumes seconds is a
non-negative finite number; add a guard at the start of formatDuration to
validate the input (check typeof seconds === 'number' and
Number.isFinite(seconds') and treat negative values or non-finite/NaN as 0 or
return a clear default string (e.g., "00:00" or "0:00:00" depending on existing
behavior). Update formatDuration to clamp seconds to Math.max(0,
Math.floor(seconds)) after validation so h/m/s calculations never see negative
or non-finite values; keep the rest of the formatting logic unchanged.

Comment on lines +4 to 25
/** Currently selected sidebar tab ID. */
activeTab: string;
/** Switches the selected sidebar tab. */
onTabChange: (tab: string) => void;
// Recording controls
/** Indicates whether recording is active. */
isRecording: boolean;
/** Starts or stops audio capture. */
onToggleRecording: () => void;
// Model state
/** Whether a model is loaded and ready for inference. */
isModelReady: boolean;
/** Opens model selection/loading flow. */
onLoadModel: () => void;
/** Current model lifecycle state. */
modelState: string;
// Device selection
/** Available microphone devices from `enumerateDevices()`. */
availableDevices: MediaDeviceInfo[];
/** Selected microphone device ID. */
selectedDeviceId: string;
/** Updates the selected microphone device. */
onDeviceSelect: (id: string) => void;
// Audio feedback
/** Current normalized input level (0-1). */
audioLevel: number;
Copy link

@coderabbitai coderabbitai bot Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's search for all audioLevel references in the codebase
rg -n "audioLevel" --type tsx --type ts -B 2 -A 2

Repository: ysdede/keet

Length of output: 82


🏁 Script executed:

# Also let's examine the actual Sidebar.tsx file to verify the structure
wc -l src/components/Sidebar.tsx

Repository: ysdede/keet

Length of output: 85


🏁 Script executed:

# Read the Sidebar component to see the actual usage
cat -n src/components/Sidebar.tsx | head -80

Repository: ysdede/keet

Length of output: 4119


🏁 Script executed:

# Read the rest of the Sidebar component
cat -n src/components/Sidebar.tsx | tail -80

Repository: ysdede/keet

Length of output: 4206


🏁 Script executed:

# Search for audioLevel using simple grep (works regardless of file extension)
grep -n "audioLevel" src/components/Sidebar.tsx

Repository: ysdede/keet

Length of output: 79


🏁 Script executed:

# Also check if audioLevel is referenced in any other files
grep -r "audioLevel" src/ --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js"

Repository: ysdede/keet

Length of output: 942


audioLevel prop is declared and documented but unused in the Sidebar component.

The new JSDoc correctly describes the prop's intent, but props.audioLevel is never read or forwarded to child components anywhere in the component body (lines 52–146). Documenting a dead prop makes the discrepancy more visible and could mislead callers into thinking it's wired into the UI.

Either remove the prop from SidebarProps since Sidebar doesn't use it, or integrate it into the UI (e.g., a level indicator in the record button area).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Sidebar.tsx` around lines 4 - 25, The SidebarProps type
declares audioLevel and the Sidebar component never reads or uses
props.audioLevel; either remove audioLevel from SidebarProps or surface it in
the UI—e.g., read audioLevel inside the Sidebar component (where
onToggleRecording/RecordButton and the record button area are implemented) and
render a simple level indicator or pass it down to the existing
RecordButton/record area so the prop is actually consumed; update SidebarProps
and any callsites accordingly to keep types consistent (modify
SidebarProps.audioLevel or the RecordButton props depending on where you wire
the value).

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue filed #152. Will address Sidebar audioLevel usage or removal in SidebarProps. Follow-up work; does not block merge.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysdede, sounds good! Issue #152 will track the audioLevel integration or removal. Thanks for filing it—happy to help review when you address it in the follow-up PR. 🚀

@@ -1,5 +1,5 @@
/**
* Keet v2.0 - Model Manager
* Keet v1.1 - Model Manager
Copy link

@coderabbitai coderabbitai bot Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stale v2.0 reference at line 67 should be updated alongside the header.

The header now reads Keet v1.1, but line 67 retains:

// In v2.0 we rely on parakeet.js/IndexedDB cache, but we can do a quick check

This internal comment contradicts the new version tag and may mislead future maintainers. Update or remove the version reference in that comment to stay consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/ModelManager.ts` at line 2, Update the stale "v2.0"
version reference in the internal comment inside ModelManager (the comment
mentioning "parakeet.js/IndexedDB cache") so it matches the file header "Keet
v1.1" or remove the version token entirely; locate the comment within the
ModelManager class (the block that begins "// In v2.0 we rely on
parakeet.js/IndexedDB cache, but we can do a quick check") and either change
"v2.0" to "v1.1" or rewrite it to avoid a specific version mention while
preserving the intent about checking the parakeet.js/IndexedDB cache.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue filed #154. Will update stale v2.0 comment to v1.1 or remove version reference in ModelManager. Follow-up work; does not block merge.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysdede, thank you for filing issue #154 to track this! Sounds like a good plan to address it in the follow-up. 👍

Comment on lines +5 to 8
/** Model loading lifecycle state. */
export type ModelState = 'unloaded' | 'loading' | 'ready' | 'error';
/** Inference backend name. */
export type BackendType = 'webgpu' | 'wasm';
Copy link

@coderabbitai coderabbitai bot Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all definitions of ModelState and BackendType across the codebase
echo "=== ModelState definitions ==="
rg -n "export type ModelState\b" --type=ts
echo ""
echo "=== BackendType definitions ==="
rg -n "export type BackendType\b" --type=ts
echo ""
echo "=== Inline literal duplicates of these union types ==="
echo "--- Pattern: 'unloaded' | 'loading' | 'ready' | 'error' ---"
rg -nP "'unloaded'\s*\|\s*'loading'\s*\|\s*'ready'\s*\|\s*'error'" --type=ts
echo ""
echo "--- Pattern: 'webgpu' | 'wasm' ---"
rg -nP "'webgpu'\s*\|\s*'wasm'" --type=ts

Repository: ysdede/keet

Length of output: 1261


BackendType is duplicated across three files; ModelState across two. Consolidate to a single canonical location.

BackendType is defined in src/types/index.ts (line 100), src/lib/model/types.ts (line 14), and src/lib/transcription/types.ts (line 8). ModelState is defined in src/types/index.ts (line 98) and src/lib/transcription/types.ts (line 6). Additionally, ModelLoadingOverlay.tsx duplicates these types as inline literals. Define these types in a single canonical location and re-export where needed to prevent drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/transcription/types.ts` around lines 5 - 8, The BackendType and
ModelState type definitions are duplicated; move the single source-of-truth for
these types into a canonical shared types module (the central types index
module), export BackendType and ModelState from there, remove the duplicate type
declarations in transcription types and model types and the inline literal types
inside ModelLoadingOverlay, and update those modules to import/re-export the
canonical types (ensure names remain BackendType and ModelState so references
compile).

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue filed #148. Will consolidate BackendType and ModelState to single source of truth in central types. Follow-up work; does not block merge.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ysdede Thank you for filing issue #148! 🎯 Deferring the type consolidation to a follow-up PR is perfectly reasonable—this keeps the documentation-focused scope of this PR clean while ensuring the duplication is tracked for resolution.

/)/)

(•.•)
/>🎫 Issue filed!


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@ysdede ysdede merged commit e940a08 into master Feb 18, 2026
2 checks passed
github-actions bot pushed a commit that referenced this pull request Feb 18, 2026
docs: improve JSDoc coverage across keet public APIs e940a08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant