Skip to content

Commit c85a707

Browse files
Merge pull request #5 from hamdymohamedak/main
Enhance documentation and modernize homepage layout
2 parents 0cfab1d + 324a27c commit c85a707

15 files changed

Lines changed: 928 additions & 645 deletions

‎.vitepress/components/ComparisonMatrix.vue‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ const cols = computed(() =>
129129
{
130130
id: "openfetch" as const,
131131
title: "openFetch",
132-
tag: "This library",
133132
brand: true,
134133
},
135134
{ id: "axios" as const, title: "Axios", tag: null, brand: false },
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
<script setup lang="ts">
2+
import { onMounted, onUnmounted, ref } from "vue";
3+
4+
/** Short, readable snippet for the hero typewriter (loops). */
5+
const CODE = `import openFetch from "@hamdymohamedak/openfetch";
6+
7+
const res = await openFetch.get("https://api.example.com/v1/items");
8+
// res.data · interceptors · middleware`;
9+
10+
const displayed = ref("");
11+
const cursorVisible = ref(true);
12+
13+
let typeTimer: ReturnType<typeof setTimeout> | undefined;
14+
let pauseTimer: ReturnType<typeof setTimeout> | undefined;
15+
let cursorTimer: ReturnType<typeof setInterval> | undefined;
16+
17+
function clearTimers() {
18+
if (typeTimer !== undefined) clearTimeout(typeTimer);
19+
if (pauseTimer !== undefined) clearTimeout(pauseTimer);
20+
if (cursorTimer !== undefined) clearInterval(cursorTimer);
21+
typeTimer = pauseTimer = cursorTimer = undefined;
22+
}
23+
24+
function schedulePauseThenRestart() {
25+
if (pauseTimer !== undefined) clearTimeout(pauseTimer);
26+
pauseTimer = window.setTimeout(() => {
27+
pauseTimer = undefined;
28+
displayed.value = "";
29+
runTypingLoop(0);
30+
}, 4200);
31+
}
32+
33+
function runTypingLoop(index: number) {
34+
if (index > CODE.length) {
35+
schedulePauseThenRestart();
36+
return;
37+
}
38+
39+
displayed.value = CODE.slice(0, index);
40+
41+
if (index === CODE.length) {
42+
schedulePauseThenRestart();
43+
return;
44+
}
45+
46+
const ch = CODE[index];
47+
const delay =
48+
ch === "\n"
49+
? 280
50+
: ch === " "
51+
? 42
52+
: 52 + Math.floor(Math.random() * 58);
53+
54+
typeTimer = window.setTimeout(() => runTypingLoop(index + 1), delay);
55+
}
56+
57+
onMounted(() => {
58+
const reduce =
59+
typeof window !== "undefined" &&
60+
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
61+
62+
if (reduce) {
63+
displayed.value = CODE;
64+
cursorVisible.value = true;
65+
return;
66+
}
67+
68+
cursorTimer = window.setInterval(() => {
69+
cursorVisible.value = !cursorVisible.value;
70+
}, 640);
71+
72+
runTypingLoop(0);
73+
});
74+
75+
onUnmounted(() => {
76+
clearTimers();
77+
});
78+
</script>
79+
80+
<template>
81+
<div class="typer" aria-label="Example openFetch usage in TypeScript">
82+
<div class="typer-chrome">
83+
<span class="dots" aria-hidden="true">
84+
<i /><i /><i />
85+
</span>
86+
<span class="typer-title">example.ts</span>
87+
<span class="typer-pill">TypeScript</span>
88+
</div>
89+
<div class="typer-body">
90+
<pre class="typer-pre"><code>{{ displayed }}<span
91+
class="typer-cursor"
92+
:data-on="cursorVisible ? 'true' : 'false'"
93+
></span></code></pre>
94+
</div>
95+
</div>
96+
</template>
97+
98+
<style scoped>
99+
.typer {
100+
width: min(100%, 22.5rem);
101+
max-width: 100%;
102+
margin: 0.85rem auto 0;
103+
border-radius: 12px;
104+
border: 1px solid
105+
color-mix(in srgb, var(--vp-c-divider) 88%, var(--vp-c-brand-1) 12%);
106+
background: color-mix(in srgb, var(--vp-c-bg-soft) 55%, var(--vp-c-bg));
107+
box-shadow:
108+
0 0 0 1px color-mix(in srgb, var(--vp-c-bg) 70%, transparent),
109+
0 12px 32px rgba(15, 23, 42, 0.07),
110+
0 2px 8px rgba(15, 23, 42, 0.04);
111+
backdrop-filter: blur(12px);
112+
overflow: hidden;
113+
}
114+
115+
:root.dark .typer {
116+
box-shadow:
117+
0 0 0 1px rgba(255, 255, 255, 0.06),
118+
0 16px 48px rgba(0, 0, 0, 0.35);
119+
}
120+
121+
.typer-chrome {
122+
display: flex;
123+
align-items: center;
124+
gap: 8px;
125+
padding: 7px 11px;
126+
border-bottom: 1px solid color-mix(in srgb, var(--vp-c-divider) 80%, transparent);
127+
background: color-mix(in srgb, var(--vp-c-bg-alt) 35%, transparent);
128+
}
129+
130+
.dots {
131+
display: inline-flex;
132+
gap: 5px;
133+
}
134+
135+
.dots i {
136+
width: 8px;
137+
height: 8px;
138+
border-radius: 50%;
139+
background: var(--vp-c-divider);
140+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vp-c-text-1) 8%, transparent);
141+
}
142+
143+
.dots i:nth-child(1) {
144+
background: #ff5f57;
145+
}
146+
.dots i:nth-child(2) {
147+
background: #febc2e;
148+
}
149+
.dots i:nth-child(3) {
150+
background: #28c840;
151+
}
152+
153+
.typer-title {
154+
flex: 1;
155+
min-width: 0;
156+
font-family: var(--of-font-mono, var(--vp-font-family-mono));
157+
font-size: 10px;
158+
font-weight: 500;
159+
color: var(--vp-c-text-3);
160+
letter-spacing: 0.02em;
161+
}
162+
163+
.typer-pill {
164+
font-size: 9px;
165+
font-weight: 700;
166+
letter-spacing: 0.07em;
167+
text-transform: uppercase;
168+
padding: 3px 7px;
169+
border-radius: 999px;
170+
color: color-mix(in srgb, var(--vp-c-brand-1) 92%, #fff);
171+
background: color-mix(in srgb, var(--vp-c-brand-1) 14%, transparent);
172+
border: 1px solid color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent);
173+
}
174+
175+
.typer-body {
176+
padding: 9px 11px 11px;
177+
min-height: 6.75rem;
178+
}
179+
180+
.typer-pre {
181+
margin: 0;
182+
font-family: var(--of-font-mono, var(--vp-font-family-mono));
183+
font-size: 10.5px;
184+
line-height: 1.55;
185+
color: var(--vp-c-text-1);
186+
white-space: pre-wrap;
187+
word-break: break-word;
188+
tab-size: 2;
189+
}
190+
191+
.typer-cursor {
192+
display: inline-block;
193+
width: 0.55ch;
194+
height: 1.15em;
195+
margin-left: 1px;
196+
vertical-align: text-bottom;
197+
border-radius: 2px;
198+
background: color-mix(in srgb, var(--vp-c-brand-1) 88%, transparent);
199+
animation: none;
200+
}
201+
202+
.typer-cursor[data-on="false"] {
203+
opacity: 0.12;
204+
}
205+
206+
@media (prefers-reduced-motion: reduce) {
207+
.typer-cursor {
208+
opacity: 0.35;
209+
}
210+
}
211+
</style>

0 commit comments

Comments
 (0)