Skip to content

Commit 0e6c060

Browse files
Merge develop into feat/shell-script-formatter and resolve conflicts
2 parents 6d2c149 + 5e37bf5 commit 0e6c060

2 files changed

Lines changed: 141 additions & 46 deletions

File tree

app/developmentToolsStyles.module.scss

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import "./styles/variables.scss";
2+
23
// serach box css
34
.searchInput {
45
display: flex;
@@ -20,6 +21,7 @@
2021

2122
&:focus {
2223
opacity: 1;
24+
2325
&::placeholder {
2426
opacity: 0%;
2527
}
@@ -30,13 +32,15 @@
3032
font-size: 16px;
3133
transition: all 0.3s linear;
3234
}
35+
3336
@media (max-width: 600px) {
3437
width: 370px;
3538
opacity: 1;
3639
height: 50px;
3740
padding: 2px 20px;
3841
padding-right: 95px;
3942
}
43+
4044
@media (min-width: 768px) {
4145
height: 50px;
4246
}
@@ -60,17 +64,20 @@
6064
// content card hover effect
6165
.contentCardHoverEffect {
6266
border-left: 2px solid #11e498;
63-
div > p > svg {
67+
68+
div>p>svg {
6469
color: #11e498;
6570
}
71+
6672
&:hover {
67-
div > p > svg {
73+
div>p>svg {
6874
color: black;
6975
}
76+
7077
background: linear-gradient(92.9deg, #00d1ff 23.92%, #16fca9 100.19%);
71-
transform: scale(0.4);
7278
-webkit-transform: scale(1.1);
7379
-moz-transform: scale(1.1);
80+
transform: scale(1.1);
7481
z-index: 0;
7582
transition: all 0.3s ease-in-out;
7683
color: black;
@@ -84,18 +91,22 @@
8491
.converterButton {
8592
background: $primary;
8693
}
94+
8795
.clearButton {
8896
background: rgb(255, 79, 108);
8997
}
98+
9099
.copyButton {
91100
background: #00d0ff9a;
92101
}
93102

94103
/* Webkit custom scrollbar styles */
95104
.scrollbar {
96105
overflow: auto;
97-
scrollbar-width: thin; /* Firefox */
98-
scrollbar-color: rgba(255, 255, 255, 0.3) rgba(255, 255, 255, 0.05); /* Firefox */
106+
scrollbar-width: thin;
107+
/* Firefox */
108+
scrollbar-color: rgba(255, 255, 255, 0.3) rgba(255, 255, 255, 0.05);
109+
/* Firefox */
99110
}
100111

101112
/* Scrollbar track (background of the scrollbar) */
@@ -127,8 +138,10 @@
127138
/* Modern scrollbar for code blocks */
128139
.modernScrollbar {
129140
overflow: auto;
130-
scrollbar-width: thin; /* Firefox */
131-
scrollbar-color: rgba(17, 228, 152, 0.4) rgba(255, 255, 255, 0.05); /* Firefox - using primary color */
141+
scrollbar-width: thin;
142+
/* Firefox */
143+
scrollbar-color: rgba(17, 228, 152, 0.4) rgba(255, 255, 255, 0.05);
144+
/* Firefox - using primary color */
132145
}
133146

134147
.modernScrollbar::-webkit-scrollbar {
@@ -152,3 +165,41 @@
152165
background-color: rgba(255, 255, 255, 0.05);
153166
border-radius: 10px;
154167
}
168+
169+
// Search highlight for matching text
170+
.searchHighlight {
171+
background: rgba(0, 218, 146, 0.3);
172+
color: #00da92;
173+
padding: 1px 3px;
174+
border-radius: 3px;
175+
font-weight: 600;
176+
transition: all 0.2s ease;
177+
}
178+
179+
// On card hover, adjust highlight for dark card background
180+
.contentCardHoverEffect:hover .searchHighlight {
181+
background: rgba(0, 0, 0, 0.2);
182+
color: #000;
183+
}
184+
185+
// Keyboard shortcut hint badge
186+
.keyboardHint {
187+
display: none;
188+
font-size: 11px;
189+
color: rgba(255, 255, 255, 0.45);
190+
background: rgba(255, 255, 255, 0.08);
191+
border: 1px solid rgba(255, 255, 255, 0.12);
192+
border-radius: 6px;
193+
padding: 2px 8px;
194+
font-family: monospace;
195+
letter-spacing: 1px;
196+
line-height: 1.4;
197+
-webkit-backdrop-filter: blur(4px);
198+
backdrop-filter: blur(4px);
199+
pointer-events: none;
200+
white-space: nowrap;
201+
202+
@media (min-width: 768px) {
203+
display: inline-block;
204+
}
205+
}

app/page.tsx

Lines changed: 83 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use client";
2-
import React, { useState, useEffect } from "react";
2+
import React, { useState, useEffect, useRef } from "react";
33
import BBIcon from "./components/theme/Icon/bbIcon";
44
import DevelopmentToolsStyles from "./developmentToolsStyles.module.scss";
55
import { InputField } from "./components/theme/form/formFeildComponent";
@@ -16,6 +16,39 @@ import ConvertXIcon from "./components/theme/Icon/convertXIcon";
1616
import GenieIcon from "./components/theme/Icon/genieIcon";
1717
import DevUtilsIcon from "./components/theme/Icon/devUtilsIcon";
1818

19+
const escapeRegex = (str: string) =>
20+
str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21+
22+
const HighlightText = ({
23+
text,
24+
searchTerm,
25+
className,
26+
}: {
27+
text: string;
28+
searchTerm: string;
29+
className?: string;
30+
}) => {
31+
if (!searchTerm?.trim()) return <span className={className}>{text}</span>;
32+
const regex = new RegExp(`(${escapeRegex(searchTerm)})`, "gi");
33+
const parts = text.split(regex);
34+
return (
35+
<span className={className}>
36+
{parts.map((part, i) =>
37+
part.toLowerCase() === searchTerm.toLowerCase() ? (
38+
<mark
39+
key={i}
40+
className={DevelopmentToolsStyles.searchHighlight}
41+
>
42+
{part}
43+
</mark>
44+
) : (
45+
<React.Fragment key={i}>{part}</React.Fragment>
46+
)
47+
)}
48+
</span>
49+
);
50+
};
51+
1952
const CATEGORY_GROUPS = [
2053
"Text Lab",
2154
"Code Forge",
@@ -99,7 +132,7 @@ const classifyBasis = (title: string, url: string): BasisType => {
99132
return "Converters";
100133

101134
if (t.includes("generator") || t.includes("random")) return "Generators";
102-
135+
103136
if (t.includes("color") || t.includes("image")) return "Color/Image";
104137

105138
if (
@@ -130,6 +163,7 @@ const Page = () => {
130163
const [selectedBasis, setSelectedBasis] = useState<BasisType>("All");
131164
const [favorites, setFavorites] = useState<string[]>([]);
132165
const [showFavoritesOnly, setShowFavoritesOnly] = useState(false);
166+
const searchInputRef = useRef<HTMLInputElement>(null);
133167

134168
useEffect(() => {
135169
try {
@@ -152,6 +186,27 @@ const Page = () => {
152186
}
153187
}, []);
154188

189+
// Keyboard shortcut: Ctrl/Cmd + K to focus search
190+
useEffect(() => {
191+
const handler = (e: KeyboardEvent) => {
192+
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
193+
e.preventDefault();
194+
const input = document.getElementById("txtSearch") as HTMLInputElement;
195+
if (input) {
196+
input.focus();
197+
input.scrollIntoView({ behavior: "smooth", block: "center" });
198+
}
199+
}
200+
// Escape to blur/clear search
201+
if (e.key === "Escape" && document.activeElement?.id === "txtSearch") {
202+
e.preventDefault();
203+
(document.activeElement as HTMLElement)?.blur();
204+
}
205+
};
206+
window.addEventListener("keydown", handler);
207+
return () => window.removeEventListener("keydown", handler);
208+
}, []);
209+
155210
const toggleFavorite = (e: React.MouseEvent, url: string) => {
156211
e.preventDefault();
157212
e.stopPropagation();
@@ -192,13 +247,15 @@ const Page = () => {
192247
}));
193248

194249
let filteredItems = itemsWithMeta
195-
.filter((item) =>
196-
searchTerm
197-
? (item?.title || "")
198-
.toLowerCase()
199-
.includes(searchTerm.toLowerCase())
200-
: true
201-
)
250+
.filter((item) => {
251+
if (!searchTerm) return true;
252+
const t = searchTerm.toLowerCase();
253+
return (
254+
(item?.title || "").toLowerCase().includes(t) ||
255+
(item?.description || "").toLowerCase().includes(t) ||
256+
(item?.url || "").toLowerCase().includes(t)
257+
);
258+
})
202259
.filter((item) => (selectedCategory ? item.__group === selectedCategory : true))
203260
.filter((item) => (selectedBasis === "All" ? true : item.__basis === selectedBasis));
204261

@@ -271,7 +328,8 @@ const Page = () => {
271328
/>
272329
</div>
273330
) : (
274-
<div className="absolute lg:right-[210px] lg:top-4 right-11 top-4 2xl:right-[13rem] 2xl:top-4">
331+
<div className="absolute lg:right-[210px] lg:top-4 right-11 top-4 2xl:right-[13rem] 2xl:top-4 flex items-center gap-2">
332+
<span className={DevelopmentToolsStyles.keyboardHint}>Ctrl K</span>
275333
<SearchIcon className="text-white" />
276334
</div>
277335
)}
@@ -330,11 +388,10 @@ const Page = () => {
330388
<button
331389
key={cat}
332390
onClick={() => setSelectedCategory((prev) => (prev === cat ? null : cat))}
333-
className={`w-full text-left px-3 py-2 rounded-lg border transition ${
334-
selectedCategory === cat
335-
? "bg-primary text-black font-bold border-primary"
336-
: "bg-black/40 text-white border-[#222] hover:bg-black/50"
337-
}`}
391+
className={`w-full text-left px-3 py-2 rounded-lg border transition ${selectedCategory === cat
392+
? "bg-primary text-black font-bold border-primary"
393+
: "bg-black/40 text-white border-[#222] hover:bg-black/50"
394+
}`}
338395
>
339396
<div className="flex items-center justify-between">
340397
<span className="text-sm flex items-center gap-2">
@@ -362,12 +419,11 @@ const Page = () => {
362419
<button
363420
key={b}
364421
onClick={() => setSelectedBasis(b)}
365-
className={`px-2.5 py-1.5 rounded-full text-xs border transition ${
366-
selectedBasis === b
367-
? "bg-primary text-black font-bold border-primary"
368-
: "bg-black/40 text-white border-[#222] hover:bg-black/50"
369-
}`}
370-
aria-pressed={selectedBasis === b}
422+
className={`px-2.5 py-1.5 rounded-full text-xs border transition ${selectedBasis === b
423+
? "bg-primary text-black font-bold border-primary"
424+
: "bg-black/40 text-white border-[#222] hover:bg-black/50"
425+
}`}
426+
aria-pressed={selectedBasis === b ? "true" : "false"}
371427
>
372428
<span className="flex items-center gap-1.5">
373429
<span>{b}</span>
@@ -426,7 +482,9 @@ const Page = () => {
426482
className={`bg-white/5 rounded-lg p-8 w-full ${DevelopmentToolsStyles.contentCardHoverEffect} group md:min-h-[160px] relative`}
427483
>
428484
<div className="flex justify-between items-start gap-2">
429-
<h3 className="text-lg font-semibold pr-6">{item?.title}</h3>
485+
<h3 className="text-lg font-semibold pr-6">
486+
<HighlightText text={item?.title || ""} searchTerm={searchTerm} />
487+
</h3>
430488
<button
431489
onClick={(e) => toggleFavorite(e, item?.url)}
432490
className="absolute right-4 top-4 text-white/30 hover:text-yellow-400 transition-colors z-10"
@@ -455,23 +513,9 @@ const Page = () => {
455513
? description.slice(0, 50) + "..."
456514
: description;
457515

458-
return truncated
459-
.split("BetterBugs.io")
460-
.map((part: any, i: any, arr: any) => (
461-
<React.Fragment key={i}>
462-
{part}
463-
{i !== arr.length - 1 && (
464-
<a
465-
href="https://BetterBugs.io"
466-
target="_blank"
467-
rel="noopener noreferrer"
468-
className="text-primary group-hover:underline group-hover:text-secondary group-hover:font-semibold"
469-
>
470-
BetterBugs.io
471-
</a>
472-
)}
473-
</React.Fragment>
474-
));
516+
return (
517+
<HighlightText text={truncated} searchTerm={searchTerm} />
518+
);
475519
})()}
476520
</p>
477521
<div className="mt-3 text-xs text-white/50">{item?.__group}{item?.__basis}</div>

0 commit comments

Comments
 (0)