chore: remove unused UI parts, dependencies and turbo tasks, and share escapeHtml

This commit is contained in:
Amruth Pillai
2026-09-29 10:21:20 +02:00
parent 0f6e08a922
commit e368e5955d
23 changed files with 30 additions and 330 deletions
-1
View File
@@ -62,7 +62,6 @@
"better-auth": "1.7.6",
"cjk-regex": "^3.5.0",
"css-tree": "^3.2.1",
"deepmerge-ts": "^8.0.2",
"drizzle-orm": "1.0.0-rc.4",
"drizzle-zod": "1.0.0-beta.14-a36c63d",
"es-toolkit": "^1.52.0",
-1
View File
@@ -46,7 +46,6 @@
"bcrypt",
"cjk-regex",
"css-tree",
"deepmerge-ts",
"drizzle-zod",
"fast-json-patch",
"fast-png",
+1 -8
View File
@@ -1,3 +1,4 @@
import { escapeHtml } from "@reactive-resume/utils/string";
/**
* Converts a summary string and optional highlights array into an HTML description.
* Summary becomes a <p> tag, highlights become a <ul> list.
@@ -32,14 +33,6 @@ export function arrayToHtmlList(items: string[]): string {
export const BULLET_PATTERN = /^\s*[-–—•*◦‣·]\s+/;
const escapeHtml = (value: string) =>
value
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
/**
* Converts plain-text lines into escaped HTML: a <ul> when most lines are bullets, otherwise one <p> per line.
*/
+1
View File
@@ -28,6 +28,7 @@
"dependencies": {
"@bramus/specificity": "^2.4.2",
"@reactive-resume/schema": "workspace:*",
"@reactive-resume/utils": "workspace:*",
"css-tree": "^3.2.1",
"fast-json-patch": "^3.1.1",
"wink-porter2-stemmer": "2.0.1",
+1 -8
View File
@@ -1,6 +1,7 @@
import type { CoverLetter, CoverLetterStyle } from "@reactive-resume/schema/cover-letter/data";
import type { ResumeData } from "@reactive-resume/schema/resume/data";
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
import { escapeHtml } from "@reactive-resume/utils/string";
export function copyCoverLetterStyle(
data: ResumeData,
@@ -79,14 +80,6 @@ export function detachEmbeddedLetters(data: ResumeData): EmbeddedLetter[] {
);
}
const escapeHtml = (text: string) =>
text
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#39;");
export function coverLetterTextToHtml(text: string): string {
return text
.trim()
+1 -3
View File
@@ -1,4 +1,5 @@
import type { ResumeData } from "@reactive-resume/schema/resume/data";
import { escapeHtml } from "@reactive-resume/utils/string";
/**
* Suggested edits to a document's text, shared by Check, the assistant and Improve. A proposal replaces one passage
@@ -101,9 +102,6 @@ export function splitBlock(html: string) {
return match ? { open: match[1] ?? "", inner: match[3] ?? "", close: match[4] ?? "" } : undefined;
}
const escapeHtml = (text: string) =>
text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
/**
* The block with its text replaced by plain text (a model's rewrite), keeping the tag and its attributes. An empty
* field's "block" becomes a paragraph.
+2 -25
View File
@@ -1,6 +1,6 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { Alert, AlertAction, AlertDescription, AlertTitle } from "./alert";
import { Alert, AlertDescription, AlertTitle } from "./alert";
describe("Alert", () => {
it("announces only errors", () => {
@@ -44,27 +44,4 @@ describe("AlertDescription", () => {
});
});
describe("AlertAction", () => {
it("renders children", () => {
render(<AlertAction>Action</AlertAction>);
expect(screen.getByText("Action")).toBeInTheDocument();
});
it("applies data-slot='alert-action'", () => {
render(<AlertAction>x</AlertAction>);
expect(screen.getByText("x")).toHaveAttribute("data-slot", "alert-action");
});
});
describe("Alert composition", () => {
it("composes all subcomponents", () => {
render(
<Alert variant="error">
<AlertTitle>Title</AlertTitle>
<AlertDescription>Body</AlertDescription>
<AlertAction>OK</AlertAction>
</Alert>,
);
expect(screen.getByRole("alert")).toHaveTextContent(/TitleBodyOK/);
});
});
describe("Alert composition", () => {});
+1 -5
View File
@@ -59,8 +59,4 @@ function AlertDescription({ className, ...props }: React.ComponentProps<"div">)
);
}
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
return <div data-slot="alert-action" className={cn("absolute inset-e-2 top-2", className)} {...props} />;
}
export { Alert, AlertAction, AlertDescription, AlertTitle, alertVariants };
export { Alert, AlertDescription, AlertTitle, alertVariants };
+1 -36
View File
@@ -1,6 +1,6 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage } from "./avatar";
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
describe("Avatar", () => {
it("renders with data-slot='avatar'", () => {
@@ -40,41 +40,6 @@ describe("AvatarFallback", () => {
});
});
describe("AvatarBadge", () => {
it("renders as span with data-slot='avatar-badge'", () => {
render(<AvatarBadge data-testid="b" />);
const badge = screen.getByTestId("b");
expect(badge.tagName).toBe("SPAN");
expect(badge).toHaveAttribute("data-slot", "avatar-badge");
});
});
describe("AvatarGroup", () => {
it("renders as div with data-slot='avatar-group'", () => {
render(<AvatarGroup data-testid="g" />);
const group = screen.getByTestId("g");
expect(group.tagName).toBe("DIV");
expect(group).toHaveAttribute("data-slot", "avatar-group");
});
it("supports children", () => {
render(
<AvatarGroup data-testid="g">
<Avatar />
<Avatar />
</AvatarGroup>,
);
expect(screen.getByTestId("g").children).toHaveLength(2);
});
});
describe("AvatarGroupCount", () => {
it("renders count children", () => {
render(<AvatarGroupCount>+3</AvatarGroupCount>);
expect(screen.getByText("+3")).toHaveAttribute("data-slot", "avatar-group-count");
});
});
describe("AvatarImage", () => {
it("accepts src prop without throwing", () => {
render(
+1 -44
View File
@@ -1,4 +1,3 @@
import type * as React from "react";
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
import { cn } from "@reactive-resume/utils/style";
@@ -48,46 +47,4 @@ function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props)
);
}
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
return (
<span
data-slot="avatar-badge"
className={cn(
"absolute inset-e-0 bottom-0 z-10 inline-flex select-none items-center justify-center rounded-full bg-accent text-on-accent bg-blend-color ring-2 ring-bg",
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
className,
)}
{...props}
/>
);
}
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="avatar-group"
className={cn(
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-bg",
className,
)}
{...props}
/>
);
}
function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="avatar-group-count"
className={cn(
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-sunken text-ink-2 text-sm ring-2 ring-bg group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
className,
)}
{...props}
/>
);
}
export { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage };
export { Avatar, AvatarFallback, AvatarImage };
@@ -1,6 +1,6 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { ButtonGroup, ButtonGroupSeparator, ButtonGroupText } from "./button-group";
import { ButtonGroup } from "./button-group";
describe("ButtonGroup", () => {
it("renders a fieldset with data-slot='button-group'", () => {
@@ -25,37 +25,3 @@ describe("ButtonGroup", () => {
expect(screen.getByTestId("g")).toHaveClass("my-custom");
});
});
describe("ButtonGroupText", () => {
it("renders content inside a div by default", () => {
render(<ButtonGroupText>Hello</ButtonGroupText>);
expect(screen.getByText("Hello").tagName).toBe("DIV");
});
it("merges custom className", () => {
render(<ButtonGroupText className="my-class">x</ButtonGroupText>);
expect(screen.getByText("x")).toHaveClass("my-class");
});
it("supports custom render function", () => {
render(<ButtonGroupText render={(props) => <span {...props} />}>label</ButtonGroupText>);
expect(screen.getByText("label").tagName).toBe("SPAN");
});
});
describe("ButtonGroupSeparator", () => {
it("defaults to vertical orientation", () => {
render(<ButtonGroupSeparator data-testid="sep" />);
expect(screen.getByTestId("sep")).toHaveAttribute("data-orientation", "vertical");
});
it("supports horizontal orientation", () => {
render(<ButtonGroupSeparator data-testid="sep" orientation="horizontal" />);
expect(screen.getByTestId("sep")).toHaveAttribute("data-orientation", "horizontal");
});
it("uses data-slot='button-group-separator'", () => {
render(<ButtonGroupSeparator data-testid="sep" />);
expect(screen.getByTestId("sep")).toHaveAttribute("data-slot", "button-group-separator");
});
});
+1 -41
View File
@@ -1,8 +1,5 @@
import type { VariantProps } from "class-variance-authority";
import { mergeProps } from "@base-ui/react/merge-props";
import { useRender } from "@base-ui/react/use-render";
import { cva } from "class-variance-authority";
import { Separator } from "@reactive-resume/ui/components/separator";
import { cn } from "@reactive-resume/utils/style";
const buttonGroupVariants = cva(
@@ -37,41 +34,4 @@ function ButtonGroup({
);
}
function ButtonGroupText({ className, render, ...props }: useRender.ComponentProps<"div">) {
return useRender({
defaultTagName: "div",
props: mergeProps<"div">(
{
className: cn(
"flex items-center gap-2 rounded-md border border-line-2 bg-sunken px-2.5 font-medium text-ink-2 text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
className,
),
},
props,
),
render,
state: {
slot: "button-group-text",
},
});
}
function ButtonGroupSeparator({
className,
orientation = "vertical",
...props
}: React.ComponentProps<typeof Separator>) {
return (
<Separator
data-slot="button-group-separator"
orientation={orientation}
className={cn(
"relative self-stretch bg-line-2 data-horizontal:mx-px data-vertical:my-px data-vertical:h-auto data-horizontal:w-auto",
className,
)}
{...props}
/>
);
}
export { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants };
export { ButtonGroup, buttonGroupVariants };
@@ -3,14 +3,7 @@ import userEvent from "@testing-library/user-event";
import { describe, expect, it, test } from "vitest";
import { createPortal } from "react-dom";
import { FormControl, FormItem, FormLabel } from "./form";
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "./input-group";
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText } from "./input-group";
function PortaledInput() {
return createPortal(<input aria-label="Portaled input" />, document.body);
@@ -203,12 +196,3 @@ describe("InputGroupInput", () => {
expect(screen.getByTestId("i")).toHaveAttribute("data-slot", "input-group-control");
});
});
describe("InputGroupTextarea", () => {
it("uses data-slot='input-group-control' and renders textarea", () => {
render(<InputGroupTextarea data-testid="t" />);
const t = screen.getByTestId("t");
expect(t.tagName).toBe("TEXTAREA");
expect(t).toHaveAttribute("data-slot", "input-group-control");
});
});
+1 -29
View File
@@ -4,7 +4,6 @@ import { cva } from "class-variance-authority";
import { Button } from "@reactive-resume/ui/components/button";
import { useFormControl } from "@reactive-resume/ui/components/form";
import { Input } from "@reactive-resume/ui/components/input";
import { Textarea } from "@reactive-resume/ui/components/textarea";
import { cn } from "@reactive-resume/utils/style";
function InputGroup({
@@ -161,31 +160,4 @@ function InputGroupInput({
);
}
function InputGroupTextarea({
className,
id: idProp,
"aria-describedby": ariaDescribedByProp,
"aria-invalid": ariaInvalidProp,
...props
}: React.ComponentProps<"textarea">) {
const formControl = useFormControl();
const controlId = idProp ?? formControl.id;
const describedBy = ariaDescribedByProp ?? formControl["aria-describedby"];
const invalid = ariaInvalidProp ?? formControl["aria-invalid"];
return (
<Textarea
data-slot="input-group-control"
id={controlId}
aria-describedby={describedBy}
aria-invalid={invalid}
className={cn(
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none focus:shadow-none disabled:bg-transparent aria-invalid:focus:shadow-none",
className,
)}
{...props}
/>
);
}
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea };
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText };
+1 -30
View File
@@ -1,6 +1,6 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { Kbd, KbdGroup } from "./kbd";
import { Kbd } from "./kbd";
describe("Kbd", () => {
it("renders as a <kbd> element with data-slot='kbd'", () => {
@@ -24,32 +24,3 @@ describe("Kbd", () => {
expect(screen.getByTestId("k")).toHaveClass("my-class");
});
});
describe("KbdGroup", () => {
it("renders as a <kbd> wrapper with data-slot='kbd-group'", () => {
render(<KbdGroup data-testid="g">x</KbdGroup>);
const g = screen.getByTestId("g");
expect(g.tagName).toBe("KBD");
expect(g).toHaveAttribute("data-slot", "kbd-group");
});
it("groups multiple Kbd elements", () => {
render(
<KbdGroup>
<Kbd>Ctrl</Kbd>
<Kbd>K</Kbd>
</KbdGroup>,
);
expect(screen.getByText("Ctrl")).toBeInTheDocument();
expect(screen.getByText("K")).toBeInTheDocument();
});
it("merges custom className", () => {
render(
<KbdGroup data-testid="g" className="my-group">
x
</KbdGroup>,
);
expect(screen.getByTestId("g")).toHaveClass("my-group");
});
});
+1 -5
View File
@@ -13,8 +13,4 @@ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
);
}
function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
return <kbd data-slot="kbd-group" className={cn("inline-flex items-center gap-1", className)} {...props} />;
}
export { Kbd, KbdGroup };
export { Kbd };
+1 -15
View File
@@ -1,7 +1,7 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it } from "vitest";
import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from "./popover";
import { Popover, PopoverContent, PopoverHeader, PopoverTitle, PopoverTrigger } from "./popover";
describe("Popover", () => {
it("trigger element has data-slot='popover-trigger'", () => {
@@ -78,20 +78,6 @@ describe("PopoverTitle", () => {
});
});
describe("PopoverDescription", () => {
it("uses data-slot='popover-description'", () => {
render(
<Popover open>
<PopoverTrigger>Open</PopoverTrigger>
<PopoverContent>
<PopoverDescription>My description</PopoverDescription>
</PopoverContent>
</Popover>,
);
expect(screen.getByText("My description")).toHaveAttribute("data-slot", "popover-description");
});
});
describe("PopoverContent", () => {
it("uses data-slot='popover-content'", () => {
render(
+1 -7
View File
@@ -51,10 +51,4 @@ function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
);
}
function PopoverDescription({ className, ...props }: PopoverPrimitive.Description.Props) {
return (
<PopoverPrimitive.Description data-slot="popover-description" className={cn("text-ink-2", className)} {...props} />
);
}
export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger };
export { Popover, PopoverContent, PopoverHeader, PopoverTitle, PopoverTrigger };
-4
View File
@@ -67,10 +67,6 @@ export function isLocale(value: unknown): value is Locale {
return localeSchema.safeParse(value).success;
}
export function isCJKLocale(locale: Locale): boolean {
return locale === "zh-CN" || locale === "zh-TW" || locale === "ja-JP" || locale === "ko-KR";
}
// A writing system that needs a dedicated fallback font in the PDF renderer,
// because react-pdf (unlike a browser) has no automatic system-font fallback:
// a glyph only renders if a registered font contains it. We pick the matching
+9
View File
@@ -66,6 +66,15 @@ export function generateRandomName() {
* @param html - The HTML string to strip.
* @returns The text content without HTML tags.
*/
/** Text safe to place inside HTML: markup characters and both quote marks become entities. */
export const escapeHtml = (text: string) =>
text
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#39;");
export function stripHtml(html: string | undefined): string {
if (!html) return "";
return html.replace(/<[^>]*>/g, "").trim();
+3 -9
View File
@@ -386,9 +386,6 @@ importers:
css-tree:
specifier: ^3.2.1
version: 3.2.1
deepmerge-ts:
specifier: ^8.0.2
version: 8.0.2
drizzle-orm:
specifier: 1.0.0-rc.4
version: 1.0.0-rc.4(@types/pg@8.23.1)(pg@8.23.0)(zod@4.6.5)
@@ -1337,6 +1334,9 @@ importers:
'@reactive-resume/schema':
specifier: workspace:*
version: link:../schema
'@reactive-resume/utils':
specifier: workspace:*
version: link:../utils
css-tree:
specifier: ^3.2.1
version: 3.2.1
@@ -5883,10 +5883,6 @@ packages:
babel-plugin-macros:
optional: true
deepmerge-ts@8.0.2:
resolution: {integrity: sha512-uqbvqLUMrc6p0MO+WBRtTxY55hmyh94WRwI5a++PZe54X+bfVh59FSN7uWCBCW1CCVjzjnrwzfI8zidE2obMMw==}
engines: {node: '>=16.9.0'}
deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
@@ -13085,8 +13081,6 @@ snapshots:
optionalDependencies:
babel-plugin-macros: 3.1.0
deepmerge-ts@8.0.2: {}
deepmerge@4.3.1: {}
default-browser-id@5.0.1: {}
+1 -1
View File
@@ -7,7 +7,7 @@ import { afterAll, beforeAll, expect, it } from "vitest";
const root = fileURLToPath(new URL("../../", import.meta.url));
const turbo = join(root, "node_modules", "turbo", "bin", "turbo");
const tasks = ["build", "check", "typecheck", "test", "test:coverage", "test:ci", "test:agent"];
const tasks = ["build", "typecheck", "test", "test:coverage", "test:ci", "test:agent"];
type DryTask = { taskId: string; task: string; hash: string; command: string; dependencies: string[] };
let directory: string;
let baseline: Map<string, DryTask>;
-6
View File
@@ -119,9 +119,6 @@
"outputs": ["dist/**", ".vercel/**"],
"dependsOn": ["transit"]
},
"check": {
"dependsOn": ["transit"]
},
"typecheck": {
"dependsOn": ["transit"]
},
@@ -134,9 +131,6 @@
"test:ci": {
"dependsOn": ["transit"]
},
"test:e2e": {
"cache": false
},
"test:agent": {
"dependsOn": ["transit"]
},