Skip to content

Conversation

@yujonglee
Copy link
Contributor

@yujonglee yujonglee commented Nov 27, 2025

fix: migrate enhanced_md to enhanced_notes table for markdown saving

Summary

This PR completes the migration from storing enhanced markdown in sessions.enhanced_md to using the separate enhanced_notes table. The codebase had already introduced the enhanced_notes table but the persistence layer and various consumers were still referencing the old enhanced_md field.

Key changes:

  • Removed enhanced_md field from session schemas (both DB and Tinybase)
  • Updated localPersister2.ts to iterate over enhanced_notes instead of sessions
  • Enhanced notes are now saved as individual markdown files in session folders: hyprnote/sessions/{session_id}/{enhanced_note_id}.md
  • Search indexing aggregates all enhanced notes per session for searchability
  • Updated UI components and AI task transforms to read from enhanced_notes

Updates since last revision

  • Extracted getEnhancedContentForSession helper into shared utility (utils.ts) to eliminate duplication between indexing.ts and listeners.ts
  • TypeScript type checks pass

Review & Testing Checklist for Human

  • Database migration: This PR removes enhanced_md from the schema but does NOT include a database migration. Verify this is intentional and existing data won't be lost, or add a migration if needed.
  • Search index updates: The search listeners only watch session changes, not enhanced_notes changes. Verify that adding/editing enhanced notes properly updates the search index.
  • File persistence: Test that enhanced notes are correctly saved as markdown files in the session folder structure. Check edge cases like creating notes for new sessions.
  • End-to-end test: Run the app locally (ONBOARDING=0 pnpm -F desktop tauri dev) and verify:
    1. Creating a new session with enhanced notes
    2. Enhanced notes appear in search results
    3. Markdown files are created in the correct location

Notes

  • Unit tests pass (123 tests). E2E tests failed due to environment issues (missing tauri-driver/binary), not code issues.
  • Argos visual regression shows 3 changed screenshots (expected due to seed data and search.tsx changes) - requires approval.

Link to Devin run: https://app.devin.ai/sessions/3fb32e6b691e49588eb56483c4bcfbe0
Requested by: yujonglee (@yujonglee)

- Remove enhanced_md field from packages/db/src/schema.ts sessions table
- Remove enhanced_md from schema-external.ts sessions schema
- Update localPersister2.ts to iterate over enhanced_notes table instead of sessions
- Update main.ts to save enhanced notes as separate md files in session folder
- Update search indexing to get enhanced content from enhanced_notes table
- Update title-transform.ts to read from enhanced_notes
- Update search.tsx to display enhanced_notes content
- Update test files and devtool seed data to remove enhanced_md references

Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Nov 27, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit 0d9db17
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/6928eb70c4294f0008c9cb76
😎 Deploy Preview https://deploy-preview-1956--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 27, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 0d9db17
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/6928eb7077ae210008ca0ee1
😎 Deploy Preview https://deploy-preview-1956--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

📝 Walkthrough

Walkthrough

Removes session-level enhanced_md and migrates enhanced content to per-note enhanced_notes; updates DB schema, seed data, TinyBase schemas, persister, search/indexing, UI, and task code to aggregate enhanced_notes for session content and persist per-note markdown files.

Changes

Cohort / File(s) Summary
DB & schema
packages/db/src/schema.ts, apps/desktop/src/store/tinybase/schema-external.ts, apps/desktop/src/components/devtool/seed/data/schema.ts, apps/desktop/src/components/devtool/seed/data/schema.gen.json
Removed enhanced_md from sessions table and TinyBase/seed schemas; session types and generated schema updated to omit enhanced_md.
Seed data & helpers
apps/desktop/src/components/devtool/seed/data/curated.json, apps/desktop/src/components/devtool/seed/data/loader.ts, apps/desktop/src/components/devtool/seed/shared/session.ts
Deleted enhanced_md from curated seed entries and removed serialization/creation of enhanced_md in seed loader and session helper.
Persistence / file writer
apps/desktop/src/store/tinybase/main.ts, apps/desktop/src/store/tinybase/localPersister2.ts
Persister now iterates enhanced_notes, accepts EnhancedNote in callback, validates tiptap content, converts to markdown (json2md), ensures per-session directories, writes hyprnote/sessions/{session_id}/{note_id}.md, parallelizes writes and adds error handling.
Search & indexing
apps/desktop/src/contexts/search/engine/utils.ts, apps/desktop/src/contexts/search/engine/content.ts, apps/desktop/src/contexts/search/engine/indexing.ts, apps/desktop/src/contexts/search/engine/listeners.ts
Added getEnhancedContentForSession(store, sessionId) to aggregate enhanced_notes; createSessionSearchableContent signature now accepts optional enhancedContent; indexing/listeners invoke helper and removed enhanced_md selection.
UI & tasks
apps/desktop/src/components/chat/message/tool/search.tsx, apps/desktop/src/store/zustand/ai-task/task-configs/title-transform.ts
UI RenderSession and title-transform logic now fetch/aggregate enhanced_notes for a session and prefer aggregated enhanced-note content over session-level enhanced_md.
Tests / fixtures
apps/desktop/src/utils/timeline.test.ts
Removed enhanced_md from test fixtures to match updated session shape.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant UI as UI / RenderSession
participant Store as TinyBase Store
participant Utils as Search Utils
participant Indexer as Search Indexer
participant Persister as Local Persister
participant FS as Filesystem

UI->>Store: read session row
UI->>Store: query enhanced_notes where session_id
Store-->>UI: session + enhanced_notes list
UI->>UI: displayContent = aggregated enhanced_notes || session.raw_md
UI->>Indexer: emit/update session index event (with sessionId)
Indexer->>Utils: getEnhancedContentForSession(store, sessionId)
Utils->>Store: read enhanced_notes for sessionId
Store-->>Utils: aggregated enhanced content
Utils-->>Indexer: enhancedContent
Indexer-->>Indexer: build searchable document (uses enhancedContent)
Note over Persister,Store: Periodic persistence of enhanced_notes
Persister->>Store: read tables.enhanced_notes
Store-->>Persister: enhanced_note rows
Persister->>FS: write hyprnote/sessions/{session_id}/{note_id}.md (json2md)
FS-->>Persister: write result / errors

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas requiring extra attention:

  • Persistence changes in apps/desktop/src/store/tinybase/main.ts and localPersister2.ts (pathing, directory creation, validation, json2md, parallel writes, error handling).
  • Search/indexing/listener updates (indexing.ts, listeners.ts, content.ts, utils.ts) to ensure aggregation correctness and indexing triggers.
  • Type/schema propagation (schema-external.ts, DB schema, generated seed schema) to confirm consistency and migration impact.
  • Any remaining codepaths that referenced session.enhanced_md (ensure all were updated).

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately and concisely summarizes the main change: migrating enhanced_md storage from the sessions table to the enhanced_notes table for markdown persistence.
Description check ✅ Passed The pull request description is detailed and directly related to the changeset, clearly explaining the migration from sessions.enhanced_md to enhanced_notes table, including specific implementation details and testing considerations.
✨ 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 devin/1764287223-fix-enhanced-notes-md-saving

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

@argos-ci
Copy link

argos-ci bot commented Nov 27, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
web (Inspect) ⚠️ Changes detected (Review) 3 changed Nov 28, 2025, 12:25 AM

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: 1

Caution

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

⚠️ Outside diff range comments (1)
apps/desktop/src/contexts/search/engine/listeners.ts (1)

13-48: Missing listener: enhanced_notes changes don't trigger session re-indexing.

When an enhanced note is added, updated, or deleted, the associated session's search index content will become stale. Consider adding a listener for the enhanced_notes table that triggers re-indexing of the affected session:

export function createEnhancedNoteListener(
  index: Index,
  sessionListener: ReturnType<typeof createSessionListener>,
): RowListener<Schemas, "enhanced_notes", null, PersistedStore> {
  return (store, _, rowId) => {
    const sessionId = store.getCell("enhanced_notes", rowId, "session_id");
    if (typeof sessionId === "string") {
      // Trigger session re-indexing
      sessionListener(store, "sessions", sessionId, () => {});
    }
  };
}
♻️ Duplicate comments (1)
apps/desktop/src/contexts/search/engine/listeners.ts (1)

51-66: Duplicate code already flagged in indexing.ts.

Same helper duplicated here. See the refactor suggestion in indexing.ts to extract this to a shared utility.

🧹 Nitpick comments (4)
apps/desktop/src/store/zustand/ai-task/task-configs/title-transform.ts (1)

17-27: Aggregation logic from enhanced_notes looks correct; consider perf/semantics checks

The iteration and filtering are straightforward and correctly aggregate all non‑empty content cells for the given sessionId and join them with "\n\n". Two small things to keep in mind:

  • This does a full scan of the enhanced_notes table on every call. If the table can grow large and the store defines an index or helper for session_id‑scoped access, it may be worth switching to that to avoid O(N) scans per transform.
  • readEnhancedMarkdown now always returns a string (possibly ""). If callers previously distinguished between “no enhanced content” (e.g., undefined/null) and “empty string”, it’s worth double‑checking that this behavior change is acceptable for all consumers of TaskArgsMapTransformed["title"].enhancedMd.
apps/desktop/src/contexts/search/engine/indexing.ts (1)

38-53: Performance concern: O(n) scan for each session lookup.

getEnhancedContentForSession iterates through all enhanced_notes rows to find notes matching the session. During indexSessions, this results in O(n×m) complexity where n = sessions and m = enhanced_notes.

Consider using an index lookup if TinyBase supports it (similar to how INDEXES.enhancedNotesBySession is used in the UI component), or pre-build a map of session_id → contents before the indexing loop.

 export function indexSessions(db: Index, store: PersistedStore): void {
+  // Pre-build enhanced content map for O(1) lookups
+  const enhancedContentMap = buildEnhancedContentMap(store);
+
   const fields = [
     "user_id",
     "created_at",
     ...
   ];

   store.forEachRow("sessions", (rowId: string, _forEachCell) => {
     const row = collectCells(store, "sessions", rowId, fields);
     const title = toTrimmedString(row.title) || "Untitled";

-    const enhancedContent = getEnhancedContentForSession(store, rowId);
+    const enhancedContent = enhancedContentMap.get(rowId) ?? "";

     void insert(db, {
       ...
     });
   });
 }
+
+function buildEnhancedContentMap(store: PersistedStore): Map<string, string> {
+  const map = new Map<string, string>();
+  store.forEachRow("enhanced_notes", (rowId: string, _forEachCell) => {
+    const sessionId = store.getCell("enhanced_notes", rowId, "session_id");
+    const content = store.getCell("enhanced_notes", rowId, "content");
+    if (typeof sessionId === "string" && typeof content === "string" && content) {
+      const existing = map.get(sessionId) ?? "";
+      map.set(sessionId, existing ? `${existing} ${content}` : content);
+    }
+  });
+  return map;
+}
apps/desktop/src/store/tinybase/localPersister2.ts (1)

4-25: EnhancedNote-based persister wiring looks correct; avoid mutating Tinybase rows / ts-ignore

Switching handlePersist to (enhancedNote: EnhancedNote & { id: string }) => Promise<void> and iterating tables.enhanced_notes with Promise.all is consistent with the new schema and async file writes.

You can drop the // @ts-ignore and avoid mutating the Tinybase snapshot row by cloning it instead:

-      const promises: Promise<void>[] = [];
-      Object.entries(tables?.enhanced_notes ?? {}).forEach(([id, row]) => {
-        // @ts-ignore
-        row.id = id;
-        promises.push(handlePersist(row as EnhancedNote & { id: string }));
-      });
-      await Promise.all(promises);
+      const promises: Promise<void>[] = [];
+      Object.entries(tables?.enhanced_notes ?? {}).forEach(([id, row]) => {
+        const enhancedNote = {
+          ...(row as EnhancedNote),
+          id,
+        } as EnhancedNote & { id: string };
+
+        promises.push(handlePersist(enhancedNote));
+      });
+      await Promise.all(promises);

This keeps Tinybase content snapshots immutable and removes the need for the ts-ignore.

apps/desktop/src/store/tinybase/main.ts (1)

179-215: Consider reducing repeated parsing/writes and handling permanently invalid content more gracefully

Because createLocalPersister2’s save is scheduled via setInterval(listener, 1000), the handler will:

  • Re-parse enhancedNote.content JSON for every row on every tick.
  • On any permanently invalid content (parse error), log "Failed to save enhanced note markdown" every second for that row.
  • Re-write markdown files for unchanged notes on every tick.

Not blockers, but you may want to:

  • Use the _changes argument from the persister callback to only process enhanced notes that actually changed in the last transaction.
  • Optionally add a more specific log/flag when isValidTiptapContent(parsed) fails, so you can distinguish “bad tiptap content” from FS errors.
  • (If needed later) short-circuit permanently broken rows after the first failure to avoid noisy repeated logs.

These would make the persister cheaper and easier to debug without changing its external behavior.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 02d5673 and ac8b400.

📒 Files selected for processing (14)
  • apps/desktop/src/components/chat/message/tool/search.tsx (2 hunks)
  • apps/desktop/src/components/devtool/seed/data/curated.json (0 hunks)
  • apps/desktop/src/components/devtool/seed/data/loader.ts (0 hunks)
  • apps/desktop/src/components/devtool/seed/data/schema.ts (0 hunks)
  • apps/desktop/src/components/devtool/seed/shared/session.ts (0 hunks)
  • apps/desktop/src/contexts/search/engine/content.ts (1 hunks)
  • apps/desktop/src/contexts/search/engine/indexing.ts (1 hunks)
  • apps/desktop/src/contexts/search/engine/listeners.ts (2 hunks)
  • apps/desktop/src/store/tinybase/localPersister2.ts (2 hunks)
  • apps/desktop/src/store/tinybase/main.ts (2 hunks)
  • apps/desktop/src/store/tinybase/schema-external.ts (1 hunks)
  • apps/desktop/src/store/zustand/ai-task/task-configs/title-transform.ts (1 hunks)
  • apps/desktop/src/utils/timeline.test.ts (0 hunks)
  • packages/db/src/schema.ts (0 hunks)
💤 Files with no reviewable changes (6)
  • apps/desktop/src/components/devtool/seed/data/loader.ts
  • apps/desktop/src/utils/timeline.test.ts
  • apps/desktop/src/components/devtool/seed/shared/session.ts
  • packages/db/src/schema.ts
  • apps/desktop/src/components/devtool/seed/data/schema.ts
  • apps/desktop/src/components/devtool/seed/data/curated.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/desktop/src/store/zustand/ai-task/task-configs/title-transform.ts
  • apps/desktop/src/contexts/search/engine/indexing.ts
  • apps/desktop/src/contexts/search/engine/content.ts
  • apps/desktop/src/contexts/search/engine/listeners.ts
  • apps/desktop/src/store/tinybase/localPersister2.ts
  • apps/desktop/src/store/tinybase/schema-external.ts
  • apps/desktop/src/store/tinybase/main.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/desktop/src/store/zustand/ai-task/task-configs/title-transform.ts
  • apps/desktop/src/contexts/search/engine/indexing.ts
  • apps/desktop/src/contexts/search/engine/content.ts
  • apps/desktop/src/contexts/search/engine/listeners.ts
  • apps/desktop/src/store/tinybase/localPersister2.ts
  • apps/desktop/src/components/chat/message/tool/search.tsx
  • apps/desktop/src/store/tinybase/schema-external.ts
  • apps/desktop/src/store/tinybase/main.ts
🧬 Code graph analysis (4)
apps/desktop/src/contexts/search/engine/indexing.ts (1)
apps/desktop/src/contexts/search/engine/content.ts (1)
  • createSessionSearchableContent (3-12)
apps/desktop/src/contexts/search/engine/listeners.ts (1)
apps/desktop/src/contexts/search/engine/content.ts (1)
  • createSessionSearchableContent (3-12)
apps/desktop/src/store/tinybase/localPersister2.ts (1)
apps/desktop/src/store/tinybase/schema-external.ts (1)
  • EnhancedNote (170-170)
apps/desktop/src/store/tinybase/main.ts (1)
packages/tiptap/src/shared/utils.ts (2)
  • isValidTiptapContent (13-20)
  • json2md (78-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (linux, depot-ubuntu-22.04-8)
  • GitHub Check: ci (linux, depot-ubuntu-24.04-8)
  • GitHub Check: ci (macos, depot-macos-14)
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt
🔇 Additional comments (7)
apps/desktop/src/store/tinybase/schema-external.ts (1)

62-68: I'll help you verify the review comment. Let me search for any remaining references to enhanced_md and validate the migration completeness.
<function_calls>

shell

#!/bin/bash

Find remaining references to enhanced_md in TypeScript/TSX files

echo "=== Searching for enhanced_md references ==="
rg -nP --type=ts --type=tsx 'enhanced_md' | head -50


</function_calls>

apps/desktop/src/components/chat/message/tool/search.tsx (2)

101-112: Inconsistent content aggregation: only first enhanced note used here vs. all notes joined elsewhere.

In indexing.ts and listeners.ts, getEnhancedContentForSession joins all enhanced notes for a session. Here, only the first note's content is displayed. If a session has multiple enhanced notes, the search preview will show partial content compared to what's indexed.

Consider whether this is intentional (show first note as preview) or if you want to aggregate all notes for display consistency.


127-137: LGTM!

The fallback logic correctly handles the case where enhanced content is unavailable, preferring enhanced note content when available and falling back to raw_md.

apps/desktop/src/contexts/search/engine/content.ts (1)

3-12: LGTM!

Clean refactor to accept enhancedContent as an optional parameter. This properly decouples content creation from the data source, making the function more flexible.

apps/desktop/src/contexts/search/engine/indexing.ts (1)

26-32: LGTM!

The integration of enhanced content into session indexing is correct. The enhanced content is properly fetched and passed to the content creation function.

apps/desktop/src/contexts/search/engine/listeners.ts (1)

33-39: LGTM!

The session listener correctly fetches enhanced content before updating the search index.

apps/desktop/src/store/tinybase/main.ts (1)

25-25: Enhanced-note → markdown export path is well-guarded and matches the new model

The new createLocalPersister2 handler correctly:

  • Requires both content and session_id before doing work.
  • Parses JSON, validates it as Tiptap content via isValidTiptapContent, and only then calls json2md.
  • Ensures the per-session directory under BaseDirectory.Data/hyprnote/sessions/{session_id} exists before writing.
  • Wraps the whole pipeline in a try/catch and logs failures with the enhanced note id, which is important given this runs on an interval and during window close/blur.

This aligns with the migration away from session.enhanced_md to enhanced_notes and looks correct end-to-end.

Also applies to: 179-215

devin-ai-integration bot and others added 2 commits November 28, 2025 00:02
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
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: 1

🧹 Nitpick comments (1)
apps/desktop/src/contexts/search/engine/utils.ts (1)

116-131: Consider performance implications of full table scan.

The function performs a full table scan of the enhanced_notes table for each session during indexing. When indexing multiple sessions, this results in O(n×m) complexity where n is the number of sessions and m is the number of enhanced notes. If you anticipate a large number of enhanced notes, consider building an index or restructuring the iteration to group notes by session upfront.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d90039e and 0d9db17.

📒 Files selected for processing (3)
  • apps/desktop/src/contexts/search/engine/indexing.ts (2 hunks)
  • apps/desktop/src/contexts/search/engine/listeners.ts (2 hunks)
  • apps/desktop/src/contexts/search/engine/utils.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/desktop/src/contexts/search/engine/utils.ts
  • apps/desktop/src/contexts/search/engine/indexing.ts
  • apps/desktop/src/contexts/search/engine/listeners.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/desktop/src/contexts/search/engine/utils.ts
  • apps/desktop/src/contexts/search/engine/indexing.ts
  • apps/desktop/src/contexts/search/engine/listeners.ts
🧬 Code graph analysis (2)
apps/desktop/src/contexts/search/engine/indexing.ts (2)
apps/desktop/src/contexts/search/engine/utils.ts (1)
  • getEnhancedContentForSession (116-131)
apps/desktop/src/contexts/search/engine/content.ts (1)
  • createSessionSearchableContent (3-12)
apps/desktop/src/contexts/search/engine/listeners.ts (2)
apps/desktop/src/contexts/search/engine/utils.ts (1)
  • getEnhancedContentForSession (116-131)
apps/desktop/src/contexts/search/engine/content.ts (1)
  • createSessionSearchableContent (3-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (linux, depot-ubuntu-22.04-8)
  • GitHub Check: ci (linux, depot-ubuntu-24.04-8)
  • GitHub Check: ci (macos, depot-macos-14)
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: Pages changed - hyprnote
🔇 Additional comments (5)
apps/desktop/src/contexts/search/engine/listeners.ts (2)

4-18: LGTM! Clean import organization.

The imports are properly updated to include the new helper function getEnhancedContentForSession and maintain good organization by grouping related imports together.


30-46: No data preservation issue found – enhanced_md is a legacy, unused column in the database schema.

The verification reveals that enhanced_md was defined in the initial Drizzle migration but is explicitly omitted from the session schema (.omit({ id: true, enhanced_md: true })) and never actually read from or written to in the codebase. The application already uses the enhanced_notes table exclusively for retrieving enhanced content via getEnhancedContentForSession(). Since enhanced_md was not actively used by the current application, there is no existing data to migrate or preserve.

apps/desktop/src/contexts/search/engine/utils.ts (1)

1-2: LGTM! Proper type import.

The import of PersistedStore type is correctly added to support the new helper function.

apps/desktop/src/contexts/search/engine/indexing.ts (2)

9-14: LGTM! Proper import of the shared helper.

The import correctly includes getEnhancedContentForSession from the shared utils module, resolving the previous code duplication concern that was flagged in earlier reviews.


17-37: Migration implementation looks correct.

The removal of enhanced_md from the fields list and the integration of getEnhancedContentForSession properly implements the migration to the enhanced_notes table. The pattern is consistent with the listener implementation.

Comment on lines +116 to +131
export function getEnhancedContentForSession(
store: PersistedStore,
sessionId: string,
): string {
const contents: string[] = [];
store.forEachRow("enhanced_notes", (rowId: string, _forEachCell) => {
const noteSessionId = store.getCell("enhanced_notes", rowId, "session_id");
if (noteSessionId === sessionId) {
const content = store.getCell("enhanced_notes", rowId, "content");
if (typeof content === "string" && content) {
contents.push(content);
}
}
});
return contents.join(" ");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add type checking for session_id comparison.

The function correctly validates that content is a non-empty string before adding it to the array. However, the session_id comparison on line 122 doesn't include a type check. If noteSessionId is undefined or a different type, the comparison may not work as expected.

Consider adding a type check:

 export function getEnhancedContentForSession(
   store: PersistedStore,
   sessionId: string,
 ): string {
   const contents: string[] = [];
   store.forEachRow("enhanced_notes", (rowId: string, _forEachCell) => {
     const noteSessionId = store.getCell("enhanced_notes", rowId, "session_id");
-    if (noteSessionId === sessionId) {
+    if (typeof noteSessionId === "string" && noteSessionId === sessionId) {
       const content = store.getCell("enhanced_notes", rowId, "content");
       if (typeof content === "string" && content) {
         contents.push(content);
       }
     }
   });
   return contents.join(" ");
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function getEnhancedContentForSession(
store: PersistedStore,
sessionId: string,
): string {
const contents: string[] = [];
store.forEachRow("enhanced_notes", (rowId: string, _forEachCell) => {
const noteSessionId = store.getCell("enhanced_notes", rowId, "session_id");
if (noteSessionId === sessionId) {
const content = store.getCell("enhanced_notes", rowId, "content");
if (typeof content === "string" && content) {
contents.push(content);
}
}
});
return contents.join(" ");
}
export function getEnhancedContentForSession(
store: PersistedStore,
sessionId: string,
): string {
const contents: string[] = [];
store.forEachRow("enhanced_notes", (rowId: string, _forEachCell) => {
const noteSessionId = store.getCell("enhanced_notes", rowId, "session_id");
if (typeof noteSessionId === "string" && noteSessionId === sessionId) {
const content = store.getCell("enhanced_notes", rowId, "content");
if (typeof content === "string" && content) {
contents.push(content);
}
}
});
return contents.join(" ");
}
🤖 Prompt for AI Agents
In apps/desktop/src/contexts/search/engine/utils.ts around lines 116 to 131, the
code compares noteSessionId to sessionId without confirming noteSessionId is a
string; add a type check before the comparison to avoid unexpected matches or
runtime issues (e.g., if noteSessionId is undefined or a number). Update the
loop so you retrieve noteSessionId, verify typeof noteSessionId === "string" (or
otherwise coerce safely) and only then compare with sessionId using strict
equality; keep the existing content type check and push logic unchanged.

@yujonglee yujonglee force-pushed the main branch 4 times, most recently from cd99174 to cb527d5 Compare November 29, 2025 13:01
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.

2 participants