docs: add animated portfolio cover (SVG) (#25)
Swadhin Biswas committed 1 months ago
73cdc6d
1 parent 0bb3187
commit 73cdc6d
1 file changed
+180 -0
Showing 1 changed file+180-0
▼assets/opengrammar-cover.svg
+180-0
| @@ -0,0 +1,180 @@ | ||
1 | +<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-labelledby="t d"> | |
2 | + <title id="t">OpenGrammar pipeline</title> | |
3 | + <desc id="d">Text from Gmail, Google Docs, Notion and Reddit runs through an offline dictionary and rule engine, then through a BYOK AI router for what local rules miss. The same backend deploys to Cloudflare, Vercel, Netlify, Docker or NPM, with no database and keys kept in the browser.</desc> | |
4 | + <defs> | |
5 | + <radialGradient id="bgGlow" cx="18%" cy="0%" r="95%"> | |
6 | + <stop offset="0%" stop-color="#171327"/> | |
7 | + <stop offset="55%" stop-color="#0C0C10"/> | |
8 | + <stop offset="100%" stop-color="#09090B"/> | |
9 | + </radialGradient> | |
10 | + <pattern id="grid" width="28" height="28" patternUnits="userSpaceOnUse"> | |
11 | + <circle cx="1" cy="1" r="1" fill="#1C1C23"/> | |
12 | + </pattern> | |
13 | + <linearGradient id="titleGrad" x1="0" y1="0" x2="1" y2="0"> | |
14 | + <stop offset="0%" stop-color="#FFFFFF"/> | |
15 | + <stop offset="60%" stop-color="#D6D6DC"/> | |
16 | + <stop offset="100%" stop-color="#9A9AA4"/> | |
17 | + </linearGradient> | |
18 | + <marker id="ah" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
19 | + <path d="M0 0 L10 5 L0 10 z" fill="#5A5A66"/> | |
20 | + </marker> | |
21 | + <style> | |
22 | + .f { font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } | |
23 | + .card { fill: #121218; stroke: #2E2E37; stroke-width: 1; } | |
24 | + .card-title { fill: #ECECF1; font-size: 12.5px; font-weight: 700; letter-spacing: .5px; } | |
25 | + .sub { fill: #8C8C97; font-size: 10px; } | |
26 | + .micro { fill: #67677A; font-size: 9px; letter-spacing: 1.8px; } | |
27 | + .item { fill: #A9A9B4; font-size: 10px; } | |
28 | + .chip { fill: #17171E; stroke: #33333D; } | |
29 | + .chip-t { fill: #BCBCC7; font-size: 9.5px; } | |
30 | + .node { fill: #13131A; stroke: #34343F; } | |
31 | + .node-t { fill: #E4E4EA; font-size: 11px; font-weight: 700; } | |
32 | + .node-s { fill: #7F7F8B; font-size: 8.8px; } | |
33 | + .flow { stroke: #565663; stroke-width: 1.4; fill: none; stroke-dasharray: 5 7; animation: dash 1.15s linear infinite; } | |
34 | + .live { fill: #34D399; animation: blink 2s ease-in-out infinite; } | |
35 | + .stage { animation: stagePulse 7.6s linear infinite; } | |
36 | + .bar { transform-box: fill-box; transform-origin: left center; animation: grow 1.1s cubic-bezier(.2,.7,.2,1) both; } | |
37 | + .track { fill: #1B1B22; } | |
38 | + .lbl { fill: #A9A9B4; font-size: 9.5px; } | |
39 | + .val { fill: #8C8C97; font-size: 9px; } | |
40 | + @keyframes dash { to { stroke-dashoffset: -24; } } | |
41 | + @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: .18; } } | |
42 | + @keyframes stagePulse { 0%, 100% { stroke: #34343F; } 8% { stroke: #A78BFA; } 20% { stroke: #34343F; } } | |
43 | + @keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } } | |
44 | + @media (prefers-reduced-motion: reduce) { | |
45 | + .flow, .live, .stage, .bar { animation: none; } | |
46 | + } | |
47 | + </style> | |
48 | + </defs> | |
49 | + | |
50 | + <rect width="1200" height="630" fill="url(#bgGlow)"/> | |
51 | + <rect width="1200" height="630" fill="url(#grid)"/> | |
52 | + <rect x="12" y="12" width="1176" height="606" rx="22" fill="none" stroke="#24242C" stroke-width="1.2"/> | |
53 | + | |
54 | + <g transform="translate(48,41) scale(1.05)"> | |
55 | + <path d="M12.67 19a2 2 0 0 0 1.416-.588l6.154-6.172a6 6 0 0 0-8.49-8.49L5.586 9.914A2 2 0 0 0 5 11.328V18a1 1 0 0 0 1 1z" fill="none" stroke="#E8E8EE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> | |
56 | + <path d="M16 8 2 22" fill="none" stroke="#A78BFA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> | |
57 | + <path d="M17.5 15H9" fill="none" stroke="#E8E8EE" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> | |
58 | + </g> | |
59 | + <text class="f" x="84" y="62" font-size="25" font-weight="800" letter-spacing="3" fill="url(#titleGrad)">OPENGRAMMAR</text> | |
60 | + <text class="f sub" x="86" y="86">privacy-first, self-hosted writing assistant</text> | |
61 | + | |
62 | + <rect x="958" y="38" width="194" height="32" rx="16" fill="#111118" stroke="#2E2E37"/> | |
63 | + <circle class="live" cx="978" cy="54" r="3.5"/> | |
64 | + <text class="f chip-t" x="992" y="58">Apache-2.0 · offline-first</text> | |
65 | + | |
66 | + <text class="f micro" x="48" y="122">01 · WRITE ANYWHERE</text> | |
67 | + <text class="f micro" x="48" y="292">02 · SELF-HOST</text> | |
68 | + <text class="f micro" x="48" y="458">03 · SCORE & PRIVACY</text> | |
69 | + | |
70 | + <rect class="card" x="48" y="132" width="328" height="118" rx="14"/> | |
71 | + <text class="f card-title" x="64" y="160">Where text lives</text> | |
72 | + <text class="f sub" x="64" y="178">extension content script</text> | |
73 | + <circle cx="66" cy="197" r="1.8" fill="#67E8F9"/> | |
74 | + <text class="f item" x="76" y="200">Gmail · Google Docs</text> | |
75 | + <circle cx="66" cy="215" r="1.8" fill="#67E8F9"/> | |
76 | + <text class="f item" x="76" y="218">Notion · Reddit</text> | |
77 | + <circle cx="66" cy="233" r="1.8" fill="#67E8F9"/> | |
78 | + <text class="f item" x="76" y="236">any rich-text editor</text> | |
79 | + | |
80 | + <path id="pa" class="flow" d="M380 191 H428" marker-end="url(#ah)"/> | |
81 | + <path id="pb" class="flow" d="M768 191 H818" marker-end="url(#ah)"/> | |
82 | + | |
83 | + <rect class="card" x="436" y="132" width="328" height="118" rx="14"/> | |
84 | + <text class="f card-title" x="452" y="160">Local engine</text> | |
85 | + <text class="f sub" x="452" y="178">offline, in the browser</text> | |
86 | + <circle cx="454" cy="197" r="1.8" fill="#A78BFA"/> | |
87 | + <text class="f item" x="464" y="200">156,000-word dictionary</text> | |
88 | + <circle cx="454" cy="215" r="1.8" fill="#A78BFA"/> | |
89 | + <text class="f item" x="464" y="218">40+ regex rule patterns</text> | |
90 | + <circle cx="454" cy="233" r="1.8" fill="#A78BFA"/> | |
91 | + <text class="f item" x="464" y="236">instant, zero requests</text> | |
92 | + | |
93 | + <rect class="card" x="824" y="132" width="328" height="118" rx="14"/> | |
94 | + <text class="f card-title" x="840" y="160">AI router (BYOK)</text> | |
95 | + <text class="f sub" x="840" y="178">only what local rules miss</text> | |
96 | + <circle cx="842" cy="197" r="1.8" fill="#FCD34D"/> | |
97 | + <text class="f item" x="852" y="200">OpenAI · Groq · OpenRouter</text> | |
98 | + <circle cx="842" cy="215" r="1.8" fill="#FCD34D"/> | |
99 | + <text class="f item" x="852" y="218">Together · Ollama</text> | |
100 | + <circle cx="842" cy="233" r="1.8" fill="#34D399"/> | |
101 | + <text class="f item" x="852" y="236">deduped against local findings</text> | |
102 | + | |
103 | + <path id="pc" class="flow" d="M988 250 V296" marker-end="url(#ah)"/> | |
104 | + | |
105 | + <rect class="card" x="48" y="302" width="1104" height="128" rx="14"/> | |
106 | + <text class="f card-title" x="64" y="330">Deploy it anywhere</text> | |
107 | + <text class="f sub" x="1136" y="330" text-anchor="end">one REST API · /analyze · /autocomplete · /providers</text> | |
108 | + | |
109 | + <rect class="node stage" x="60" y="348" width="196" height="62" rx="10" style="animation-delay:0s"/> | |
110 | + <text class="f node-t" x="158" y="376" text-anchor="middle">Cloudflare Workers</text> | |
111 | + <text class="f node-s" x="158" y="392" text-anchor="middle">edge runtime</text> | |
112 | + | |
113 | + <rect class="node stage" x="281" y="348" width="196" height="62" rx="10" style="animation-delay:1.5s"/> | |
114 | + <text class="f node-t" x="379" y="376" text-anchor="middle">Vercel Edge</text> | |
115 | + <text class="f node-s" x="379" y="392" text-anchor="middle">edge runtime</text> | |
116 | + | |
117 | + <rect class="node stage" x="502" y="348" width="196" height="62" rx="10" style="animation-delay:3s"/> | |
118 | + <text class="f node-t" x="600" y="376" text-anchor="middle">Netlify Functions</text> | |
119 | + <text class="f node-s" x="600" y="392" text-anchor="middle">serverless</text> | |
120 | + | |
121 | + <rect class="node stage" x="723" y="348" width="196" height="62" rx="10" style="animation-delay:4.5s"/> | |
122 | + <text class="f node-t" x="821" y="376" text-anchor="middle">Docker</text> | |
123 | + <text class="f node-s" x="821" y="392" text-anchor="middle">multi-arch image</text> | |
124 | + | |
125 | + <rect class="node stage" x="944" y="348" width="196" height="62" rx="10" style="animation-delay:6s"/> | |
126 | + <text class="f node-t" x="1042" y="376" text-anchor="middle">NPM / Node</text> | |
127 | + <text class="f node-s" x="1042" y="392" text-anchor="middle">npx opengrammar-server</text> | |
128 | + | |
129 | + <path class="flow" d="M260 379 H275" marker-end="url(#ah)"/> | |
130 | + <path class="flow" d="M481 379 H496" marker-end="url(#ah)"/> | |
131 | + <path class="flow" d="M702 379 H717" marker-end="url(#ah)"/> | |
132 | + <path class="flow" d="M923 379 H938" marker-end="url(#ah)"/> | |
133 | + <circle r="3" fill="#A78BFA" opacity="0.9"><animateMotion dur="7.6s" repeatCount="indefinite"><mpath href="#pl"/></animateMotion></circle> | |
134 | + <path id="pl" d="M72 379 H1128" fill="none" stroke="none"/> | |
135 | + | |
136 | + <path id="pd" class="flow" d="M600 434 V462" marker-end="url(#ah)"/> | |
137 | + <path id="pe" class="flow" d="M672 522 H718" marker-end="url(#ah)"/> | |
138 | + | |
139 | + <rect class="card" x="48" y="468" width="620" height="110" rx="14"/> | |
140 | + <text class="f card-title" x="64" y="496">Writing score</text> | |
141 | + <text class="f sub" x="636" y="496" text-anchor="end">0-100 · four dimensions</text> | |
142 | + | |
143 | + <text class="f lbl" x="64" y="514">correctness</text> | |
144 | + <rect class="track" x="150" y="508" width="320" height="8" rx="4"/> | |
145 | + <rect class="bar" x="150" y="508" width="282" height="8" rx="4" fill="#67E8F9"/> | |
146 | + <text class="f val" x="482" y="516">88</text> | |
147 | + | |
148 | + <text class="f lbl" x="64" y="533">readability</text> | |
149 | + <rect class="track" x="150" y="527" width="320" height="8" rx="4"/> | |
150 | + <rect class="bar" x="150" y="527" width="243" height="8" rx="4" fill="#A78BFA" style="animation-delay:.18s"/> | |
151 | + <text class="f val" x="482" y="535">76</text> | |
152 | + | |
153 | + <text class="f lbl" x="64" y="552">engagement</text> | |
154 | + <rect class="track" x="150" y="546" width="320" height="8" rx="4"/> | |
155 | + <rect class="bar" x="150" y="546" width="205" height="8" rx="4" fill="#FCD34D" style="animation-delay:.36s"/> | |
156 | + <text class="f val" x="482" y="554">64</text> | |
157 | + | |
158 | + <text class="f lbl" x="64" y="571">delivery</text> | |
159 | + <rect class="track" x="150" y="565" width="320" height="8" rx="4"/> | |
160 | + <rect class="bar" x="150" y="565" width="262" height="8" rx="4" fill="#34D399" style="animation-delay:.54s"/> | |
161 | + <text class="f val" x="482" y="573">82</text> | |
162 | + | |
163 | + <rect class="card" x="724" y="468" width="428" height="110" rx="14"/> | |
164 | + <text class="f card-title" x="740" y="496">Privacy by default</text> | |
165 | + <rect class="chip" x="740" y="504" width="85" height="22" rx="11"/> | |
166 | + <text class="f chip-t" x="782" y="519" text-anchor="middle">no database</text> | |
167 | + <rect class="chip" x="833" y="504" width="157" height="22" rx="11"/> | |
168 | + <text class="f chip-t" x="911" y="519" text-anchor="middle">keys stay in the browser</text> | |
169 | + <rect class="chip" x="740" y="536" width="123" height="22" rx="11"/> | |
170 | + <text class="f chip-t" x="801" y="551" text-anchor="middle">stateless AI calls</text> | |
171 | + <rect class="chip" x="871" y="536" width="78" height="22" rx="11"/> | |
172 | + <text class="f chip-t" x="910" y="551" text-anchor="middle">Apache-2.0</text> | |
173 | + <text class="f sub" x="740" y="572">text is never stored on a server</text> | |
174 | + | |
175 | + <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite"><mpath href="#pa"/></animateMotion></circle> | |
176 | + <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.4s"><mpath href="#pb"/></animateMotion></circle> | |
177 | + <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.2s"><mpath href="#pc"/></animateMotion></circle> | |
178 | + <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.1s"><mpath href="#pd"/></animateMotion></circle> | |
179 | + <circle r="2.6" fill="#A78BFA"><animateMotion dur="1.5s" repeatCount="indefinite" begin="0.6s"><mpath href="#pe"/></animateMotion></circle> | |
180 | +</svg> | |
0 | 181 | |