myoperator-ui 0.0.204-beta.1 → 0.0.204-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +426 -368
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1033,7 +1033,7 @@ import { cn } from "../../lib/utils";
1033
1033
  * SelectTrigger variants matching TextField styling
1034
1034
  */
1035
1035
  const selectTriggerVariants = cva(
1036
- "flex h-10 w-full items-center justify-between rounded bg-semantic-bg-primary px-4 py-2.5 text-sm text-semantic-text-primary outline-none transition-all disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)] [&>span]:line-clamp-1",
1036
+ "flex h-10 w-full items-center justify-between rounded bg-semantic-bg-primary px-4 py-2.5 text-sm text-semantic-text-primary outline-none transition-all disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-[var(--color-neutral-50)] [&>span]:line-clamp-1 [&_[data-placeholder]]:text-semantic-text-placeholder",
1037
1037
  {
1038
1038
  variants: {
1039
1039
  state: {
@@ -3334,14 +3334,14 @@ const DialogOverlay = React.forwardRef<
3334
3334
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
3335
3335
 
3336
3336
  const dialogContentVariants = cva(
3337
- "fixed left-[50%] top-[50%] z-[9999] grid translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg",
3337
+ "fixed left-[50%] top-[50%] z-[9999] grid translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-4 sm:p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg max-h-[calc(100vh-2rem)] overflow-y-auto [&::-webkit-scrollbar]:hidden [scrollbar-width:none] [&>*]:min-w-0",
3338
3338
  {
3339
3339
  variants: {
3340
3340
  size: {
3341
- sm: "w-full max-w-sm",
3342
- default: "w-full max-w-lg",
3343
- lg: "w-full max-w-2xl",
3344
- xl: "w-full max-w-4xl",
3341
+ sm: "w-[calc(100%-2rem)] max-w-sm",
3342
+ default: "w-[calc(100%-2rem)] max-w-lg",
3343
+ lg: "w-[calc(100%-2rem)] max-w-2xl",
3344
+ xl: "w-[calc(100%-2rem)] max-w-4xl",
3345
3345
  full: "w-[calc(100%-2rem)] h-[calc(100%-2rem)] max-w-none",
3346
3346
  },
3347
3347
  },
@@ -3418,7 +3418,7 @@ const DialogHeader = ({
3418
3418
  }: React.HTMLAttributes<HTMLDivElement>) => (
3419
3419
  <div
3420
3420
  className={cn(
3421
- "flex flex-col space-y-1.5 text-center sm:text-left",
3421
+ "flex flex-col space-y-1.5 text-left",
3422
3422
  className
3423
3423
  )}
3424
3424
  {...props}
@@ -12030,8 +12030,10 @@ export const BotCard = React.forwardRef<HTMLDivElement, BotCardProps>(
12030
12030
  Last Published
12031
12031
  </span>
12032
12032
  {bot.lastPublishedBy && bot.lastPublishedDate ? (
12033
- <p className="m-0 text-sm text-semantic-text-muted">
12034
- {bot.lastPublishedBy} | {bot.lastPublishedDate}
12033
+ <p className="m-0 text-sm text-semantic-text-muted break-words">
12034
+ <span className="inline-block">{bot.lastPublishedBy}</span>
12035
+ <span className="mx-1">|</span>
12036
+ <span className="inline-block">{bot.lastPublishedDate}</span>
12035
12037
  </p>
12036
12038
  ) : (
12037
12039
  <p className="m-0 text-sm text-semantic-text-muted">\u2014</p>
@@ -12136,7 +12138,7 @@ export const CreateBotModal = React.forwardRef<
12136
12138
  <span className="text-sm font-semibold text-semantic-text-secondary tracking-[0.014px]">
12137
12139
  Select Bot Type
12138
12140
  </span>
12139
- <div className="flex gap-3">
12141
+ <div className="flex flex-col sm:flex-row gap-3">
12140
12142
  {BOT_TYPE_OPTIONS.map(({ id, label, description }) => {
12141
12143
  const isSelected = selectedType === id;
12142
12144
  return (
@@ -12145,7 +12147,7 @@ export const CreateBotModal = React.forwardRef<
12145
12147
  type="button"
12146
12148
  onClick={() => setSelectedType(id)}
12147
12149
  className={cn(
12148
- "flex flex-col gap-2.5 p-3 rounded-lg border text-left flex-1 h-[134px] justify-center",
12150
+ "flex flex-col gap-2.5 p-3 rounded-lg border text-left flex-1 sm:h-[134px] justify-center",
12149
12151
  "transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-semantic-border-focus",
12150
12152
  isSelected
12151
12153
  ? "bg-semantic-info-surface border-semantic-border-focus shadow-sm"
@@ -12247,7 +12249,7 @@ export const BotList = React.forwardRef<HTMLDivElement, BotListProps>(
12247
12249
  return (
12248
12250
  <div ref={ref} className={cn("flex flex-col w-full", className)}>
12249
12251
  {/* Page header */}
12250
- <div className="flex items-center justify-between pb-5 mb-6 border-b border-semantic-border-layout">
12252
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 pb-5 mb-6 border-b border-semantic-border-layout">
12251
12253
  <div className="flex flex-col gap-1.5">
12252
12254
  <h1 className="m-0 text-base font-semibold text-semantic-text-primary tracking-[0.064px]">
12253
12255
  {title}
@@ -12258,20 +12260,20 @@ export const BotList = React.forwardRef<HTMLDivElement, BotListProps>(
12258
12260
  </div>
12259
12261
 
12260
12262
  {/* Search bar */}
12261
- <div className="flex items-center gap-2 h-10 px-2.5 border border-semantic-border-input rounded bg-semantic-bg-primary hover:border-semantic-border-input-focus focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]">
12263
+ <div className="flex items-center gap-2 h-10 px-2.5 border border-semantic-border-input rounded bg-semantic-bg-primary hover:border-semantic-border-input-focus focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)] w-full sm:w-auto">
12262
12264
  <Search className="size-[14px] text-semantic-text-muted shrink-0" />
12263
12265
  <input
12264
12266
  type="text"
12265
12267
  value={searchQuery}
12266
12268
  onChange={(e) => handleSearch(e.target.value)}
12267
12269
  placeholder="Search bot..."
12268
- className="text-sm text-semantic-text-primary placeholder:text-semantic-text-muted bg-transparent outline-none w-[180px]"
12270
+ className="text-sm text-semantic-text-primary placeholder:text-semantic-text-muted bg-transparent outline-none w-full sm:w-[180px]"
12269
12271
  />
12270
12272
  </div>
12271
12273
  </div>
12272
12274
 
12273
12275
  {/* Bot grid */}
12274
- <div className="grid grid-cols-3 gap-6">
12276
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
12275
12277
  {/* Create new bot card */}
12276
12278
  <button
12277
12279
  type="button"
@@ -12406,7 +12408,6 @@ export type { BotListProps, Bot, BotType } from "./types";
12406
12408
  content: prefixTailwindClasses(`import * as React from "react";
12407
12409
  import {
12408
12410
  ChevronLeft,
12409
- ChevronDown,
12410
12411
  Plus,
12411
12412
  Download,
12412
12413
  Trash2,
@@ -12430,6 +12431,20 @@ import {
12430
12431
  AccordionTrigger,
12431
12432
  AccordionContent,
12432
12433
  } from "../accordion";
12434
+ import {
12435
+ Select,
12436
+ SelectTrigger,
12437
+ SelectValue,
12438
+ SelectContent,
12439
+ SelectItem,
12440
+ } from "../select";
12441
+ import {
12442
+ Tooltip,
12443
+ TooltipTrigger,
12444
+ TooltipContent,
12445
+ TooltipArrow,
12446
+ TooltipProvider,
12447
+ } from "../tooltip";
12433
12448
  import { CreateFunctionModal } from "./create-function-modal";
12434
12449
  import type {
12435
12450
  IvrBotConfigProps,
@@ -12458,13 +12473,13 @@ function SectionCard({
12458
12473
  className
12459
12474
  )}
12460
12475
  >
12461
- <div className="flex items-center justify-between px-6 py-4 border-b border-semantic-border-layout">
12476
+ <div className="flex items-center justify-between px-4 sm:px-6 py-4 border-b border-semantic-border-layout">
12462
12477
  <h2 className="m-0 text-base font-semibold text-semantic-text-primary">
12463
12478
  {title}
12464
12479
  </h2>
12465
12480
  {action}
12466
12481
  </div>
12467
- <div className="px-6 py-5">{children}</div>
12482
+ <div className="px-4 sm:px-6 py-5">{children}</div>
12468
12483
  </div>
12469
12484
  );
12470
12485
  }
@@ -12521,7 +12536,7 @@ function StyledInput({
12521
12536
  className={cn(
12522
12537
  "w-full h-10 px-4 text-sm rounded border",
12523
12538
  "border-semantic-border-input bg-semantic-bg-primary",
12524
- "text-semantic-text-primary placeholder:text-semantic-text-muted",
12539
+ "text-semantic-text-primary placeholder:text-semantic-text-placeholder",
12525
12540
  "outline-none hover:border-semantic-border-input-focus",
12526
12541
  "focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
12527
12542
  className
@@ -12553,7 +12568,7 @@ function StyledTextarea({
12553
12568
  className={cn(
12554
12569
  "w-full px-4 py-2.5 text-sm rounded border resize-none",
12555
12570
  "border-semantic-border-input bg-semantic-bg-primary",
12556
- "text-semantic-text-primary placeholder:text-semantic-text-muted",
12571
+ "text-semantic-text-primary placeholder:text-semantic-text-placeholder",
12557
12572
  "outline-none hover:border-semantic-border-input-focus",
12558
12573
  "focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
12559
12574
  className
@@ -12562,56 +12577,6 @@ function StyledTextarea({
12562
12577
  );
12563
12578
  }
12564
12579
 
12565
- // \u2500\u2500\u2500 Styled Select \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
12566
- function StyledSelect({
12567
- placeholder,
12568
- value,
12569
- onChange,
12570
- options,
12571
- disabled,
12572
- className,
12573
- }: {
12574
- placeholder?: string;
12575
- value?: string;
12576
- onChange?: (v: string) => void;
12577
- options?: { label: string; value: string }[];
12578
- disabled?: boolean;
12579
- className?: string;
12580
- }) {
12581
- return (
12582
- <div className={cn("relative w-full", className)}>
12583
- <select
12584
- value={value ?? ""}
12585
- onChange={(e) => onChange?.(e.target.value)}
12586
- disabled={disabled}
12587
- className={cn(
12588
- "w-full h-10 pl-4 pr-10 text-sm rounded border appearance-none cursor-pointer",
12589
- "border-semantic-border-input bg-semantic-bg-primary",
12590
- value ? "text-semantic-text-primary" : "text-semantic-text-muted",
12591
- "outline-none hover:border-semantic-border-input-focus",
12592
- "focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
12593
- "disabled:opacity-50 disabled:cursor-not-allowed"
12594
- )}
12595
- >
12596
- {placeholder && (
12597
- <option value="" disabled>
12598
- {placeholder}
12599
- </option>
12600
- )}
12601
- {options?.map((opt) => (
12602
- <option key={opt.value} value={opt.value}>
12603
- {opt.label}
12604
- </option>
12605
- ))}
12606
- </select>
12607
- <ChevronDown
12608
- className="absolute right-3 top-1/2 -translate-y-1/2 size-4 text-semantic-text-muted pointer-events-none shrink-0"
12609
- aria-hidden="true"
12610
- />
12611
- </div>
12612
- );
12613
- }
12614
-
12615
12580
  // \u2500\u2500\u2500 Who The Bot Is \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
12616
12581
  function WhoTheBotIs({
12617
12582
  data,
@@ -12635,54 +12600,74 @@ function WhoTheBotIs({
12635
12600
  </Field>
12636
12601
 
12637
12602
  <Field label="Primary Role">
12638
- <StyledInput
12639
- placeholder="e.g., Customer Support Agent"
12640
- value={data.primaryRole}
12641
- onChange={(v) => onChange({ primaryRole: v })}
12642
- />
12603
+ <Select
12604
+ value={data.primaryRole || undefined}
12605
+ onValueChange={(v) => onChange({ primaryRole: v })}
12606
+ >
12607
+ <SelectTrigger>
12608
+ <SelectValue placeholder="e.g., Customer Support Agent" />
12609
+ </SelectTrigger>
12610
+ <SelectContent>
12611
+ <SelectItem value="customer-support-agent">Customer Support Agent</SelectItem>
12612
+ <SelectItem value="sales-representative">Sales Representative</SelectItem>
12613
+ <SelectItem value="technical-support">Technical Support</SelectItem>
12614
+ <SelectItem value="billing-support">Billing Support</SelectItem>
12615
+ <SelectItem value="general-inquiries">General Inquiries</SelectItem>
12616
+ </SelectContent>
12617
+ </Select>
12643
12618
  </Field>
12644
12619
 
12645
12620
  <Field label="Tone">
12646
- <StyledSelect
12647
- placeholder="Enter or select tone"
12648
- value={data.tone}
12649
- onChange={(v) => onChange({ tone: v })}
12650
- options={[
12651
- { value: "conversational", label: "Conversational" },
12652
- { value: "professional", label: "Professional" },
12653
- { value: "friendly", label: "Friendly" },
12654
- { value: "formal", label: "Formal" },
12655
- ]}
12656
- />
12657
- <p className="m-0 text-xs text-semantic-text-muted mt-0.5">
12658
- Press Enter to add &ldquo;Conversational&rdquo;
12659
- </p>
12621
+ <Select
12622
+ value={data.tone || undefined}
12623
+ onValueChange={(v) => onChange({ tone: v })}
12624
+ >
12625
+ <SelectTrigger>
12626
+ <SelectValue placeholder="Enter or select tone" />
12627
+ </SelectTrigger>
12628
+ <SelectContent>
12629
+ <SelectItem value="conversational">Conversational</SelectItem>
12630
+ <SelectItem value="professional">Professional</SelectItem>
12631
+ <SelectItem value="friendly">Friendly</SelectItem>
12632
+ <SelectItem value="formal">Formal</SelectItem>
12633
+ </SelectContent>
12634
+ </Select>
12635
+ <div className="flex items-center gap-1.5 text-xs text-semantic-text-muted">
12636
+ <Info className="size-3.5 shrink-0" />
12637
+ <p className="m-0">Press Enter to add &ldquo;Conversational&rdquo;</p>
12638
+ </div>
12660
12639
  </Field>
12661
12640
 
12662
- <div className="grid grid-cols-2 gap-4">
12641
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
12663
12642
  <Field label="How It Sounds">
12664
- <StyledSelect
12665
- placeholder="Rhea - Female"
12666
- value={data.voice}
12667
- onChange={(v) => onChange({ voice: v })}
12668
- options={[
12669
- { value: "rhea-female", label: "Rhea - Female" },
12670
- { value: "james-male", label: "James - Male" },
12671
- { value: "aria-female", label: "Aria - Female" },
12672
- ]}
12673
- />
12643
+ <Select
12644
+ value={data.voice || undefined}
12645
+ onValueChange={(v) => onChange({ voice: v })}
12646
+ >
12647
+ <SelectTrigger>
12648
+ <SelectValue placeholder="Rhea - Female" />
12649
+ </SelectTrigger>
12650
+ <SelectContent>
12651
+ <SelectItem value="rhea-female">Rhea - Female</SelectItem>
12652
+ <SelectItem value="james-male">James - Male</SelectItem>
12653
+ <SelectItem value="aria-female">Aria - Female</SelectItem>
12654
+ </SelectContent>
12655
+ </Select>
12674
12656
  </Field>
12675
12657
  <Field label="What Language It Speaks">
12676
- <StyledSelect
12677
- placeholder="Select Language Mode"
12678
- value={data.language}
12679
- onChange={(v) => onChange({ language: v })}
12680
- options={[
12681
- { value: "en-in", label: "English (India)" },
12682
- { value: "en-us", label: "English (US)" },
12683
- { value: "hi-in", label: "Hindi" },
12684
- ]}
12685
- />
12658
+ <Select
12659
+ value={data.language || undefined}
12660
+ onValueChange={(v) => onChange({ language: v })}
12661
+ >
12662
+ <SelectTrigger>
12663
+ <SelectValue placeholder="Select Language Mode" />
12664
+ </SelectTrigger>
12665
+ <SelectContent>
12666
+ <SelectItem value="en-in">English (India)</SelectItem>
12667
+ <SelectItem value="en-us">English (US)</SelectItem>
12668
+ <SelectItem value="hi-in">Hindi</SelectItem>
12669
+ </SelectContent>
12670
+ </Select>
12686
12671
  </Field>
12687
12672
  </div>
12688
12673
  </div>
@@ -12694,7 +12679,9 @@ function WhoTheBotIs({
12694
12679
  const SESSION_VARIABLES = [
12695
12680
  "{{Caller number}}",
12696
12681
  "{{Time}}",
12682
+ "{{Morning/Afternoon/Evening}}",
12697
12683
  "{{Contact Details}}",
12684
+ "{{DID}}",
12698
12685
  ];
12699
12686
 
12700
12687
  function HowItBehaves({
@@ -12713,11 +12700,12 @@ function HowItBehaves({
12713
12700
 
12714
12701
  return (
12715
12702
  <SectionCard title="How It Behaves">
12716
- <div className="flex flex-col gap-3">
12717
- <p className="m-0 text-xs text-semantic-text-muted">
12718
- Define workflows, conditions and handover logic (System prompt)
12719
- </p>
12720
- <div className="relative">
12703
+ <div className="flex flex-col gap-6">
12704
+ {/* Prompt editor group */}
12705
+ <div className="flex flex-col gap-1.5">
12706
+ <p className="m-0 text-sm font-semibold text-semantic-text-secondary">
12707
+ Define workflows, conditions and handover logic (System prompt)
12708
+ </p>
12721
12709
  <StyledTextarea
12722
12710
  value={prompt}
12723
12711
  rows={6}
@@ -12725,17 +12713,22 @@ function HowItBehaves({
12725
12713
  if (v.length <= MAX) onChange({ systemPrompt: v });
12726
12714
  }}
12727
12715
  placeholder="You are a helpful assistant. Always start by greeting the user politely: 'Hello! Welcome. How can I assist you today?'"
12728
- className="pb-8"
12729
12716
  />
12730
- <span className="absolute bottom-2.5 right-3 text-xs text-semantic-text-muted">
12731
- {prompt.length}/{MAX}
12732
- </span>
12717
+ {/* Helper row: info + hint text on left, counter on right */}
12718
+ <div className="flex items-center justify-between gap-2">
12719
+ <div className="flex items-center gap-1.5 text-xs text-semantic-text-muted">
12720
+ <Info className="size-3.5 shrink-0" />
12721
+ <p className="m-0">Type {"{{"} to enable dropdown or use the below chips to input variables.</p>
12722
+ </div>
12723
+ <span className="shrink-0 text-xs text-semantic-text-muted whitespace-nowrap">
12724
+ {prompt.length}/{MAX}
12725
+ </span>
12726
+ </div>
12733
12727
  </div>
12734
- <p className="m-0 text-xs text-semantic-text-muted">
12735
- Type [[ to enable dropdown or use the below chips to input variables.
12736
- </p>
12728
+
12729
+ {/* Session variables row */}
12737
12730
  <div className="flex flex-wrap items-center gap-2">
12738
- <span className="text-xs text-semantic-text-secondary">
12731
+ <span className="text-xs font-semibold text-semantic-text-muted">
12739
12732
  Session variables:
12740
12733
  </span>
12741
12734
  {SESSION_VARIABLES.map((v) => (
@@ -12743,9 +12736,12 @@ function HowItBehaves({
12743
12736
  key={v}
12744
12737
  type="button"
12745
12738
  onClick={() => insertVariable(v)}
12746
- className={cn(tagVariants(), "gap-1.5 cursor-pointer hover:opacity-80 transition-opacity")}
12739
+ className={cn(
12740
+ tagVariants({ size: "sm" }),
12741
+ "gap-2.5 cursor-pointer hover:opacity-80 transition-opacity"
12742
+ )}
12747
12743
  >
12748
- <Plus className="size-3 shrink-0" />
12744
+ <Plus className="size-2.5 shrink-0" />
12749
12745
  {v}
12750
12746
  </button>
12751
12747
  ))}
@@ -12767,14 +12763,24 @@ function FallbackPromptsAccordion({
12767
12763
  <div className="bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden">
12768
12764
  <Accordion type="single">
12769
12765
  <AccordionItem value="fallback">
12770
- <AccordionTrigger className="px-6 py-5 border-b border-semantic-border-layout hover:no-underline">
12766
+ <AccordionTrigger className="px-4 sm:px-6 py-5 border-b border-semantic-border-layout hover:no-underline">
12771
12767
  <span className="flex items-center gap-1.5 text-base font-semibold text-semantic-text-primary">
12772
12768
  Fallback Prompts
12773
- <Info className="size-3.5 text-semantic-text-muted shrink-0" />
12769
+ <Tooltip>
12770
+ <TooltipTrigger asChild>
12771
+ <span className="inline-flex cursor-default">
12772
+ <Info className="size-3.5 text-semantic-text-muted shrink-0" />
12773
+ </span>
12774
+ </TooltipTrigger>
12775
+ <TooltipContent>
12776
+ <p className="m-0">Configure what the bot says when agents are busy or extensions are unavailable.</p>
12777
+ <TooltipArrow />
12778
+ </TooltipContent>
12779
+ </Tooltip>
12774
12780
  </span>
12775
12781
  </AccordionTrigger>
12776
12782
  <AccordionContent>
12777
- <div className="px-6 pt-6 pb-2 flex flex-col gap-6">
12783
+ <div className="px-4 sm:px-6 pt-6 pb-2 flex flex-col gap-6">
12778
12784
  <Field label="Agent Busy Prompt">
12779
12785
  <StyledTextarea
12780
12786
  value={data.agentBusyPrompt ?? ""}
@@ -12886,7 +12892,7 @@ function FileUploadModal({
12886
12892
  <DialogContent
12887
12893
  size="default"
12888
12894
  hideCloseButton
12889
- className="max-w-[660px] rounded-xl p-6 gap-0"
12895
+ className="max-w-[660px] rounded-xl gap-0"
12890
12896
  >
12891
12897
  {/* Header */}
12892
12898
  <div className="flex items-center justify-between mb-6">
@@ -12905,7 +12911,7 @@ function FileUploadModal({
12905
12911
  </div>
12906
12912
 
12907
12913
  {/* Body */}
12908
- <div className="flex flex-col gap-4 items-end w-full">
12914
+ <div className="flex flex-col gap-4 items-center sm:items-end w-full">
12909
12915
  {/* Download sample file */}
12910
12916
  <button
12911
12917
  type="button"
@@ -12925,7 +12931,7 @@ function FileUploadModal({
12925
12931
  }}
12926
12932
  onDragOver={(e) => e.preventDefault()}
12927
12933
  >
12928
- <div className="flex items-center gap-4">
12934
+ <div className="flex flex-col items-center sm:flex-row sm:items-center gap-3 sm:gap-4">
12929
12935
  <button
12930
12936
  type="button"
12931
12937
  onClick={() => fileInputRef.current?.click()}
@@ -12933,7 +12939,7 @@ function FileUploadModal({
12933
12939
  >
12934
12940
  Upload from device
12935
12941
  </button>
12936
- <div className="flex flex-col gap-1">
12942
+ <div className="flex flex-col gap-1 text-center sm:text-left">
12937
12943
  <p className="m-0 text-sm text-semantic-text-secondary tracking-[0.035px]">
12938
12944
  or drag and drop file here
12939
12945
  </p>
@@ -13021,11 +13027,11 @@ function FileUploadModal({
13021
13027
  </div>
13022
13028
 
13023
13029
  {/* Footer */}
13024
- <div className="flex items-center justify-end gap-2 mt-6">
13025
- <Button variant="outline" onClick={handleClose}>
13030
+ <div className="flex items-center justify-between gap-2 mt-6">
13031
+ <Button variant="outline" onClick={handleClose} className="flex-1 sm:flex-none sm:w-auto">
13026
13032
  Cancel
13027
13033
  </Button>
13028
- <Button onClick={handleSave}>
13034
+ <Button onClick={handleSave} className="flex-1 sm:flex-none sm:w-auto">
13029
13035
  Save
13030
13036
  </Button>
13031
13037
  </div>
@@ -13062,12 +13068,22 @@ function KnowledgeBase({
13062
13068
  <>
13063
13069
  <div className="bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden">
13064
13070
  {/* Header */}
13065
- <div className="flex items-center justify-between px-6 py-4 border-b border-semantic-border-layout">
13071
+ <div className="flex items-center justify-between px-4 sm:px-6 py-4 border-b border-semantic-border-layout">
13066
13072
  <div className="flex items-center gap-1.5">
13067
13073
  <h2 className="m-0 text-base font-semibold text-semantic-text-primary">
13068
13074
  Knowledge Base
13069
13075
  </h2>
13070
- <Info className="size-3.5 text-semantic-text-muted shrink-0" />
13076
+ <Tooltip>
13077
+ <TooltipTrigger asChild>
13078
+ <span className="inline-flex cursor-default">
13079
+ <Info className="size-3.5 text-semantic-text-muted shrink-0" />
13080
+ </span>
13081
+ </TooltipTrigger>
13082
+ <TooltipContent>
13083
+ <p className="m-0">Upload documents and files to train your bot with your company knowledge.</p>
13084
+ <TooltipArrow />
13085
+ </TooltipContent>
13086
+ </Tooltip>
13071
13087
  </div>
13072
13088
  <button
13073
13089
  type="button"
@@ -13079,7 +13095,7 @@ function KnowledgeBase({
13079
13095
  </button>
13080
13096
  </div>
13081
13097
  {/* File list */}
13082
- <div className="px-6">
13098
+ <div className="px-4 sm:px-6">
13083
13099
  {files.length === 0 ? (
13084
13100
  <p className="m-0 text-sm text-semantic-text-muted text-center py-5">
13085
13101
  No files added yet. Click &ldquo;+ Files&rdquo; to upload.
@@ -13091,9 +13107,9 @@ function KnowledgeBase({
13091
13107
  return (
13092
13108
  <div
13093
13109
  key={file.id}
13094
- className="flex items-center justify-between py-4"
13110
+ className="flex flex-wrap items-center justify-between gap-2 py-4"
13095
13111
  >
13096
- <div className="flex items-center gap-2 min-w-0">
13112
+ <div className="flex items-center gap-2 min-w-0 flex-1">
13097
13113
  <span className="text-sm text-semantic-text-primary truncate">
13098
13114
  File {file.name}
13099
13115
  </span>
@@ -13105,7 +13121,7 @@ function KnowledgeBase({
13105
13121
  {status.label}
13106
13122
  </Badge>
13107
13123
  </div>
13108
- <div className="flex items-center gap-1 shrink-0 ml-2">
13124
+ <div className="flex items-center gap-1 shrink-0">
13109
13125
  <button
13110
13126
  type="button"
13111
13127
  onClick={() => onDownload?.(file.id)}
@@ -13151,12 +13167,22 @@ function FunctionsSection({
13151
13167
  return (
13152
13168
  <div className="bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden">
13153
13169
  {/* Header */}
13154
- <div className="flex items-center justify-between px-6 py-4 border-b border-semantic-border-layout">
13170
+ <div className="flex items-center justify-between px-4 sm:px-6 py-4 border-b border-semantic-border-layout">
13155
13171
  <div className="flex items-center gap-1.5">
13156
13172
  <h2 className="m-0 text-base font-semibold text-semantic-text-primary">
13157
13173
  Functions
13158
13174
  </h2>
13159
- <Info className="size-3.5 text-semantic-text-muted shrink-0" />
13175
+ <Tooltip>
13176
+ <TooltipTrigger asChild>
13177
+ <span className="inline-flex cursor-default">
13178
+ <Info className="size-3.5 text-semantic-text-muted shrink-0" />
13179
+ </span>
13180
+ </TooltipTrigger>
13181
+ <TooltipContent>
13182
+ <p className="m-0">Add custom API functions the bot can call to fetch or submit data during conversations.</p>
13183
+ <TooltipArrow />
13184
+ </TooltipContent>
13185
+ </Tooltip>
13160
13186
  </div>
13161
13187
  <button
13162
13188
  type="button"
@@ -13168,7 +13194,7 @@ function FunctionsSection({
13168
13194
  </button>
13169
13195
  </div>
13170
13196
  {/* Function list */}
13171
- <div className="px-6 py-4">
13197
+ <div className="px-4 sm:px-6 py-4">
13172
13198
  {functions.length === 0 ? (
13173
13199
  <p className="m-0 text-sm text-semantic-text-muted text-center py-2">
13174
13200
  No functions added yet.
@@ -13178,16 +13204,16 @@ function FunctionsSection({
13178
13204
  {functions.map((fn) => (
13179
13205
  <div
13180
13206
  key={fn.id}
13181
- className="flex items-center justify-between px-4 py-3 rounded border border-semantic-border-layout bg-semantic-bg-primary"
13207
+ className="flex flex-wrap items-center gap-2 px-4 py-3 rounded border border-semantic-border-layout bg-semantic-bg-primary"
13182
13208
  >
13183
- <div className="flex items-center gap-2 min-w-0">
13209
+ <div className="flex items-center gap-2 min-w-0 flex-1">
13184
13210
  <Info className="size-4 text-semantic-text-muted shrink-0" />
13185
13211
  <span className="text-sm text-semantic-text-primary truncate">
13186
13212
  {fn.name}
13187
13213
  </span>
13188
13214
  </div>
13189
13215
  {fn.isBuiltIn && (
13190
- <Badge size="sm" className="font-normal shrink-0 ml-3">
13216
+ <Badge size="sm" className="font-normal shrink-0">
13191
13217
  Built-in
13192
13218
  </Badge>
13193
13219
  )}
@@ -13212,15 +13238,25 @@ function FrustrationHandoverAccordion({
13212
13238
  <div className="bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden">
13213
13239
  <Accordion type="single">
13214
13240
  <AccordionItem value="frustration">
13215
- <AccordionTrigger className="px-6 py-5 border-b border-semantic-border-layout hover:no-underline">
13241
+ <AccordionTrigger className="px-4 sm:px-6 py-5 border-b border-semantic-border-layout hover:no-underline">
13216
13242
  <span className="flex items-center gap-1.5 text-base font-semibold text-semantic-text-primary">
13217
13243
  Frustration Handover
13218
- <Info className="size-3.5 text-semantic-text-muted shrink-0" />
13244
+ <Tooltip>
13245
+ <TooltipTrigger asChild>
13246
+ <span className="inline-flex cursor-default">
13247
+ <Info className="size-3.5 text-semantic-text-muted shrink-0" />
13248
+ </span>
13249
+ </TooltipTrigger>
13250
+ <TooltipContent>
13251
+ <p className="m-0">Automatically transfer the call to a human agent when the bot detects customer frustration.</p>
13252
+ <TooltipArrow />
13253
+ </TooltipContent>
13254
+ </Tooltip>
13219
13255
  </span>
13220
13256
  </AccordionTrigger>
13221
13257
  <AccordionContent>
13222
13258
  <div className="flex flex-col gap-6 pt-0 pb-2">
13223
- <div className="flex items-center justify-between px-6 py-2.5">
13259
+ <div className="flex items-center justify-between px-4 sm:px-6 py-2.5">
13224
13260
  <span className="text-sm text-semantic-text-primary">
13225
13261
  Enable frustration-based escalation
13226
13262
  </span>
@@ -13231,19 +13267,22 @@ function FrustrationHandoverAccordion({
13231
13267
  }
13232
13268
  />
13233
13269
  </div>
13234
- <div className="px-6 pb-2">
13270
+ <div className="px-4 sm:px-6 pb-2">
13235
13271
  <Field label="Escalation Department">
13236
- <StyledSelect
13237
- placeholder="Select department/user"
13238
- value={data.escalationDepartment}
13239
- onChange={(v) => onChange({ escalationDepartment: v })}
13272
+ <Select
13273
+ value={data.escalationDepartment || undefined}
13274
+ onValueChange={(v) => onChange({ escalationDepartment: v })}
13240
13275
  disabled={!data.frustrationHandoverEnabled}
13241
- options={[
13242
- { value: "support", label: "Support" },
13243
- { value: "sales", label: "Sales" },
13244
- { value: "billing", label: "Billing" },
13245
- ]}
13246
- />
13276
+ >
13277
+ <SelectTrigger>
13278
+ <SelectValue placeholder="Select department/user" />
13279
+ </SelectTrigger>
13280
+ <SelectContent>
13281
+ <SelectItem value="support">Support</SelectItem>
13282
+ <SelectItem value="sales">Sales</SelectItem>
13283
+ <SelectItem value="billing">Billing</SelectItem>
13284
+ </SelectContent>
13285
+ </Select>
13247
13286
  </Field>
13248
13287
  </div>
13249
13288
  </div>
@@ -13267,31 +13306,35 @@ function NumberSpinner({
13267
13306
  max?: number;
13268
13307
  }) {
13269
13308
  return (
13270
- <div className="flex h-10 w-full border border-semantic-border-input rounded overflow-hidden">
13309
+ <div className="flex items-center gap-2.5 w-full h-10 px-4 border border-semantic-border-layout bg-semantic-bg-primary rounded hover:border-semantic-border-input-focus focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]">
13271
13310
  <input
13272
13311
  type="number"
13273
13312
  value={value}
13274
13313
  min={min}
13275
13314
  max={max}
13276
13315
  onChange={(e) => onChange(Number(e.target.value))}
13277
- className="flex-1 px-4 text-sm text-semantic-text-primary bg-semantic-bg-primary outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
13316
+ className="flex-1 text-sm text-semantic-text-primary bg-transparent outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
13278
13317
  />
13279
- <div className="flex flex-col shrink-0 border-l border-semantic-border-layout">
13318
+ <div className="flex flex-col items-center shrink-0 size-5 overflow-hidden">
13280
13319
  <button
13281
13320
  type="button"
13282
13321
  onClick={() => onChange(Math.min(max, value + 1))}
13283
- className="flex-1 flex items-center justify-center px-2.5 text-xs text-semantic-text-muted hover:bg-semantic-bg-hover leading-none border-b border-semantic-border-layout"
13322
+ className="flex flex-1 items-center justify-center w-full text-semantic-text-muted hover:text-semantic-text-primary transition-colors"
13284
13323
  aria-label="Increase"
13285
13324
  >
13286
- \u25B2
13325
+ <svg width="10" height="6" viewBox="0 0 10 6" fill="currentColor" aria-hidden="true">
13326
+ <path d="M5 0L10 6H0L5 0Z" />
13327
+ </svg>
13287
13328
  </button>
13288
13329
  <button
13289
13330
  type="button"
13290
13331
  onClick={() => onChange(Math.max(min, value - 1))}
13291
- className="flex-1 flex items-center justify-center px-2.5 text-xs text-semantic-text-muted hover:bg-semantic-bg-hover leading-none"
13332
+ className="flex flex-1 items-center justify-center w-full text-semantic-text-muted hover:text-semantic-text-primary transition-colors"
13292
13333
  aria-label="Decrease"
13293
13334
  >
13294
- \u25BC
13335
+ <svg width="10" height="6" viewBox="0 0 10 6" fill="currentColor" aria-hidden="true">
13336
+ <path d="M5 6L0 0H10L5 6Z" />
13337
+ </svg>
13295
13338
  </button>
13296
13339
  </div>
13297
13340
  </div>
@@ -13309,7 +13352,7 @@ function AdvancedSettingsAccordion({
13309
13352
  <div className="bg-semantic-bg-primary border border-semantic-border-layout rounded-lg overflow-hidden">
13310
13353
  <Accordion type="single">
13311
13354
  <AccordionItem value="advanced">
13312
- <AccordionTrigger className="px-6 py-5 border-b border-semantic-border-layout hover:no-underline">
13355
+ <AccordionTrigger className="px-4 sm:px-6 py-5 border-b border-semantic-border-layout hover:no-underline">
13313
13356
  <span className="text-base font-semibold text-semantic-text-primary">
13314
13357
  Advanced Settings
13315
13358
  </span>
@@ -13317,7 +13360,7 @@ function AdvancedSettingsAccordion({
13317
13360
  <AccordionContent>
13318
13361
  <div className="flex flex-col">
13319
13362
  {/* Number fields section */}
13320
- <div className="px-6 pt-5 pb-6 flex flex-col gap-5 border-b border-semantic-border-layout">
13363
+ <div className="px-4 sm:px-6 pt-5 pb-6 flex flex-col gap-5 border-b border-semantic-border-layout">
13321
13364
  <Field label="Silence Timeout (seconds)">
13322
13365
  <NumberSpinner
13323
13366
  value={data.silenceTimeout ?? 15}
@@ -13344,7 +13387,7 @@ function AdvancedSettingsAccordion({
13344
13387
  </div>
13345
13388
 
13346
13389
  {/* Interruption Handling \u2014 separated by divider */}
13347
- <div className="px-6 py-5 flex items-center gap-3">
13390
+ <div className="px-4 sm:px-6 py-5 flex items-center gap-3">
13348
13391
  <div className="flex flex-col gap-0.5 flex-1 min-w-0">
13349
13392
  <span className="text-sm font-semibold text-semantic-text-primary">
13350
13393
  Interruption Handling
@@ -13427,9 +13470,10 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
13427
13470
  };
13428
13471
 
13429
13472
  return (
13473
+ <TooltipProvider delayDuration={300}>
13430
13474
  <div ref={ref} className={cn("flex flex-col min-h-screen bg-semantic-bg-ui", className)}>
13431
13475
  {/* Page header */}
13432
- <header className="flex items-center justify-between px-6 h-[76px] bg-semantic-bg-primary border-b border-semantic-border-layout shrink-0">
13476
+ <header className="flex flex-wrap items-center justify-between gap-3 px-4 sm:px-6 min-h-[76px] py-3 bg-semantic-bg-primary border-b border-semantic-border-layout shrink-0">
13433
13477
  <div className="flex items-center gap-3">
13434
13478
  <button
13435
13479
  type="button"
@@ -13446,9 +13490,9 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
13446
13490
  {botType}
13447
13491
  </Badge>
13448
13492
  </div>
13449
- <div className="flex items-center gap-3">
13493
+ <div className="flex flex-wrap items-center gap-2 sm:gap-3">
13450
13494
  {lastUpdatedAt && (
13451
- <span className="text-sm text-semantic-text-muted">
13495
+ <span className="hidden sm:inline text-sm text-semantic-text-muted">
13452
13496
  Last updated at: {lastUpdatedAt}
13453
13497
  </span>
13454
13498
  )}
@@ -13469,17 +13513,17 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
13469
13513
  </div>
13470
13514
  </header>
13471
13515
 
13472
- {/* Body \u2014 two-column layout */}
13473
- <div className="flex flex-1 gap-6 px-6 py-6 max-w-[1220px] mx-auto w-full">
13516
+ {/* Body \u2014 single column on mobile, two-column on large screens */}
13517
+ <div className="flex flex-col lg:flex-row flex-1 gap-6 px-4 sm:px-6 py-6 max-w-[1220px] mx-auto w-full">
13474
13518
  {/* Left column */}
13475
- <div className="flex flex-col gap-6 flex-[3] min-w-0">
13519
+ <div className="flex flex-col gap-6 lg:flex-[3] min-w-0">
13476
13520
  <WhoTheBotIs data={data} onChange={update} />
13477
13521
  <HowItBehaves data={data} onChange={update} />
13478
13522
  <FallbackPromptsAccordion data={data} onChange={update} />
13479
13523
  </div>
13480
13524
 
13481
13525
  {/* Right column */}
13482
- <div className="flex flex-col gap-6 flex-[2] min-w-0">
13526
+ <div className="flex flex-col gap-6 lg:flex-[2] min-w-0">
13483
13527
  <KnowledgeBase
13484
13528
  files={data.knowledgeBaseFiles}
13485
13529
  onSaveFiles={onSaveKnowledgeFiles}
@@ -13511,6 +13555,7 @@ export const IvrBotConfig = React.forwardRef<HTMLDivElement, IvrBotConfigProps>(
13511
13555
  onTestApi={onTestApi}
13512
13556
  />
13513
13557
  </div>
13558
+ </TooltipProvider>
13514
13559
  );
13515
13560
  }
13516
13561
  );
@@ -13521,7 +13566,7 @@ IvrBotConfig.displayName = "IvrBotConfig";
13521
13566
  {
13522
13567
  name: "create-function-modal.tsx",
13523
13568
  content: prefixTailwindClasses(`import * as React from "react";
13524
- import { Trash2, ChevronDown } from "lucide-react";
13569
+ import { Trash2 } from "lucide-react";
13525
13570
  import { cn } from "../../../lib/utils";
13526
13571
  import {
13527
13572
  Dialog,
@@ -13530,6 +13575,13 @@ import {
13530
13575
  DialogTitle,
13531
13576
  } from "../dialog";
13532
13577
  import { Button } from "../button";
13578
+ import {
13579
+ Select,
13580
+ SelectTrigger,
13581
+ SelectValue,
13582
+ SelectContent,
13583
+ SelectItem,
13584
+ } from "../select";
13533
13585
  import type {
13534
13586
  CreateFunctionModalProps,
13535
13587
  CreateFunctionData,
@@ -13571,64 +13623,68 @@ function KeyValueTable({
13571
13623
  return (
13572
13624
  <div className="flex flex-col gap-1.5">
13573
13625
  <span className="text-xs text-semantic-text-muted">{label}</span>
13574
- <div className="border border-semantic-border-layout rounded overflow-hidden">
13575
- {/* Header row */}
13576
- <div className="flex bg-semantic-bg-primary border-b border-semantic-border-layout">
13577
- <div className="flex-1 px-3 py-2.5 text-sm font-semibold text-semantic-text-muted border-r border-semantic-border-layout">
13578
- Key
13626
+ <div className="w-full overflow-x-auto rounded border border-semantic-border-layout">
13627
+ <div className="min-w-[380px]">
13628
+ {/* Header row \u2014 always visible */}
13629
+ <div className="flex bg-semantic-bg-ui border-b border-semantic-border-layout">
13630
+ <div className="flex-1 px-3 py-2.5 text-sm font-semibold text-semantic-text-muted border-r border-semantic-border-layout">
13631
+ Key
13632
+ </div>
13633
+ <div className="flex-[2] px-3 py-2.5 text-sm font-semibold text-semantic-text-muted">
13634
+ Value
13635
+ </div>
13636
+ <div className="w-10" />
13579
13637
  </div>
13580
- <div className="flex-[2] px-3 py-2.5 text-sm font-semibold text-semantic-text-muted">
13581
- Value
13638
+ {/* Data rows \u2014 scrollable vertically */}
13639
+ <div className="max-h-[160px] overflow-y-auto [&::-webkit-scrollbar]:hidden [scrollbar-width:none]">
13640
+ {rows.map((row) => (
13641
+ <div
13642
+ key={row.id}
13643
+ className="flex border-b border-semantic-border-layout last:border-b-0"
13644
+ >
13645
+ <input
13646
+ type="text"
13647
+ value={row.key}
13648
+ onChange={(e) => handleKeyChange(row.id, e.target.value)}
13649
+ placeholder="Key"
13650
+ className="flex-1 px-3 py-2.5 text-sm text-semantic-text-primary placeholder:text-semantic-text-placeholder bg-semantic-bg-primary border-r border-semantic-border-layout outline-none focus:bg-semantic-bg-hover"
13651
+ />
13652
+ <input
13653
+ type="text"
13654
+ value={row.value}
13655
+ onChange={(e) => handleValueChange(row.id, e.target.value)}
13656
+ placeholder="Type {{ to add variables"
13657
+ className="flex-[2] px-3 py-2.5 text-sm text-semantic-text-primary placeholder:text-semantic-text-placeholder bg-semantic-bg-primary outline-none focus:bg-semantic-bg-hover"
13658
+ />
13659
+ <button
13660
+ type="button"
13661
+ onClick={() => handleDelete(row.id)}
13662
+ className="w-10 flex items-center justify-center text-semantic-text-muted hover:text-semantic-error-primary hover:bg-semantic-error-surface transition-colors"
13663
+ aria-label="Delete row"
13664
+ >
13665
+ <Trash2 className="size-3.5" />
13666
+ </button>
13667
+ </div>
13668
+ ))}
13582
13669
  </div>
13583
- <div className="w-10" />
13584
- </div>
13585
- {/* Data rows */}
13586
- {rows.map((row) => (
13587
- <div
13588
- key={row.id}
13589
- className="flex border-b border-semantic-border-layout last:border-b-0"
13590
- >
13670
+ {/* Empty input row \u2014 always visible at bottom */}
13671
+ <div className="flex border-t border-semantic-border-layout">
13591
13672
  <input
13592
13673
  type="text"
13593
- value={row.key}
13594
- onChange={(e) => handleKeyChange(row.id, e.target.value)}
13595
13674
  placeholder="Key"
13596
- className="flex-1 px-3 py-2.5 text-sm text-semantic-text-primary placeholder:text-semantic-text-muted bg-semantic-bg-primary border-r border-semantic-border-layout outline-none focus:bg-semantic-bg-hover"
13675
+ readOnly
13676
+ onClick={handleAdd}
13677
+ className="flex-1 px-3 py-2.5 text-sm placeholder:text-semantic-text-placeholder bg-semantic-bg-primary border-r border-semantic-border-layout outline-none cursor-pointer"
13597
13678
  />
13598
13679
  <input
13599
13680
  type="text"
13600
- value={row.value}
13601
- onChange={(e) => handleValueChange(row.id, e.target.value)}
13602
13681
  placeholder="Type {{ to add variables"
13603
- className="flex-[2] px-3 py-2.5 text-sm text-semantic-text-primary placeholder:text-semantic-text-muted bg-semantic-bg-primary outline-none focus:bg-semantic-bg-hover"
13682
+ readOnly
13683
+ onClick={handleAdd}
13684
+ className="flex-[2] px-3 py-2.5 text-sm placeholder:text-semantic-text-placeholder bg-semantic-bg-primary outline-none cursor-pointer"
13604
13685
  />
13605
- <button
13606
- type="button"
13607
- onClick={() => handleDelete(row.id)}
13608
- className="w-10 flex items-center justify-center text-semantic-text-muted hover:text-semantic-error-primary hover:bg-semantic-error-surface transition-colors"
13609
- aria-label="Delete row"
13610
- >
13611
- <Trash2 className="size-3.5" />
13612
- </button>
13686
+ <div className="w-10" />
13613
13687
  </div>
13614
- ))}
13615
- {/* Empty input row */}
13616
- <div className="flex">
13617
- <input
13618
- type="text"
13619
- placeholder="Key"
13620
- readOnly
13621
- onClick={handleAdd}
13622
- className="flex-1 px-3 py-2.5 text-sm placeholder:text-semantic-text-muted bg-semantic-bg-primary border-r border-semantic-border-layout outline-none cursor-pointer"
13623
- />
13624
- <input
13625
- type="text"
13626
- placeholder="Type {{ to add variables"
13627
- readOnly
13628
- onClick={handleAdd}
13629
- className="flex-[2] px-3 py-2.5 text-sm placeholder:text-semantic-text-muted bg-semantic-bg-primary outline-none cursor-pointer"
13630
- />
13631
- <div className="w-10" />
13632
13688
  </div>
13633
13689
  </div>
13634
13690
  </div>
@@ -13730,7 +13786,15 @@ export const CreateFunctionModal = React.forwardRef<
13730
13786
  Functions Name{" "}
13731
13787
  <span className="text-semantic-error-primary font-semibold">*</span>
13732
13788
  </label>
13733
- <div className="relative">
13789
+ <div
13790
+ className={cn(
13791
+ "flex items-center h-10 rounded border",
13792
+ "border-semantic-border-input bg-semantic-bg-primary",
13793
+ "hover:border-semantic-border-input-focus",
13794
+ "focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
13795
+ "transition-shadow"
13796
+ )}
13797
+ >
13734
13798
  <input
13735
13799
  id="fn-name"
13736
13800
  type="text"
@@ -13738,15 +13802,9 @@ export const CreateFunctionModal = React.forwardRef<
13738
13802
  maxLength={FUNCTION_NAME_MAX}
13739
13803
  onChange={(e) => setName(e.target.value)}
13740
13804
  placeholder="Enter Name of the function"
13741
- className={cn(
13742
- "w-full h-10 px-4 py-2.5 pr-16 text-sm rounded border",
13743
- "border-semantic-border-input bg-semantic-bg-primary",
13744
- "text-semantic-text-primary placeholder:text-semantic-text-muted",
13745
- "outline-none hover:border-semantic-border-input-focus",
13746
- "focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]"
13747
- )}
13805
+ className="flex-1 h-full px-4 text-sm bg-transparent text-semantic-text-primary placeholder:text-semantic-text-placeholder outline-none"
13748
13806
  />
13749
- <span className="absolute right-3 top-1/2 -translate-y-1/2 text-xs italic text-semantic-text-muted">
13807
+ <span className="px-3 shrink-0 text-xs italic text-semantic-text-muted">
13750
13808
  {name.length}/{FUNCTION_NAME_MAX}
13751
13809
  </span>
13752
13810
  </div>
@@ -13770,7 +13828,7 @@ export const CreateFunctionModal = React.forwardRef<
13770
13828
  className={cn(
13771
13829
  "w-full px-4 py-2.5 text-sm rounded border",
13772
13830
  "border-semantic-border-input bg-semantic-bg-primary resize-none",
13773
- "text-semantic-text-primary placeholder:text-semantic-text-muted",
13831
+ "text-semantic-text-primary placeholder:text-semantic-text-placeholder",
13774
13832
  "outline-none hover:border-semantic-border-input-focus",
13775
13833
  "focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]"
13776
13834
  )}
@@ -13782,6 +13840,7 @@ export const CreateFunctionModal = React.forwardRef<
13782
13840
  variant="default"
13783
13841
  onClick={handleNext}
13784
13842
  disabled={!isStep1Valid}
13843
+ className="w-full sm:w-auto"
13785
13844
  >
13786
13845
  Next
13787
13846
  </Button>
@@ -13791,150 +13850,149 @@ export const CreateFunctionModal = React.forwardRef<
13791
13850
 
13792
13851
  {step === 2 && (
13793
13852
  <div className="flex flex-col gap-6">
13794
- {/* API URL */}
13795
- <div className="flex flex-col gap-1">
13796
- <span className="text-xs text-semantic-text-muted tracking-[0.048px]">API url</span>
13797
- <div className="flex h-10 border border-semantic-border-input rounded overflow-hidden hover:border-semantic-border-input-focus focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)] transition-shadow">
13798
- {/* Method selector */}
13799
- <div className="relative shrink-0">
13800
- <select
13801
- value={method}
13802
- onChange={(e) => setMethod(e.target.value as HttpMethod)}
13803
- className="h-full pl-4 pr-9 text-sm text-semantic-text-primary bg-semantic-bg-primary border-r border-semantic-border-input outline-none cursor-pointer appearance-none w-[104px]"
13804
- aria-label="HTTP method"
13805
- >
13806
- {HTTP_METHODS.map((m) => (
13807
- <option key={m} value={m}>
13808
- {m}
13809
- </option>
13810
- ))}
13811
- </select>
13812
- <ChevronDown
13813
- className="absolute right-3 top-1/2 -translate-y-1/2 size-3.5 text-semantic-text-muted pointer-events-none shrink-0"
13814
- aria-hidden="true"
13815
- />
13853
+ {/* Horizontally scrollable form area */}
13854
+ <div className="overflow-x-auto [&::-webkit-scrollbar]:hidden [scrollbar-width:none]">
13855
+ <div className="flex flex-col gap-6 min-w-[440px]">
13856
+ {/* API URL */}
13857
+ <div className="flex flex-col gap-1">
13858
+ <span className="text-xs text-semantic-text-muted tracking-[0.048px]">API url</span>
13859
+ <div className="flex flex-row border border-semantic-border-input rounded overflow-hidden hover:border-semantic-border-input-focus focus-within:border-semantic-border-input-focus focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)] transition-shadow">
13860
+ {/* Method selector */}
13861
+ <Select value={method} onValueChange={(v) => setMethod(v as HttpMethod)}>
13862
+ <SelectTrigger
13863
+ className="w-[104px] shrink-0 rounded-none border-0 border-r border-semantic-border-input focus:shadow-none"
13864
+ aria-label="HTTP method"
13865
+ >
13866
+ <SelectValue />
13867
+ </SelectTrigger>
13868
+ <SelectContent>
13869
+ {HTTP_METHODS.map((m) => (
13870
+ <SelectItem key={m} value={m}>{m}</SelectItem>
13871
+ ))}
13872
+ </SelectContent>
13873
+ </Select>
13874
+ {/* URL input */}
13875
+ <input
13876
+ type="text"
13877
+ value={url}
13878
+ onChange={(e) => setUrl(e.target.value)}
13879
+ placeholder="Enter URL or Type {{ to add variables"
13880
+ className="flex-1 min-w-0 h-10 px-3 text-sm text-semantic-text-primary placeholder:text-semantic-text-placeholder bg-semantic-bg-primary border-0 outline-none"
13881
+ />
13882
+ </div>
13816
13883
  </div>
13817
- <input
13818
- type="text"
13819
- value={url}
13820
- onChange={(e) => setUrl(e.target.value)}
13821
- placeholder="Enter URL or Type {{ to add variables"
13822
- className="flex-1 px-4 text-sm text-semantic-text-primary placeholder:text-semantic-text-muted bg-semantic-bg-primary outline-none"
13823
- />
13824
- </div>
13825
- </div>
13826
13884
 
13827
- {/* Tabs */}
13828
- <div className="flex flex-col gap-4">
13829
- <div className="flex gap-0 border-b border-semantic-border-layout">
13830
- {(["header", "queryParams", "body"] as FunctionTabType[]).map(
13831
- (tab) => {
13832
- const labels: Record<FunctionTabType, string> = {
13833
- header: \`Header(\${tabCount.header})\`,
13834
- queryParams: \`Query parameter(\${tabCount.queryParams})\`,
13835
- body: "Body",
13836
- };
13837
- return (
13838
- <button
13839
- key={tab}
13840
- type="button"
13841
- onClick={() => setActiveTab(tab)}
13842
- className={cn(
13843
- "px-2.5 py-1.5 text-sm font-semibold transition-colors whitespace-nowrap",
13844
- activeTab === tab
13845
- ? "text-semantic-text-secondary border-b-2 border-semantic-text-secondary -mb-px"
13846
- : "text-semantic-text-muted"
13847
- )}
13848
- >
13849
- {labels[tab]}
13850
- </button>
13851
- );
13852
- }
13853
- )}
13854
- </div>
13885
+ {/* Tabs */}
13886
+ <div className="flex flex-col gap-4">
13887
+ {/* Tabs row */}
13888
+ <div className="relative flex gap-8 border-b border-semantic-border-layout">
13889
+ {(["header", "queryParams", "body"] as FunctionTabType[]).map(
13890
+ (tab) => {
13891
+ const labels: Record<FunctionTabType, string> = {
13892
+ header: \`Header(\${tabCount.header})\`,
13893
+ queryParams: \`Query parameter(\${tabCount.queryParams})\`,
13894
+ body: "Body",
13895
+ };
13896
+ return (
13897
+ <button
13898
+ key={tab}
13899
+ type="button"
13900
+ onClick={() => setActiveTab(tab)}
13901
+ className={cn(
13902
+ "relative -mb-px px-2.5 py-1 pb-2 text-sm font-semibold transition-colors whitespace-nowrap",
13903
+ activeTab === tab
13904
+ ? "text-semantic-text-secondary border-b-[1.5px] border-semantic-text-secondary"
13905
+ : "text-semantic-text-muted"
13906
+ )}
13907
+ >
13908
+ {labels[tab]}
13909
+ </button>
13910
+ );
13911
+ }
13912
+ )}
13913
+ </div>
13855
13914
 
13856
- {/* Tab Content */}
13857
- {activeTab === "header" && (
13858
- <KeyValueTable
13859
- rows={headers}
13860
- onChange={setHeaders}
13861
- label="Header"
13862
- />
13863
- )}
13864
- {activeTab === "queryParams" && (
13865
- <KeyValueTable
13866
- rows={queryParams}
13867
- onChange={setQueryParams}
13868
- label="Query parameter"
13869
- />
13870
- )}
13871
- {activeTab === "body" && (
13872
- <div className="flex flex-col gap-1">
13873
- <span className="text-xs text-semantic-text-muted">Body</span>
13874
- <div className="relative">
13875
- <textarea
13876
- value={body}
13877
- maxLength={BODY_MAX}
13878
- onChange={(e) => setBody(e.target.value)}
13879
- placeholder="Enter request body (JSON, XML etc). Type {{ to add variables"
13880
- rows={6}
13881
- className={cn(
13882
- "w-full px-4 py-2.5 pb-7 text-sm rounded border resize-none",
13883
- "border-semantic-border-input bg-semantic-bg-primary",
13884
- "text-semantic-text-primary placeholder:text-semantic-text-muted",
13885
- "outline-none hover:border-semantic-border-input-focus",
13886
- "focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]"
13887
- )}
13915
+ {/* Tab Content */}
13916
+ {activeTab === "header" && (
13917
+ <KeyValueTable
13918
+ rows={headers}
13919
+ onChange={setHeaders}
13920
+ label="Header"
13921
+ />
13922
+ )}
13923
+ {activeTab === "queryParams" && (
13924
+ <KeyValueTable
13925
+ rows={queryParams}
13926
+ onChange={setQueryParams}
13927
+ label="Query parameter"
13888
13928
  />
13889
- <span className="absolute bottom-2 right-3 text-xs italic text-semantic-text-muted">
13890
- {body.length}/{BODY_MAX}
13929
+ )}
13930
+ {activeTab === "body" && (
13931
+ <div className="flex flex-col gap-1">
13932
+ <span className="text-xs text-semantic-text-muted">Body</span>
13933
+ <div className="relative">
13934
+ <textarea
13935
+ value={body}
13936
+ maxLength={BODY_MAX}
13937
+ onChange={(e) => setBody(e.target.value)}
13938
+ placeholder="Enter request body (JSON, XML etc). Type {{ to add variables"
13939
+ rows={6}
13940
+ className={cn(
13941
+ "w-full px-4 py-2.5 pb-7 text-sm rounded border resize-none",
13942
+ "border-semantic-border-input bg-semantic-bg-primary",
13943
+ "text-semantic-text-primary placeholder:text-semantic-text-placeholder",
13944
+ "outline-none hover:border-semantic-border-input-focus",
13945
+ "focus:border-semantic-border-input-focus focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]"
13946
+ )}
13947
+ />
13948
+ <span className="absolute bottom-2 right-3 text-xs italic text-semantic-text-muted">
13949
+ {body.length}/{BODY_MAX}
13950
+ </span>
13951
+ </div>
13952
+ </div>
13953
+ )}
13954
+ </div>
13955
+
13956
+ {/* Test Your API */}
13957
+ <div className="flex flex-col gap-4">
13958
+ <div className="flex flex-col gap-1.5">
13959
+ <span className="text-xs text-semantic-text-muted tracking-[0.048px]">
13960
+ Test Your API
13961
+ </span>
13962
+ <div className="border-t border-semantic-border-layout w-full" />
13963
+ </div>
13964
+ <div className="flex justify-end">
13965
+ <button
13966
+ type="button"
13967
+ onClick={handleTestApi}
13968
+ disabled={isTesting || !url.trim()}
13969
+ className="h-10 px-6 rounded text-sm font-semibold text-semantic-text-secondary bg-semantic-primary-surface disabled:opacity-50 disabled:cursor-not-allowed transition-colors hover:bg-semantic-primary-surface/80"
13970
+ >
13971
+ {isTesting ? "Testing..." : "Test API"}
13972
+ </button>
13973
+ </div>
13974
+ <div className="flex flex-col gap-1">
13975
+ <span className="text-xs text-semantic-text-muted">
13976
+ Response from API
13891
13977
  </span>
13978
+ <textarea
13979
+ readOnly
13980
+ value={apiResponse}
13981
+ rows={4}
13982
+ className="w-full px-3 py-2.5 text-sm rounded border border-semantic-border-layout bg-semantic-bg-primary text-semantic-text-primary resize-none outline-none"
13983
+ placeholder=""
13984
+ />
13892
13985
  </div>
13893
13986
  </div>
13894
- )}
13895
- </div>
13896
-
13897
- {/* Test Your API */}
13898
- <div className="flex flex-col gap-6">
13899
- {/* Label stacked above full-width divider */}
13900
- <div className="flex flex-col gap-1.5">
13901
- <span className="text-xs text-semantic-text-muted tracking-[0.048px]">
13902
- Test Your API
13903
- </span>
13904
- <div className="border-t border-semantic-border-layout w-full" />
13905
- </div>
13906
- {/* Test API button right-aligned */}
13907
- <div className="flex justify-end">
13908
- <button
13909
- type="button"
13910
- onClick={handleTestApi}
13911
- disabled={isTesting || !url.trim()}
13912
- className="h-10 px-6 rounded text-sm font-semibold text-semantic-text-secondary bg-semantic-primary-surface disabled:opacity-50 disabled:cursor-not-allowed transition-colors hover:bg-semantic-primary-surface/80"
13913
- >
13914
- {isTesting ? "Testing..." : "Test API"}
13915
- </button>
13916
- </div>
13917
- {/* Row 3: Response from API */}
13918
- <div className="flex flex-col gap-1">
13919
- <span className="text-xs text-semantic-text-muted">
13920
- Response from API
13921
- </span>
13922
- <textarea
13923
- readOnly
13924
- value={apiResponse}
13925
- rows={4}
13926
- className="w-full px-3 py-2.5 text-sm rounded border border-semantic-border-layout bg-semantic-bg-ui text-semantic-text-primary resize-none outline-none"
13927
- placeholder=""
13928
- />
13929
13987
  </div>
13930
13988
  </div>
13931
13989
 
13932
- {/* Footer */}
13933
- <div className="flex justify-end gap-3">
13934
- <Button variant="outline" onClick={handleBack}>
13990
+ {/* Footer \u2014 always full-width, outside the scroll area */}
13991
+ <div className="flex flex-row justify-between gap-3">
13992
+ <Button variant="outline" onClick={handleBack} className="flex-1 sm:flex-none sm:w-auto">
13935
13993
  Back
13936
13994
  </Button>
13937
- <Button variant="default" onClick={handleSubmit}>
13995
+ <Button variant="default" onClick={handleSubmit} className="flex-1 sm:flex-none sm:w-auto">
13938
13996
  Submit
13939
13997
  </Button>
13940
13998
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-ui",
3
- "version": "0.0.204-beta.1",
3
+ "version": "0.0.204-beta.2",
4
4
  "description": "CLI for adding myOperator UI components to your project",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",