website-xp-phone 1.5.0

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 (123) hide show
  1. package/.astro/content-assets.mjs +1 -0
  2. package/.astro/content-modules.mjs +1 -0
  3. package/.astro/content.d.ts +199 -0
  4. package/.astro/data-store.json +1 -0
  5. package/.astro/settings.json +8 -0
  6. package/.astro/types.d.ts +1 -0
  7. package/.devcontainer/devcontainer.json +23 -0
  8. package/.env.firebase.example +8 -0
  9. package/.firebaserc +5 -0
  10. package/.gitattributes +2 -0
  11. package/.github/copilot-instructions.md +131 -0
  12. package/.github/dependabot.yml +11 -0
  13. package/.github/workflows/ci.yml +45 -0
  14. package/.github/workflows/deploy-admin.yml +48 -0
  15. package/.github/workflows/static.yml +43 -0
  16. package/.gitmodules +5 -0
  17. package/FIREBASE_SETUP.md +69 -0
  18. package/README.md +63 -0
  19. package/SECURITY.md +11 -0
  20. package/admin/Admin.csproj +7 -0
  21. package/admin/Dockerfile +14 -0
  22. package/admin/Program.cs +8 -0
  23. package/deploy-admin-cloud-run.md +229 -0
  24. package/eslint.config.js +28 -0
  25. package/firebase.json +5 -0
  26. package/firestore.rules +29 -0
  27. package/index.html +52 -0
  28. package/package.json +48 -0
  29. package/pagerts_output.json +1 -0
  30. package/public/5.html +967 -0
  31. package/public/BAHNSCHRIFT.TTF +0 -0
  32. package/public/Beep.ogg +0 -0
  33. package/public/Clippy.png +0 -0
  34. package/public/Layered Network Security Model for Home Networks (slides).pdf +0 -0
  35. package/public/Layered Network Security Model for Home Networks.pdf +0 -0
  36. package/public/TODO.pdf +0 -0
  37. package/public/WoW_Config.zip +3 -0
  38. package/public/addons/energy-swing.txt +1 -0
  39. package/public/addons/lego-yoda-death-readme.txt +11 -0
  40. package/public/addons/lego-yoda-death.mp3 +0 -0
  41. package/public/addons/mana-blast.txt +1 -0
  42. package/public/addons/rage-volley.txt +1 -0
  43. package/public/addons/rueg-cell.txt +1 -0
  44. package/public/addons/rueg-elvui-profile.txt +1 -0
  45. package/public/addons/rueg-grid2.txt +214 -0
  46. package/public/addons/rueg-plater-smol.txt +1 -0
  47. package/public/addons/rueg-plater.txt +1 -0
  48. package/public/addons/rueg-wa-druid.txt +1 -0
  49. package/public/addons/rueg-wa-priest.txt +1 -0
  50. package/public/addons/rueg-wa-rogue.txt +1 -0
  51. package/public/addons/rueg-wa-shaman.txt +1 -0
  52. package/public/addons/rueg-wa-warrior.txt +1 -0
  53. package/public/addons/spirit-smash.txt +1 -0
  54. package/public/avatar.jpg +0 -0
  55. package/public/avatar.png +0 -0
  56. package/public/crunchy_kick.ogg +0 -0
  57. package/public/documents/resume.html +312 -0
  58. package/public/favicon.ico +0 -0
  59. package/public/images/Ateric1.png +0 -0
  60. package/public/images/Ateric2.png +0 -0
  61. package/public/images/equal1.png +0 -0
  62. package/public/images/hyperawareofwhatacatis.png +0 -0
  63. package/public/images/kogg1.png +0 -0
  64. package/public/images/kogg2.png +0 -0
  65. package/public/images/rueg1.png +0 -0
  66. package/public/images/rueg2.png +0 -0
  67. package/public/incorrect_responses.txt +126 -0
  68. package/public/loading.css +51 -0
  69. package/public/resume.pdf +0 -0
  70. package/public/robots.txt +9 -0
  71. package/public/soundcloud.json +57 -0
  72. package/public/spinner.svg +12 -0
  73. package/public/tada.wav +0 -0
  74. package/public/yooh.mp3 +0 -0
  75. package/render.yaml +5 -0
  76. package/scripts/ensure-blog-worktree.mjs +24 -0
  77. package/scripts/generate-soundcloud-json.mjs +198 -0
  78. package/scripts/git-worktree-helper.mjs +122 -0
  79. package/scripts/hoist-dev-blog-local.mjs +149 -0
  80. package/scripts/music-schema.mjs +56 -0
  81. package/scripts/publish-soundcloud-json.mjs +32 -0
  82. package/scripts/sync-music-links-from-worktree.mjs +32 -0
  83. package/src/App.tsx +1500 -0
  84. package/src/addons.json +76 -0
  85. package/src/components/Addon.tsx +223 -0
  86. package/src/components/BlogContent.tsx +103 -0
  87. package/src/components/CopyToClipboardButton.tsx +21 -0
  88. package/src/components/MenuBar.tsx +151 -0
  89. package/src/components/MenuBarWithContext.tsx +6 -0
  90. package/src/components/Modal.tsx +17 -0
  91. package/src/components/MusicContent.tsx +309 -0
  92. package/src/components/NavBarController.tsx +55 -0
  93. package/src/components/NavBarControllerWrapper.tsx +13 -0
  94. package/src/components/Page.tsx +56 -0
  95. package/src/components/SitemapContent.tsx +125 -0
  96. package/src/contacts.json +32 -0
  97. package/src/env.d.ts +13 -0
  98. package/src/lib/assistantStateMachine.ts +80 -0
  99. package/src/lib/audioOverlap.ts +99 -0
  100. package/src/lib/keyboardInputUtils.ts +182 -0
  101. package/src/lib/musicSchema.ts +85 -0
  102. package/src/lib/naggingAssistantClient.ts +241 -0
  103. package/src/lib/resumeAnalytics.ts +163 -0
  104. package/src/main.tsx +35 -0
  105. package/src/pages.json +50 -0
  106. package/src/sections.json +243 -0
  107. package/src/src+addons.zip +3 -0
  108. package/src/styles/main.css +465 -0
  109. package/src/utils/blogSecurity.ts +87 -0
  110. package/src/utils/menuItems.ts +33 -0
  111. package/src/windowing/MinimizedSections.tsx +86 -0
  112. package/src/windowing/Section.tsx +586 -0
  113. package/src/windowing/context.tsx +13 -0
  114. package/src/windowing/hooks.ts +10 -0
  115. package/src/windowing/index.ts +7 -0
  116. package/src/windowing/provider.tsx +74 -0
  117. package/src/windowing/server.ts +3 -0
  118. package/src/windowing/types.ts +33 -0
  119. package/src/windowing/utils.ts +135 -0
  120. package/tests/generate-soundcloud-json.test.mjs +63 -0
  121. package/tests/music-schema.test.mjs +53 -0
  122. package/tsconfig.json +26 -0
  123. package/vite.config.ts +304 -0
@@ -0,0 +1,149 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import { spawn } from "node:child_process";
5
+
6
+ function createProjectPaths(currentWorkingDirectory) {
7
+ const workspaceRoot = currentWorkingDirectory;
8
+ return Object.freeze({
9
+ workspaceRoot,
10
+ publicBlogAssetsPath: path.resolve(workspaceRoot, "public", "blog-assets"),
11
+ externalBlogPath: path.resolve(workspaceRoot, "..", "blog", "blog"),
12
+ viteBinPath: path.resolve(workspaceRoot, "node_modules/vite/bin/vite.js"),
13
+ });
14
+ }
15
+
16
+ function pathExists(targetPath) {
17
+ return fs.existsSync(targetPath);
18
+ }
19
+
20
+ function getPathStatsOrNull(targetPath) {
21
+ if (!pathExists(targetPath)) {
22
+ return null;
23
+ }
24
+ return fs.lstatSync(targetPath);
25
+ }
26
+
27
+ function isDirectorySymlink(targetPath) {
28
+ const stats = getPathStatsOrNull(targetPath);
29
+ return stats ? stats.isSymbolicLink() : false;
30
+ }
31
+
32
+ function isSymlinkTarget(linkPath, expectedTargetPath) {
33
+ if (!isDirectorySymlink(linkPath) || !pathExists(expectedTargetPath)) {
34
+ return false;
35
+ }
36
+ return fs.realpathSync(linkPath) === fs.realpathSync(expectedTargetPath);
37
+ }
38
+
39
+ function unlinkPath(targetPath) {
40
+ fs.unlinkSync(targetPath);
41
+ }
42
+
43
+ function createRelativeDirectorySymlink(linkPath, targetPath) {
44
+ const relativeTarget = path.relative(path.dirname(linkPath), targetPath) || ".";
45
+ fs.symlinkSync(relativeTarget, linkPath, "dir");
46
+ }
47
+
48
+ function logWarning(message) {
49
+ console.warn(`[dev] ${message}`);
50
+ }
51
+
52
+ function probeInterruptedRunAndCleanup(paths) {
53
+ if (!pathExists(paths.publicBlogAssetsPath)) {
54
+ return;
55
+ }
56
+
57
+ if (isSymlinkTarget(paths.publicBlogAssetsPath, paths.externalBlogPath)) {
58
+ unlinkPath(paths.publicBlogAssetsPath);
59
+ }
60
+ }
61
+
62
+ function ensureExternalBlogExists(paths) {
63
+ if (pathExists(paths.externalBlogPath)) {
64
+ return true;
65
+ }
66
+
67
+ logWarning("../blog/blog is missing; blog-assets symlink was not created.");
68
+ return false;
69
+ }
70
+
71
+ function ensurePublicBlogAssetsPathCanBeLinked(paths) {
72
+ if (!pathExists(paths.publicBlogAssetsPath)) {
73
+ return true;
74
+ }
75
+
76
+ if (isDirectorySymlink(paths.publicBlogAssetsPath)) {
77
+ logWarning("public/blog-assets exists as a symlink to another target; leaving it unchanged.");
78
+ } else {
79
+ logWarning("public/blog-assets exists as a real directory; leaving it unchanged.");
80
+ }
81
+ return false;
82
+ }
83
+
84
+ function ensureBlogSymlink(paths) {
85
+ if (!ensureExternalBlogExists(paths)) {
86
+ return;
87
+ }
88
+
89
+ if (!ensurePublicBlogAssetsPathCanBeLinked(paths)) {
90
+ return;
91
+ }
92
+
93
+ createRelativeDirectorySymlink(paths.publicBlogAssetsPath, paths.externalBlogPath);
94
+ }
95
+
96
+ function cleanupBlogSymlink(paths) {
97
+ if (!pathExists(paths.publicBlogAssetsPath)) {
98
+ return;
99
+ }
100
+
101
+ if (isSymlinkTarget(paths.publicBlogAssetsPath, paths.externalBlogPath)) {
102
+ unlinkPath(paths.publicBlogAssetsPath);
103
+ }
104
+ }
105
+
106
+ function spawnVite(paths) {
107
+ return spawn(process.execPath, [paths.viteBinPath], {
108
+ cwd: paths.workspaceRoot,
109
+ stdio: "inherit",
110
+ env: process.env,
111
+ });
112
+ }
113
+
114
+ function forwardSignalToVite(viteProcess, signalName) {
115
+ viteProcess.kill(signalName);
116
+ }
117
+
118
+ function registerProcessHandlers(viteProcess, cleanupHandler) {
119
+ process.on("exit", cleanupHandler);
120
+ process.on("SIGINT", () => forwardSignalToVite(viteProcess, "SIGINT"));
121
+ process.on("SIGTERM", () => forwardSignalToVite(viteProcess, "SIGTERM"));
122
+ }
123
+
124
+ function registerViteExitHandler(viteProcess, cleanupHandler) {
125
+ viteProcess.on("exit", (code, signal) => {
126
+ cleanupHandler();
127
+
128
+ if (signal) {
129
+ process.kill(process.pid, signal);
130
+ return;
131
+ }
132
+
133
+ process.exit(code ?? 0);
134
+ });
135
+ }
136
+
137
+ function run() {
138
+ const paths = createProjectPaths(process.cwd());
139
+ probeInterruptedRunAndCleanup(paths);
140
+ ensureBlogSymlink(paths);
141
+
142
+ const cleanupHandler = () => cleanupBlogSymlink(paths);
143
+ const viteProcess = spawnVite(paths);
144
+
145
+ registerProcessHandlers(viteProcess, cleanupHandler);
146
+ registerViteExitHandler(viteProcess, cleanupHandler);
147
+ }
148
+
149
+ run();
@@ -0,0 +1,56 @@
1
+ const MUSIC_GROUP_NAME = "akinevz";
2
+ const MUSIC_GROUP_URL = "https://akinevz.com";
3
+ const MUSIC_GROUP_DESCRIPTION =
4
+ "Independent sound designer, music creator, and electronic music artist.";
5
+ const MUSIC_GROUP_ALTERNATE_NAMES = [
6
+ "KINE",
7
+ "KALE",
8
+ "I lied my name isn't actually KINE",
9
+ "I lied my name isn't actually KALE",
10
+ ];
11
+ const MUSIC_GROUP_GENRES = [
12
+ "Electronic",
13
+ "Experimental",
14
+ "Industrial",
15
+ "Drone",
16
+ "Glitch",
17
+ ];
18
+ const MUSIC_GROUP_SAME_AS = [
19
+ "https://soundcloud.com/akinevz",
20
+ "https://youtube.com/@akinevz",
21
+ "https://x.com/akinevz",
22
+ "https://github.com/akinevz2",
23
+ ];
24
+ const MUSIC_GROUP_PRIMARY_ALIAS = MUSIC_GROUP_ALTERNATE_NAMES[0];
25
+
26
+ export const serializeJsonLd = (value) =>
27
+ JSON.stringify(value).replace(/</g, "\\u003c");
28
+
29
+ export const buildMusicGroupSchema = (tracks = []) => ({
30
+ "@context": "https://schema.org",
31
+ "@type": "MusicGroup",
32
+ name: MUSIC_GROUP_NAME,
33
+ alternateName: MUSIC_GROUP_ALTERNATE_NAMES,
34
+ url: MUSIC_GROUP_URL,
35
+ genre: MUSIC_GROUP_GENRES,
36
+ description: MUSIC_GROUP_DESCRIPTION,
37
+ sameAs: MUSIC_GROUP_SAME_AS,
38
+ track: Array.isArray(tracks)
39
+ ? tracks
40
+ .filter(
41
+ (track) =>
42
+ track && typeof track.title === "string" && typeof track.url === "string",
43
+ )
44
+ .map((track) => {
45
+ return {
46
+ "@type": "MusicRecording",
47
+ name: track.title,
48
+ url: track.url,
49
+ byArtist: {
50
+ "@type": "MusicGroup",
51
+ name: MUSIC_GROUP_PRIMARY_ALIAS,
52
+ },
53
+ };
54
+ })
55
+ : [],
56
+ });
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, resolve, join } from "node:path";
5
+ import { copyFileSync, existsSync } from "node:fs";
6
+ import { withGitWorktree } from "./git-worktree-helper.mjs";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // Paths
12
+ const BLOG_DIRSTRING = "blog";
13
+ const WEBSITE_DIR = resolve(__dirname, "..");
14
+ const BLOG_DIR = resolve(WEBSITE_DIR, BLOG_DIRSTRING);
15
+ const SOUNDCloudJSON_PATH = resolve(WEBSITE_DIR, "public", "soundcloud.json");
16
+ const BLOG_SOUNDCloudJSON_PATH = resolve(BLOG_DIR, "soundcloud.json");
17
+
18
+ console.log("🚀 Publishing soundcloud.json to GitHub...");
19
+
20
+ // Step 1: Ensure soundcloud.json exists
21
+ if (!existsSync(SOUNDCloudJSON_PATH)) {
22
+ console.error("❌ soundcloud.json not found. Run 'npm run generate:music' first.");
23
+ process.exit(1);
24
+ }
25
+
26
+ // Step 2: Copy to blog directory
27
+ withGitWorktree(BLOG_DIRSTRING, "blog-posts", async (worktreeDir) => {
28
+ console.log("📋 Copying soundcloud.json to blog worktree...");
29
+ copyFileSync(SOUNDCloudJSON_PATH, BLOG_SOUNDCloudJSON_PATH);
30
+ });
31
+
32
+ console.log("✅ Successfully published soundcloud.json to GitHub!");
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ // Fetches music-links.json from the blog-posts branch on GitHub CDN,
3
+ // validates its schema in memory, and reports the result.
4
+ // Does not write to disk — the file is consumed at runtime by MusicContent.tsx.
5
+
6
+ const MUSIC_LINKS_URL =
7
+ "https://raw.githubusercontent.com/akinevz2/frontend/blog-posts/music-links.json";
8
+
9
+ const isFavouriteLink = (value) =>
10
+ value !== null &&
11
+ typeof value === "object" &&
12
+ typeof value.title === "string" &&
13
+ typeof value.url === "string";
14
+
15
+ const response = await fetch(MUSIC_LINKS_URL);
16
+ if (!response.ok) {
17
+ process.stderr.write(`Failed to fetch ${MUSIC_LINKS_URL}: HTTP ${response.status}\n`);
18
+ process.exit(1);
19
+ }
20
+
21
+ const payload = await response.json();
22
+ if (!Array.isArray(payload)) {
23
+ process.stderr.write(`Invalid music-links.json: expected a JSON array\n`);
24
+ process.exit(1);
25
+ }
26
+
27
+ const valid = payload.filter(isFavouriteLink);
28
+ const invalid = payload.length - valid.length;
29
+
30
+ process.stdout.write(
31
+ `music-links.json OK — ${valid.length} valid link(s)${invalid > 0 ? `, ${invalid} skipped (missing title/url)` : ""}\n`,
32
+ );