website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


1980b5cpyrossh 2026-08-06T17:26:55+05:30
fix icons
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": 29,
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": "f64c9052-0f71-5705-a183-e85f891292b2",
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-05T06:08:43Z"
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": "7173b400-cbc8-069b-7c74-6fa0bae8b3e2",
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-05T06:27:38Z"
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": "1fe8b333-1b40-c0c6-b113-1e7ce0519af8",
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-05T06:09:03Z"
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": 23,
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": "pending",
82
+ "ownership": "active",
83
- "ssl": "initializing"
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": "06e5fdf0-4974-c673-b975-2f3fa99846fc",
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-05T05:36:25Z"
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": "sync_website",
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": "2036dc20-874b-06df-a0e0-481ee25c8d06",
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-05T05:36:38Z"
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: "Zen", link: "https://github.com/zen-browser/desktop", image: "/assets/logos/zen.svg" },
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 hidden <svg><symbol> sprite holding every file/folder icon this repo
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
- * browser can use, meant to be embedded once per page. fileIcon()/
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
- * folderIcon() then emit a tiny `<use>` reference instead of repeating the
85
+ * (and cached by the browser) once for the whole site instead of being
126
- * full icon body on every row for a repo with hundreds of files (e.g.
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}">${flattenGradients(icon.body)}</symbol>`;
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" style="display:none" aria-hidden="true">${symbols}</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>