Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions apps/mobile/src/components/ui/AppSymbol.ios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import IconGitPullRequestDraft from "@tabler/icons-react-native/IconGitPullReque
import type { SFSymbol } from "expo-symbols";
import IconGitPullRequest from "@tabler/icons-react-native/IconGitPullRequest";
import IconFileDiff from "@tabler/icons-react-native/IconFileDiff";
import IconRobot from "@tabler/icons-react-native/IconRobot";
import { SymbolView as ExpoSymbolView } from "expo-symbols";
import { withUniwind } from "uniwind";
import type { AppSymbolViewProps } from "./AppSymbol";
Expand All @@ -22,6 +23,7 @@ const CUSTOM_ICONS = {
"git.pull-request.closed": IconGitPullRequestClosed,
"git.pull-request.draft": IconGitPullRequestDraft,
"git.commit": IconGitCommit,
robot: IconRobot,
};

function AppSymbolView(props: AppSymbolViewProps) {
Expand Down
20 changes: 15 additions & 5 deletions apps/mobile/src/components/ui/AppSymbol.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ import IconPlayerStopFilled from "@tabler/icons-react-native/IconPlayerStopFille
import IconPlus from "@tabler/icons-react-native/IconPlus";
import IconQrcode from "@tabler/icons-react-native/IconQrcode";
import IconRefresh from "@tabler/icons-react-native/IconRefresh";
import IconRobot from "@tabler/icons-react-native/IconRobot";
import IconSearch from "@tabler/icons-react-native/IconSearch";
import IconShare from "@tabler/icons-react-native/IconShare";
import IconServer from "@tabler/icons-react-native/IconServer";
import IconSettings from "@tabler/icons-react-native/IconSettings";
import IconSparkles from "@tabler/icons-react-native/IconSparkles";
import IconStack2 from "@tabler/icons-react-native/IconStack2";
import IconStar from "@tabler/icons-react-native/IconStar";
import IconStarFilled from "@tabler/icons-react-native/IconStarFilled";
Expand Down Expand Up @@ -271,8 +271,8 @@ const ANDROID_ICON_BY_SF_SYMBOL = {
"exclamationmark.shield": IconShieldQuestion,
"bubble.left.and.bubble.right": IconMessage,
"bubble.left": IconMessage,
sparkles: IconSparkles,
"plus.bubble": IconMessagePlus,
robot: IconRobot,
"text.word.spacing": IconLetterSpacing,
"textformat.size": IconTypography,
"textformat.size.larger": IconTextIncrease,
Expand All @@ -281,23 +281,33 @@ const ANDROID_ICON_BY_SF_SYMBOL = {
trash: IconTrash,
wifi: IconWifi,
"wifi.slash": IconWifiOff,
"wrench.and.screwdriver": IconTool,
xmark: IconX,
"xmark.circle.fill": IconCircleXFilled,
} satisfies Partial<
Record<
SFSymbol | "git.pull-request.closed" | "git.pull-request.draft" | "git.commit" | "file.diff",
| SFSymbol
| "git.pull-request.closed"
| "git.pull-request.draft"
| "git.commit"
| "file.diff"
| "robot",
Icon
>
>;
const SF_ICON_LOOKUP: Partial<
Record<
SFSymbol | "git.pull-request.closed" | "git.pull-request.draft" | "git.commit" | "file.diff",
| SFSymbol
| "git.pull-request.closed"
| "git.pull-request.draft"
| "git.commit"
| "file.diff"
| "robot",
Icon
>
> = ANDROID_ICON_BY_SF_SYMBOL;

const ANDROID_ICON_BY_MATERIAL_NAME = {
auto_awesome: IconSparkles,
bolt: IconBolt,
build: IconTool,
chat: IconMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function PullRequestAgentRow(props: {
const row = (
<PullRequestCardRow
title={props.title ?? action.title}
icon="sparkles"
icon="hammer"
accessibilityLabel={props.title ?? action.title}
menu={agent.threads.length > 1}
onPress={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export function PullRequestActionToolbar(props: {
>
<View pointerEvents="none">
<IconButton
icon="sparkles"
icon="wrench.and.screwdriver"
accessibilityLabel={`${shortLabel} agent actions`}
disabled={agent.disabled}
/>
Expand Down Expand Up @@ -247,7 +247,7 @@ export function PullRequestActionToolbar(props: {
const agentControl = showAgentControls ? (
<Toolbar.Menu
identifier="pull-request-agent"
icon="sparkles"
icon="wrench.and.screwdriver"
title="AI helpers"
accessibilityLabel={`${shortLabel} agent actions`}
disabled={agent.disabled}
Expand Down
10 changes: 1 addition & 9 deletions apps/mobile/src/features/threads/ThreadFeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -859,21 +859,13 @@ function ArtifactTemplateCard(props: {
}) {
return (
<View className="my-2 min-w-0 flex-row items-center gap-3 rounded-2xl border border-border bg-card px-3 py-3">
<View className="relative h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-border bg-subtle">
<View className="h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-border bg-subtle">
<SymbolView
name={ARTIFACT_TEMPLATE_SYMBOL_BY_KIND[props.template.artifactKind]}
size={20}
tintColorClassName="accent-foreground-muted"
type="monochrome"
/>
<View className="absolute -right-1 -bottom-1 h-4 w-4 items-center justify-center rounded-full bg-fuchsia-500">
<SymbolView
name={{ ios: "sparkles", android: "auto_awesome" }}
size={9}
tintColor="white"
type="monochrome"
/>
</View>
</View>
<View className="min-w-0 flex-1">
<Text className="font-t3-bold text-sm text-foreground" numberOfLines={1}>
Expand Down
6 changes: 3 additions & 3 deletions apps/mobile/src/features/threads/thread-work-log.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export function ShimmeringWorkContent(props: {
function workRowSymbolName(icon: ThreadFeedActivity["icon"]): WorkContentIcon {
switch (icon) {
case "agent":
return { ios: "sparkles", android: "auto_awesome" };
return "robot";
case "alert":
return { ios: "exclamationmark.triangle", android: "error" };
case "browser":
Expand Down Expand Up @@ -1401,7 +1401,7 @@ export const ThreadAgentSpawnCard = function ThreadAgentSpawnCard(props: {
<View className="flex-row items-center gap-2">
<View className="h-6 w-6 shrink-0 items-center justify-center">
<SymbolView
name={{ ios: "sparkles", android: "auto_awesome" }}
name="robot"
size={14}
weight="medium"
tintColor={props.iconSubtleColor}
Expand Down Expand Up @@ -1637,7 +1637,7 @@ function toolGroupSummarySymbolName(kind: ToolGroupSummaryKind): AppSymbolName {
case "reasoning":
return { ios: "brain", android: "psychology" };
case "agent-tool":
return { ios: "sparkles", android: "auto_awesome" };
return "robot";
case "tone-tool":
return { ios: "bolt", android: "bolt" };
case "dynamic-tool":
Expand Down
6 changes: 1 addition & 5 deletions apps/web/src/components/ChatMarkdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import {
MessageSquareWarningIcon,
OctagonAlertIcon,
PresentationIcon,
SparklesIcon,
TriangleAlertIcon,
type LucideIcon,
} from "lucide-react";
Expand Down Expand Up @@ -284,11 +283,8 @@ function CodexArtifactTemplateCard(props: {
data-skill-name={props.template.skillName}
>
<div className="flex min-w-0 flex-1 items-center gap-3">
<span className="relative flex size-9 shrink-0 items-center justify-center rounded-lg border border-border/70 bg-background text-muted-foreground shadow-xs">
<span className="flex size-9 shrink-0 items-center justify-center rounded-lg border border-border/70 bg-background text-muted-foreground shadow-xs">
<Icon aria-hidden className="size-5" />
<span className="absolute -right-1 -bottom-1 flex size-4 items-center justify-center rounded-full border border-background bg-primary text-primary-foreground shadow-xs">
<SparklesIcon aria-hidden className="size-2.5" />
</span>
</span>
<div className="min-w-0">
<div className="truncate text-sm font-medium text-foreground">
Expand Down
24 changes: 3 additions & 21 deletions apps/web/src/components/chat/ModelPickerSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Toolbar } from "@base-ui/react/toolbar";
import { type ProviderInstanceId } from "@t3tools/contracts";
import { memo, useLayoutEffect, useRef, useState } from "react";
import { SparklesIcon, StarIcon } from "lucide-react";
import { StarIcon } from "lucide-react";
import { ProviderInstanceIcon } from "./ProviderInstanceIcon";
import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
import { cn } from "~/lib/utils";
Expand All @@ -27,9 +27,6 @@ function describeUnavailableInstance(entry: ProviderInstanceEntry): string {

const SELECTED_INDICATOR_CLASS =
"pointer-events-none absolute -right-1 top-1/2 z-10 h-5 w-0.75 -translate-y-1/2 rounded-l-full bg-primary redesign:right-1 redesign:rounded-full";
const BADGE_BASE_CLASS =
"pointer-events-none absolute -right-0.5 top-0.5 z-10 flex size-3.5 items-center justify-center rounded-full bg-transparent shadow-sm ";
const NEW_BADGE_CLASS = `${BADGE_BASE_CLASS} text-update-foreground `;

const PICKER_TOOLTIP_SIDE = "left" as const;
const PICKER_TOOLTIP_SIDE_OFFSET = 8;
Expand All @@ -43,7 +40,6 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {
disabledInstanceIds?: ReadonlySet<ProviderInstanceId>;
selectableUnavailableInstanceIds?: ReadonlySet<ProviderInstanceId>;
getDisabledInstanceTooltip?: (entry: ProviderInstanceEntry) => string;
newBadgeInstanceIds?: ReadonlySet<ProviderInstanceId>;
}) {
const handleSelect = (instanceId: ProviderInstanceId | "favorites") => {
props.onSelectInstance(instanceId);
Expand Down Expand Up @@ -140,16 +136,13 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {
(isUnavailable && !unavailableSelectionIsReachable) || isContextDisabled;
const isSelected = props.selectedInstanceId === entry.instanceId;
const isHovered = hoveredInstanceId === entry.instanceId;
const showNewBadge = props.newBadgeInstanceIds?.has(entry.instanceId) ?? false;
const showInstanceBadge = shouldShowInstanceBadge(entry, props.instanceEntries);

const tooltip = isUnavailable
? describeUnavailableInstance(entry)
: isContextDisabled
? (props.getDisabledInstanceTooltip?.(entry) ?? entry.displayName)
: showNewBadge
? `${entry.displayName} (New)`
: entry.displayName;
: entry.displayName;

const button = (
<Toolbar.Button
Expand All @@ -171,13 +164,7 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {
focusableWhenDisabled={!isDisabled}
aria-pressed={isSelected}
type="button"
aria-label={
isUnavailable || isContextDisabled
? tooltip
: showNewBadge
? `${entry.displayName}, new`
: entry.displayName
}
aria-label={isUnavailable || isContextDisabled ? tooltip : entry.displayName}
>
<ProviderInstanceIcon
driverKind={entry.driverKind}
Expand All @@ -197,11 +184,6 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {
}
{...(entry.accentColor ? { badgeClassName: "h-3 min-w-3 px-0.5 text-5xs" } : {})}
/>
{showNewBadge ? (
<span className={NEW_BADGE_CLASS} aria-hidden>
<SparklesIcon className="size-2" />
</span>
) : null}
</Toolbar.Button>
);

Expand Down
10 changes: 8 additions & 2 deletions apps/web/src/components/chat/runtimeModeConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { RuntimeMode } from "@t3tools/contracts";
import { type LucideIcon, LockIcon, LockOpenIcon, PenLineIcon, SparklesIcon } from "lucide-react";
import {
type LucideIcon,
LockIcon,
LockOpenIcon,
PenLineIcon,
ShieldCheckIcon,
} from "lucide-react";

export const runtimeModeConfig: Record<
RuntimeMode,
Expand All @@ -18,7 +24,7 @@ export const runtimeModeConfig: Record<
auto: {
label: "Auto",
description: "Supported providers approve routine actions; others still ask.",
icon: SparklesIcon,
icon: ShieldCheckIcon,
},
"full-access": {
label: "Full access",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
MessageCircleQuestionIcon,
MoreHorizontalIcon,
RotateCcwIcon,
SparklesIcon,
ScrollTextIcon,
Trash2Icon,
} from "lucide-react";

Expand Down Expand Up @@ -205,7 +205,7 @@ export function PullRequestActionsMenu({
</MenuItem>
{canWriteGuide ? (
<MenuItem disabled={guideRunning} onClick={writeGuide}>
<SparklesIcon className="size-3.5" />
<ScrollTextIcon className="size-3.5" />
{guideRunning ? "Writing guide..." : !guideWritten ? "Write guide" : "Rewrite guide"}
</MenuItem>
) : null}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/pullRequest/PullRequestGuideTab.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { EnvironmentId, PullRequestRef, PullRequestReviewGuideStop } from "@t3tools/contracts";
import { SparklesIcon, TriangleAlertIcon } from "lucide-react";
import { ScrollTextIcon, TriangleAlertIcon } from "lucide-react";
import { useEffect, useEffectEvent } from "react";

import type { ReviewCommentContext } from "~/reviewCommentContext";
Expand Down Expand Up @@ -100,7 +100,7 @@ export function PullRequestGuideTab({
const writeButton = (label: string, size: "sm" | "xs") =>
canWrite ? (
<Button size={size} variant="outline" disabled={running} onClick={() => void write()}>
{running ? <Spinner /> : <SparklesIcon aria-hidden className="size-3.5" />}
{running ? <Spinner /> : <ScrollTextIcon aria-hidden className="size-3.5" />}
{running ? "Reading the change..." : label}
</Button>
) : null;
Expand Down
2 changes: 2 additions & 0 deletions oxlint-plugin-t3code/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import noMobileUniwindThemeEscapeHatches from "./rules/no-mobile-uniwind-theme-e
import noAmbiguousDetailNavigation from "./rules/no-ambiguous-detail-navigation.ts";
import noMobilePullRequestGlyphLiterals from "./rules/no-mobile-pull-request-glyph-literals.ts";
import noNativeTitleTooltip from "./rules/no-native-title-tooltip.ts";
import noSparklesIcon from "./rules/no-sparkles-icon.ts";
import reactEffectBudget from "./rules/react-effect-budget.ts";
import requireRegisteredListScroll from "./rules/require-registered-list-scroll.ts";
import noOutsetStateIndicators from "./rules/no-outset-state-indicators.ts";
Expand Down Expand Up @@ -53,6 +54,7 @@ export default definePlugin({
"no-ambiguous-detail-navigation": noAmbiguousDetailNavigation,
"no-mobile-pull-request-glyph-literals": noMobilePullRequestGlyphLiterals,
"no-native-title-tooltip": noNativeTitleTooltip,
"no-sparkles-icon": noSparklesIcon,
"react-effect-budget": reactEffectBudget,
"require-registered-list-scroll": requireRegisteredListScroll,
"no-outset-state-indicators": noOutsetStateIndicators,
Expand Down
46 changes: 46 additions & 0 deletions oxlint-plugin-t3code/rules/no-sparkles-icon.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe } from "@effect/vitest";

import { createOxlintRuleHarness } from "../test/utils.ts";

const rule = createOxlintRuleHarness("t3code/no-sparkles-icon", {
filename: "apps/web/src/components/chat/Example.tsx",
});

describe("t3code/no-sparkles-icon", () => {
rule.valid(
"allows icons that name the action",
`
import { BotIcon, ShieldCheckIcon } from "lucide-react";
import IconRobot from "@tabler/icons-react-native/IconRobot";

const icon = <SymbolView name={{ ios: "puzzlepiece", android: "extension" }} />;
`,
);

rule.valid(
"allows copy and identifiers that mention sparkles",
`
const NIGHTLY_SPARKLES = [];
const label = "Add some sparkles to the release notes";
`,
);

rule.invalid("reports Lucide sparkles icons", `import { SparklesIcon } from "lucide-react";`);

rule.invalid(
"reports every Lucide sparkle variant",
`import { Sparkle, WandSparklesIcon, LucideSparkles } from "lucide-react";`,
);

rule.invalid(
"reports Tabler sparkles deep imports",
`import IconSparkles from "@tabler/icons-react-native/IconSparkles";`,
);

rule.invalid("reports SF Symbol sparkles names", `const icon = <SymbolView name="sparkles" />;`);

rule.invalid(
"reports platform symbol objects",
`const icon = { ios: "wand.and.sparkles", android: "auto_awesome" };`,
);
});
Loading
Loading