Skip to content

Template copy#1419

Merged
duckduckhero merged 4 commits intomainfrom
template-copy
Aug 29, 2025
Merged

Template copy#1419
duckduckhero merged 4 commits intomainfrom
template-copy

Conversation

@duckduckhero
Copy link
Contributor

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 29, 2025

📝 Walkthrough

Walkthrough

Adds a chat-specific branch to the ProGate modal content, extends ProGate type union with "template_duplicate", and implements template duplication via a new onDuplicate prop in TemplateEditor and a handler in TemplatesView. Locales file references updated and new datetime strings added; translations unchanged.

Changes

Cohort / File(s) Summary
ProGate modal logic and types
apps/desktop/src/components/pro-gate-modal/index.tsx, apps/desktop/src/components/pro-gate-modal/types.ts
index.tsx: Introduces explicit type === "chat" branch in getContent() turning prior two-branch logic into template → chat → default. types.ts: Extends ProGateModalProps.type and ProGateType to include "template_duplicate".
Template editor UI & duplication flow
apps/desktop/src/components/settings/views/template.tsx, apps/desktop/src/components/settings/views/templates.tsx
template.tsx: Adds onDuplicate?: (template: Template) => void to TemplateEditorProps, replaces dropdown with inline Duplicate/Delete actions, and calls onDuplicate for built-in templates. templates.tsx: Adds handleDuplicateTemplate (creates duplicate id/title/tags, checks license, shows pro-gate if needed, saves via TemplateService.saveTemplate, reloads/selects/switches editor, emits analytics). Note: handleDuplicateTemplate appears duplicated in the diff.
Locales
apps/desktop/src/locales/en/messages.po, apps/desktop/src/locales/ko/messages.po
en: Adds datetime-related strings and updates many source file:line references; message texts unchanged. ko: Updates source location references only; translations unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant TemplatesView
  participant License
  participant ProGateModal
  participant TemplateService
  participant TemplatesStore as Store

  User->>TemplatesView: Click "Duplicate" in TemplateEditor
  TemplatesView->>License: isValid()
  alt License invalid
    TemplatesView->>ProGateModal: Open(type="template_duplicate")
    Note over ProGateModal: Shows pro-gate for duplication
    TemplatesView-->>User: Exit (no duplication)
  else License valid
    TemplatesView->>TemplatesView: Build duplicated template\n(emoji/title/id/tags)
    TemplatesView->>TemplateService: saveTemplate(duplicate)
    TemplateService-->>TemplatesView: OK
    TemplatesView->>Store: reloadTemplates()
    TemplatesView->>Store: selectTemplate(newId)
    TemplatesView->>TemplatesView: switchToEditor()
    TemplatesView-->>User: Editor opened with copy
  end
Loading
sequenceDiagram
  autonumber
  participant ProGateModal
  participant getContent

  ProGateModal->>getContent: with type
  alt type == "template"
    getContent-->>ProGateModal: Template creation message
  else type == "chat"
    getContent-->>ProGateModal: Chat-specific message
  else default
    getContent-->>ProGateModal: Template-duplication / default message
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

✨ 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-copy

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

Caution

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

⚠️ Outside diff range comments (3)
apps/desktop/src/components/settings/views/template.tsx (3)

85-88: Remove debug logs (noise + potential PII leak).

These logs can leak user/template data and clutter console.

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

90-97: Emoji regex is fragile; prefer a more robust matcher.

\p{Emoji} support differs across engines and won’t match multi-codepoint sequences. Use Extended_Pictographic (broader support) or an emoji-regex.

-  const extractEmojiFromTitle = (title: string) => {
-    const emojiMatch = title.match(/^(\p{Emoji})\s*/u);
-    return emojiMatch ? emojiMatch[1] : "📄";
-  };
+  const extractEmojiFromTitle = (title: string) => {
+    // Heuristic: pick the first extended pictographic at start; fallback default
+    const m = title.match(/^(\p{Extended_Pictographic}\uFE0F?)\s*/u);
+    return m ? m[1] : "📄";
+  };
 
-  const getTitleWithoutEmoji = (title: string) => {
-    return title.replace(/^(\p{Emoji})\s*/u, "");
-  };
+  const getTitleWithoutEmoji = (title: string) => {
+    return title.replace(/^(\p{Extended_Pictographic}\uFE0F?)\s*/u, "");
+  };

If you want full RGI coverage, switch to the emoji-regex package.


198-226: Show “Duplicate” even for built-in/non-creator templates; keep “Delete” gated.

Users often duplicate built-ins they can't edit/delete. Current isCreator guard hides the whole menu.

-          {isCreator && (
-            <DropdownMenu>
+          <DropdownMenu>
               <DropdownMenuTrigger asChild>
                 <Button variant="outline" size="sm">
                   <MoreHorizontalIcon className="h-4 w-4" />
                 </Button>
               </DropdownMenuTrigger>
               <DropdownMenuContent align="end">
                 <DropdownMenuItem onClick={handleDuplicate} className="cursor-pointer">
                   <CopyIcon className="mr-2 h-4 w-4" />
                   <Trans>Duplicate</Trans>
                 </DropdownMenuItem>
 
-                {/* Only show separator and delete option for custom templates */}
-                {!isBuiltinTemplate && (
+                {/* Delete only for custom templates created by the user */}
+                {!isBuiltinTemplate && isCreator && (
                   <>
                     <DropdownMenuSeparator />
                     <DropdownMenuItem
                       onClick={handleDelete}
                       className="text-destructive hover:bg-red-100 hover:text-red-600 cursor-pointer"
                     >
                       <TrashIcon className="mr-2 h-4 w-4" />
                       <Trans>Delete</Trans>
                     </DropdownMenuItem>
                   </>
                 )}
               </DropdownMenuContent>
-            </DropdownMenu>
-          )}
+          </DropdownMenu>
🧹 Nitpick comments (4)
apps/desktop/src/components/pro-gate-modal/index.tsx (1)

81-83: Optional: localize modal copy.

These strings bypass Lingui. Consider moving them to i18n to keep UX copy consistent with the rest of the app.

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

155-156: Prune “what” comments.

Comments explain what the UI does, not why. Remove or rewrite if a non-obvious rationale exists.

-            {/* Emoji Selector - unchanged */}
...
-                {/* Only show separator and delete option for custom templates */}

Also applies to: 211-213

apps/desktop/src/components/settings/views/templates.tsx (2)

221-226: Use the saved record returned from TemplateService.

Avoid stale state; set selection from the persisted value.

-      await TemplateService.saveTemplate(duplicatedTemplate);
+      const saved = await TemplateService.saveTemplate(duplicatedTemplate);
@@
-      setSelectedTemplate(duplicatedTemplate);
+      setSelectedTemplate(saved);

228-231: Enrich analytics payload for better observability.

Add context like template_id and source ("editor" vs "list") if supported by the analytics SDK.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 91e137a and 0fd69b8.

📒 Files selected for processing (6)
  • apps/desktop/src/components/pro-gate-modal/index.tsx (1 hunks)
  • apps/desktop/src/components/pro-gate-modal/types.ts (1 hunks)
  • apps/desktop/src/components/settings/views/template.tsx (3 hunks)
  • apps/desktop/src/components/settings/views/templates.tsx (2 hunks)
  • apps/desktop/src/locales/en/messages.po (28 hunks)
  • apps/desktop/src/locales/ko/messages.po (28 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit configuration file

**/*.{js,ts,tsx,rs}: 1. No error handling.
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/pro-gate-modal/types.ts
  • apps/desktop/src/components/pro-gate-modal/index.tsx
  • apps/desktop/src/components/settings/views/template.tsx
  • apps/desktop/src/components/settings/views/templates.tsx
🧬 Code graph analysis (3)
apps/desktop/src/components/pro-gate-modal/types.ts (1)
apps/desktop/src/components/pro-gate-modal/index.tsx (1)
  • ProGateType (173-173)
apps/desktop/src/components/settings/views/template.tsx (1)
plugins/db/js/bindings.gen.ts (1)
  • Template (184-184)
apps/desktop/src/components/settings/views/templates.tsx (3)
plugins/db/js/bindings.gen.ts (1)
  • Template (184-184)
apps/desktop/src/components/pro-gate-modal/service.ts (1)
  • showProGateModal (6-27)
apps/desktop/src/utils/template-service.ts (1)
  • TemplateService (5-66)
⏰ 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-latest)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (4)
apps/desktop/src/locales/ko/messages.po (1)

17-17: LGTM: reference realignments and new datetime keys only.

No msgid/msgstr semantic changes detected. Keep KO in sync if new gating copy gets localized later.

Also applies to: 274-276, 467-471, 650-652, 675-678, 688-693, 710-713, 730-733, 746-749, 969-971, 1004-1007, 1146-1149, 1471-1474, 1512-1515, 1545-1549, 1746-1749

apps/desktop/src/locales/en/messages.po (1)

17-20: LGTM: adds datetime strings and updates source refs.

Strings/placements look consistent; placeholders preserved.

Also applies to: 212-215, 467-471, 650-653, 675-678, 688-693, 710-713, 730-733, 746-749, 969-971, 1004-1007, 1146-1149, 1471-1474, 1512-1515, 1545-1549, 1746-1749

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

24-26: Wiring for onDuplicate looks correct.

Optional chaining avoids breaking older callers.

Also applies to: 76-78, 142-145


106-111: Maintain dependency on template.id only—syncing on template.title/template.description would reset local edits on every prop update

Likely an incorrect or invalid review comment.

Comment on lines +20 to 28
} else if (type === "chat") {
return {
description: "4 messages are allowed per conversation for free users. Upgrade to pro for unlimited chat.",
};
} else {
return {
description: "Template duplication is only available for pro users. Please upgrade your account to continue.",
};
}
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

Make branching exhaustive and explicit for "template_duplicate".

Replace the if/else chain with a switch that enumerates all ProGateType members to prevent future silent defaults.

Apply the diff suggested in types.ts review comment.

🤖 Prompt for AI Agents
In apps/desktop/src/components/pro-gate-modal/index.tsx around lines 20 to 28,
replace the current if/else chain for handling ProGateType values with a switch
statement that explicitly enumerates every ProGateType member (including "chat",
"template_duplicate" and any other members defined in types.ts), returning the
corresponding description for each case; add an explicit default that either
throws an error or uses a never-exhaustive-check helper so new enum values cause
a compile/runtime failure rather than silently falling back, and align wording
with the types.ts suggested diff.

isOpen: boolean;
onClose: () => void;
type: "template" | "chat";
type: "template" | "chat" | "template_duplicate";
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Ensure exhaustive handling of the new ProGate type across call sites.

Adding "template_duplicate" is fine. Prefer explicit handling (no implicit default) wherever the type is branched on to avoid future silent fallthrough.

You can make getContent() in index.tsx exhaustive:

-  const getContent = () => {
-    if (type === "template") {
-      return { description: "You've reached the custom template limit for free users. Please upgrade your account to continue." };
-    } else if (type === "chat") {
-      return { description: "4 messages are allowed per conversation for free users. Upgrade to pro for unlimited chat." };
-    } else {
-      return { description: "Template duplication is only available for pro users. Please upgrade your account to continue." };
-    }
-  };
+  const getContent = () => {
+    switch (type) {
+      case "template":
+        return { description: "You've reached the custom template limit for free users. Please upgrade your account to continue." };
+      case "chat":
+        return { description: "4 messages are allowed per conversation for free users. Upgrade to pro for unlimited chat." };
+      case "template_duplicate":
+        return { description: "Template duplication is only available for pro users. Please upgrade your account to continue." };
+      default: {
+        const _exhaustive: never = type;
+        return { description: "" };
+      }
+    }
+  };

Run to find any non-exhaustive branches on ProGate type:

Also applies to: 7-7


🏁 Script executed:

#!/bin/bash
rg -nP --type=ts --type=tsx -C2 '\btype\s*===\s*"(template|chat|template_duplicate)"|switch\s*\(\s*type\s*\)' apps/desktop/src

Length of output: 156


🏁 Script executed:

#!/bin/bash
# Search for direct comparisons to ProGate type values and switch statements across .ts and .tsx files
rg -nP -C2 'type\s*===\s*"(template|chat|template_duplicate)"' -g '*.ts' -g '*.tsx' apps/desktop/src
rg -nP -C2 'switch\s*\(\s*type\s*\)' -g '*.ts' -g '*.tsx' apps/desktop/src

Length of output: 1120


Use an exhaustive switch for getContent in pro-gate-modal/index.tsx
Replace the current if/else if/else chain with a switch (type) that has explicit cases for "template", "chat", and "template_duplicate", and a default branch assigning never to enforce exhaustiveness.

🤖 Prompt for AI Agents
In apps/desktop/src/components/pro-gate-modal/types.ts line 4 and
apps/desktop/src/components/pro-gate-modal/index.tsx around the getContent
implementation, replace the existing if/else if/else chain with a switch(type)
that has explicit cases for "template", "chat", and "template_duplicate"; in
each case return the appropriate content narrowing the props to that branch, and
add a default branch that assigns the variable to never (e.g., const
_exhaustiveCheck: never = type) to enforce exhaustiveness so the compiler fails
if the union changes.

Comment on lines +199 to +200
const handleDuplicateTemplate = async (template: Template) => {
try {
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

Remove local try/catch; follow repo rule “No error handling.”

Let errors bubble to the caller/UI boundary or centralize in TemplateService.

-  const handleDuplicateTemplate = async (template: Template) => {
-    try {
+  const handleDuplicateTemplate = async (template: Template) => {
@@
-    } catch (error) {
-      console.error("Failed to duplicate template:", error);
-    }
-  };
+  };

Also applies to: 232-235

🤖 Prompt for AI Agents
In apps/desktop/src/components/settings/views/templates.tsx around lines 199-200
(and also for the block at 232-235), remove the local try/catch wrappers in
handleDuplicateTemplate (and the other duplicated block) so errors are not
handled locally; instead let the async function return/throw normally so errors
bubble to the caller or the centralized TemplateService error boundary; if any
cleanup is required keep a finally block only, otherwise delete the catch and
its contents and ensure the function returns the Promise directly.

Comment on lines +201 to +208
if (!getLicense.data?.valid) {
analyticsCommands.event({
event: "pro_license_required_template",
distinct_id: userId,
});
await showProGateModal("template_duplicate");
return;
}
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

Align duplication gating with creation gating (respect free quota).

Creation checks !valid && customTemplates.length > 1 while duplication gates on any invalid license. Use the same threshold to keep UX consistent.

-      if (!getLicense.data?.valid) {
+      if (!getLicense.data?.valid && customTemplates.length > 1) {
         analyticsCommands.event({
           event: "pro_license_required_template",
           distinct_id: userId,
         });
         await showProGateModal("template_duplicate");
         return;
       }
📝 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
if (!getLicense.data?.valid) {
analyticsCommands.event({
event: "pro_license_required_template",
distinct_id: userId,
});
await showProGateModal("template_duplicate");
return;
}
if (!getLicense.data?.valid && customTemplates.length > 1) {
analyticsCommands.event({
event: "pro_license_required_template",
distinct_id: userId,
});
await showProGateModal("template_duplicate");
return;
}
🤖 Prompt for AI Agents
In apps/desktop/src/components/settings/views/templates.tsx around lines 201 to
208, the duplication gating currently blocks all users without a valid license
but creation gating only blocks when the user is over the free quota (i.e.,
!valid && customTemplates.length > 1); update the duplication check to use the
same threshold so it only triggers analytics/event and showProGateModal when
getLicense.data?.valid is false AND customTemplates.length > 1, matching the
creation logic and keeping the UX consistent.

template={selectedTemplate}
onTemplateUpdate={handleTemplateUpdate}
onDelete={handleTemplateDeleteFromEditor}
onDuplicate={handleDuplicateTemplate}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify TemplateEditor prop contract and unify duplication paths.

  • Ensure TemplateEditor accepts onDuplicate?: (t: Template) => void and wires it to its UI.
  • Route TemplateCard’s “Clone” to this same handler to avoid two divergent code paths (handleCloneTemplate vs handleDuplicateTemplate) and to keep gating consistent. Example updates (outside this hunk):
    • custom list: onClone={() => handleDuplicateTemplate(template)}
    • builtin list: onClone={() => handleDuplicateTemplate(template)}
  • Afterwards, delete handleCloneTemplate.

🏁 Script executed:

#!/bin/bash
# Verify ProGate type supports "template_duplicate"
rg -nP '"template_duplicate"|ProGateType' apps/desktop/src

# Verify TemplateEditor prop includes onDuplicate and is used
rg -nP -C3 'onDuplicate\??\s*:\s*\(template:\s*Template' apps/desktop/src
rg -nP -C3 'export default function TemplateEditor|interface .*Props|type .*Props' apps/desktop/src/components

# Ensure only one duplicate handler exists in this file
rg -nP 'handleDuplicateTemplate\s*=\s*async' apps/desktop/src/components/settings/views/templates.tsx

Length of output: 65257


results:

apps/desktop/src/components/settings/views/templates.tsx:150:  const handleCloneTemplate = async (template: Template) => {
apps/desktop/src/components/settings/views/templates.tsx:250:    onClone={() => handleCloneTemplate(template)}

The search confirms that handleCloneTemplate is still defined and used within templates.tsx at lines 150 and 250, and onClone prop is wired to handleCloneTemplate. The review comment’s suggestion to unify the duplication paths by routing onClone to handleDuplicateTemplate and then removing handleCloneTemplate remains valid.


Unify duplication handlers

  • In apps/desktop/src/components/settings/views/templates.tsx, change all onClone={() => handleCloneTemplate(template)} (usages around line 250) to onClone={() => handleDuplicateTemplate(template)} and delete the handleCloneTemplate definition (around line 150).
  • In TemplateEditor (in apps/desktop/src/components/settings/views/template.tsx), destructure and call its onDuplicate prop in the UI so the “Duplicate” action uses handleDuplicateTemplate.
🤖 Prompt for AI Agents
In apps/desktop/src/components/settings/views/templates.tsx around lines
250-262, replace all usages of onClone={() => handleCloneTemplate(template)}
with onClone={() => handleDuplicateTemplate(template)} and remove the now-unused
handleCloneTemplate function definition located around line 150; in
apps/desktop/src/components/settings/views/template.tsx (TemplateEditor) update
the component props to destructure onDuplicate and invoke onDuplicate where the
“Duplicate” action is rendered so the Duplicate UI calls the passed-in
handleDuplicateTemplate handler. Ensure imports/prop types are updated and no
references to handleCloneTemplate remain.

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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0fd69b8 and b426075.

📒 Files selected for processing (3)
  • apps/desktop/src/components/settings/views/template.tsx (4 hunks)
  • apps/desktop/src/locales/en/messages.po (28 hunks)
  • apps/desktop/src/locales/ko/messages.po (28 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/locales/en/messages.po
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}

⚙️ CodeRabbit configuration file

**/*.{js,ts,tsx,rs}: 1. No error handling.
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/settings/views/template.tsx
🧬 Code graph analysis (1)
apps/desktop/src/components/settings/views/template.tsx (2)
plugins/db/js/bindings.gen.ts (1)
  • Template (184-184)
packages/ui/src/components/ui/button.tsx (1)
  • Button (37-89)
⏰ 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-latest)
  • GitHub Check: ci (windows, windows-latest)
🔇 Additional comments (5)
apps/desktop/src/components/settings/views/template.tsx (3)

16-16: Add onDuplicate prop — LGTM

Type looks correct and aligns with Template shape.


68-70: Prop destructuring — LGTM

Optional prop handled safely with optional chaining downstream.


134-137: Disable duplicate button during processing or confirm backend idempotency
The duplicate handler immediately calls TemplateService.saveTemplate without disabling the button, so rapid clicks can spawn concurrent duplicate‐creation requests. Either disable the Duplicate button until the operation completes or verify that the backend API safely deduplicates concurrent calls.

apps/desktop/src/locales/ko/messages.po (2)

22-209: New datetime keys added — LGTM

References added; translations left empty as expected for now.


335-337: Recompile message catalogs and verify translations — only reference locations changed; regenerate the compiled catalogs and ensure any newly added msgids (e.g. “Add a system instruction…”) have corresponding translations.

Comment on lines 190 to 209
{isCreator && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm">
<MoreHorizontalIcon className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={handleDuplicate} className="cursor-pointer">
<CopyIcon className="mr-2 h-4 w-4" />
<Trans>Duplicate</Trans>
</DropdownMenuItem>

{/* Only show separator and delete option for custom templates */}
{!isBuiltinTemplate && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={handleDelete}
className="text-destructive hover:bg-red-100 hover:text-red-600 cursor-pointer"
>
<TrashIcon className="mr-2 h-4 w-4" />
<Trans>Delete</Trans>
</DropdownMenuItem>
</>
<div className="flex gap-2">
{isBuiltinTemplate
? (
<Button variant="outline" size="sm" onClick={handleDuplicate}>
<Trans>Duplicate</Trans>
</Button>
)
: (
<Button
variant="outline"
size="sm"
onClick={handleDelete}
className="text-destructive hover:bg-red-50 hover:text-red-600 hover:border-red-200"
>
<Trans>Delete</Trans>
</Button>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
)}
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

Duplicate button hidden for built-ins when user isn’t creator; avoid inert buttons

Showing Duplicate behind isCreator likely hides it for built-ins owned by the app. Also hide action buttons when handlers are absent to avoid no-ops.

Apply this diff:

-          {isCreator && (
-            <div className="flex gap-2">
-              {isBuiltinTemplate
-                ? (
-                  <Button variant="outline" size="sm" onClick={handleDuplicate}>
-                    <Trans>Duplicate</Trans>
-                  </Button>
-                )
-                : (
-                  <Button
-                    variant="outline"
-                    size="sm"
-                    onClick={handleDelete}
-                    className="text-destructive hover:bg-red-50 hover:text-red-600 hover:border-red-200"
-                  >
-                    <Trans>Delete</Trans>
-                  </Button>
-                )}
-            </div>
-          )}
+          {((isBuiltinTemplate && !!onDuplicate) || (!isBuiltinTemplate && isCreator && !!onDelete)) ? (
+            <div className="flex gap-2">
+              {isBuiltinTemplate && onDuplicate && (
+                <Button variant="outline" size="sm" onClick={handleDuplicate}>
+                  <Trans>Duplicate</Trans>
+                </Button>
+              )}
+              {!isBuiltinTemplate && isCreator && onDelete && (
+                <Button
+                  variant="outline"
+                  size="sm"
+                  onClick={handleDelete}
+                  className="text-destructive hover:bg-red-50 hover:text-red-600 hover:border-red-200"
+                >
+                  <Trans>Delete</Trans>
+                </Button>
+              )}
+            </div>
+          ) : null}
📝 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
{isCreator && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm">
<MoreHorizontalIcon className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={handleDuplicate} className="cursor-pointer">
<CopyIcon className="mr-2 h-4 w-4" />
<Trans>Duplicate</Trans>
</DropdownMenuItem>
{/* Only show separator and delete option for custom templates */}
{!isBuiltinTemplate && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={handleDelete}
className="text-destructive hover:bg-red-100 hover:text-red-600 cursor-pointer"
>
<TrashIcon className="mr-2 h-4 w-4" />
<Trans>Delete</Trans>
</DropdownMenuItem>
</>
<div className="flex gap-2">
{isBuiltinTemplate
? (
<Button variant="outline" size="sm" onClick={handleDuplicate}>
<Trans>Duplicate</Trans>
</Button>
)
: (
<Button
variant="outline"
size="sm"
onClick={handleDelete}
className="text-destructive hover:bg-red-50 hover:text-red-600 hover:border-red-200"
>
<Trans>Delete</Trans>
</Button>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
)}
{((isBuiltinTemplate && !!onDuplicate) || (!isBuiltinTemplate && isCreator && !!onDelete)) ? (
<div className="flex gap-2">
{isBuiltinTemplate && onDuplicate && (
<Button variant="outline" size="sm" onClick={handleDuplicate}>
<Trans>Duplicate</Trans>
</Button>
)}
{!isBuiltinTemplate && isCreator && onDelete && (
<Button
variant="outline"
size="sm"
onClick={handleDelete}
className="text-destructive hover:bg-red-50 hover:text-red-600 hover:border-red-200"
>
<Trans>Delete</Trans>
</Button>
)}
</div>
) : null}
🤖 Prompt for AI Agents
In apps/desktop/src/components/settings/views/template.tsx around lines 190-209,
the current conditional shows the action wrapper only when isCreator, which
hides the Duplicate button for built-in templates and can render inert buttons
when handlers are missing; change the rendering so the Duplicate button is shown
when the template is built-in and a handleDuplicate handler exists, and show the
Delete button when the user is the creator and a handleDelete handler exists;
also only render the surrounding action container if at least one visible button
will be rendered to avoid empty/no-op controls.

@duckduckhero duckduckhero merged commit 6a1d28f into main Aug 29, 2025
11 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Sep 9, 2025
@coderabbitai coderabbitai bot mentioned this pull request Oct 13, 2025
@coderabbitai coderabbitai bot mentioned this pull request Oct 22, 2025
@ComputelessComputer ComputelessComputer deleted the template-copy branch December 14, 2025 15:20
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