Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/ai/src/ChatComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export function ChatComposer({
}}
placeholder={placeholder}
rows={2}
className="text-ink placeholder:text-ink-faint block w-full resize-none border-0 bg-transparent text-sm leading-6 outline-none focus:border-0 focus:outline-none focus:ring-0"
className="text-ink placeholder:text-ink-faint block w-full resize-none rounded-md border-0 bg-transparent text-sm leading-6 outline-none focus:border-0 focus:outline-none focus:ring-0 focus-visible:ring-2 focus-visible:ring-accent"
/>

<div className="mt-4 flex items-center justify-between gap-3">
Expand Down Expand Up @@ -139,7 +139,11 @@ export function ChatComposer({
{toolbarExtra}

{onOpenVoice && (
<CircleButton icon={Microphone} onClick={onOpenVoice} />
<CircleButton
icon={Microphone}
onClick={onOpenVoice}
aria-label="Open Voice Input"
/>
)}

<AnimatePresence initial={false}>
Expand Down
2 changes: 0 additions & 2 deletions packages/ai/src/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import { clsx } from 'clsx';
import { forwardRef } from 'react';

Expand All @@ -18,7 +17,6 @@ const Markdown = forwardRef<HTMLDivElement, MarkdownProps>(
>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
>
{content}
</ReactMarkdown>
Expand Down
3 changes: 3 additions & 0 deletions packages/ai/src/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export function MessageBubble({
<CircleButton
icon={Copy}
onClick={() => onCopy(content)}
aria-label={
isStreaming ? 'Copy Streaming Message' : 'Copy Message'
}
title={
isStreaming ? 'Copy streaming message' : 'Copy message'
}
Expand Down
16 changes: 13 additions & 3 deletions packages/ai/src/TaskCreateForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Select, SelectOption } from "@spacedrive/primitives";
import clsx from "clsx";
import { useCallback, useState } from "react";
import { useCallback, useId, useState } from "react";

import type { TaskPriority } from "./types";
import { TASK_PRIORITY_LABEL } from "./types";
Expand Down Expand Up @@ -31,6 +31,8 @@ export function TaskCreateForm({
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [priority, setPriority] = useState<TaskPriority>(defaultPriority);
const titleInputId = useId();
const descriptionInputId = useId();

const canSubmit = title.trim().length > 0 && !isSubmitting;

Expand All @@ -45,7 +47,11 @@ export function TaskCreateForm({
return (
<div className={clsx("flex flex-col gap-2", className)}>
<div className="flex items-center gap-2">
<label htmlFor={titleInputId} className="sr-only">
Task Title
</label>
<input
id={titleInputId}
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
Expand All @@ -54,7 +60,7 @@ export function TaskCreateForm({
if (e.key === "Escape") onCancel?.();
}}
placeholder="Task title..."
className="min-w-0 flex-1 bg-transparent px-2 py-1.5 text-sm text-ink placeholder:text-ink-faint focus:outline-none"
className="min-w-0 flex-1 rounded-md bg-transparent px-2 py-1.5 text-sm text-ink placeholder:text-ink-faint focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
disabled={isSubmitting}
autoFocus
/>
Expand All @@ -79,12 +85,16 @@ export function TaskCreateForm({
Create
</Button>
</div>
<label htmlFor={descriptionInputId} className="sr-only">
Description
</label>
<textarea
id={descriptionInputId}
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Description (optional)"
rows={2}
className="w-full resize-none bg-transparent px-2 py-1.5 text-sm text-ink placeholder:text-ink-faint focus:outline-none"
className="w-full resize-none rounded-md bg-transparent px-2 py-1.5 text-sm text-ink placeholder:text-ink-faint focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
disabled={isSubmitting}
/>
</div>
Expand Down
36 changes: 27 additions & 9 deletions packages/ai/src/TaskRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,32 @@ export function TaskRow({
const completedSubtasks = task.subtasks.filter((s) => s.completed).length;
const assigneeName = resolveAgentName?.(task.assigned_agent_id) ?? task.assigned_agent_id;
const hasActions = onStatusChange || onDelete;
const isRowInteractive = typeof onClick === "function";
const handleRowKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.repeat) return;
if (e.key === "Enter") {
e.preventDefault();
onClick?.(task);
}
};
const handleRowKeyUp = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.repeat) return;
if (e.key === " ") {
e.preventDefault();
onClick?.(task);
}
};

return (
<button
type="button"
onClick={() => onClick?.(task)}
<div
role={isRowInteractive ? "button" : undefined}
tabIndex={isRowInteractive ? 0 : undefined}
onClick={isRowInteractive ? () => onClick(task) : undefined}
onKeyDown={isRowInteractive ? handleRowKeyDown : undefined}
onKeyUp={isRowInteractive ? handleRowKeyUp : undefined}
className={clsx(
"task-row group grid w-full items-center border-b border-app-line/40 text-left transition-colors",
"hover:bg-app-hover/60",
"hover:bg-app-hover/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent",
isActive
? "bg-app-selected/50"
: "bg-transparent",
Expand Down Expand Up @@ -75,17 +93,17 @@ export function TaskRow({
{hasActions ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<span
role="button"
tabIndex={0}
<button
type="button"
aria-label="Task Actions"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") e.stopPropagation();
}}
className="flex size-5 items-center justify-center rounded opacity-0 transition-opacity hover:bg-app-hover group-hover:opacity-100"
>
<DotsThree size={16} weight="bold" className="text-ink-dull" />
</span>
</button>
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end" sideOffset={4}>
{onStatusChange &&
Expand Down Expand Up @@ -117,6 +135,6 @@ export function TaskRow({
</DropdownMenu.Root>
) : null}
</span>
</button>
</div>
);
}
8 changes: 4 additions & 4 deletions packages/explorer/src/GridItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ export function GridItem({
const displayName = extension ? `${name}.${extension}` : name;

return (
<div
<button
type="button"
onClick={onClick}
onDoubleClick={onDoubleClick}
onContextMenu={onContextMenu}
tabIndex={-1}
className={clsx(
"cursor-default transition-colors outline-none focus:outline-none",
"cursor-default border-0 bg-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent",
"flex flex-col items-center gap-2 p-1 rounded-lg",
className,
)}
Expand Down Expand Up @@ -116,6 +116,6 @@ export function GridItem({
</div>
)}
</div>
</div>
</button>
);
}
2 changes: 1 addition & 1 deletion packages/explorer/src/TagPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function TagPill({
size === "xs" && "px-1.5 py-0.5 text-[10px]",
size === "sm" && "px-2 py-0.5 text-xs",
size === "md" && "px-2.5 py-1 text-sm",
(onClick || onRemove) && "transition-all hover:brightness-110",
(onClick || onRemove) && "transition-[filter] hover:brightness-110",
className,
)}
style={{ backgroundColor: `${color}20`, color }}
Expand Down
36 changes: 26 additions & 10 deletions packages/forms/src/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { clsx } from 'clsx';
import { createContext, useContext, useId } from 'react';
import { cloneElement, createContext, isValidElement, useContext, useId } from 'react';
import {
Controller,
FormProvider,
Expand Down Expand Up @@ -84,17 +84,33 @@ const FormLabel = ({ className, ...props }: React.LabelHTMLAttributes<HTMLLabelE
);
};

const FormControl = ({ ...props }: React.HTMLAttributes<HTMLDivElement>) => {
interface FormControlProps {
children: React.ReactElement;
}

const FormControl = ({ children }: FormControlProps) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
const describedBy = !error ? formDescriptionId : `${formDescriptionId} ${formMessageId}`;

return (
<div
id={formItemId}
aria-describedby={!error ? formDescriptionId : `${formDescriptionId} ${formMessageId}`}
aria-invalid={!!error}
{...props}
/>
);
if (!isValidElement(children)) {
return null;
}

const childProps = children.props as { 'aria-describedby'?: string };
const existingDescribed = childProps['aria-describedby'];
const mergedDescribed = Array.from(
new Set(
[existingDescribed, describedBy]
.filter(Boolean)
.flatMap((value) => value!.split(/\s+/).filter(Boolean)),
),
).join(' ');

return cloneElement(children, {
id: formItemId,
'aria-describedby': mergedDescribed,
'aria-invalid': !!error,
} as React.HTMLAttributes<HTMLElement>);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
};

const FormDescription = ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/primitives/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const buttonStyles = cva(
[
"inline-flex cursor-default items-center justify-center gap-1.5 border font-medium tracking-wide outline-none transition-colors duration-100",
"disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-70",
"focus:ring-none focus:ring-offset-none cursor-pointer ring-offset-app-box",
"cursor-pointer ring-offset-app-box focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2",
],
{
variants: {
Expand Down
2 changes: 1 addition & 1 deletion packages/primitives/src/CircleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {forwardRef} from "react";
export const circleButtonStyles = cva(
[
"flex items-center justify-center",
"backdrop-blur-xl transition-all",
"backdrop-blur-xl transition-[background-color,border-color,color,transform]",
"border border-app-line/50",
"rounded-full",
"active:scale-95",
Expand Down
3 changes: 2 additions & 1 deletion packages/primitives/src/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const inputSizes = {
export const inputStyles = cva(
[
"rounded-lg border text-sm leading-4",
"outline-none transition-all focus-within:ring-2 focus-within:ring-accent",
"outline-none transition-[background-color,border-color,box-shadow] focus-within:ring-2 focus-within:ring-accent",
"text-ink",
],
{
Expand Down Expand Up @@ -212,6 +212,7 @@ export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
tabIndex={0}
onClick={() => setShowPassword(!showPassword)}
size="icon"
aria-label={showPassword ? "Hide Password" : "Show Password"}
className={clsx(props.buttonClassnames)}
>
<CurrentEyeIcon className="!pointer-events-none size-4" />
Expand Down
2 changes: 1 addition & 1 deletion packages/primitives/src/NumberStepper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ const NumberStepper = forwardRef<HTMLDivElement, NumberStepperProps>(
{showProgress && progress !== undefined && (
<div className="h-1 w-full overflow-hidden rounded-full bg-app-line">
<div
className="h-full bg-accent transition-all duration-200"
className="h-full bg-accent transition-[width] duration-200"
style={{width: `${progress}%`}}
/>
</div>
Expand Down
4 changes: 3 additions & 1 deletion packages/primitives/src/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const SearchBar = forwardRef<HTMLInputElement, SearchBarProps>(
"flex h-8 items-center gap-2 px-3",
"rounded-full backdrop-blur-xl",
"border border-app-line/30 bg-app-overlay/80 hover:bg-app-box",
"transition-all focus-within:bg-sidebar-box/30",
"transition-colors focus-within:bg-sidebar-box/30",
className,
)}
>
Expand All @@ -68,7 +68,9 @@ export const SearchBar = forwardRef<HTMLInputElement, SearchBarProps>(
/>
{currentValue && (
<button
type="button"
onClick={handleClear}
aria-label="Clear Search"
className="flex-shrink-0 rounded-full p-0.5 transition-colors hover:bg-sidebar-selected/40"
>
<X className="size-3 text-sidebar-inkDull" weight="bold" />
Expand Down
2 changes: 1 addition & 1 deletion packages/primitives/src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const ChevronDouble = (props: React.SVGProps<SVGSVGElement>) => (
export const selectStyles = cva(
[
"flex items-center justify-between whitespace-nowrap rounded-md border py-0.5 pl-3 pr-[10px] text-sm",
"shadow-sm outline-none transition-all focus:ring-2",
"shadow-sm outline-none transition-[background-color,border-color,box-shadow] focus:ring-2",
"text-ink radix-placeholder:text-ink-faint",
],
{
Expand Down
2 changes: 1 addition & 1 deletion packages/primitives/src/ShinyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const shinyButtonVariants = cva(
"noise with-rounded-2px-border-images inline-flex flex-row items-center justify-center gap-x-2 overflow-hidden",
"bg-gradient-to-b from-[#42B2FD] to-[#0078F0] [--border-image:linear-gradient(to_bottom,hsl(200_100%_77%/100%),hsl(200_0%_100%/5%)75%)]",
"will-change-transform will-change-[box-shadow]",
"transition-all duration-200 ease-out",
"transition-[box-shadow,filter,transform] duration-200 ease-out",
"cursor-pointer",
],
{
Expand Down
Loading
Loading