Skip to content

Template context final (with tags) #1460

Merged
yujonglee merged 7 commits intomainfrom
template-context-final
Sep 10, 2025
Merged

Template context final (with tags) #1460
yujonglee merged 7 commits intomainfrom
template-context-final

Conversation

@duckduckhero
Copy link
Contributor

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 10, 2025

📝 Walkthrough

Walkthrough

Adds template-level context configuration (stored as JSON in a new context_option column) and wires it through UI, persistence, and enhancement flow. Introduces a utility to assemble prior-session context from tags, injects contextText into enhancement payloads, and updates the Jinja user template to render it. Includes DB migration and type updates.

Changes

Cohort / File(s) Summary
Editor enhance flow
apps/desktop/src/components/editor-area/index.tsx, crates/template/assets/enhance.user.jinja
Enhancement pipeline reads template.context_option, derives contextText via prepareContextText when type "tags" is selected, and passes it into the request; Jinja adds a conditional block to render contextText.
Context utility
apps/desktop/src/components/editor-area/utils/summary-prepare.ts
New helper prepareContextText(tags, currentSessionId, userId) queries tags/sessions, extracts recent enhanced memo content, cleans HTML, and returns concatenated context text.
Template editor UI
apps/desktop/src/components/settings/views/template.tsx
Adds UI/state to configure context_option (none/tags). Loads tags, supports selection via command palette, serializes to JSON, and persists via onTemplateUpdate.
Template creation and defaults
apps/desktop/src/components/settings/views/templates.tsx, apps/desktop/src/utils/default-templates.ts
Adds context_option: null to new Template instances and all DEFAULT_TEMPLATES entries.
DB schema and ops
crates/db-user/src/lib.rs, crates/db-user/src/templates_migration_1.sql, crates/db-user/src/templates_ops.rs, crates/db-user/src/templates_types.rs
Adds context_option TEXT column via migration; updates Template struct to include Option; extends upsert SQL/bindings and row mapping; registers new migration.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant TemplateEditor as Template Editor (UI)
  participant DB as dbCommands (@hypr/plugin-db)
  participant Editor as Editor Area
  participant Util as prepareContextText
  participant Engine as Enhance Engine (Jinja)

  User->>TemplateEditor: Edit custom template
  TemplateEditor->>DB: listAllTags()
  DB-->>TemplateEditor: Tags
  TemplateEditor->>TemplateEditor: Set context_option (type, selections)
  TemplateEditor->>DB: Save template (context_option)

  User->>Editor: Run Enhance with template
  Editor->>Editor: Parse template.context_option
  alt context type = "tags" with selections
    Editor->>Util: prepareContextText(tags, sessionId, userId)
    Util->>DB: listAllTags()
    DB-->>Util: Tags (with IDs)
    loop per tag
      Util->>DB: listSessions(tagFilter, tag_id, user_id)
      DB-->>Util: Sessions
    end
    Util-->>Editor: contextText (string)
  else no context
    Editor-->>Editor: contextText = ""
  end
  Editor->>Engine: Render enhance.user.jinja with { transcript, participants, contextText? }
  Engine-->>Editor: Rendered prompt
  Editor-->>User: Enhancement result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks (1 warning, 2 inconclusive)

❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The current title “Template context final (with tags)” loosely references the feature area and mentions tags but adds the word “final,” which is noise, and fails to clearly summarize the primary change in a concise, specific sentence. It is only partially related to the changeset and does not convey the core implementation of adding tag-based context support to templates. According to the title guidelines, the PR title should be a clear, single-sentence summary of the main change without extraneous terms. Please revise the title to succinctly describe the main change, for example “Add tag-based context_option support to template editor,” which directly communicates the feature implemented without unnecessary words.
Description Check ❓ Inconclusive The pull request description is currently missing or empty, providing no details about the changes introduced in the PR and making it impossible to determine whether it relates to the changeset. Even a brief summary of the key updates—such as the addition of context_option, UI enhancements for tag selection, and underlying database migrations—would satisfy the lenient requirement that the description be at least topically relevant. Please add a concise description summarizing the main updates in this PR, such as the introduction of context_option support for templates, the new tag-based selection UI, and corresponding database schema changes.

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch template-context-final

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
Contributor

@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 (1)
apps/desktop/src/components/editor-area/index.tsx (1)

500-508: Condition always true; always passes contextText (even when empty)

Using OR makes the guard tautological. Pass contextText only when non-empty.

-          ...((contextText !== "" || contextText !== undefined || contextText !== null) ? { contextText } : {}),
+          ...(contextText && contextText.trim().length > 0 ? { contextText } : {}),
🧹 Nitpick comments (11)
crates/template/assets/enhance.user.jinja (1)

15-20: Minor copy tweak: singular “context”

Tighten phrasing for clarity.

 {% if contextText %}
-Below are additional context from previous notes, refer to them if necessary:
+Below is additional context from previous notes; refer to it if necessary:
 <previous_context>
 {{ contextText }}
 </previous_context>
 {% endif %}
apps/desktop/src/components/editor-area/index.tsx (1)

451-469: Comment doesn’t match behavior

The comment says “Silent catch” but we still log. Adjust the comment for accuracy or remove it.

-          // Silent catch for malformed JSON
+          // Ignore parse errors; log and continue
apps/desktop/src/components/editor-area/utils/summary-prepare.ts (3)

30-31: Type the sessions map to prevent implicit-any bleed-through.

Adds clarity and catches shape drift at compile time.

 import { commands as dbCommands } from "@hypr/plugin-db";
 
+// Derive the session item type from the db command
+type Session = Awaited<ReturnType<typeof dbCommands.listSessions>>[number];
+
 ...
-  const allSessions = new Map();
+  const allSessions = new Map<string, Session>();

32-53: Fetch per-tag sessions in parallel to reduce latency.

Current loop awaits sequentially; parallelizing keeps behavior (including per-tag logging) and improves responsiveness with many tags.

-  for (const tagId of tagIds) {
-    try {
-      const sessions = await dbCommands.listSessions({
-        type: "tagFilter",
-        tag_ids: [tagId],
-        user_id: userId,
-        limit: 10,
-      });
-
-      sessions.forEach(session => {
-        if (
-          session.id !== currentSessionId
-          && session.enhanced_memo_html
-          && session.enhanced_memo_html.trim().length > 0
-        ) {
-          allSessions.set(session.id, session);
-        }
-      });
-    } catch (error) {
-      console.warn(`Failed to fetch sessions for tag ${tagId}:`, error);
-    }
-  }
+  await Promise.all(
+    tagIds.map(async (tagId) => {
+      try {
+        const sessions = await dbCommands.listSessions({
+          type: "tagFilter",
+          tag_ids: [tagId],
+          user_id: userId,
+          limit: 10,
+        });
+        sessions.forEach((session) => {
+          if (
+            session.id !== currentSessionId &&
+            session.enhanced_memo_html &&
+            session.enhanced_memo_html.trim().length > 0
+          ) {
+            allSessions.set(session.id, session);
+          }
+        });
+      } catch (error) {
+        console.warn(`Failed to fetch sessions for tag ${tagId}:`, error);
+      }
+    }),
+  );

63-71: Preserve line breaks and decode common entities for better readability.

Plain tag stripping collapses paragraphs/lists; add minimal newline handling and decode quotes.

   const contextParts = sortedSessions.map((session, index) => {
     const cleanContent = session.enhanced_memo_html!
-      .replace(/<[^>]*>/g, "")
+      .replace(/<(br|p|\/p|li|\/li)[^>]*>/gi, "\n")
+      .replace(/<[^>]*>/g, "")
       .replace(/&nbsp;/g, " ")
       .replace(/&amp;/g, "&")
       .replace(/&lt;/g, "<")
       .replace(/&gt;/g, ">")
+      .replace(/&quot;/g, '"')
+      .replace(/&#39;|&apos;/g, "'")
+      .replace(/\n{3,}/g, "\n\n")
       .trim();
apps/desktop/src/components/settings/views/template.tsx (6)

88-91: Remove debug logs.

Avoids noisy console output in production.

-  console.log("now in template editor");
-  console.log("template: ", template);
-  console.log("isBuiltinTemplate: ", isBuiltinTemplate);

118-125: Harden parsing to ensure selections is string[].

Prevents unexpected shapes from leaking into state.

   const parseContextOption = (contextOption: string | null) => {
     if (!contextOption) {
       return { type: "", selections: [] };
     }
     try {
       const parsed = JSON.parse(contextOption);
-      return {
-        type: parsed.type || "",
-        selections: parsed.selections || [],
-      };
+      const selections =
+        Array.isArray(parsed.selections)
+          ? parsed.selections.filter((s: unknown): s is string => typeof s === "string")
+          : [];
+      return { type: parsed.type || "", selections };
     } catch {
       return { type: "", selections: [] };
     }
   };

337-353: Add aria-label to the remove-tag icon button.

Improves accessibility for screen readers.

                           <Button
                             type="button"
                             variant="ghost"
                             size="sm"
                             className="h-3 w-3 p-0 hover:bg-transparent ml-0.5"
+                            aria-label={t`Remove tag`}
                             onClick={() => {
                               const newSelectedTags = selectedTags.filter((t) => t !== tag);
                               setSelectedTags(newSelectedTags);

365-372: Add aria-label to the add-tag icon button.

Improves accessibility for screen readers.

                         <Button
                           type="button"
                           variant="outline"
                           size="icon"
                           className="h-[38px] w-[38px]"
+                          aria-label={t`Add tag`}
                         >
                           <Plus className="h-4 w-4" />

376-381: Localize static UI strings in the tag picker.

Aligns with existing i18n usage in this component.

-                          <CommandInput placeholder="Search tags..." className="h-9" />
+                          <CommandInput placeholder={t`Search tags...`} className="h-9" />
                           <CommandEmpty>
-                            {allTags.length === 0
-                              ? "No tags available. Create tags by tagging your notes first."
-                              : "No tag found."}
+                            {allTags.length === 0
+                              ? <Trans>No tags available. Create tags by tagging your notes first.</Trans>
+                              : <Trans>No tag found.</Trans>}
                           </CommandEmpty>

78-83: Trim “what” comments; keep only “why” where helpful.

Reduces noise per project guidelines.

Also applies to: 108-111, 142-146, 274-281

📜 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 b2cfdbb and 77e3ea7.

⛔ Files ignored due to path filters (3)
  • apps/desktop/src/locales/en/messages.po is excluded by !**/*.po
  • apps/desktop/src/locales/ko/messages.po is excluded by !**/*.po
  • plugins/db/js/bindings.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (10)
  • apps/desktop/src/components/editor-area/index.tsx (3 hunks)
  • apps/desktop/src/components/editor-area/utils/summary-prepare.ts (1 hunks)
  • apps/desktop/src/components/settings/views/template.tsx (4 hunks)
  • apps/desktop/src/components/settings/views/templates.tsx (1 hunks)
  • apps/desktop/src/utils/default-templates.ts (13 hunks)
  • crates/db-user/src/lib.rs (2 hunks)
  • crates/db-user/src/templates_migration_1.sql (1 hunks)
  • crates/db-user/src/templates_ops.rs (3 hunks)
  • crates/db-user/src/templates_types.rs (2 hunks)
  • crates/template/assets/enhance.user.jinja (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit configuration file

**/*.{js,ts,tsx,rs}: 1. Do not add any error handling. Keep the existing one.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".

Files:

  • apps/desktop/src/components/editor-area/utils/summary-prepare.ts
  • apps/desktop/src/utils/default-templates.ts
  • crates/db-user/src/templates_types.rs
  • crates/db-user/src/lib.rs
  • apps/desktop/src/components/settings/views/templates.tsx
  • apps/desktop/src/components/editor-area/index.tsx
  • crates/db-user/src/templates_ops.rs
  • apps/desktop/src/components/settings/views/template.tsx
🧬 Code graph analysis (4)
apps/desktop/src/components/editor-area/utils/summary-prepare.ts (1)
apps/admin/src/lib/db/schema/auth.ts (1)
  • session (13-23)
crates/db-user/src/templates_types.rs (1)
crates/db-user/src/sessions_ops.rs (1)
  • row (281-281)
apps/desktop/src/components/editor-area/index.tsx (1)
apps/desktop/src/components/editor-area/utils/summary-prepare.ts (1)
  • prepareContextText (10-76)
apps/desktop/src/components/settings/views/template.tsx (4)
packages/ui/src/components/ui/select.tsx (1)
  • Select (174-174)
packages/ui/src/components/ui/badge.tsx (1)
  • Badge (60-60)
packages/ui/src/components/ui/popover.tsx (1)
  • Popover (85-85)
packages/ui/src/components/ui/command.tsx (5)
  • Command (200-200)
  • CommandInput (204-204)
  • CommandEmpty (202-202)
  • CommandGroup (203-203)
  • CommandItem (205-205)
⏰ 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). (2)
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (8)
apps/desktop/src/components/settings/views/templates.tsx (1)

130-139: Default new template includes context_option: null — good

Initializing as null is consistent with defaults and avoids ambiguous empty-string states.

crates/db-user/src/templates_migration_1.sql (1)

1-4: Migration adds context_option column — OK

Append-only and nullable as expected. No further changes needed here.

crates/db-user/src/lib.rs (1)

132-159: Migration wiring appended correctly

Length bumped and new migration placed at the end of the list.

apps/desktop/src/utils/default-templates.ts (1)

18-19: Consistently setting context_option: null across defaults — good

Keeps the shape uniform and interoperates cleanly with the editor logic.

Also applies to: 34-35, 50-51, 67-68, 84-85, 101-102, 118-119, 135-136, 152-153, 169-170, 187-187, 203-204, 220-221

crates/db-user/src/templates_types.rs (1)

11-12: Template gains context_option — good

Public API matches the new column and UI usage.

apps/desktop/src/components/editor-area/index.tsx (1)

30-31: New utility import — OK

Used below; no unused imports.

crates/db-user/src/templates_ops.rs (1)

103-106: Test case covers tag-based JSON — OK

Exercising upsert with a non-null context_option looks good.

apps/desktop/src/components/settings/views/template.tsx (1)

287-305: Verify Select placeholder behavior when value is empty.

With value set to "", some Select implementations won’t show the placeholder and can emit “value not in items” warnings. Consider mapping empty to undefined or using "none" as the controlled value; verify in the running app.

Comment on lines +33 to 35
tags,
context_option
) VALUES (
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Store NULL, not empty string, for absent context_option

Empty strings complicate semantics across layers. Persist NULLs and keep reads simple. Backward-compatible, especially with the read-side normalization suggested in templates_types.rs.

Apply:

                 ) VALUES (
                     :id,
                     :user_id,
                     :title,
                     :description,
                     :sections,
-                    :tags,
-                    :context_option
+                    :tags,
+                    NULLIF(:context_option, '')
                 ) ON CONFLICT(id) DO UPDATE SET
                     title = :title,
                     description = :description,
                     sections = :sections,
-                    tags = :tags,
-                    context_option = :context_option
+                    tags = :tags,
+                    context_option = NULLIF(:context_option, '')

Optionally, once this is in place you can later simplify the TS checks to a direct truthy test without worrying about empty strings.

Also applies to: 41-43, 47-49, 57-57

🤖 Prompt for AI Agents
In crates/db-user/src/templates_ops.rs around lines 33-35 (and similarly at
41-43, 47-49, 57-57), the INSERT currently writes an empty string for absent
context_option; change the code to insert SQL NULL instead of an empty string
when context_option is None/absent. Update the parameter binding logic so that
if the Rust value is Option::None you bind a NULL (e.g., use the DB client's
null/Option-aware binding or map to Option::<String>::None) and if Some(v) bind
the string; ensure the SQL VALUES list and prepared parameters align with this
change so reads will return NULL rather than an empty string.

Comment on lines +37 to 38
context_option: row.get(6).ok(),
})
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Normalize empty string to None when reading context_option

Rows saved with empty-string values will deserialize as Some(""), which is semantically “unset”. Normalize to None for consistency and simpler client checks.

Apply:

-            context_option: row.get(6).ok(),
+            context_option: row
+                .get_str(6)
+                .and_then(|s| {
+                    let t = s.trim();
+                    if t.is_empty() { None } else { Some(t.to_string()) }
+                }),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In crates/db-user/src/templates_types.rs around lines 37 to 38, the current
deserialization uses row.get(6).ok() which yields Some("") for stored empty
strings; update the read to normalize an empty string into None by mapping the
retrieved value: get the value via row.get(6), convert any Err to None, and if
the retrieved string is present but empty return None, otherwise return
Some(the_string). Replace the simple .ok() with this conditional mapping so
clients see None for semantically unset context_option.

Comment on lines 24 to 26
Your job is to write a perfect note based on the above informations.
Note that above given informations like participants, transcript, etc. are already displayed in the UI, so you don't need to repeat them.
Note that above given informations like participants, transcript, previous context, etc. are already displayed in the UI, so you don't need to repeat them.

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix grammar in instructions

Use “information” (uncountable) and concise wording.

-Your job is to write a perfect note based on the above informations.
-Note that above given informations like participants, transcript, previous context, etc. are already displayed in the UI, so you don't need to repeat them.
+Your job is to write a perfect note based on the information above.
+Note that participants, transcript, and previous context are already displayed in the UI; do not repeat them.
📝 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
Your job is to write a perfect note based on the above informations.
Note that above given informations like participants, transcript, etc. are already displayed in the UI, so you don't need to repeat them.
Note that above given informations like participants, transcript, previous context, etc. are already displayed in the UI, so you don't need to repeat them.
Your job is to write a perfect note based on the information above.
Note that participants, transcript, and previous context are already displayed in the UI; do not repeat them.
🤖 Prompt for AI Agents
In crates/template/assets/enhance.user.jinja around lines 24 to 26, the
instruction text uses the incorrect plural "informations" and is wordy; change
"above informations" to the uncountable "above information" and tighten the
sentence to something concise (e.g., "Your job is to write a perfect note based
on the above information. Note that participants, transcript, previous context,
etc. are already displayed in the UI, so do not repeat them.").

@yujonglee yujonglee merged commit e59b81b into main Sep 10, 2025
15 checks passed
@yujonglee yujonglee deleted the template-context-final branch September 10, 2025 19:23
@coderabbitai coderabbitai bot mentioned this pull request Sep 10, 2025
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