website
git clone https://git.pyrossh.dev/website
木 Personal website of pyrossh. Built with astrojs, shiki, vite.
1980b5c
— pyrossh
2026-08-06T17:26:55+05:30
fix icons
- assets/logos/helium.png +0 -0
- assets/logos/zen.svg +0 -20
- eleventy.config.js +5 -1
- infra/terraform.tfstate +7 -8
- infra/terraform.tfstate.backup +42 -8
- src/_data/site.js +1 -2
- src/_helpers/fileIcons.js +9 -51
- src/_includes/repo-layout.njk +0 -1
assets/logos/helium.png
ADDED
|
Binary file
|
assets/logos/zen.svg
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
<svg width="1058" height="1058" viewBox="0 0 1058 1058" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<g filter="url(#filter0_d_9_47)">
|
|
3
|
-
<rect x="17" y="3" width="1024" height="1024" rx="225" fill="#202020"/>
|
|
4
|
-
<circle cx="529" cy="515" r="340" stroke="#F2F0E3" stroke-width="70"/>
|
|
5
|
-
<circle cx="529" cy="515" r="224.915" stroke="#F2F0E3" stroke-width="51"/>
|
|
6
|
-
<circle cx="529" cy="515" r="129.018" stroke="#F2F0E3" stroke-width="31"/>
|
|
7
|
-
</g>
|
|
8
|
-
<defs>
|
|
9
|
-
<filter id="filter0_d_9_47" x="0.6" y="0.6" width="1056.8" height="1056.8" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
10
|
-
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
11
|
-
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
12
|
-
<feOffset dy="14"/>
|
|
13
|
-
<feGaussianBlur stdDeviation="8.2"/>
|
|
14
|
-
<feComposite in2="hardAlpha" operator="out"/>
|
|
15
|
-
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
16
|
-
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_9_47"/>
|
|
17
|
-
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_9_47" result="shape"/>
|
|
18
|
-
</filter>
|
|
19
|
-
</defs>
|
|
20
|
-
</svg>
|
eleventy.config.js
CHANGED
|
@@ -64,7 +64,6 @@ export default function (eleventyConfig) {
|
|
|
64
64
|
);
|
|
65
65
|
eleventyConfig.addFilter("fileIcon", fileIcon);
|
|
66
66
|
eleventyConfig.addFilter("folderIcon", folderIcon);
|
|
67
|
-
eleventyConfig.addNunjucksGlobal("iconSpriteDefs", iconSpriteDefs);
|
|
68
67
|
// Nunjucks' built-in `selectattr` only checks truthiness of the named
|
|
69
68
|
// attribute — it has no Jinja2-style `equalto` test, so a third argument
|
|
70
69
|
// is silently ignored. These do the actual equality filtering in JS.
|
|
@@ -96,6 +95,11 @@ export default function (eleventyConfig) {
|
|
|
96
95
|
const merged = CSS_FILES_TO_MERGE.map((file) => readFileSync(path.join(outputDir, file), "utf8")).join("\n");
|
|
97
96
|
writeFileSync(path.join(outputDir, "assets/css/bundle.css"), merged);
|
|
98
97
|
for (const file of CSS_FILES_TO_MERGE) rmSync(path.join(outputDir, file));
|
|
98
|
+
|
|
99
|
+
// Written once here rather than inlined per page (see fileIcons.js) —
|
|
100
|
+
// every repo page's fileIcon()/folderIcon() <use> just points at this
|
|
101
|
+
// one shared file instead of shipping the ~30-icon sprite body again.
|
|
102
|
+
writeFileSync(path.join(outputDir, "assets/icons/sprite.svg"), iconSpriteDefs());
|
|
99
103
|
});
|
|
100
104
|
|
|
101
105
|
return {
|
infra/terraform.tfstate
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 4,
|
|
3
3
|
"terraform_version": "1.13.3",
|
|
4
|
-
"serial":
|
|
4
|
+
"serial": 36,
|
|
5
5
|
"lineage": "6cb48eaa-b069-089b-fce3-e277626defab",
|
|
6
6
|
"outputs": {
|
|
7
7
|
"bucket_name": {
|
|
@@ -133,12 +133,12 @@
|
|
|
133
133
|
{
|
|
134
134
|
"schema_version": 0,
|
|
135
135
|
"attributes": {
|
|
136
|
-
"id": "
|
|
136
|
+
"id": "f9e2efc0-9697-398f-e7f6-f3ea5efece30",
|
|
137
137
|
"input": null,
|
|
138
138
|
"output": null,
|
|
139
139
|
"triggers_replace": {
|
|
140
140
|
"value": [
|
|
141
|
-
"2026-08-
|
|
141
|
+
"2026-08-06T04:46:00Z"
|
|
142
142
|
],
|
|
143
143
|
"type": [
|
|
144
144
|
"tuple",
|
|
@@ -160,15 +160,14 @@
|
|
|
160
160
|
"provider": "provider[\"terraform.io/builtin/terraform\"]",
|
|
161
161
|
"instances": [
|
|
162
162
|
{
|
|
163
|
-
"status": "tainted",
|
|
164
163
|
"schema_version": 0,
|
|
165
164
|
"attributes": {
|
|
166
|
-
"id": "
|
|
165
|
+
"id": "f02b05d0-7342-0e9c-70fd-7c6dbdf38aae",
|
|
167
166
|
"input": null,
|
|
168
167
|
"output": null,
|
|
169
168
|
"triggers_replace": {
|
|
170
169
|
"value": [
|
|
171
|
-
"2026-08-
|
|
170
|
+
"2026-08-06T05:23:01Z"
|
|
172
171
|
],
|
|
173
172
|
"type": [
|
|
174
173
|
"tuple",
|
|
@@ -197,12 +196,12 @@
|
|
|
197
196
|
{
|
|
198
197
|
"schema_version": 0,
|
|
199
198
|
"attributes": {
|
|
200
|
-
"id": "
|
|
199
|
+
"id": "37f88cab-3401-5a19-c280-24458203edda",
|
|
201
200
|
"input": null,
|
|
202
201
|
"output": null,
|
|
203
202
|
"triggers_replace": {
|
|
204
203
|
"value": [
|
|
205
|
-
"2026-08-
|
|
204
|
+
"2026-08-06T04:46:18Z"
|
|
206
205
|
],
|
|
207
206
|
"type": [
|
|
208
207
|
"tuple",
|
infra/terraform.tfstate.backup
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 4,
|
|
3
3
|
"terraform_version": "1.13.3",
|
|
4
|
-
"serial":
|
|
4
|
+
"serial": 29,
|
|
5
5
|
"lineage": "6cb48eaa-b069-089b-fce3-e277626defab",
|
|
6
6
|
"outputs": {
|
|
7
7
|
"bucket_name": {
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"jurisdiction": "default",
|
|
80
80
|
"min_tls": null,
|
|
81
81
|
"status": {
|
|
82
|
-
"ownership": "
|
|
82
|
+
"ownership": "active",
|
|
83
|
-
"ssl": "
|
|
83
|
+
"ssl": "active"
|
|
84
84
|
},
|
|
85
85
|
"zone_id": "e95a5d8957b75859c814579deebfce33",
|
|
86
86
|
"zone_name": "pyrossh.dev"
|
|
@@ -133,12 +133,12 @@
|
|
|
133
133
|
{
|
|
134
134
|
"schema_version": 0,
|
|
135
135
|
"attributes": {
|
|
136
|
-
"id": "
|
|
136
|
+
"id": "f64c9052-0f71-5705-a183-e85f891292b2",
|
|
137
137
|
"input": null,
|
|
138
138
|
"output": null,
|
|
139
139
|
"triggers_replace": {
|
|
140
140
|
"value": [
|
|
141
|
-
"2026-08-
|
|
141
|
+
"2026-08-05T06:08:43Z"
|
|
142
142
|
],
|
|
143
143
|
"type": [
|
|
144
144
|
"tuple",
|
|
@@ -156,19 +156,53 @@
|
|
|
156
156
|
{
|
|
157
157
|
"mode": "managed",
|
|
158
158
|
"type": "terraform_data",
|
|
159
|
-
"name": "
|
|
159
|
+
"name": "purge_cache",
|
|
160
160
|
"provider": "provider[\"terraform.io/builtin/terraform\"]",
|
|
161
161
|
"instances": [
|
|
162
162
|
{
|
|
163
163
|
"status": "tainted",
|
|
164
164
|
"schema_version": 0,
|
|
165
165
|
"attributes": {
|
|
166
|
-
"id": "
|
|
166
|
+
"id": "7173b400-cbc8-069b-7c74-6fa0bae8b3e2",
|
|
167
|
+
"input": null,
|
|
168
|
+
"output": null,
|
|
169
|
+
"triggers_replace": {
|
|
170
|
+
"value": [
|
|
171
|
+
"2026-08-05T06:27:38Z"
|
|
172
|
+
],
|
|
173
|
+
"type": [
|
|
174
|
+
"tuple",
|
|
175
|
+
[
|
|
176
|
+
"string"
|
|
177
|
+
]
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"sensitive_attributes": [],
|
|
182
|
+
"identity_schema_version": 0,
|
|
183
|
+
"dependencies": [
|
|
184
|
+
"cloudflare_r2_bucket.website",
|
|
185
|
+
"terraform_data.build_website",
|
|
186
|
+
"terraform_data.sync_website"
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"mode": "managed",
|
|
193
|
+
"type": "terraform_data",
|
|
194
|
+
"name": "sync_website",
|
|
195
|
+
"provider": "provider[\"terraform.io/builtin/terraform\"]",
|
|
196
|
+
"instances": [
|
|
197
|
+
{
|
|
198
|
+
"schema_version": 0,
|
|
199
|
+
"attributes": {
|
|
200
|
+
"id": "1fe8b333-1b40-c0c6-b113-1e7ce0519af8",
|
|
167
201
|
"input": null,
|
|
168
202
|
"output": null,
|
|
169
203
|
"triggers_replace": {
|
|
170
204
|
"value": [
|
|
171
|
-
"2026-08-
|
|
205
|
+
"2026-08-05T06:09:03Z"
|
|
172
206
|
],
|
|
173
207
|
"type": [
|
|
174
208
|
"tuple",
|
src/_data/site.js
CHANGED
|
@@ -81,9 +81,8 @@ const TOOLS = [
|
|
|
81
81
|
{ name: "Nushell", link: "https://github.com/nushell/nushell", image: "/assets/logos/nu.png" },
|
|
82
82
|
{ name: "Ghostty", link: "https://github.com/ghostty-org/ghostty", image: "/assets/logos/ghostty.png" },
|
|
83
83
|
{ name: "Zellij", link: "https://zellij.dev/", image: "/assets/logos/zellij.png" },
|
|
84
|
-
{ name: "
|
|
84
|
+
{ name: "Helium", link: "https://github.com/imputnet/helium", image: "/assets/logos/helium.png" },
|
|
85
85
|
{ name: "Bruno", link: "https://github.com/usebruno/bruno", image: "/assets/logos/bruno.png" },
|
|
86
|
-
{ name: "Secretive", link: "https://github.com/maxgoedjen/secretive", image: null },
|
|
87
86
|
];
|
|
88
87
|
|
|
89
88
|
// Eleventy only unwraps a JS data file's `default` export when it's the module's
|
src/_helpers/fileIcons.js
CHANGED
|
@@ -1,47 +1,5 @@
|
|
|
1
1
|
import iconSet from "@iconify-json/vscode-icons/icons.json" with { type: "json" };
|
|
2
2
|
|
|
3
|
-
// Chrome (confirmed in headless and in production) never paints a
|
|
4
|
-
// gradient-filled path that lives inside a <symbol> instantiated through
|
|
5
|
-
// <use> — the same markup renders fine as a standalone inline <svg>, so
|
|
6
|
-
// this is specific to the symbol/use indirection the sprite relies on.
|
|
7
|
-
// vscode-icons ships several file-type icons (rust, license, webp, python,
|
|
8
|
-
// kotlin, the "dist" folder) as gradients, so rather than special-case
|
|
9
|
-
// each one, every icon's gradients are resolved to one flat representative
|
|
10
|
-
// color at build time before being embedded in the sprite. Barely
|
|
11
|
-
// noticeable at the 16px size these render at, and keeps the one-symbol-
|
|
12
|
-
// shared-by-every-row sprite this module is built around (see
|
|
13
|
-
// iconSpriteDefs below) instead of duplicating full icon bodies per file.
|
|
14
|
-
const GRADIENT_TAG_RE = /<(linearGradient|radialGradient)\b([^>]*?)(?:\/>|>([\s\S]*?)<\/\1>)/g;
|
|
15
|
-
const STOP_COLOR_RE = /<stop\b[^>]*\bstop-color="([^"]+)"[^>]*\/?>/g;
|
|
16
|
-
const ATTR_RE = (attr) => new RegExp(`\\b${attr}="([^"]+)"`);
|
|
17
|
-
|
|
18
|
-
const flattenGradients = (body) => {
|
|
19
|
-
if (!body.includes("Gradient")) return body;
|
|
20
|
-
|
|
21
|
-
const gradients = new Map();
|
|
22
|
-
for (const match of body.matchAll(GRADIENT_TAG_RE)) {
|
|
23
|
-
const [, , attrs, inner] = match;
|
|
24
|
-
const id = attrs.match(ATTR_RE("id"))?.[1];
|
|
25
|
-
if (!id) continue;
|
|
26
|
-
const href = attrs.match(ATTR_RE("href"))?.[1]?.replace(/^#/, "");
|
|
27
|
-
const stops = inner ? [...inner.matchAll(STOP_COLOR_RE)].map((m) => m[1]) : [];
|
|
28
|
-
gradients.set(id, { href, stops });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const resolveColor = (id, seen = new Set()) => {
|
|
32
|
-
if (seen.has(id)) return null;
|
|
33
|
-
seen.add(id);
|
|
34
|
-
const g = gradients.get(id);
|
|
35
|
-
if (!g) return null;
|
|
36
|
-
if (g.stops.length) return g.stops[Math.floor(g.stops.length / 2)];
|
|
37
|
-
return g.href ? resolveColor(g.href, seen) : null;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
return body
|
|
41
|
-
.replace(GRADIENT_TAG_RE, "")
|
|
42
|
-
.replace(/(fill|stroke)="url\(#([^)]+)\)"/g, (whole, prop, id) => `${prop}="${resolveColor(id) ?? "#888"}"`);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
3
|
// The old Astro version resolved per-extension file/folder icons from the
|
|
46
4
|
// full vscode-icons manifest (see git history at 7325531^:src/utils/files.ts).
|
|
47
5
|
// That manifest is itself a large VS Code extension repo installed as a git
|
|
@@ -120,24 +78,24 @@ const ICON_NAMES = new Set([
|
|
|
120
78
|
]);
|
|
121
79
|
|
|
122
80
|
/**
|
|
123
|
-
* A
|
|
81
|
+
* A <svg><symbol> sprite holding every file/folder icon this repo browser
|
|
82
|
+
* can use, written once to assets/icons/sprite.svg (see eleventy.config.js)
|
|
124
|
-
*
|
|
83
|
+
* rather than inlined per page. fileIcon()/folderIcon() emit a tiny <use>
|
|
84
|
+
* that points at that shared file, so the ~30 icon bodies are fetched
|
|
125
|
-
*
|
|
85
|
+
* (and cached by the browser) once for the whole site instead of being
|
|
126
|
-
*
|
|
86
|
+
* duplicated in every one of the thousands of generated repo pages.
|
|
127
|
-
* many .rs files pulling in the same Rust icon), that's the difference
|
|
128
|
-
* between shipping one icon body and shipping it hundreds of times over.
|
|
129
87
|
*/
|
|
130
88
|
export const iconSpriteDefs = () => {
|
|
131
89
|
const symbols = [...ICON_NAMES]
|
|
132
90
|
.map((name) => {
|
|
133
91
|
const icon = iconSet.icons[name];
|
|
134
|
-
return `<symbol id="icon-${name}" viewBox="0 0 ${iconSet.width} ${iconSet.height}">${
|
|
92
|
+
return `<symbol id="icon-${name}" viewBox="0 0 ${iconSet.width} ${iconSet.height}">${icon.body}</symbol>`;
|
|
135
93
|
})
|
|
136
94
|
.join("");
|
|
137
|
-
return `<svg xmlns="http://www.w3.org/2000/svg"
|
|
95
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true">${symbols}</svg>`;
|
|
138
96
|
};
|
|
139
97
|
|
|
140
|
-
const useIcon = (name) => `<svg width="16" height="16"><use href="#icon-${name}"></use></svg>`;
|
|
98
|
+
const useIcon = (name) => `<svg width="16" height="16"><use href="/assets/icons/sprite.svg#icon-${name}"></use></svg>`;
|
|
141
99
|
|
|
142
100
|
export const fileIcon = (filename, ext) => {
|
|
143
101
|
const key = FILENAME_TO_ICON[filename.toLowerCase()] ?? EXT_TO_ICON[ext] ?? "default-file";
|
src/_includes/repo-layout.njk
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
{% macro repoLayout(repo, title, desc, tags, tab, version) %}
|
|
2
|
-
{{ iconSpriteDefs() | safe }}
|
|
3
2
|
<div class="repo">
|
|
4
3
|
<div class="header">
|
|
5
4
|
<h1><span style="view-transition-name:repo-title-{{ title }}">{{ title }}</span>{% if version %} <span class="repo-version">v{{ version }}</span>{% endif %}</h1>
|