Compare commits

..

4 Commits

Author SHA1 Message Date
ddc7a82e0e refactor(ai-event): migrate to OpenRouter chat.send API
Replace the deprecated callModel / getText pattern with the chat.send
method and extract the response content via extractContentFromChatResponse.
This aligns with the current OpenRouter SDK interface.
2026-04-15 18:21:23 -04:00
6bc84d5b58 feat(date-time-picker): add native date input alongside calendar popover
Replace the button-only trigger with a native <input type="date"> (or
datetime-local when not all-day) paired with an icon button that opens
the calendar popover.  This gives users direct keyboard entry while
keeping the rich calendar + quick-shortcut picker accessible.

- Add getInputValue helper to format the current value for the native
  input.
- Import the Input component.
- Restructure the layout to place the input and popover trigger
  side-by-side.
2026-04-15 18:21:17 -04:00
29bf4d2200 fix(time-picker): improve layout stability, a11y, and separator styling
- Change input group horizontal padding from px-3 to pl-3 pr-2 to
  better accommodate the trigger button.
- Add shrink-0 to the trigger button so it does not collapse in flex
  layouts.
- Mark the Clock icon with aria-hidden to keep it out of the
  accessibility tree.
- Forward the className prop in TimePickerSeparator and apply default
  muted-foreground text styling.
2026-04-15 18:21:09 -04:00
8d7948298b fix(button): suppress Firefox inner focus ring and padding
Add [&::-moz-focus-inner]:border-0 and [&::-moz-focus-inner]:p-0 to
the base buttonVariants to prevent the extra inner dotted outline and
padding that Firefox renders on focused buttons.
2026-04-15 18:21:03 -04:00
4 changed files with 76 additions and 56 deletions

View File

@@ -39,13 +39,17 @@ Rules:
`;
const callTextOnly = async (systemPrompt: string, prompt: string) => {
const result = openRouterClient.callModel({
const response = await openRouterClient.chat.send({
chatRequest: {
model: MODEL,
instructions: systemPrompt,
input: prompt,
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: prompt },
],
},
});
const rawResponse = await result.getText();
const rawResponse = extractContentFromChatResponse(response);
return { rawResponse };
};

View File

@@ -13,6 +13,7 @@ import { CalendarIcon } from "lucide-react";
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Input } from "@/components/ui/input";
import {
Popover,
PopoverContent,
@@ -36,6 +37,15 @@ interface QuickShortcutResult {
nextValue: string;
}
function getInputValue(value: string, allDay: boolean): string {
const parsed = parseValue(value);
if (!parsed) return "";
return allDay
? format(parsed, "yyyy-MM-dd")
: format(parsed, "yyyy-MM-dd'T'HH:mm");
}
/** Parse the incoming ISO / date string into a Date object, or return undefined. */
function parseValue(value: string): Date | undefined {
if (!value) return undefined;
@@ -128,7 +138,6 @@ export function DateTimePicker({
value,
allDay,
);
const [open, setOpen] = React.useState(false);
const [visibleMonth, setVisibleMonth] = React.useState(() =>
getCalendarMonthForValue(value, new Date()),
@@ -168,21 +177,26 @@ export function DateTimePicker({
return (
<div className={cn("w-full", className)}>
{/* Date popover trigger */}
<div className="flex items-center gap-2">
<Input
type={allDay ? "date" : "datetime-local"}
value={getInputValue(value, allDay)}
onChange={(event) => {
onChange(event.target.value);
}}
placeholder={placeholder}
className="flex-1"
/>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
type="button"
variant="outline"
className={cn(
"flex flex-1 px-3 py-1 font-normal justify-start",
!displayLabel && "text-muted-foreground",
)}
size="icon"
className={cn(!displayLabel && "text-muted-foreground")}
aria-label={displayLabel ?? placeholder}
>
<CalendarIcon className="h-3.5 w-3.5 shrink-0 text-muted-foreground/70" />
<span className="flex-1 truncate text-left">
{displayLabel ?? placeholder}
</span>
</Button>
</PopoverTrigger>
<PopoverContent
@@ -215,5 +229,6 @@ export function DateTimePicker({
</PopoverContent>
</Popover>
</div>
</div>
);
}

View File

@@ -5,7 +5,7 @@ import type * as React from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"active:scale-[.95] inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium duration-100 transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
"active:scale-[.95] inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium duration-100 transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive [&::-moz-focus-inner]:border-0 [&::-moz-focus-inner]:p-0",
{
variants: {
variant: {

View File

@@ -723,7 +723,7 @@ function TimePickerInputGroup(props: DivProps) {
data-invalid={invalid ? "" : undefined}
{...inputGroupProps}
className={cn(
"flex h-10 w-full cursor-text items-center gap-0.5 rounded-md border border-input bg-background px-3 py-2 shadow-xs outline-none transition-shadow",
"flex h-10 w-full cursor-text items-center gap-0.5 rounded-md border border-input bg-background pl-3 pr-2 py-2 shadow-xs outline-none transition-shadow",
"has-[input:focus]:border-ring has-[input:focus]:ring-[3px] has-[input:focus]:ring-ring/50",
invalid && "border-destructive ring-destructive/20",
disabled && "cursor-not-allowed opacity-50",
@@ -1468,11 +1468,11 @@ function TimePickerTrigger(props: ButtonProps) {
ref={composedRef}
{...triggerProps}
className={cn(
"ml-auto size-7 text-muted-foreground hover:text-foreground",
"ml-auto size-7 shrink-0 text-muted-foreground hover:text-foreground",
className,
)}
>
{children ?? <Clock className="size-4" />}
{children ?? <Clock aria-hidden="true" className="size-4" />}
</Button>
</PopoverTrigger>
);
@@ -2149,7 +2149,7 @@ interface TimePickerSeparatorProps extends React.ComponentProps<"span"> {
}
function TimePickerSeparator(props: TimePickerSeparatorProps) {
const { asChild, children, ...separatorProps } = props;
const { asChild, children, className, ...separatorProps } = props;
const SeparatorPrimitive = asChild ? SlotPrimitive.Slot : "span";
@@ -2157,6 +2157,7 @@ function TimePickerSeparator(props: TimePickerSeparatorProps) {
<SeparatorPrimitive
aria-hidden="true"
data-slot="time-picker-separator"
className={cn("text-sm text-muted-foreground", className)}
{...separatorProps}
>
{children ?? ":"}