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
13 changes: 12 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,21 @@ The codebase uses Docker to run third-party services locally (Meilisearch for se
- Vanilla CSS modules
- Zod for schemas

# General

- If you need to add dependencies, add them with `bun install XYZ` and ensure that the bun.lock file is updated and part of the commit or PR.

# Typescript

- Use `== null` (double equals null) instead of either `=== null` or `=== undefined` (triple equals null / undefined), and the same for `!=`. This is to make `null` and `undefined` mean the same thing everywhere in our codebase to avoid any potential serialisation/deserialisation confusion or issues.
- Avoid use of `any` unless absolutely necessary.
- Prefer early-exit if statements rather than nested if statements.

# CSS

- Use design tokens present in the root layout as CSS variables
- Any metric should be based on a multiple of gridBaseline. Fractional multipliers are okay if necessary, e.g. calc(1.5 \* var(--gridBaseline))
- Metrics that aren't dependent on "UI scale" should not be based on gridBaseline, e.g. font sizes, line heights, percentage border radii, 1px thin borders, etc.

# Commands

Expand Down Expand Up @@ -53,7 +64,7 @@ If you're receiving a request through an issue or PR comment, always:
- Based on your max turn limit, you may need to break up your proposal into multiple steps. If so, report back to the requester with these multiple proposed stages and implement them one by one instead, after confirmation from the requester.
- Most important - if the request is (verbatim and exactly) "test" with no other words other than the trigger phrase ping itself, respond with ONLY "meow", with no other words, context, or punctuation.
- When creating a PR, always add the relevant CODEOWNER as a reviewer on the PR with the `-r` argument on `gh pr create`.
- When updating a PR to address review comments, always re-request a review from the person that reviewed it using `gh pr edit` using the `--add-reviewer` argument.
- When updating a PR to address review comments, always re-request a review from the person that reviewed it using `gh pr edit` using the `--add-reviewer` argument, and "Resolve" any review comments that were addressed via the Github pull request API.

# English style

Expand Down
1 change: 1 addition & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import styles from './page.module.css';
import { Search } from './search';
import useInfiniteScroll from 'hooks/useInfiniteScroll';
import { useSkeletonRef } from 'app/skeleton_provider';
import { Tooltip } from 'ui/base/tooltip/tooltip';

export default function Page() {
return (
Expand Down
72 changes: 72 additions & 0 deletions src/ui/base/tooltip/tooltip.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.tooltip {
background: var(--colorBackground);
color: var(--colorForeground);
padding: var(--gridBaseline) calc(var(--gridBaseline) * 1.5);
border-radius: calc(var(--gridBaseline) * 0.5);
border: 1px solid var(--colorPurple);
box-sizing: border-box;
font-size: 14px;
max-width: calc(var(--gridBaseline) * 32);
transition: opacity 100ms ease-in-out;
}

.tooltip[data-placement='top'] {
margin-bottom: var(--gridBaseline);
}

.tooltip[data-placement='bottom'] {
margin-top: var(--gridBaseline);
}

.tooltip[data-placement='left'] {
margin-right: var(--gridBaseline);
}

.tooltip[data-placement='right'] {
margin-left: var(--gridBaseline);
}

.arrow {
position: absolute;
}

.arrow svg {
fill: var(--colorBackground);
display: block;
}

.tooltip[data-placement='top'] .arrow {
bottom: calc(var(--gridBaseline) * -0.5);
}

.tooltip[data-placement='bottom'] .arrow {
top: calc(var(--gridBaseline) * -1);
}

.tooltip[data-placement='left'] .arrow {
right: calc(var(--gridBaseline) * -0.5);
}

.tooltip[data-placement='right'] .arrow {
left: calc(var(--gridBaseline) * -1);
}

.tooltip[data-placement='bottom'] .arrow svg {
transform: rotate(180deg);
}

.tooltip[data-placement='left'] .arrow svg {
transform: rotate(-90deg);
}

.tooltip[data-placement='right'] .arrow svg {
transform: rotate(90deg);
}

.tooltip[data-entering] {
opacity: 0;
}

.tooltip[data-exiting] {
opacity: 0;
}
40 changes: 40 additions & 0 deletions src/ui/base/tooltip/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use client';

import React from 'react';
import {
Tooltip as AriaTooltip,
TooltipTrigger,
OverlayArrow,
type TooltipProps as AriaTooltipProps,
} from 'react-aria-components';
import styles from './tooltip.module.css';
import { colors } from '../design_system/design_tokens';

export type TooltipProps = {
/** Content to display in the tooltip */
content: React.ReactNode;
/** Position of the tooltip relative to the trigger */
placement?: AriaTooltipProps['placement'];
/** Delay in ms before showing the tooltip */
delay?: number;
/** The trigger element */
children: React.ReactElement;
};

export const Tooltip = (props: TooltipProps) => {
const { content, placement = 'top', delay = 300, children } = props;

return (
<TooltipTrigger delay={delay}>
{children}
<AriaTooltip placement={placement} className={styles.tooltip}>
<OverlayArrow className={styles.arrow}>
<svg width={8} height={8} viewBox="0 0 8 8">
<path strokeWidth="1" stroke={colors.purple} d="M0 0 L4 4 L8 0" />
</svg>
</OverlayArrow>
{content}
</AriaTooltip>
</TooltipTrigger>
);
};