feat(web): footer credit — Made with ♥ by techfreakworm
Browse files- web/src/components/MadeBy.tsx +28 -0
- web/src/pages/Studio.tsx +3 -0
- web/src/test/MadeBy.test.tsx +21 -0
web/src/components/MadeBy.tsx
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const URL = "https://mayankgupta.in";
|
| 2 |
+
|
| 3 |
+
export default function MadeBy() {
|
| 4 |
+
return (
|
| 5 |
+
<a
|
| 6 |
+
href={URL}
|
| 7 |
+
target="_blank"
|
| 8 |
+
rel="noopener noreferrer"
|
| 9 |
+
aria-label="Made by Mayank Gupta — opens mayankgupta.in in a new tab"
|
| 10 |
+
className="group block text-center py-6 select-none"
|
| 11 |
+
>
|
| 12 |
+
<div className="label-mono inline-flex items-center gap-1.5 text-muted-foreground">
|
| 13 |
+
<span>Made with</span>
|
| 14 |
+
<span
|
| 15 |
+
aria-hidden
|
| 16 |
+
className="text-[hsl(var(--ember))] group-hover:animate-pulse-dot"
|
| 17 |
+
>
|
| 18 |
+
♥
|
| 19 |
+
</span>
|
| 20 |
+
<span>by</span>
|
| 21 |
+
</div>
|
| 22 |
+
<div className="display-serif text-[24px] mt-1 transition-colors duration-200 group-hover:text-[hsl(var(--ember))]">
|
| 23 |
+
techfreakworm
|
| 24 |
+
</div>
|
| 25 |
+
<div className="label-mono mt-1 text-muted-foreground/70">2026</div>
|
| 26 |
+
</a>
|
| 27 |
+
);
|
| 28 |
+
}
|
web/src/pages/Studio.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import DeviceBadge from "@/components/DeviceBadge";
|
|
| 13 |
import DialogComposer, { type DialogSubmit } from "@/components/DialogComposer";
|
| 14 |
import HistoryList from "@/components/HistoryList";
|
| 15 |
import LoadingBanner from "@/components/LoadingBanner";
|
|
|
|
| 16 |
import ModelPicker from "@/components/ModelPicker";
|
| 17 |
import ModeToggle, { type Mode } from "@/components/ModeToggle";
|
| 18 |
import ParamsPanel from "@/components/ParamsPanel";
|
|
@@ -355,6 +356,8 @@ export default function Studio() {
|
|
| 355 |
</main>
|
| 356 |
|
| 357 |
<footer className="border-t border-border mt-16">
|
|
|
|
|
|
|
| 358 |
<div className="mx-auto max-w-[1280px] px-8 py-6 flex items-center justify-between">
|
| 359 |
<span className="label-mono">chatterbox · resemble ai</span>
|
| 360 |
<span className="label-mono">stateless · browser-persisted</span>
|
|
|
|
| 13 |
import DialogComposer, { type DialogSubmit } from "@/components/DialogComposer";
|
| 14 |
import HistoryList from "@/components/HistoryList";
|
| 15 |
import LoadingBanner from "@/components/LoadingBanner";
|
| 16 |
+
import MadeBy from "@/components/MadeBy";
|
| 17 |
import ModelPicker from "@/components/ModelPicker";
|
| 18 |
import ModeToggle, { type Mode } from "@/components/ModeToggle";
|
| 19 |
import ParamsPanel from "@/components/ParamsPanel";
|
|
|
|
| 356 |
</main>
|
| 357 |
|
| 358 |
<footer className="border-t border-border mt-16">
|
| 359 |
+
<MadeBy />
|
| 360 |
+
<div className="rule-dotted mx-8" />
|
| 361 |
<div className="mx-auto max-w-[1280px] px-8 py-6 flex items-center justify-between">
|
| 362 |
<span className="label-mono">chatterbox · resemble ai</span>
|
| 363 |
<span className="label-mono">stateless · browser-persisted</span>
|
web/src/test/MadeBy.test.tsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { render, screen } from "@testing-library/react";
|
| 2 |
+
import { describe, expect, it } from "vitest";
|
| 3 |
+
import MadeBy from "@/components/MadeBy";
|
| 4 |
+
|
| 5 |
+
describe("MadeBy", () => {
|
| 6 |
+
it("renders an anchor to mayankgupta.in opening in a new tab", () => {
|
| 7 |
+
render(<MadeBy />);
|
| 8 |
+
const link = screen.getByRole("link", { name: /made by/i });
|
| 9 |
+
expect(link).toHaveAttribute("href", "https://mayankgupta.in");
|
| 10 |
+
expect(link).toHaveAttribute("target", "_blank");
|
| 11 |
+
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
| 12 |
+
expect(link.textContent).toMatch(/techfreakworm/);
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
it("includes the heart and the year", () => {
|
| 16 |
+
render(<MadeBy />);
|
| 17 |
+
const link = screen.getByRole("link", { name: /made by/i });
|
| 18 |
+
expect(link.textContent).toMatch(/♥/);
|
| 19 |
+
expect(link.textContent).toMatch(/2026/);
|
| 20 |
+
});
|
| 21 |
+
});
|