website-xp-phone 2.2.1 → 2.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.github/workflows/ci.yml +8 -0
  2. package/Makefile +5 -1
  3. package/README.md +9 -0
  4. package/build/routes.ts +27 -10
  5. package/package.json +2 -1
  6. package/public/Click.ogg +0 -0
  7. package/public/addons/rage-volley.txt +1 -1
  8. package/public/addons/rueg-wa-druid.txt +1 -1
  9. package/public/addons/rueg-wa-mage.txt +1 -1
  10. package/public/addons/rueg-wa-paladin.txt +1 -0
  11. package/public/addons/rueg-wa-priest.txt +1 -1
  12. package/public/addons/rueg-wa-rueg.txt +1 -1
  13. package/public/addons/rueg-wa-shaman.txt +1 -1
  14. package/public/blog/random/branch.md +29 -0
  15. package/public/blog/random/google.md +25 -0
  16. package/public/checksums.txt +70 -47
  17. package/public/documents/drift-report.json +46 -148
  18. package/public/loading.css +7 -0
  19. package/scripts/asset-reference.mjs +37 -0
  20. package/scripts/mutable-assets.mjs +46 -39
  21. package/scripts/refresh-soundcloud.mjs +152 -414
  22. package/scripts/verify-policy.mjs +206 -0
  23. package/scripts/write-dist-checksums.mjs +251 -135
  24. package/src/content/addons.json +13 -17
  25. package/src/content/blog.json +5 -26
  26. package/src/content/characters.json +6 -6
  27. package/src/content/configs.json +6 -2
  28. package/src/content/index.ts +105 -12
  29. package/src/content/pages.json +10 -1
  30. package/src/content/projects/development.json +9 -1
  31. package/src/content/projects/doftiles.json +9 -1
  32. package/src/content/projects/frontend.json +9 -1
  33. package/src/content/projects/graphbrowser.json +9 -1
  34. package/src/content/projects/herbstutil.json +93 -4
  35. package/src/content/projects/lightdni-jssas-toggle.json +9 -1
  36. package/src/content/projects/pagerts.json +13 -2
  37. package/src/content/projects/rarebert.json +12 -2
  38. package/src/content/projects/webuser.json +9 -1
  39. package/src/content/schema.ts +10 -1
  40. package/src/content/sections.json +2 -3
  41. package/src/content/soundcloud.json +204 -72
  42. package/src/lib/audio.ts +14 -0
  43. package/src/lib/clippy.ts +1 -7
  44. package/src/lib/embeds.ts +1 -1
  45. package/src/lib/musicSchema.ts +1 -0
  46. package/src/main.tsx +8 -0
  47. package/src/pages/MusicPage.tsx +0 -1
  48. package/src/pages/SitemapPage.tsx +1 -1
  49. package/src/pages/WowPage.tsx +1 -1
  50. package/src/shell/App.tsx +4 -1
  51. package/src/styles/extras.css +85 -30
  52. package/src/styles/layout.css +27 -18
  53. package/src/styles/window.css +53 -50
  54. package/src/themes/pageSlide/index.ts +115 -13
  55. package/src/themes/pageSlide/pageSlide.css +99 -6
  56. package/src/window/Embed.tsx +3 -1
  57. package/src/window/MaximizedWindow.tsx +250 -0
  58. package/src/window/OkButton.tsx +6 -1
  59. package/src/window/PageWindows.tsx +3 -9
  60. package/src/window/Window.tsx +129 -53
  61. package/src/window/model.ts +22 -0
  62. package/src/window/state.tsx +54 -30
  63. package/test/asset-reference.test.js +53 -0
  64. package/test/content.test.js +60 -6
  65. package/test/mutable-assets.test.js +27 -0
  66. package/test/verify-policy.test.js +92 -0
  67. package/tsconfig.node.tsbuildinfo +1 -1
  68. package/tsconfig.tsbuildinfo +1 -1
  69. package/xp.css/README.md +10 -0
@@ -7,45 +7,46 @@
7
7
  "heading": "Rueg WeakAuras — class aura sets",
8
8
  "content": [
9
9
  {
10
- "heading": "Rogue (v5.0.1)",
10
+ "heading": "Rogue (v5.0.5)",
11
11
  "content": "Weak Aura tracker for GCD, energy tickrate, and range",
12
12
  "status": "available",
13
13
  "link": "/addons/rueg-wa-rueg.txt"
14
14
  },
15
15
  {
16
- "heading": "Mage (v3.0.0)",
16
+ "heading": "Mage (v5.0.5)",
17
17
  "content": "Weak Aura tracker for GCD, energy tickrate, and range",
18
18
  "status": "available",
19
19
  "link": "/addons/rueg-wa-mage.txt"
20
20
  },
21
21
  {
22
- "heading": "Priest (v5.0.1)",
22
+ "heading": "Priest (v5.0.5)",
23
23
  "content": "Weak Aura tracker for GCD, mana tickrate, and range",
24
24
  "status": "available",
25
25
  "link": "/addons/rueg-wa-priest.txt"
26
26
  },
27
27
  {
28
- "heading": "Warrior (v2.3.1)",
28
+ "heading": "Warrior (v5.0.5)",
29
29
  "content": "Weak Aura tracker for GCD, rage starvation, and range",
30
30
  "status": "available",
31
31
  "link": "/addons/rage-volley.txt"
32
32
  },
33
33
  {
34
- "heading": "Druid (v5.0.0)",
34
+ "heading": "Druid (v5.0.5)",
35
35
  "content": "Weak Aura tracker for swing, energy recharge, and range",
36
36
  "status": "available",
37
37
  "link": "/addons/rueg-wa-druid.txt"
38
38
  },
39
39
  {
40
- "heading": "Shaman (v5.0.1)",
40
+ "heading": "Shaman (v5.0.5)",
41
41
  "content": "Weak Aura tracker for swing, energy recharge, and range",
42
42
  "status": "available",
43
43
  "link": "/addons/rueg-wa-shaman.txt"
44
44
  },
45
45
  {
46
- "heading": "Paladin (coming soon)",
46
+ "heading": "Paladin (v5.0.5)",
47
47
  "content": "Weak Aura tracker for GCD, mana starvation, and range",
48
- "status": "coming soon"
48
+ "status": "available",
49
+ "link": "/addons/rueg-wa-paladin.txt"
49
50
  }
50
51
  ]
51
52
  },
@@ -83,21 +84,16 @@
83
84
  "content": [
84
85
  {
85
86
  "heading": "Rueg Distributable Config (dist.7z)",
86
- "content": "Full distributable World of Warcraft configuration archive — addons, WeakAuras, and profiles, packaged for easy install.",
87
- "status": "available",
88
- "link": "/dist.7z"
87
+ "content": "See bottom of this page.",
88
+ "status": "available"
89
89
  },
90
90
  {
91
- "heading": "addons config to play like Rueg",
91
+ "heading": "addons list",
92
92
  "content": [
93
93
  "![addons1](/images/addons11.png)",
94
94
  "![addons1](/images/addons12.png)"
95
95
  ],
96
- "status": "available (and supports easy hypelinking)"
97
- },
98
- {
99
- "heading": "more addons coming after I finish my work",
100
- "link": "https://akinevz.dev/"
96
+ "status": "available"
101
97
  }
102
98
  ]
103
99
  }
@@ -3,7 +3,7 @@
3
3
  "heading": "kine's blog posts",
4
4
  "content": [
5
5
  {
6
- "heading": "Stories",
6
+ "heading": "Stories / Writing",
7
7
  "content": [
8
8
  "### Hot Action under a Cold Moon",
9
9
  "Click here to read",
@@ -60,28 +60,13 @@
60
60
  "theme": "experimental",
61
61
  "link": "/images/booger_Boogie%20Long.gif"
62
62
  },
63
- "blog operating normally! :D",
64
- {
65
- "heading": "Rueg Config published! (#world of warcraft, #addons)",
66
- "theme": "open",
67
- "content": [
68
- "Packed up the full Rueg configuration into a distributable archive — addons, WeakAuras, and profiles in one drop.",
69
- "Grab it from the [download page](/wow#download) or the [addons page](/addons).",
70
- "I had a bad copy of the zip stuck in website cache:",
71
- "If you downloaded an archive with 2 folders",
72
- "Delete it, the correct dist.7z should have 3",
73
- "You can skip copying WTF/ and just copy interface",
74
- {
75
- "heading": "Open the download page",
76
- "link": "/wow#download"
77
- }
78
- ]
79
- },
80
63
  {
81
64
  "heading": "Runisch Glossary (#russian, #glossary, #tool)",
82
65
  "content": [
83
66
  {
84
- "content": "An interactive SQLite-backed REPL for storing and retrieving Runisch terms, with translation, usage examples, and tags.",
67
+ "heading": "An interactive SQLite-backed REPL for storing and retrieving Runisch terms.",
68
+ "theme": "centered",
69
+ "content": "with translation, usage examples, and tags",
85
70
  "embed": "/blog/рунисч-глоссарий.md"
86
71
  }
87
72
  ]
@@ -201,13 +186,7 @@
201
186
  ]
202
187
  },
203
188
  {
204
- "heading": "twelfth post (#discussion, #security, #wow, #addons)",
205
- "content": [
206
- "Although no verified cases of official ElvUI performing malicious opaque bank transfers (gold+items) have ever been publicly confirmed by Blizzard or the developer, concerns about its security have circulated for years. While the 2014 ElvUI backdoor - a debug command that allowed the addon's developers to remotely execute arbitrary Lua code on users' clients - was widely documented across gaming forums, Reddit, and tech blogs at the time and subsequently removed from official releases, similar malicious behavior reportedly persisted years later. During the World of Warcraft Classic Hardcore rush, one player encountered what appeared to be surreptitious opaque bank transfer functionality (gold+items) while using an ElvUI installation, an incident captured on livestream that raised fresh questions about the risks of third-party UI modifications and unverified addon distributions"
207
- ]
208
- },
209
- {
210
- "heading": "thirteenth post (#mixing, #phase, #technique, #audio, #music)",
189
+ "heading": "twelfth post (#mixing, #phase, #technique, #audio, #music)",
211
190
  "content": [
212
191
  "i found a mixing technique by accident and now i can't stop using it",
213
192
  "* i usually build my final mixes from 3 \"core\" channels",
@@ -1,9 +1,9 @@
1
1
  {
2
- "heading": "world of warcraft character gallery",
2
+ "heading": "Gallery",
3
3
  "content": [
4
4
  {
5
5
  "heading": "rueg",
6
- "theme": ["open", "centered"],
6
+ "theme": "centered",
7
7
  "content": [
8
8
  "<img src=\"/images/rueg1.png\" alt=\"rueg\" style=\"width: auto; height: auto; max-height: 600px;\" />",
9
9
  "<img src=\"/images/rueg2.png\" alt=\"rueg\" style=\"width: auto; height: auto; max-height: 600px;\" />"
@@ -11,7 +11,7 @@
11
11
  },
12
12
  {
13
13
  "heading": "ateric",
14
- "theme": ["open", "centered"],
14
+ "theme": "centered",
15
15
  "content": [
16
16
  "<img src=\"/images/Ateric1.png\" alt=\"ateric\" style=\"width: auto; height: auto; max-height: 600px;\" />",
17
17
  "<img src=\"/images/Ateric2.png\" alt=\"ateric\" style=\"width: auto; height: auto; max-height: 600px;\" />"
@@ -19,14 +19,14 @@
19
19
  },
20
20
  {
21
21
  "heading": "equal",
22
- "theme": ["open", "centered"],
22
+ "theme": "centered",
23
23
  "content": [
24
24
  "<img src=\"/images/equal1.png\" alt=\"equal\" style=\"width: auto; height: auto; max-height: 600px;\" />"
25
25
  ]
26
26
  },
27
27
  {
28
28
  "heading": "kogg",
29
- "theme": ["open", "centered"],
29
+ "theme": "centered",
30
30
  "content": [
31
31
  "<img src=\"/images/kogg1.png\" alt=\"kogg\" style=\"width: auto; height: auto; max-height: 600px;\" />",
32
32
  "<img src=\"/images/kogg2.png\" alt=\"kogg\" style=\"width: auto; height: auto; max-height: 600px;\" />"
@@ -34,7 +34,7 @@
34
34
  },
35
35
  {
36
36
  "heading": "brúdder",
37
- "theme": ["open", "centered"],
37
+ "theme": "centered",
38
38
  "content": [
39
39
  "<img src=\"/images/brudder.png\" alt=\"brúdder\" style=\"width: auto; height: auto; max-height: 600px;\" />"
40
40
  ]
@@ -6,7 +6,6 @@
6
6
  "heading": "Linux Configs",
7
7
  "content": [
8
8
  "My Linux setup files — a window-manager configuration based on herbstluftwm, documented in the doftiles project.",
9
- "[doftiles project](/projects/doftiles)",
10
9
  "[github.com/akinevz2/doftiles](https://github.com/akinevz2/doftiles)",
11
10
  {
12
11
  "heading": "Gallery",
@@ -22,7 +21,12 @@
22
21
  "link": "/projects/doftiles"
23
22
  },
24
23
  {
25
- "heading": "Wow Configs",
24
+ "heading": "WoW Configs",
25
+ "content": [
26
+ "My World of Warcraft configuration files — the window layout of Rueg's UI, the Simple WoW UI Pack, running wow classic.",
27
+ "[everything world of warcraft](/wow)",
28
+ { "include": "characters" }
29
+ ],
26
30
  "link": "/wow"
27
31
  }
28
32
  ]
@@ -23,22 +23,115 @@ export const projects = Object.values(projectModules).sort((a, b) =>
23
23
  a.title.localeCompare(b.title),
24
24
  );
25
25
 
26
- /** Static pages from pages.json plus one hidden-from-menu route per project. */
27
- export const routes: Route[] = [
28
- ...(pagesJson as Route[]),
29
- ...projects.map((project): Route => ({
26
+ /**
27
+ * Short-hand URL for a project: /<slug>. Redirects to the canonical
28
+ * /projects/<slug> route via the same alias mechanism pages.json uses.
29
+ */
30
+ export const projectShortPath = (project: Project) => `/${project.slug}`;
31
+
32
+ function projectRoute(project: Project): Route {
33
+ return {
30
34
  path: projectPath(project),
31
35
  title: project.title,
32
36
  description: project.description,
33
37
  page: "ProjectPage",
34
38
  menu: false,
35
- })),
36
- ];
37
- export const sections = sectionsJson as Section;
38
- export const contacts = contactsJson as Section;
39
- export const addons = addonsJson as Section;
40
- export const configs = configsJson as Section;
41
- export const characters = charactersJson as Section;
42
- export const blog = blogJson as Section;
39
+ };
40
+ }
41
+
42
+ /** Alias route: hidden, redirects to the canonical project route. */
43
+ function projectAliasRoute(project: Project): Route {
44
+ return {
45
+ path: projectShortPath(project),
46
+ title: project.title,
47
+ description: project.description,
48
+ page: "ProjectPage",
49
+ alias: projectPath(project),
50
+ menu: false,
51
+ hidden: true,
52
+ };
53
+ }
54
+
55
+ /** Static pages from pages.json, kept separate so project aliases can check clashes. */
56
+ const pageRoutes: Route[] = pagesJson as Route[];
57
+
58
+ /**
59
+ * One canonical route per project (/projects/<slug>) plus a hidden
60
+ * short-hand alias route (/<slug>) redirecting to it. The alias is
61
+ * skipped when the slug would clash with an existing route path.
62
+ */
63
+ const projectRoutes: Route[] = projects.flatMap((project): Route[] => {
64
+ const taken =
65
+ pageRoutes.some((page) => page.path === projectShortPath(project)) ||
66
+ projects.some(
67
+ (other) =>
68
+ other !== project && projectPath(other) === projectShortPath(project),
69
+ );
70
+ return taken
71
+ ? [projectRoute(project)]
72
+ : [projectRoute(project), projectAliasRoute(project)];
73
+ });
74
+
75
+ export const routes: Route[] = [...pageRoutes, ...projectRoutes];
76
+ /* ------------------------------------------------------------------ includes
77
+ * A content file can embed another content file's window tree with an
78
+ * include directive instead of duplicating it:
79
+ *
80
+ * { "include": "characters" } → the top-level Section of characters.json
81
+ *
82
+ * Directives are expanded at load time, recursively (an included tree may
83
+ * itself contain directives). Unknown or circular directives throw during
84
+ * module load, failing the build loudly instead of rendering an opaque
85
+ * object. Each file may be included multiple times across sibling items;
86
+ * cycles are broken by the seen-set, which tracks one inclusion chain.
87
+ */
88
+ const includeRegistry: Record<string, Section> = {
89
+ characters: charactersJson as Section,
90
+ };
91
+
92
+ function expandContent(
93
+ content: NonNullable<Section["content"]>,
94
+ ancestors: Set<string>,
95
+ ): NonNullable<Section["content"]> {
96
+ if (typeof content === "string") return content;
97
+ const items = Array.isArray(content) ? content : [content];
98
+ return items.map((item) => {
99
+ if (typeof item !== "object" || item === null) return item;
100
+ if ("include" in item) {
101
+ const name = item.include;
102
+ if (typeof name !== "string" || !includeRegistry[name]) {
103
+ throw new Error(`Unknown include directive: ${String(name)}`);
104
+ }
105
+ if (ancestors.has(name)) {
106
+ throw new Error(`Circular include directive: ${name}`);
107
+ }
108
+ const target = includeRegistry[name];
109
+ const chain = new Set(ancestors);
110
+ chain.add(name);
111
+ return expandSection(target, chain);
112
+ }
113
+ if ("banner" in item || "url" in item) return item;
114
+ return expandSection(item as Section, ancestors);
115
+ });
116
+ }
117
+
118
+ /** Expands a nested section, preserving the absence of content exactly. */
119
+ function expandSection(section: Section, ancestors: Set<string>): Section {
120
+ if (!section.content) return section;
121
+ return { ...section, content: expandContent(section.content, ancestors) };
122
+ }
123
+
124
+ /** Expands every include directive in the tree rooted at `section`. */
125
+ const resolveIncludes = (section: Section): Section => {
126
+ if (!section.content) return section;
127
+ return { ...section, content: expandContent(section.content, new Set()) };
128
+ };
129
+
130
+ export const sections = resolveIncludes(sectionsJson as Section);
131
+ export const contacts = resolveIncludes(contactsJson as Section);
132
+ export const addons = resolveIncludes(addonsJson as Section);
133
+ export const configs = resolveIncludes(configsJson as Section);
134
+ export const characters = resolveIncludes(charactersJson as Section);
135
+ export const blog = resolveIncludes(blogJson as Section);
43
136
  export const soundcloud = soundcloudJson as SoundCloudData;
44
137
  export const musicExt = musicExtJson;
@@ -37,11 +37,20 @@
37
37
  },
38
38
  {
39
39
  "path": "/wow",
40
- "menuLabel": "wow addons",
40
+ "menuLabel": "wow & addons",
41
41
  "title": "everything world of warcraft",
42
42
  "description": "my addons and configurations for wow classic",
43
43
  "page": "WowPage"
44
44
  },
45
+ {
46
+ "path": "/addons",
47
+ "title": "wow addons",
48
+ "description": "short-hand redirect to the world of warcraft page",
49
+ "page": "AddonsPage",
50
+ "alias": "/wow#addons",
51
+ "menu": false,
52
+ "hidden": true
53
+ },
45
54
  {
46
55
  "path": "/music",
47
56
  "menuLabel": "music",
@@ -10,7 +10,15 @@
10
10
  "It contains various projects and configurations that power my personal website.",
11
11
  "I work inside a WSL Ubuntu environment, with the development repository mounted as a devcontainer.",
12
12
  "",
13
- "[https://github.com/akinevz2/development](https://github.com/akinevz2/development)"
13
+ "[https://github.com/akinevz2/development](https://github.com/akinevz2/development)",
14
+ {
15
+ "heading": "From the README",
16
+ "theme": "open",
17
+ "content": [
18
+ "The repo runs as a persistent Development Workstation Devcontainer for VS Code.",
19
+ "Ships the latest Git and GitHub CLI, bootstraps Node.js LTS via nvm, restores akinevz2/doftiles into ~/dots, and stows the bar, dunst, herbst, rofi, shell and vim packages with `stow --adopt`."
20
+ ]
21
+ }
14
22
  ]
15
23
  }
16
24
  }
@@ -7,7 +7,15 @@
7
7
  "heading": "doftiles",
8
8
  "content": [
9
9
  "A window manager setup based on herbstluftwm, almost ready for public use.",
10
- "[https://github.com/akinevz2/doftiles](https://github.com/akinevz2/doftiles)"
10
+ "[https://github.com/akinevz2/doftiles](https://github.com/akinevz2/doftiles)",
11
+ {
12
+ "heading": "From the README",
13
+ "theme": "open",
14
+ "content": [
15
+ "configs for kine@home - Ubuntu deploys are as simple as `make install` now.",
16
+ "Deployed with GNU stow: the repo is cloned into ~/dots and its packages (bar, dunst, herbst, rofi, shell, vim) are adopted into $HOME."
17
+ ]
18
+ }
11
19
  ]
12
20
  }
13
21
  }
@@ -7,7 +7,15 @@
7
7
  "heading": "frontend",
8
8
  "content": [
9
9
  "The source of this personal website.",
10
- "[https://github.com/akinevz2/frontend](https://github.com/akinevz2/frontend)"
10
+ "[https://github.com/akinevz2/frontend](https://github.com/akinevz2/frontend)",
11
+ {
12
+ "heading": "From the README",
13
+ "theme": "open",
14
+ "content": [
15
+ "Built as a landing page and a point-of-interest for those that stumble upon it: personal storytelling through UI, JSON-driven content that is easy to edit, and small interactive features (windows, modals, media embeds).",
16
+ "React + TypeScript, Vite for development and builds, xp.css for the nostalgia."
17
+ ]
18
+ }
11
19
  ]
12
20
  }
13
21
  }
@@ -7,7 +7,15 @@
7
7
  "heading": "graphbrowser",
8
8
  "content": [
9
9
  "Describes a website's external dependency graph one layer at a time as structured markdown, with hyperlinks for navigating between graph nodes.",
10
- "[https://github.com/akinevz2/graphbrowser](https://github.com/akinevz2/graphbrowser)"
10
+ "[https://github.com/akinevz2/graphbrowser](https://github.com/akinevz2/graphbrowser)",
11
+ {
12
+ "heading": "From the README",
13
+ "theme": "open",
14
+ "content": [
15
+ "A software package to visualise page dependencies without having to view the page content: the set of navigable items of a webpage, linking from page to page within the World Wide Web.",
16
+ "GraphBrowser is the GUI counterpart of PagerTS, built with Quarkus and web technologies - the same dependency list that pagerts parses with JSDOM into `title`, `url` and `resources`."
17
+ ]
18
+ }
11
19
  ]
12
20
  }
13
21
  }
@@ -1,13 +1,102 @@
1
1
  {
2
2
  "slug": "herbstutil",
3
3
  "title": "herbstutil",
4
- "description": "Converts the output of `herbstclient attr` into JSON.",
4
+ "description": "A stdin filter that turns the verbose output of herbstclient attr into clean JSON - for scripting the herbstluftwm tiling window manager.",
5
5
  "github": "https://github.com/akinevz2/herbstutil",
6
6
  "content": {
7
- "heading": "herbstutil",
7
+ "heading": "herbstutil - JSON bridge to herbstluftwm",
8
8
  "content": [
9
- "Converts the output of `herbstclient attr` into JSON.",
10
- "[https://github.com/akinevz2/herbstutil](https://github.com/akinevz2/herbstutil)"
9
+ "herbstutil is a small command-line filter that post-processes the output of `herbstclient attr` into clean JSON - so the state of the herbstluftwm window manager can be consumed wherever JSON is spoken.",
10
+ "[https://github.com/akinevz2/herbstutil](https://github.com/akinevz2/herbstutil)",
11
+ {
12
+ "heading": "herbstluftwm in a nutshell",
13
+ "anchor": "herbstluftwm",
14
+ "theme": "open",
15
+ "content": [
16
+ "herbstluftwm is a manual tiling window manager for Linux. Its core idea: the whole desktop is one global object tree - monitors, tags, clients (windows), settings - queried and modified over IPC with the companion tool herbstclient.",
17
+ "The tree is probed with `herbstclient attr <path>`: it prints a short description of the node, its children, and a table of every attribute (typed, writable, hookable).",
18
+ "```sh\n$ herbstclient attr clients. # children of the clients node\n$ herbstclient attr clients.focus. # introspect the focused window\n$ herbstclient attr clients.focus.tag # read a single leaf value\n```",
19
+ "The catch: `attr` output is written for humans first - padded type tables, prose, legends. Great to read, fragile to parse."
20
+ ]
21
+ },
22
+ {
23
+ "heading": "What herbstutil does",
24
+ "anchor": "about",
25
+ "status": "early release",
26
+ "theme": "open",
27
+ "content": [
28
+ "herbstutil reads `herbstclient attr` output on stdin and rebuilds it as JSON, preserving the node's shape:",
29
+ "value - the node's description; children - the names of the child nodes; attributes - every attribute as a name-value pair.",
30
+ "Where herbstclient speaks humans, herbstutil speaks machine.",
31
+ {
32
+ "heading": "Usage",
33
+ "theme": "open",
34
+ "content": [
35
+ "herbstutil is a stdin filter built with Bun and bunli, packaged as a single executable:",
36
+ "```sh\n$ herbstclient attr clients. | herbstutil\n$ herbstclient attr clients.focus. | herbstutil --space 2\n```",
37
+ "s, --space <n> - pretty-print with `n` spaces of indentation (default 4)",
38
+ "It refuses to run attached to a terminal: herbstutil always reads from stdin."
39
+ ]
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ "heading": "Examples",
45
+ "anchor": "examples",
46
+ "theme": "open",
47
+ "content": [
48
+ "The same node, before and after `| herbstutil`.",
49
+ {
50
+ "heading": "Root node: herbstclient attr clients.",
51
+ "theme": "open",
52
+ "content": [
53
+ "Raw:",
54
+ "```text\n$ herbstclient attr clients.\nThe managed windows. For every (managed) window id there is an entry here.\n\n5 children:\n 0x400003.\n 0x700003.\n 0xc00003.\n 0xe80003.\n focus.\n0 attributes.\n .---- type\n | .-- writable\n | | .-- hookable\n V V V\n```",
55
+ "Piped through herbstutil:",
56
+ "```json\n{\n \"value\": \"The managed windows. For every (managed) window id there is an entry here.\",\n \"children\": [\n \"0x400003.\",\n \"0x700003.\",\n \"0xc00003.\",\n \"0xe80003.\",\n \"focus.\"\n ],\n \"attributes\": {}\n}\n```"
57
+ ]
58
+ },
59
+ {
60
+ "heading": "Node with attributes: herbstclient attr clients.focus.",
61
+ "theme": "open",
62
+ "content": [
63
+ "Raw (excerpt):",
64
+ "```text\n$ herbstclient attr clients.focus.\na managed window\n\n1 child:\n parent_frame.\n24 attributes:\n s - - class = \"Alacritty\"\n b w h decorated = true\n b w h floating = false\n i - h pid = 61763\n s - h tag = \"work\"\n s - h title = \"OC | Customizing opencode and dotfiles\"\n b - h visible = true\n```",
65
+ "Piped through herbstutil (excerpt - all 24 attributes arrive in the JSON):",
66
+ "```json\n{\n \"value\": \"a managed window\",\n \"children\": [\n \"parent_frame.\"\n ],\n \"attributes\": {\n \"class\": \"\\\"Alacritty\\\"\",\n \"decorated\": \"true\",\n \"floating\": \"false\",\n \"pid\": \"61763\",\n \"tag\": \"\\\"work\\\"\",\n \"title\": \"\\\"OC | Customizing opencode and dotfiles\\\"\",\n \"urgent\": \"false\",\n \"visible\": \"true\",\n \"winid\": \"\\\"0x700003\\\"\"\n }\n}\n```",
67
+ "Note how attribute values stay as raw strings - a reminder that everything coming from `attr` is text until you parse it downstream."
68
+ ]
69
+ },
70
+ {
71
+ "heading": "Leaf node: herbstclient attr clients.focus.tag",
72
+ "theme": "open",
73
+ "content": [
74
+ "Raw:",
75
+ "```text\n$ herbstclient attr clients.focus.tag\nwork\n```",
76
+ "Piped through herbstutil:",
77
+ "```json\n{\n \"value\": \"work\",\n \"children\": [],\n \"attributes\": {}\n}\n```"
78
+ ]
79
+ }
80
+ ]
81
+ },
82
+ {
83
+ "heading": "Links",
84
+ "theme": "open",
85
+ "content": [
86
+ {
87
+ "heading": "GitHub repository",
88
+ "link": "https://github.com/akinevz2/herbstutil"
89
+ },
90
+ {
91
+ "heading": "herbstluftwm project",
92
+ "link": "https://herbstluftwm.org"
93
+ },
94
+ {
95
+ "heading": "herbstclient reference",
96
+ "link": "https://herbstluftwm.org/herbstclient.html"
97
+ }
98
+ ]
99
+ }
11
100
  ]
12
101
  }
13
102
  }
@@ -7,7 +7,15 @@
7
7
  "heading": "lightdni-jssas-toggle",
8
8
  "content": [
9
9
  "LightDNI (Doesn't Need Integration) provides a simple declarative API for adding a theme toggle to your website.",
10
- "[https://github.com/akinevz2/lightdni-jssas-toggle](https://github.com/akinevz2/lightdni-jssas-toggle)"
10
+ "[https://github.com/akinevz2/lightdni-jssas-toggle](https://github.com/akinevz2/lightdni-jssas-toggle)",
11
+ {
12
+ "heading": "From the README",
13
+ "theme": "open",
14
+ "content": [
15
+ "TypeScript utility package for applying and toggling CSS themes: theme classes and attributes on the root element, dynamic CSS variables, localStorage/cookie persistence, and accessibility metadata (color-scheme and aria-live announcements).",
16
+ "A consolidated one-call setTheme API - dependency-safe, and works in vanilla JS/TS, React, and Vue projects."
17
+ ]
18
+ }
11
19
  ]
12
20
  }
13
21
  }
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "slug": "pagerts",
3
- "path": "/pagerts",
4
3
  "title": "pagerts",
5
4
  "description": "A TypeScript CLI that lists every link and resource a webpage references, as plain JSON.",
6
5
  "github": "https://github.com/akinevz2/pagerts",
@@ -8,9 +7,21 @@
8
7
  "heading": "pagerts - Webpage resource extractor",
9
8
  "content": [
10
9
  "pagerts fetches a URL (or reads a local HTML file) and prints every external resource it references - links, meta tags, scripts, styles - as JSON.",
10
+ {
11
+ "heading": "From the README",
12
+ "theme": "open",
13
+ "content": [
14
+ "PagerTS fetches web pages, parses their HTML, and reports each page's title, URL, and external resources - inspecting common resource-bearing tags, or scanning every element for selected link properties.",
15
+ "Requires Node.js 20 or later; installs globally with `npm install --global pagerts`."
16
+ ]
17
+ },
11
18
  {
12
19
  "heading": "Quick start",
13
- "content": ["npm install -g pagerts", "", "pagerts https://example.com"],
20
+ "content": [
21
+ "npm install -g pagerts",
22
+ "",
23
+ "pagerts https://example.com"
24
+ ],
14
25
  "theme": "open"
15
26
  },
16
27
  {
@@ -6,8 +6,18 @@
6
6
  "content": {
7
7
  "heading": "rarebert",
8
8
  "content": [
9
- "AI-powered notification daemon. On hold until there is time to port it to Opencode V2.",
10
- "[https://github.com/akinevz2/rarebert](https://github.com/akinevz2/rarebert)"
9
+ "A self-modifying scripting environment for interactive, AI-driven development.",
10
+ "[https://github.com/akinevz2/rarebert](https://github.com/akinevz2/rarebert)",
11
+ {
12
+ "heading": "From the README",
13
+ "theme": "open",
14
+ "content": [
15
+ "Rarebert scaffolds modules, opens them in your editor side-by-side with opencode, lets the model implement them, summarises the diff, and commits - then runs the result and remembers what happened.",
16
+ "Built around a TypeScript runtime (`lib/runtime/` + Bun) that orchestrates $EDITOR, git, and opencode, and a memo subsystem that threads observations between the model and your shell.",
17
+ "Each turn ends in a commit, so the working tree is always clean before the next cycle.",
18
+ "Work in progress: the branch is mid-port from Node.js/ESM to TypeScript/Bun, not yet stable for daily use."
19
+ ]
20
+ }
11
21
  ]
12
22
  }
13
23
  }
@@ -7,7 +7,15 @@
7
7
  "heading": "webuser",
8
8
  "content": [
9
9
  "A jail and shell with mounted paths, packaged as a bun CLI.",
10
- "[https://github.com/akinevz2/webuser](https://github.com/akinevz2/webuser)"
10
+ "[https://github.com/akinevz2/webuser](https://github.com/akinevz2/webuser)",
11
+ {
12
+ "heading": "From the README",
13
+ "theme": "open",
14
+ "content": [
15
+ "A CLI built with Bunli on the Bun runtime: `bun install`, `bun dev -- [command]`, `bun run build`.",
16
+ "MIT licensed."
17
+ ]
18
+ }
11
19
  ]
12
20
  }
13
21
  }
@@ -10,12 +10,21 @@ export type Embed = {
10
10
  provider?: "soundcloud";
11
11
  };
12
12
 
13
+ /**
14
+ * A content-file include directive: `{"include": "<name>"}` expands, at
15
+ * load time (src/content/index.ts), into the referenced content file's
16
+ * top-level Section as a nested child — JSON cannot import, so this is
17
+ * the "include" mechanism for reusing a section across pages.
18
+ */
19
+ export type IncludeRef = { include: string };
20
+
13
21
  /** A clickable image tile with caption lines (artist profile headers). */
14
22
  export type Banner = {
15
23
  banner: { image: string; href: string; lines: string[] };
16
24
  };
17
25
 
18
- export type Item = string | Section | Embed | Banner | ReactElement;
26
+ export type Item =
27
+ string | Section | Embed | Banner | IncludeRef | ReactElement;
19
28
 
20
29
  /** A window as written in the JSON content files. */
21
30
  export type Section = {