);
diff --git a/apps/web/app/(app)/[emailAccountId]/assistant/examples.ts b/apps/web/app/(app)/[emailAccountId]/assistant/examples.ts
index bfdcc8e984..badc7cb29c 100644
--- a/apps/web/app/(app)/[emailAccountId]/assistant/examples.ts
+++ b/apps/web/app/(app)/[emailAccountId]/assistant/examples.ts
@@ -22,39 +22,39 @@ function formatPromptArray(promptArray: string[]): string {
}
const commonPrompts = [
- "Label urgent emails as 'Urgent'",
- "Label emails from @mycompany.com addresses as 'Team'",
+ "Label urgent emails as @[Urgent]",
+ "Label emails from @mycompany.com addresses as @[Team]",
];
export const examplePrompts = [
...commonPrompts,
- 'Forward receipts to jane@accounting.com and label them "Receipt"',
- 'Forward pitch decks to john@investing.com and label them "Pitch Deck"',
+ "Forward receipts to jane@accounting.com and label them @[Receipt]",
+ "Forward pitch decks to john@investing.com and label them @[Pitch Deck]",
"Reply to cold emails by telling them to check out Inbox Zero. Then mark them as spam",
- 'Label high priority emails as "High Priority"',
+ "Label high priority emails as @[High Priority]",
"If a founder asks to set up a call, draft a reply with my calendar link: https://cal.com/example",
"If someone asks to cancel a plan, draft a reply with the cancellation link: https://company.com/cancel",
- 'If a founder sends me an investor update, label it "Investor Update" and archive it',
- 'If someone pitches me their startup, label it as "Investing", archive it, and draft a friendly reply that I no longer have time to look at the email but if they get a warm intro, that\'s their best bet to get funding from me',
+ "If a founder sends me an investor update, label it @[Investor Update] and archive it",
+ "If someone pitches me their startup, label it as @[Investing], archive it, and draft a friendly reply that I no longer have time to look at the email but if they get a warm intro, that's their best bet to get funding from me",
"If someone asks for a discount, reply with the discount code INBOX20",
"If someone asks for help with Product or Company, draft a reply telling them I no longer work there, but they should reach out to Company for support",
"Review any emails from questions@pr.com and see if any are about finance. If so, draft a friendly reply that answers the question",
- 'If people ask me to speak at an event, label the email "Speaker Opportunity" and archive it',
- 'Label customer emails as "Customer"',
- 'Label legal documents as "Legal"',
- 'Label server errors as "Error"',
- 'Label Stripe emails as "Stripe"',
+ "If people ask me to speak at an event, label the email @[Speaker Opportunity] and archive it",
+ "Label customer emails as @[Customer]",
+ "Label legal documents as @[Legal]",
+ "Label server errors as @[Error]",
+ "Label Stripe emails as @[Stripe]",
];
const founderPromptArray = [
...commonPrompts,
"If someone asks to set up a call, draft a reply with my calendar link: https://cal.com/example",
- 'Label customer feedback emails as "Customer Feedback"',
- 'Label customer support emails as "Customer Support"',
- 'Label emails from investors as "Investor"',
- 'Label legal documents as "Legal"',
- 'Label emails about travel as "Travel"',
- 'Label recruitment related emails as "Hiring"',
+ "Label customer feedback emails as @[Customer Feedback]",
+ "Label customer support emails as @[Customer Support]",
+ "Label emails from investors as @[Investor]",
+ "Label legal documents as @[Legal]",
+ "Label emails about travel as @[Travel]",
+ "Label recruitment related emails as @[Hiring]",
];
export const personas = {
@@ -69,7 +69,7 @@ export const personas = {
label: "📹 Influencer",
promptArray: [
...commonPrompts,
- `Label sponsorship inquiries as "Sponsorship" and draft a reply as follows:
+ `Label sponsorship inquiries as @[Sponsorship] and draft a reply as follows:
---
Hey NAME,
@@ -77,10 +77,10 @@ SENTENCE RELATED TO THEIR INQUIRY
I've attached my media kit and pricing.
---`,
- 'Label emails about affiliate programs as "Affiliate" and archive them',
- 'Label collaboration requests as "Collab" and draft a reply asking about their audience size and engagement rates',
- 'Label brand partnership emails as "Brand Deal" and forward to manager@example.com',
- 'Label media inquiries as "Press" and draft a reply a polite reply',
+ "Label emails about affiliate programs as @[Affiliate] and archive them",
+ "Label collaboration requests as @[Collab] and draft a reply asking about their audience size and engagement rates",
+ "Label brand partnership emails as @[Brand Deal] and forward to manager@example.com",
+ "Label media inquiries as @[Press] and draft a reply a polite reply",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -90,15 +90,15 @@ I've attached my media kit and pricing.
label: "🏠 Realtor",
promptArray: [
...commonPrompts,
- 'Label emails from potential buyers as "Buyer Lead" and draft a reply asking about their budget and preferred neighborhoods',
- 'Label emails from potential sellers as "Seller Lead" and draft a reply with my calendar link to schedule a home evaluation: https://cal.com/example',
- 'If someone asks about home prices in a specific area, label as "Market Inquiry" and draft a reply with recent comparable sales data',
- 'Label emails from mortgage brokers and lenders as "Lender" and archive them',
- 'If someone asks to schedule a showing, label as "Showing Request" and draft a reply with available time slots',
- 'Label emails about closing documents as "Closing" and forward to transactions@realty.com',
+ "Label emails from potential buyers as @[Buyer Lead] and draft a reply asking about their budget and preferred neighborhoods",
+ "Label emails from potential sellers as @[Seller Lead] and draft a reply with my calendar link to schedule a home evaluation: https://cal.com/example",
+ "If someone asks about home prices in a specific area, label as @[Market Inquiry] and draft a reply with recent comparable sales data",
+ "Label emails from mortgage brokers and lenders as @[Lender] and archive them",
+ "If someone asks to schedule a showing, label as @[Showing Request] and draft a reply with available time slots",
+ "Label emails about closing documents as @[Closing] and forward to transactions@realty.com",
"If someone asks about the home buying process, draft a reply with our buyer's guide link: https://realty.com/buyers-guide",
- 'Label emails from home inspectors as "Inspector" and forward to scheduling@realty.com',
- 'If someone refers a client to me, label as "Referral" and draft a thank you reply with my calendar link to schedule a consultation',
+ "Label emails from home inspectors as @[Inspector] and forward to scheduling@realty.com",
+ "If someone refers a client to me, label as @[Referral] and draft a thank you reply with my calendar link to schedule a consultation",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -109,19 +109,19 @@ I've attached my media kit and pricing.
promptArray: [
...commonPrompts,
"If a founder asks to set up a call, draft a reply with my calendar link: https://cal.com/example",
- 'If a founder sends me an investor update, label it "Investor Update" and archive it',
- 'Forward pitch decks to analyst@vc.com that asks them to review it and label them "Pitch Deck"',
- 'Label emails from LPs as "LP"',
- 'Label legal documents as "Legal"',
- 'Label emails about travel as "Travel"',
- 'Label emails about portfolio company exits as "Exit Opportunity"',
- 'Label emails containing term sheets as "Term Sheet"',
- 'If a portfolio company reports bad news, label as "Portfolio Alert" and draft a reply to schedule an emergency call',
- 'Label due diligence related emails as "Due Diligence"',
+ "If a founder sends me an investor update, label it @[Investor Update] and archive it",
+ "Forward pitch decks to analyst@vc.com that asks them to review it and label them @[Pitch Deck]",
+ "Label emails from LPs as @[LP]",
+ "Label legal documents as @[Legal]",
+ "Label emails about travel as @[Travel]",
+ "Label emails about portfolio company exits as @[Exit Opportunity]",
+ "Label emails containing term sheets as @[Term Sheet]",
+ "If a portfolio company reports bad news, label as @[Portfolio Alert] and draft a reply to schedule an emergency call",
+ "Label due diligence related emails as @[Due Diligence]",
"Forward emails about industry research reports to research@vc.com",
"If someone asks for a warm intro to a portfolio company, draft a reply asking for more context about why they want to connect",
- 'Label emails about fund administration as "Fund Admin"',
- 'Label emails about speaking at investment conferences as "Speaking Opportunity"',
+ "Label emails about fund administration as @[Fund Admin]",
+ "Label emails about speaking at investment conferences as @[Speaking Opportunity]",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -138,15 +138,15 @@ I've attached my media kit and pricing.
label: "👨💻 Developer",
promptArray: [
...commonPrompts,
- 'Label server errors, deployment failures, and other server alerts as "Alert" and forward to oncall@company.com',
- 'Label emails from GitHub as "GitHub" and archive them',
- 'Label emails from Figma as "Design" and archive them',
- 'Label emails from Stripe as "Stripe" and archive them',
- 'Label emails from Slack as "Slack" and archive them',
- 'Label emails about bug reports as "Bug"',
- 'If someone reports a security vulnerability, label as "Security" and forward to security@company.com',
- 'Label emails about job interviews as "Job Search"',
- 'Label emails from recruiters as "Recruiter" and archive them',
+ "Label server errors, deployment failures, and other server alerts as @[Alert] and forward to oncall@company.com",
+ "Label emails from GitHub as @[GitHub] and archive them",
+ "Label emails from Figma as @[Design] and archive them",
+ "Label emails from Stripe as @[Stripe] and archive them",
+ "Label emails from Slack as @[Slack] and archive them",
+ "Label emails about bug reports as @[Bug]",
+ "If someone reports a security vulnerability, label as @[Security] and forward to security@company.com",
+ "Label emails about job interviews as @[Job Search]",
+ "Label emails from recruiters as @[Recruiter] and archive them",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -156,15 +156,15 @@ I've attached my media kit and pricing.
label: "🎨 Designer",
promptArray: [
...commonPrompts,
- 'Label emails from Figma, Adobe, Sketch, and other design tools as "Design" and archive them',
- 'Label emails from clients as "Client"',
- 'If someone sends design assets, label as "Design Assets" and forward to assets@company.com',
- 'Label emails from Dribbble, Behance, and other design inspiration sites as "Inspiration" and archive them',
- 'Label emails about design conferences as "Conference"',
+ "Label emails from Figma, Adobe, Sketch, and other design tools as @[Design] and archive them",
+ "Label emails from clients as @[Client]",
+ "If someone sends design assets, label as @[Design Assets] and forward to assets@company.com",
+ "Label emails from Dribbble, Behance, and other design inspiration sites as @[Inspiration] and archive them",
+ "Label emails about design conferences as @[Conference]",
"If someone requests brand assets, draft a reply with a link to our brand portal: https://brand.company.com",
- 'Label emails about user research as "Research"',
- 'Label emails about job interviews as "Job Search"',
- 'Label emails from recruiters as "Recruiter" and archive them',
+ "Label emails about user research as @[Research]",
+ "Label emails about job interviews as @[Job Search]",
+ "Label emails from recruiters as @[Recruiter] and archive them",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -174,18 +174,18 @@ I've attached my media kit and pricing.
label: "🤝 Sales",
promptArray: [
...commonPrompts,
- 'Label emails from prospects as "Prospect"',
- 'Label emails from customers as "Customer"',
- 'Label emails about deal negotiations as "Deal Discussion"',
- 'Label emails from sales tools as "Sales Tool" and archive them',
- 'Label emails about sales opportunities as "Sales Opportunity"',
+ "Label emails from prospects as @[Prospect]",
+ "Label emails from customers as @[Customer]",
+ "Label emails about deal negotiations as @[Deal Discussion]",
+ "Label emails from sales tools as @[Sales Tool] and archive them",
+ "Label emails about sales opportunities as @[Sales Opportunity]",
"If someone asks for pricing, draft a reply with our pricing page link: https://company.com/pricing",
- 'Label emails containing signed contracts as "Signed Contract" and forward to legal@company.com',
+ "Label emails containing signed contracts as @[Signed Contract] and forward to legal@company.com",
"If someone requests a demo, draft a reply with my calendar link: https://cal.com/example",
"If someone asks about product features, draft a reply with relevant feature documentation links",
- 'If someone reports implementation issues, label as "Support Need" and forward to support@company.com',
+ "If someone reports implementation issues, label as @[Support Need] and forward to support@company.com",
"If someone asks about enterprise pricing, draft a reply asking about their company size and requirements",
- 'If a customer mentions churn risk, label as "Churn Risk" and draft an urgent notification to the customer success team',
+ "If a customer mentions churn risk, label as @[Churn Risk] and draft an urgent notification to the customer success team",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -195,14 +195,14 @@ I've attached my media kit and pricing.
label: "📢 Marketer",
promptArray: [
...commonPrompts,
- 'Label emails from influencers as "Influencer"',
- 'Label emails from ad platforms (Google, Meta, LinkedIn) as "Advertising"',
- 'Label press inquiries as "Press" and forward to pr@company.com',
- 'Label emails about content marketing as "Content"',
- 'If someone asks about sponsorship, label as "Sponsorship" and draft a reply asking about their audience size',
- 'If someone requests to guest post, label as "Guest Post" and draft a reply with our guidelines',
- 'If someone asks about partnership opportunities, label as "Partnership" and draft a reply asking for their media kit',
- 'If someone reports broken marketing links, label as "Bug" and forward to tech@company.com',
+ "Label emails from influencers as @[Influencer]",
+ "Label emails from ad platforms (Google, Meta, LinkedIn) as @[Advertising]",
+ "Label press inquiries as @[Press] and forward to pr@company.com",
+ "Label emails about content marketing as @[Content]",
+ "If someone asks about sponsorship, label as @[Sponsorship] and draft a reply asking about their audience size",
+ "If someone requests to guest post, label as @[Guest Post] and draft a reply with our guidelines",
+ "If someone asks about partnership opportunities, label as @[Partnership] and draft a reply asking for their media kit",
+ "If someone reports broken marketing links, label as @[Bug] and forward to tech@company.com",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -212,18 +212,18 @@ I've attached my media kit and pricing.
label: "🛠️ Support",
promptArray: [
...commonPrompts,
- 'Label customer support tickets as "Support Ticket"',
- 'If someone reports a critical issue, label as "Urgent Support" and forward to urgent@company.com',
- 'Label bug reports as "Bug" and forward to engineering@company.com',
- 'Label feature requests as "Feature Request" and forward to product@company.com',
+ "Label customer support tickets as @[Support Ticket]",
+ "If someone reports a critical issue, label as @[Urgent Support] and forward to urgent@company.com",
+ "Label bug reports as @[Bug] and forward to engineering@company.com",
+ "Label feature requests as @[Feature Request] and forward to product@company.com",
"If someone asks for refund, draft a reply with our refund policy link: https://company.com/refund-policy",
- 'Label emails about account access issues as "Access Issue" and draft a reply asking for their account details',
+ "Label emails about account access issues as @[Access Issue] and draft a reply asking for their account details",
"If someone asks for product documentation, draft a reply with our help center link: https://help.company.com",
- 'Label emails about service outages as "Service Issue" and forward to status@company.com',
+ "Label emails about service outages as @[Service Issue] and forward to status@company.com",
"If someone needs technical assistance, draft a reply asking for their account details and specific error messages",
- 'Label positive feedback as "Testimonial" and forward to marketing@company.com',
- 'Label emails about API integration issues as "API Support"',
- 'If someone reports data privacy concerns, label as "Privacy", and draft a reply with our privacy policy link: https://company.com/privacy-policy',
+ "Label positive feedback as @[Testimonial] and forward to marketing@company.com",
+ "Label emails about API integration issues as @[API Support]",
+ "If someone reports data privacy concerns, label as @[Privacy], and draft a reply with our privacy policy link: https://company.com/privacy-policy",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -233,25 +233,25 @@ I've attached my media kit and pricing.
label: "👥 Recruiter",
promptArray: [
...commonPrompts,
- 'Label emails from candidates as "Candidate"',
- 'Label emails from hiring managers as "Hiring Manager"',
- 'Label emails from recruiters as "Recruiter" and draft a reply with our hiring process overview link: https://company.com/hiring-process',
- 'Label emails from job boards as "Job Board" and archive them',
- 'Label emails from LinkedIn as "LinkedIn" and archive them',
- 'If someone applies for a job, label as "New Application" and draft a reply acknowledging their application',
- 'Label emails containing resumes or CVs as "Resume"',
- 'If a candidate asks about application status, label as "Status Update" and draft a reply asking for their position and date applied',
- 'Label emails about interview scheduling as "Interview Scheduling"',
- 'If someone accepts an interview invite, label as "Interview Confirmed" and forward to calendar@company.com',
- 'If someone declines a job offer, label as "Offer Declined" and forward to hiring-updates@company.com',
- 'If someone accepts a job offer, label as "Offer Accepted" and forward to onboarding@company.com',
- 'Label emails about salary negotiations as "Compensation"',
- 'Label emails about reference checks as "References"',
+ "Label emails from candidates as @[Candidate]",
+ "Label emails from hiring managers as @[Hiring Manager]",
+ "Label emails from recruiters as @[Recruiter] and draft a reply with our hiring process overview link: https://company.com/hiring-process",
+ "Label emails from job boards as @[Job Board] and archive them",
+ "Label emails from LinkedIn as @[LinkedIn] and archive them",
+ "If someone applies for a job, label as @[New Application] and draft a reply acknowledging their application",
+ "Label emails containing resumes or CVs as @[Resume]",
+ "If a candidate asks about application status, label as @[Status Update] and draft a reply asking for their position and date applied",
+ "Label emails about interview scheduling as @[Interview Scheduling]",
+ "If someone accepts an interview invite, label as @[Interview Confirmed] and forward to calendar@company.com",
+ "If someone declines a job offer, label as @[Offer Declined] and forward to hiring-updates@company.com",
+ "If someone accepts a job offer, label as @[Offer Accepted] and forward to onboarding@company.com",
+ "Label emails about salary negotiations as @[Compensation]",
+ "Label emails about reference checks as @[References]",
"If someone asks about benefits, draft a reply with our benefits overview link: https://company.com/benefits",
- 'Label emails about background checks as "Background Check"',
- 'If an internal employee refers someone, label as "Employee Referral"',
- 'Label emails about recruitment events or job fairs as "Recruiting Event"',
- 'If someone withdraws their application, label as "Withdrawn"',
+ "Label emails about background checks as @[Background Check]",
+ "If an internal employee refers someone, label as @[Employee Referral]",
+ "Label emails about recruitment events or job fairs as @[Recruiting Event]",
+ "If someone withdraws their application, label as @[Withdrawn]",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -260,14 +260,14 @@ I've attached my media kit and pricing.
student: {
label: "👩🎓 Student",
promptArray: [
- 'Label emails from professors and teaching assistants as "School"',
- 'Label emails about assignments and homework as "Assignment"',
- 'If someone sends class notes or study materials, label as "Study Materials"',
- 'Label emails about internships as "Internship" and forward to my personal email me@example.com',
- 'Label emails about exam schedules as "Exam"',
- 'Label emails about campus events as "Event" and archive them',
+ "Label emails from professors and teaching assistants as @[School]",
+ "Label emails about assignments and homework as @[Assignment]",
+ "If someone sends class notes or study materials, label as @[Study Materials]",
+ "Label emails about internships as @[Internship] and forward to my personal email me@example.com",
+ "Label emails about exam schedules as @[Exam]",
+ "Label emails about campus events as @[Event] and archive them",
"If someone asks for class notes, draft a reply with our shared Google Drive folder link: https://drive.google.com/drive/u/0/folders/1234567890",
- 'Label emails about tutoring opportunities as "Tutoring" and draft a reply with that my rate is $70/hour or $40/hour for group tutoring',
+ "Label emails about tutoring opportunities as @[Tutoring] and draft a reply with that my rate is $70/hour or $40/hour for group tutoring",
],
get prompt() {
return formatPromptArray(this.promptArray);
@@ -276,7 +276,7 @@ I've attached my media kit and pricing.
reachout: {
label: "💬 Outreach",
promptArray: [
- 'If someone replies to me that they\'re interested, label it "Interested" and draft a reply with my calendar link: https://cal.com/example',
+ "If someone replies to me that they're interested, label it @[Interested] and draft a reply with my calendar link: https://cal.com/example",
],
get prompt() {
return formatPromptArray(this.promptArray);
diff --git a/apps/web/app/(app)/onboarding/OnboardingEmailAssistant.tsx b/apps/web/app/(app)/onboarding/OnboardingEmailAssistant.tsx
index 4d349112d1..1b96747262 100644
--- a/apps/web/app/(app)/onboarding/OnboardingEmailAssistant.tsx
+++ b/apps/web/app/(app)/onboarding/OnboardingEmailAssistant.tsx
@@ -30,6 +30,7 @@ import {
type RulesExamplesBody,
} from "@/utils/actions/rule.validation";
import { examplePrompts } from "@/app/(app)/[emailAccountId]/assistant/examples";
+import { convertLabelsToDisplay } from "@/utils/mention";
import { useAccount } from "@/providers/EmailAccountProvider";
type RulesExamplesResponse = InferSafeActionFnResult<
@@ -127,7 +128,7 @@ ${defaultPrompt}`}
className="h-auto w-full justify-start text-wrap py-2 text-left"
onClick={() => addExamplePrompt(example)}
>
- {example}
+ {convertLabelsToDisplay(example)}
))}
diff --git a/apps/web/components/assistant-chat/examples-dialog.tsx b/apps/web/components/assistant-chat/examples-dialog.tsx
index ef137a5ff7..cd52e1d562 100644
--- a/apps/web/components/assistant-chat/examples-dialog.tsx
+++ b/apps/web/components/assistant-chat/examples-dialog.tsx
@@ -18,6 +18,7 @@ import {
CheckCircle2Icon,
} from "lucide-react";
import { personas } from "@/app/(app)/[emailAccountId]/assistant/examples";
+import { convertLabelsToDisplay } from "@/utils/mention";
import { Tooltip } from "@/components/Tooltip";
import { ButtonList } from "@/components/ButtonList";
import { parseAsStringEnum, useQueryState } from "nuqs";
@@ -144,7 +145,7 @@ export function ExamplesDialog({
)}
- {example}
+ {convertLabelsToDisplay(example)}
diff --git a/apps/web/components/editor/extensions/LabelMention.tsx b/apps/web/components/editor/extensions/LabelMention.tsx
index 2953979680..dda52ad1c7 100644
--- a/apps/web/components/editor/extensions/LabelMention.tsx
+++ b/apps/web/components/editor/extensions/LabelMention.tsx
@@ -180,17 +180,18 @@ export const createLabelMentionExtension = (labels: UserLabel[]) => {
// Find the label in our labels array
const label = labels.find((l) => l.name === labelName);
- if (!label) return false;
+ // Create mention node even if label doesn't exist yet
+ // This allows examples to work even when labels haven't been created in Gmail
if (!silent) {
const token = state.push("mention_open", "mention", 1);
token.attrs = [
- ["id", label.id],
- ["label", label.name],
+ ["id", label?.id || `placeholder-${labelName}`],
+ ["label", labelName],
];
const textToken = state.push("text", "", 0);
- textToken.content = `@${label.name}`;
+ textToken.content = `@${labelName}`;
state.push("mention_close", "mention", -1);
}
diff --git a/apps/web/utils/mention.test.ts b/apps/web/utils/mention.test.ts
index 4952a2b0aa..2b70d7dfca 100644
--- a/apps/web/utils/mention.test.ts
+++ b/apps/web/utils/mention.test.ts
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
-import { convertMentionsToLabels } from "./mention";
+import { convertMentionsToLabels, convertLabelsToDisplay } from "./mention";
describe("convertMentionsToLabels", () => {
it("converts single mention to label", () => {
@@ -110,3 +110,101 @@ describe("convertMentionsToLabels", () => {
expect(convertMentionsToLabels(input)).toBe(expected);
});
});
+
+describe("convertLabelsToDisplay", () => {
+ it("converts single mention to quoted label", () => {
+ const input = "Label this email as @[Newsletter]";
+ const expected = 'Label this email as "Newsletter"';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("converts multiple mentions to quoted labels", () => {
+ const input = "Label as @[Important] and @[Work] and archive";
+ const expected = 'Label as "Important" and "Work" and archive';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles mentions with spaces in label names", () => {
+ const input = "Apply @[Very Important] and @[Work Project] labels";
+ const expected = 'Apply "Very Important" and "Work Project" labels';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles mentions with special characters in label names", () => {
+ const input = "Label as @[Finance/Tax] and @[Client-A] and @[2024_Q1]";
+ const expected = 'Label as "Finance/Tax" and "Client-A" and "2024_Q1"';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles mentions at the beginning of text", () => {
+ const input = "@[Newsletter] emails should be archived";
+ const expected = '"Newsletter" emails should be archived';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles mentions at the end of text", () => {
+ const input = "Archive and label as @[Newsletter]";
+ const expected = 'Archive and label as "Newsletter"';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles text with no mentions", () => {
+ const input = "Archive all newsletters automatically";
+ const expected = "Archive all newsletters automatically";
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles empty string", () => {
+ const input = "";
+ const expected = "";
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles mentions in multiline text", () => {
+ const input = `When I get a newsletter, archive it and label it as @[Newsletter]
+
+ For urgent emails from company.com, label as @[Urgent] and forward to support@company.com`;
+
+ const expected = `When I get a newsletter, archive it and label it as "Newsletter"
+
+ For urgent emails from company.com, label as "Urgent" and forward to support@company.com`;
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("preserves regular @ symbols that are not mentions", () => {
+ const input = "Forward to support@company.com and label as @[Support]";
+ const expected = 'Forward to support@company.com and label as "Support"';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles malformed mentions gracefully", () => {
+ const input = "Label as @[Newsletter and @Missing] and @[Complete]";
+ const expected = 'Label as "Newsletter and @Missing" and "Complete"';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles nested brackets in mentions", () => {
+ const input = "Label as @[Project [Alpha]] and continue";
+ const expected = 'Label as "Project [Alpha]" and continue';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+
+ it("handles mentions with numbers and symbols", () => {
+ const input = "Apply @[2024-Q1] and @[Client#123] labels";
+ const expected = 'Apply "2024-Q1" and "Client#123" labels';
+
+ expect(convertLabelsToDisplay(input)).toBe(expected);
+ });
+});
diff --git a/apps/web/utils/mention.ts b/apps/web/utils/mention.ts
index 05d9074a4b..44bfb58316 100644
--- a/apps/web/utils/mention.ts
+++ b/apps/web/utils/mention.ts
@@ -4,5 +4,58 @@
* receives clean label names without the mention syntax
*/
export function convertMentionsToLabels(promptFile: string): string {
- return promptFile.replace(/@\[([^\]]+)\]/g, "$1");
+ return processMentions(promptFile, (match) => match);
+}
+
+/**
+ * Converts @[LABEL] format to "LABEL" for display in the UI
+ * This is the inverse of convertMentionsToLabels
+ */
+export function convertLabelsToDisplay(text: string): string {
+ return processMentions(text, (match) => `"${match}"`);
+}
+
+/**
+ * Helper function to process mentions with proper bracket matching
+ */
+function processMentions(
+ text: string,
+ transformer: (match: string) => string,
+): string {
+ let result = "";
+ let i = 0;
+
+ while (i < text.length) {
+ // Look for @[
+ if (i < text.length - 1 && text[i] === "@" && text[i + 1] === "[") {
+ // Found start of mention, find the matching closing bracket
+ let bracketCount = 1;
+ let j = i + 2;
+
+ while (j < text.length && bracketCount > 0) {
+ if (text[j] === "[") {
+ bracketCount++;
+ } else if (text[j] === "]") {
+ bracketCount--;
+ }
+ j++;
+ }
+
+ if (bracketCount === 0) {
+ // Found matching closing bracket
+ const labelContent = text.slice(i + 2, j - 1);
+ result += transformer(labelContent);
+ i = j;
+ } else {
+ // No matching bracket found, treat as regular text
+ result += text[i];
+ i++;
+ }
+ } else {
+ result += text[i];
+ i++;
+ }
+ }
+
+ return result;
}
From b94ed4b8f22f1004f7bbac1219121cc944df22e6 Mon Sep 17 00:00:00 2001
From: Eliezer Steinbock <3090527+elie222@users.noreply.github.com>
Date: Wed, 16 Jul 2025 09:41:59 +0200
Subject: [PATCH 2/2] v1.9.9
---
version.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.txt b/version.txt
index b0376728d8..ecd7e10ccf 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-v1.9.8
+v1.9.9
\ No newline at end of file