Skip to content

Commit 2ee2a18

Browse files
committed
feat: v1.11.0 — add nativeprobe as third flagship project
- nativeprobe (react native/expo, open source, beta) joins liflow and infralens as flagship projects on the homepage and projects city, linked via github only until its public store release - project cards can now show a logo and an "open source" badge (abbreviated "oss" below 420px), applied to liflow, infralens and nativeprobe - homepage hero tagline now mentions react native alongside typescript and react
1 parent 2f3b118 commit 2ee2a18

10 files changed

Lines changed: 199 additions & 42 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@ InfraLens's history as a standalone product (2026-01-06 to 2026-08-10) is
99
frozen in [`docs/infralens/CHANGELOG.md`](docs/infralens/CHANGELOG.md).
1010
InfraLens changes since its native migration are recorded here.
1111

12+
## [1.11.0] — 2026-09-11
13+
14+
### Added
15+
16+
- **NativeProbe joins the portfolio** as the third flagship project
17+
alongside Liflow and InfraLens — an open-source React Native/Expo
18+
playground for exploring real device capabilities, currently in beta
19+
ahead of Google Play testing. Linked from the homepage, Projects City
20+
and its own project page, with a GitHub link only (no store link until
21+
public release).
22+
- Project cards can show a logo and an "Open Source" badge (shortened to
23+
"OSS" below 420px to avoid wrapping) — applied to InfraLens and
24+
NativeProbe. Liflow's card also gained its logo.
25+
26+
### Changed
27+
28+
- Homepage hero tagline now reads "TypeScript, React et React Native — du
29+
prototype au produit déployé." to reflect the portfolio's mobile work.
30+
1231
## [1.10.1] — 2026-09-10
1332

1433
### Added

‎app/globals.css‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
@custom-variant dark (&:is(.dark *));
66

77
@theme {
8+
--breakpoint-xs: 26.25rem;
9+
810
/* InfraLens brand tokens — kept as named aliases because InfraLens's
911
components reference bg-brand-accent, text-brand-secondary-hover, etc.
1012
directly. brand-secondary used to alias the blue scale (a leftover from

‎app/page.tsx‎

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import { GitHubIcon } from "@/components/github-icon";
12
import { HeroText } from "@/components/hero-text";
23
import { WorldMap } from "@/components/map/world-map";
34
import { brand } from "@/lib/brand";
45
import { getFeaturedProjects, statusLabel } from "@/lib/projects";
56
import { tools } from "@/lib/tools";
67
import { ExternalLink, Wrench } from "lucide-react";
8+
import Image from "next/image";
79
import Link from "next/link";
810

911
const featuredProjects = getFeaturedProjects();
@@ -65,15 +67,35 @@ export default function Home() {
6567
}}
6668
>
6769
<div className="mb-3 flex items-center justify-between">
68-
<span
69-
className="inline-block w-fit rounded-md px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider"
70-
style={{
71-
backgroundColor: `${cardColor}18`,
72-
color: cardColor,
73-
}}
74-
>
75-
{statusLabel(project.status)}
76-
</span>
70+
<div className="flex items-center gap-2">
71+
{project.logo && (
72+
<Image
73+
src={project.logo.src}
74+
alt={project.logo.alt}
75+
width={24}
76+
height={24}
77+
className="h-6 w-6"
78+
/>
79+
)}
80+
<span
81+
className="inline-block w-fit rounded-md px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider"
82+
style={{
83+
backgroundColor: `${cardColor}18`,
84+
color: cardColor,
85+
}}
86+
>
87+
{statusLabel(project.status)}
88+
</span>
89+
{project.openSource && (
90+
<span
91+
className="inline-block w-fit rounded-md px-2.5 py-1 text-[10px] font-semibold whitespace-nowrap uppercase tracking-wider text-zinc-300"
92+
style={{ background: brand.colors.surface[3] }}
93+
>
94+
<span className="xs:hidden">OSS</span>
95+
<span className="hidden xs:inline">Open Source</span>
96+
</span>
97+
)}
98+
</div>
7799
{project.type === "tool" && (
78100
<Wrench
79101
size={16}
@@ -122,6 +144,22 @@ export default function Home() {
122144
Voir le produit
123145
</a>
124146
)}
147+
{project.repositoryUrl && (
148+
<a
149+
href={project.repositoryUrl}
150+
target="_blank"
151+
rel="noopener noreferrer"
152+
className="inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs font-medium transition-all duration-200 hover:brightness-125"
153+
style={{
154+
borderColor: `${cardColor}30`,
155+
backgroundColor: `${cardColor}0d`,
156+
color: cardColor,
157+
}}
158+
>
159+
<GitHubIcon size={11} />
160+
GitHub
161+
</a>
162+
)}
125163
</div>
126164
</div>
127165
);

‎app/projects/[slug]/page.tsx‎

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { GitHubIcon } from "@/components/github-icon";
12
import { brand } from "@/lib/brand";
23
import { buildSoftwareApplicationSchema } from "@/lib/json-ld";
34
import { getProject, projects, statusLabel } from "@/lib/projects";
45
import { ArrowLeft, ExternalLink } from "lucide-react";
56
import type { Metadata } from "next";
7+
import Image from "next/image";
68
import Link from "next/link";
79
import { notFound } from "next/navigation";
810

@@ -72,12 +74,32 @@ export default async function ProjectPage({ params }: Props) {
7274

7375
{/* Hero */}
7476
<header className="mb-10">
75-
<span
76-
className="mb-3 inline-block w-fit rounded-md px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider"
77-
style={{ backgroundColor: `${color}18`, color }}
78-
>
79-
{statusLabel(project.status)}
80-
</span>
77+
<div className="mb-3 flex items-center gap-2">
78+
{project.logo && (
79+
<Image
80+
src={project.logo.src}
81+
alt={project.logo.alt}
82+
width={24}
83+
height={24}
84+
className="h-6 w-6"
85+
/>
86+
)}
87+
<span
88+
className="inline-block w-fit rounded-md px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider"
89+
style={{ backgroundColor: `${color}18`, color }}
90+
>
91+
{statusLabel(project.status)}
92+
</span>
93+
{project.openSource && (
94+
<span
95+
className="inline-block w-fit rounded-md px-2.5 py-1 text-[10px] font-semibold whitespace-nowrap uppercase tracking-wider text-zinc-300"
96+
style={{ background: brand.colors.surface[3] }}
97+
>
98+
<span className="xs:hidden">OSS</span>
99+
<span className="hidden xs:inline">Open Source</span>
100+
</span>
101+
)}
102+
</div>
81103
<h1 className="text-3xl font-bold tracking-tight text-white md:text-4xl">
82104
{project.name}
83105
</h1>
@@ -150,21 +172,39 @@ export default async function ProjectPage({ params }: Props) {
150172
</div>
151173

152174
{/* Liens */}
153-
{project.projectUrl && (
154-
<div className="mt-8">
155-
<a
156-
href={project.projectUrl}
157-
target="_blank"
158-
rel="noopener noreferrer"
159-
className="inline-flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm font-medium transition-opacity hover:opacity-80"
160-
style={{
161-
backgroundColor: `${color}18`,
162-
color,
163-
border: `1px solid ${color}30`,
164-
}}
165-
>
166-
Voir le produit <ExternalLink size={12} />
167-
</a>
175+
{(project.projectUrl || project.repositoryUrl) && (
176+
<div className="mt-8 flex flex-wrap items-center gap-3">
177+
{project.projectUrl && (
178+
<a
179+
href={project.projectUrl}
180+
target="_blank"
181+
rel="noopener noreferrer"
182+
className="inline-flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm font-medium transition-opacity hover:opacity-80"
183+
style={{
184+
backgroundColor: `${color}18`,
185+
color,
186+
border: `1px solid ${color}30`,
187+
}}
188+
>
189+
Voir le produit <ExternalLink size={12} />
190+
</a>
191+
)}
192+
{project.repositoryUrl && (
193+
<a
194+
href={project.repositoryUrl}
195+
target="_blank"
196+
rel="noopener noreferrer"
197+
className="inline-flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm font-medium transition-opacity hover:opacity-80"
198+
style={{
199+
backgroundColor: `${color}18`,
200+
color,
201+
border: `1px solid ${color}30`,
202+
}}
203+
>
204+
<GitHubIcon size={12} />
205+
GitHub
206+
</a>
207+
)}
168208
</div>
169209
)}
170210
</div>

‎app/projects/page.tsx‎

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { openSourceProjects } from "@/lib/open-source";
55
import { projects, statusLabel } from "@/lib/projects";
66
import { ExternalLink, Package, Wrench } from "lucide-react";
77
import type { Metadata } from "next";
8+
import Image from "next/image";
89
import Link from "next/link";
910

1011
export const metadata: Metadata = {
@@ -39,15 +40,35 @@ export default function ProjectsPage() {
3940
}}
4041
>
4142
<div className="flex items-center justify-between gap-2">
42-
<span
43-
className="inline-block rounded-md px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider"
44-
style={{
45-
backgroundColor: `${cardColor}18`,
46-
color: cardColor,
47-
}}
48-
>
49-
{statusLabel(project.status)}
50-
</span>
43+
<div className="flex items-center gap-2">
44+
{project.logo && (
45+
<Image
46+
src={project.logo.src}
47+
alt={project.logo.alt}
48+
width={24}
49+
height={24}
50+
className="h-6 w-6"
51+
/>
52+
)}
53+
<span
54+
className="inline-block rounded-md px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider"
55+
style={{
56+
backgroundColor: `${cardColor}18`,
57+
color: cardColor,
58+
}}
59+
>
60+
{statusLabel(project.status)}
61+
</span>
62+
{project.openSource && (
63+
<span
64+
className="inline-block rounded-md px-2.5 py-1 text-[10px] font-semibold whitespace-nowrap uppercase tracking-wider text-zinc-300"
65+
style={{ background: brand.colors.surface[3] }}
66+
>
67+
<span className="xs:hidden">OSS</span>
68+
<span className="hidden xs:inline">Open Source</span>
69+
</span>
70+
)}
71+
</div>
5172
{project.type === "tool" && (
5273
<Wrench
5374
size={16}
@@ -122,6 +143,18 @@ export default function ProjectsPage() {
122143
Voir le projet <ExternalLink size={11} />
123144
</a>
124145
)}
146+
{project.repositoryUrl && (
147+
<a
148+
href={project.repositoryUrl}
149+
target="_blank"
150+
rel="noopener noreferrer"
151+
className="inline-flex items-center gap-1 text-xs font-medium"
152+
style={{ color: cardColor }}
153+
>
154+
<GitHubIcon size={11} />
155+
GitHub
156+
</a>
157+
)}
125158
</div>
126159
{project.relatedArticleSlug && (
127160
<Link

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "randy-code",
3-
"version": "1.10.1",
3+
"version": "1.11.0",
44
"private": true,
55
"packageManager": "pnpm@10.7.0",
66
"scripts": {

‎public/projects/logos/liflow.png‎

41.7 KB
Loading
117 KB
Loading

‎src/components/hero-text.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const heroContent = {
55
eyebrow: "Développeur TypeScript",
66
headline:
77
"Développeur TypeScript, je construis des applications web, mobiles et des outils métier pensés pour des usages réels.",
8-
tagline: "React, Next.js et TypeScript — du prototype au produit déployé.",
8+
tagline:
9+
"TypeScript, React et React Native — du prototype au produit déployé.",
910
};
1011

1112
export function HeroText() {

‎src/lib/projects.ts‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface Project {
1414
slug: string;
1515
name: string;
1616
type: "product" | "tool" | "client" | "experiment";
17-
status: "active" | "maintained" | "completed" | "experimental";
17+
status: "active" | "maintained" | "completed" | "experimental" | "beta";
1818
tagline: string;
1919
problem: string;
2020
solution: string;
@@ -23,6 +23,8 @@ export interface Project {
2323
projectUrl?: string;
2424
repositoryUrl?: string;
2525
image?: string;
26+
logo?: { src: string; alt: string };
27+
openSource?: boolean;
2628
featured: boolean;
2729
caseStudy?: ProjectCaseStudy;
2830
relatedArticleSlug?: string;
@@ -44,6 +46,7 @@ export const projects: Project[] = [
4446
"Application live et disponible sur liflow.app, en phase de croissance post-lancement.",
4547
technologies: ["Next.js", "TypeScript", "Mistral AI", "Mux", "Upstash"],
4648
projectUrl: "https://liflow.app",
49+
logo: { src: "/projects/logos/liflow.png", alt: "" },
4750
featured: true,
4851
relatedArticleSlug: "liflow-refonte-souvenirs-familiaux",
4952
caseStudy: {
@@ -80,6 +83,8 @@ export const projects: Project[] = [
8083
result: "Outil utilisé au quotidien, intégré nativement à Randy Code.",
8184
technologies: ["Next.js", "TypeScript", "Node.js", "Vercel"],
8285
projectUrl: "https://randy-code.dev/tools/infralens",
86+
logo: { src: "/infralens/brand/logo-symbol.png", alt: "" },
87+
openSource: true,
8388
featured: true,
8489
relatedArticleSlug: "securiser-analyseur-url-contre-ssrf",
8590
caseStudy: {
@@ -100,6 +105,24 @@ export const projects: Project[] = [
100105
"Garder InfraLens simple, gratuit et transparent demande de la discipline : résister à la tentation d'ajouter des fonctionnalités qui le feraient glisser vers un SaaS ou un outil de pentest commercial, alors que ce n'est ni son objectif ni son positionnement.",
101106
},
102107
},
108+
{
109+
slug: "nativeprobe",
110+
name: "NativeProbe",
111+
type: "product",
112+
status: "beta",
113+
tagline:
114+
"Un playground mobile open source pour explorer, tester et comprendre les capacités réelles d'un appareil.",
115+
problem:
116+
"Les développeurs React Native n'ont pas de terrain de jeu pour tester en direct les capacités réelles d'un appareil — capteurs, permissions, réseau, biométrie — sans écrire un projet de test à chaque fois.",
117+
solution:
118+
"Playground mobile open source avec des probes interactifs qui déclenchent chaque capacité en direct (capteurs, réseau, localisation, biométrie, haptique...) et montrent l'API Expo derrière chaque test.",
119+
result: "Application en beta, en approche de la phase de test Google Play.",
120+
technologies: ["React Native", "Expo", "TypeScript", "iOS", "Android"],
121+
repositoryUrl: "https://github.com/Randy-R-code/nativeprobe",
122+
logo: { src: "/projects/logos/nativeprobe.png", alt: "" },
123+
openSource: true,
124+
featured: true,
125+
},
103126
{
104127
slug: "specialiste-automobile",
105128
name: "Site vitrine — Spécialiste automobile",
@@ -137,6 +160,7 @@ const statusLabels: Record<Project["status"], string> = {
137160
maintained: "Maintenu",
138161
completed: "Projet client livré",
139162
experimental: "Expérimentation",
163+
beta: "Beta",
140164
};
141165

142166
export function statusLabel(status: Project["status"]): string {

0 commit comments

Comments
 (0)