feat(web): play a random track from the Frutiger Aero SoundCloud playlist in aero mode (v2.2.22)
This commit is contained in:
parent
e4530afd98
commit
be33317d54
12 changed files with 387 additions and 83 deletions
|
|
@ -56,7 +56,7 @@ frosted-glass **web** site, and an alternate KDE/Plasma **desktop simulation**
|
||||||
utilities in the components (Tailwind is present via PostCSS but the look is
|
utilities in the components (Tailwind is present via PostCSS but the look is
|
||||||
hand-written CSS).
|
hand-written CSS).
|
||||||
- Path alias: `@/*` → `src/*`.
|
- Path alias: `@/*` → `src/*`.
|
||||||
- Version: `2.2.21` (see `package.json`).
|
- Version: `2.2.22` (see `package.json`).
|
||||||
|
|
||||||
### Scripts
|
### Scripts
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -103,6 +103,7 @@ src/app/
|
||||||
components/
|
components/
|
||||||
ThemeRoot.tsx → picks which shell to mount (the router of the UI)
|
ThemeRoot.tsx → picks which shell to mount (the router of the UI)
|
||||||
WebLayout.tsx → "web" theme (default): sticky sidebar + glass content
|
WebLayout.tsx → "web" theme (default): sticky sidebar + glass content
|
||||||
|
AeroMusic.tsx → Frutiger Aero soundtrack (random SoundCloud playlist track)
|
||||||
FeatureWindow.tsx → decorative CSS "windows" (game / browser / forge); used in the
|
FeatureWindow.tsx → decorative CSS "windows" (game / browser / forge); used in the
|
||||||
web feature panels and the desktop app windows
|
web feature panels and the desktop app windows
|
||||||
WindowsDesktop.tsx → "skeuo" theme, desktop: icons, windows, taskbar
|
WindowsDesktop.tsx → "skeuo" theme, desktop: icons, windows, taskbar
|
||||||
|
|
@ -181,7 +182,11 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark),
|
||||||
bubbles (`.web-aero-bubbles` / `.web-aero-bubble`) rise across the backdrop, and
|
bubbles (`.web-aero-bubbles` / `.web-aero-bubble`) rise across the backdrop, and
|
||||||
all `.glass` panels and interactive controls become crystal-clear 3D bevel-rimmed
|
all `.glass` panels and interactive controls become crystal-clear 3D bevel-rimmed
|
||||||
liquid glass (the `.web-ctl-scheme` light/dark switch is hidden in aero mode —
|
liquid glass (the `.web-ctl-scheme` light/dark switch is hidden in aero mode —
|
||||||
it's a fixed bright-sky look). The two remaining feature panels (`.web-split`) stretch
|
it's a fixed bright-sky look). While aero is on, `AeroMusic.tsx` autoplays a
|
||||||
|
random track from the *Frutiger Aero* SoundCloud playlist (a hidden SoundCloud
|
||||||
|
iframe driven through their widget API: it picks a random index from `getSounds`
|
||||||
|
and re-randomises on every `PLAY` event so the set never plays in order; the chip
|
||||||
|
label is synced from `getCurrentSound`, not from what we asked for). The two remaining feature panels (`.web-split`) stretch
|
||||||
to equal height and share a fixed `16 / 10` `.fw` window (the `FeatureWindow`
|
to equal height and share a fixed `16 / 10` `.fw` window (the `FeatureWindow`
|
||||||
component) — decorative, **asset-free CSS mock-ups** rather than screenshots:
|
component) — decorative, **asset-free CSS mock-ups** rather than screenshots:
|
||||||
Revival Inc. is a little necromancy game scene (night sky, moon, tombstones,
|
Revival Inc. is a little necromancy game scene (night sky, moon, tombstones,
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,10 @@ more.
|
||||||
`freezmo_aero.png` and transforms the web shell into a Frutiger Aero theme:
|
`freezmo_aero.png` and transforms the web shell into a Frutiger Aero theme:
|
||||||
`frutigerbackground.png` as the full-screen backdrop, floating glossy water/soap bubbles
|
`frutigerbackground.png` as the full-screen backdrop, floating glossy water/soap bubbles
|
||||||
across the viewport, and crystal-clear 3D bevel-rimmed liquid-glass panels and buttons
|
across the viewport, and crystal-clear 3D bevel-rimmed liquid-glass panels and buttons
|
||||||
with subtle cyan refractive rims. The light/dark switch is hidden while the mode is
|
with subtle cyan refractive rims. A random track from the *Frutiger Aero* SoundCloud
|
||||||
active (it's a fixed bright-sky look), and the desktop mode is left untouched.
|
playlist plays while the mode is on (driven through SoundCloud's widget API), with a
|
||||||
|
small glass "now playing" chip to pause it. The light/dark switch is hidden while the
|
||||||
|
mode is active (it's a fixed bright-sky look), and the desktop mode is left untouched.
|
||||||
- **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per
|
- **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per
|
||||||
cell, with a follow-the-cursor drag ghost. Icons shrink on cramped viewports so they never
|
cell, with a follow-the-cursor drag ghost. Icons shrink on cramped viewports so they never
|
||||||
collide.
|
collide.
|
||||||
|
|
@ -93,7 +95,7 @@ more.
|
||||||
icons (`public/freezmo.png`, `public/freezmo_aero.png`), the Frutiger Aero wallpaper
|
icons (`public/freezmo.png`, `public/freezmo_aero.png`), the Frutiger Aero wallpaper
|
||||||
(`public/frutigerbackground.png`), the Joker easter egg and the Gentoo mark; all cards,
|
(`public/frutigerbackground.png`), the Joker easter egg and the Gentoo mark; all cards,
|
||||||
feature/app previews and the desktop wallpaper are CSS.
|
feature/app previews and the desktop wallpaper are CSS.
|
||||||
- **Version:** `2.2.21` (see `package.json`)
|
- **Version:** `2.2.22` (see `package.json`)
|
||||||
- **Dependencies:** `npm audit` reports **0 vulnerabilities**. `next` is kept at a patched
|
- **Dependencies:** `npm audit` reports **0 vulnerabilities**. `next` is kept at a patched
|
||||||
release (`>=16.3.6`) for the `next/og` RCE, and `brace-expansion` is force-upgraded via
|
release (`>=16.3.6`) for the `next/og` RCE, and `brace-expansion` is force-upgraded via
|
||||||
`overrides`. `braces` (a transitive ESLint/`fast-glob` dependency) has **no upstream fix**,
|
`overrides`. `braces` (a transitive ESLint/`fast-glob` dependency) has **no upstream fix**,
|
||||||
|
|
@ -215,6 +217,7 @@ src/app/
|
||||||
useTheme.ts → theme state via useSyncExternalStore
|
useTheme.ts → theme state via useSyncExternalStore
|
||||||
useColorScheme.ts → light/dark state via useSyncExternalStore
|
useColorScheme.ts → light/dark state via useSyncExternalStore
|
||||||
WebLayout.tsx → default web shell: sidebar + glass content
|
WebLayout.tsx → default web shell: sidebar + glass content
|
||||||
|
AeroMusic.tsx → Frutiger Aero soundtrack (random SoundCloud playlist track)
|
||||||
FeatureWindow.tsx → decorative CSS windows for the two feature panels
|
FeatureWindow.tsx → decorative CSS windows for the two feature panels
|
||||||
WindowsDesktop.tsx → desktop shell: icons, windows, taskbar, Start menu
|
WindowsDesktop.tsx → desktop shell: icons, windows, taskbar, Start menu
|
||||||
WinWindow.tsx → one draggable/resizable window
|
WinWindow.tsx → one draggable/resizable window
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,11 @@
|
||||||
"6": "braces Character Constants",
|
"6": "braces Character Constants",
|
||||||
"7": "Next Config & API Routes",
|
"7": "Next Config & API Routes",
|
||||||
"8": "TypeScript Compiler Config",
|
"8": "TypeScript Compiler Config",
|
||||||
"9": "Root Package Dependencies",
|
|
||||||
"10": "Root Dev Dependencies",
|
"10": "Root Dev Dependencies",
|
||||||
"12": "Debian Dependency Installer",
|
"12": "Debian Dependency Installer",
|
||||||
"13": "brace-expansion Overrides",
|
|
||||||
"14": "npm Scripts",
|
|
||||||
"15": "Freezmo Icon Visual Identity",
|
"15": "Freezmo Icon Visual Identity",
|
||||||
"16": "braces Public API Methods",
|
"16": "braces Public API Methods",
|
||||||
"17": "Eye Logo & Favicon Assets",
|
"17": "Eye Logo & Favicon Assets",
|
||||||
"18": "ESLint Configuration",
|
|
||||||
"19": "Runtime Dependencies",
|
|
||||||
"20": "Discord Icon Asset",
|
"20": "Discord Icon Asset",
|
||||||
"22": "Instagram Icon Asset",
|
"22": "Instagram Icon Asset",
|
||||||
"23": "Gentoo Logo Asset",
|
"23": "Gentoo Logo Asset",
|
||||||
|
|
@ -23,7 +18,6 @@
|
||||||
"25": "Revival Inc. Game Favicon",
|
"25": "Revival Inc. Game Favicon",
|
||||||
"26": "TikTok Icon Asset",
|
"26": "TikTok Icon Asset",
|
||||||
"27": "System Service Installer",
|
"27": "System Service Installer",
|
||||||
"28": "Package allowScripts Overrides",
|
|
||||||
"29": "PostCSS Configuration",
|
"29": "PostCSS Configuration",
|
||||||
"30": "GitHub Icon Asset",
|
"30": "GitHub Icon Asset",
|
||||||
"31": "itch.io Icon Asset",
|
"31": "itch.io Icon Asset",
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
# Graph Report - dvorakmortonxd.dev (2026-10-15)
|
# Graph Report - dvorakmortonxd.dev (2026-10-16)
|
||||||
|
|
||||||
## Corpus Check
|
## Corpus Check
|
||||||
- 43 files · ~319,284 words
|
- 44 files · ~320,188 words
|
||||||
- Verdict: corpus is large enough that graph structure adds value.
|
- Verdict: corpus is large enough that graph structure adds value.
|
||||||
- Unclassified: 19 file(s) not represented in the graph (top: (none) 6, .ttf 3, .css 3)
|
- Unclassified: 19 file(s) not represented in the graph (top: (none) 6, .ttf 3, .css 3)
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
- 424 nodes · 592 edges · 40 communities (25 shown, 15 thin omitted)
|
- 434 nodes · 607 edges · 37 communities (23 shown, 14 thin omitted)
|
||||||
- Extraction: 93% EXTRACTED · 7% INFERRED · 0% AMBIGUOUS · INFERRED: 44 edges (avg confidence: 0.89)
|
- Extraction: 93% EXTRACTED · 7% INFERRED · 0% AMBIGUOUS · INFERRED: 44 edges (avg confidence: 0.89)
|
||||||
- Token cost: 0 input · 0 output
|
- Token cost: 0 input · 0 output
|
||||||
|
|
||||||
## Graph Freshness
|
## Graph Freshness
|
||||||
- Built from commit: `15ef4db7`
|
- Built from commit: `e4530afd`
|
||||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||||
- Run `graphify update .` after code changes (no API cost).
|
- Run `graphify update .` after code changes (no API cost).
|
||||||
|
|
||||||
|
|
@ -19,22 +19,20 @@
|
||||||
- Desktop App Catalog
|
- Desktop App Catalog
|
||||||
- Deployment & Ops Docs
|
- Deployment & Ops Docs
|
||||||
- braces Package Metadata
|
- braces Package Metadata
|
||||||
- WindowsDesktop.tsx
|
- useTheme.ts
|
||||||
- braces Parsing Engine
|
- braces Parsing Engine
|
||||||
- apps.tsx
|
- apps.tsx
|
||||||
- braces Character Constants
|
- braces Character Constants
|
||||||
- Next Config & API Routes
|
- Next Config & API Routes
|
||||||
- TypeScript Compiler Config
|
- TypeScript Compiler Config
|
||||||
- Root Package Dependencies
|
- package.json
|
||||||
- Root Dev Dependencies
|
- Root Dev Dependencies
|
||||||
|
- ProjectIndex.tsx
|
||||||
- Debian Dependency Installer
|
- Debian Dependency Installer
|
||||||
- brace-expansion Overrides
|
- dev-server.mjs
|
||||||
- npm Scripts
|
|
||||||
- Freezmo Icon Visual Identity
|
- Freezmo Icon Visual Identity
|
||||||
- braces Public API Methods
|
- braces Public API Methods
|
||||||
- Eye Logo & Favicon Assets
|
- Eye Logo & Favicon Assets
|
||||||
- ESLint Configuration
|
|
||||||
- Runtime Dependencies
|
|
||||||
- Discord Icon Asset
|
- Discord Icon Asset
|
||||||
- AGENTS.md — cold-boot notes
|
- AGENTS.md — cold-boot notes
|
||||||
- Instagram Icon Asset
|
- Instagram Icon Asset
|
||||||
|
|
@ -43,7 +41,6 @@
|
||||||
- Revival Inc. Game Favicon
|
- Revival Inc. Game Favicon
|
||||||
- TikTok Icon Asset
|
- TikTok Icon Asset
|
||||||
- System Service Installer
|
- System Service Installer
|
||||||
- Package allowScripts Overrides
|
|
||||||
- PostCSS Configuration
|
- PostCSS Configuration
|
||||||
- GitHub Icon Asset
|
- GitHub Icon Asset
|
||||||
- itch.io Icon Asset
|
- itch.io Icon Asset
|
||||||
|
|
@ -55,9 +52,9 @@
|
||||||
- Twitter/X Icon Asset
|
- Twitter/X Icon Asset
|
||||||
|
|
||||||
## God Nodes (most connected - your core abstractions)
|
## God Nodes (most connected - your core abstractions)
|
||||||
1. `WebLayout()` - 18 edges
|
1. `WebLayout()` - 19 edges
|
||||||
2. `compilerOptions` - 16 edges
|
2. `react` - 16 edges
|
||||||
3. `react` - 15 edges
|
3. `compilerOptions` - 16 edges
|
||||||
4. `next` - 12 edges
|
4. `next` - 12 edges
|
||||||
5. `FeatureWindow()` - 12 edges
|
5. `FeatureWindow()` - 12 edges
|
||||||
6. `useTheme()` - 12 edges
|
6. `useTheme()` - 12 edges
|
||||||
|
|
@ -86,11 +83,11 @@
|
||||||
- **** — public_revival_inc_favicon_png_image, public_revival_inc_favicon_png_revival_inc_game, public_revival_inc_favicon_png_game_controller_mark [INFERRED 0.75]
|
- **** — public_revival_inc_favicon_png_image, public_revival_inc_favicon_png_revival_inc_game, public_revival_inc_favicon_png_game_controller_mark [INFERRED 0.75]
|
||||||
- **Site identity: eye logo used as app icon and in-page logo** — src_app_icon_png_image, src_app_icon_png_eye_logo, src_app_icon_png_favicon, src_app_components_jokerlogo_tsx_logo [INFERRED 0.85]
|
- **Site identity: eye logo used as app icon and in-page logo** — src_app_icon_png_image, src_app_icon_png_eye_logo, src_app_icon_png_favicon, src_app_components_jokerlogo_tsx_logo [INFERRED 0.85]
|
||||||
|
|
||||||
## Communities (40 total, 15 thin omitted)
|
## Communities (37 total, 14 thin omitted)
|
||||||
|
|
||||||
### Community 0 - "Desktop App Catalog"
|
### Community 0 - "Desktop App Catalog"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.08
|
||||||
Nodes (26): Web theme + light/dark (v2.2 rework), react, ContributionsApp(), Clock(), getTime(), subscribe(), Activity, ActivityPayload (+18 more)
|
Nodes (35): Web theme + light/dark (v2.2 rework), react, AeroMusic(), loadWidgetApi(), PLACEHOLDER, SoundCloudGlobal, SoundCloudSound, SoundCloudWidget (+27 more)
|
||||||
|
|
||||||
### Community 1 - "Deployment & Ops Docs"
|
### Community 1 - "Deployment & Ops Docs"
|
||||||
Cohesion: 0.09
|
Cohesion: 0.09
|
||||||
|
|
@ -100,17 +97,17 @@ Nodes (30): Autostart on Boot (systemd / OpenRC), Cloudflare Tunnel Reverse Prox
|
||||||
Cohesion: 0.05
|
Cohesion: 0.05
|
||||||
Nodes (37): ansi-colors, bash-path, gulp-format-md, mocha, author, bugs, url, contributors (+29 more)
|
Nodes (37): ansi-colors, bash-path, gulp-format-md, mocha, author, bugs, url, contributors (+29 more)
|
||||||
|
|
||||||
### Community 3 - "WindowsDesktop.tsx"
|
### Community 3 - "useTheme.ts"
|
||||||
Cohesion: 0.08
|
Cohesion: 0.19
|
||||||
Nodes (33): Conventions & things that will bite you, How UI selection works, APP_BY_ID, AppDef, APPS, createWebsiteApp(), EmbeddedSite(), WebsiteApp() (+25 more)
|
Nodes (13): How UI selection works, WelcomeApp(), ThemeRoot(), DEFAULT_THEME, getServerSnapshot(), getSnapshot(), setTheme(), subscribe() (+5 more)
|
||||||
|
|
||||||
### Community 4 - "braces Parsing Engine"
|
### Community 4 - "braces Parsing Engine"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.10
|
||||||
Nodes (17): compile, expand, parse, stringify, compile(), fill, utils, append() (+9 more)
|
Nodes (17): compile, expand, parse, stringify, compile(), fill, utils, append() (+9 more)
|
||||||
|
|
||||||
### Community 5 - "apps.tsx"
|
### Community 5 - "apps.tsx"
|
||||||
Cohesion: 0.07
|
Cohesion: 0.06
|
||||||
Nodes (36): ALACRITTY_APP, FriendsApp(), GitdxdApp(), HardwareApp(), NEOFETCH, ProjectsApp(), RevivalApp(), RustyWolfApp() (+28 more)
|
Nodes (36): Conventions & things that will bite you, ALACRITTY_APP, APP_BY_ID, AppDef, APPS, createWebsiteApp(), EmbeddedSite(), FriendsApp() (+28 more)
|
||||||
|
|
||||||
### Community 6 - "braces Character Constants"
|
### Community 6 - "braces Character Constants"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.11
|
||||||
|
|
@ -133,32 +130,32 @@ Nodes (3): {
|
||||||
}, parse(), stringify
|
}, parse(), stringify
|
||||||
|
|
||||||
### Community 7 - "Next Config & API Routes"
|
### Community 7 - "Next Config & API Routes"
|
||||||
Cohesion: 0.10
|
Cohesion: 0.15
|
||||||
Nodes (20): nextConfig, next, app, hasForwardedHeaders(), isDevDebugRequest(), isLocalDebugRequest(), isLoopback(), port (+12 more)
|
Nodes (13): nextConfig, next, ALLOWED_HOSTS, clickShim(), fail(), GET(), isAllowedHost(), ALLOWED_HOSTS (+5 more)
|
||||||
|
|
||||||
### Community 8 - "TypeScript Compiler Config"
|
### Community 8 - "TypeScript Compiler Config"
|
||||||
Cohesion: 0.11
|
Cohesion: 0.11
|
||||||
Nodes (18): compilerOptions, allowJs, esModuleInterop, incremental, isolatedModules, jsx, lib, module (+10 more)
|
Nodes (18): compilerOptions, allowJs, esModuleInterop, incremental, isolatedModules, jsx, lib, module (+10 more)
|
||||||
|
|
||||||
### Community 9 - "Root Package Dependencies"
|
### Community 9 - "package.json"
|
||||||
Cohesion: 0.13
|
Cohesion: 0.06
|
||||||
Nodes (14): fill-range, name, private, version, autoprefixer, postcss, react-dom, @svgr/webpack (+6 more)
|
Nodes (35): eslintConfig, allowScripts, unrs-resolver@1.12.2, dependencies, next, react, react-dom, fill-range (+27 more)
|
||||||
|
|
||||||
### Community 10 - "Root Dev Dependencies"
|
### Community 10 - "Root Dev Dependencies"
|
||||||
Cohesion: 0.15
|
Cohesion: 0.15
|
||||||
Nodes (13): devDependencies, autoprefixer, eslint, eslint-config-next, fill-range, postcss, @svgr/webpack, tailwindcss (+5 more)
|
Nodes (13): devDependencies, autoprefixer, eslint, eslint-config-next, fill-range, postcss, @svgr/webpack, tailwindcss (+5 more)
|
||||||
|
|
||||||
|
### Community 11 - "ProjectIndex.tsx"
|
||||||
|
Cohesion: 0.14
|
||||||
|
Nodes (20): HardwareApp(), ProjectsApp(), CodingProjectIndex(), CodingRow(), Group(), HARDWARE_GROUPS, HardwareProjectIndex(), HardwareRow() (+12 more)
|
||||||
|
|
||||||
### Community 12 - "Debian Dependency Installer"
|
### Community 12 - "Debian Dependency Installer"
|
||||||
Cohesion: 0.43
|
Cohesion: 0.43
|
||||||
Nodes (6): die(), log(), node_ok(), install-dependencies-deb.sh script, usage(), warn()
|
Nodes (6): die(), log(), node_ok(), install-dependencies-deb.sh script, usage(), warn()
|
||||||
|
|
||||||
### Community 13 - "brace-expansion Overrides"
|
### Community 13 - "dev-server.mjs"
|
||||||
Cohesion: 0.33
|
Cohesion: 0.29
|
||||||
Nodes (6): brace-expansion, brace-expansion, overrides, braces, minimatch@^10.0.0, minimatch@^3.0.0
|
Nodes (7): app, hasForwardedHeaders(), isDevDebugRequest(), isLocalDebugRequest(), isLoopback(), port, server
|
||||||
|
|
||||||
### Community 14 - "npm Scripts"
|
|
||||||
Cohesion: 0.33
|
|
||||||
Nodes (6): scripts, build, dev, dev:next, lint, start
|
|
||||||
|
|
||||||
### Community 15 - "Freezmo Icon Visual Identity"
|
### Community 15 - "Freezmo Icon Visual Identity"
|
||||||
Cohesion: 0.53
|
Cohesion: 0.53
|
||||||
|
|
@ -168,14 +165,6 @@ Nodes (5): Dark Navy Rounded-Square (Squircle) Frame, Freeze / Preserve State, F
|
||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (4): JokerLogo Component Logo Image, Eye Logo Mark (amber iris and pupil), App Icon - Macro Photograph of a Human Eye, All-Seeing Eye / Surveillance Motif
|
Nodes (4): JokerLogo Component Logo Image, Eye Logo Mark (amber iris and pupil), App Icon - Macro Photograph of a Human Eye, All-Seeing Eye / Surveillance Motif
|
||||||
|
|
||||||
### Community 18 - "ESLint Configuration"
|
|
||||||
Cohesion: 0.50
|
|
||||||
Nodes (3): eslintConfig, eslint, eslint-config-next
|
|
||||||
|
|
||||||
### Community 19 - "Runtime Dependencies"
|
|
||||||
Cohesion: 0.50
|
|
||||||
Nodes (4): dependencies, next, react, react-dom
|
|
||||||
|
|
||||||
### Community 20 - "Discord Icon Asset"
|
### Community 20 - "Discord Icon Asset"
|
||||||
Cohesion: 0.50
|
Cohesion: 0.50
|
||||||
Nodes (4): Discord (chat platform brand mark), Discord Icon (SVG social link asset), Profile social link icon, SVG Repo (icon asset source)
|
Nodes (4): Discord (chat platform brand mark), Discord Icon (SVG social link asset), Profile social link icon, SVG Repo (icon asset source)
|
||||||
|
|
@ -206,23 +195,23 @@ Nodes (3): TikTok, TikTok Logo Icon, SVG Repo
|
||||||
|
|
||||||
## Knowledge Gaps
|
## Knowledge Gaps
|
||||||
- **54 isolated node(s):** `react-dom`, `@svgr/webpack`, `@tailwindcss/postcss`, `@types/node`, `@types/react` (+49 more)
|
- **54 isolated node(s):** `react-dom`, `@svgr/webpack`, `@tailwindcss/postcss`, `@types/node`, `@types/react` (+49 more)
|
||||||
These have ≤1 connection - possible missing edges or undocumented components. (Counts symbols only; 227 node(s) total have ≤1 connection when file, concept and rationale nodes are included.)
|
These have ≤1 connection - possible missing edges or undocumented components. (Counts symbols only; 233 node(s) total have ≤1 connection when file, concept and rationale nodes are included.)
|
||||||
- **15 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
- **14 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||||
|
|
||||||
## Suggested Questions
|
## Suggested Questions
|
||||||
_Questions this graph is uniquely positioned to answer:_
|
_Questions this graph is uniquely positioned to answer:_
|
||||||
|
|
||||||
- **Why does `Conventions & things that will bite you` connect `WindowsDesktop.tsx` to `braces Public API Methods`, `AGENTS.md — cold-boot notes`?**
|
- **Why does `Conventions & things that will bite you` connect `apps.tsx` to `braces Public API Methods`, `AGENTS.md — cold-boot notes`?**
|
||||||
_High betweenness centrality (0.183) - this node is a cross-community bridge._
|
_High betweenness centrality (0.181) - this node is a cross-community bridge._
|
||||||
- **Are the 2 inferred relationships involving `WebLayout()` (e.g. with `How UI selection works` and `Web theme + light/dark (v2.2 rework)`) actually correct?**
|
- **Are the 2 inferred relationships involving `WebLayout()` (e.g. with `How UI selection works` and `Web theme + light/dark (v2.2 rework)`) actually correct?**
|
||||||
_`WebLayout()` has 2 INFERRED edges - model-reasoned connections that need verification._
|
_`WebLayout()` has 2 INFERRED edges - model-reasoned connections that need verification._
|
||||||
- **What connects `react-dom`, `@svgr/webpack`, `@tailwindcss/postcss` to the rest of the system?**
|
- **What connects `react-dom`, `@svgr/webpack`, `@tailwindcss/postcss` to the rest of the system?**
|
||||||
_54 weakly-connected nodes found - possible documentation gaps or missing edges._
|
_54 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||||
- **Should `Desktop App Catalog` be split into smaller, more focused modules?**
|
- **Should `Desktop App Catalog` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.10873440285204991 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.080338266384778 - nodes in this community are weakly interconnected._
|
||||||
- **Why does `braces()` connect `braces Public API Methods` to `WindowsDesktop.tsx`, `braces Parsing Engine`?**
|
- **Why does `braces()` connect `braces Public API Methods` to `braces Parsing Engine`, `apps.tsx`?**
|
||||||
_High betweenness centrality (0.150) - this node is a cross-community bridge._
|
_High betweenness centrality (0.149) - this node is a cross-community bridge._
|
||||||
- **Should `Deployment & Ops Docs` be split into smaller, more focused modules?**
|
- **Should `Deployment & Ops Docs` be split into smaller, more focused modules?**
|
||||||
_Cohesion score 0.08735632183908046 - nodes in this community are weakly interconnected._
|
_Cohesion score 0.08735632183908046 - nodes in this community are weakly interconnected._
|
||||||
- **Why does `next` connect `Next Config & API Routes` to `Desktop App Catalog`, `Root Package Dependencies`, `WindowsDesktop.tsx`, `apps.tsx`?**
|
- **Why does `react` connect `Desktop App Catalog` to `useTheme.ts`, `apps.tsx`, `Next Config & API Routes`, `package.json`, `ProjectIndex.tsx`?**
|
||||||
_High betweenness centrality (0.126) - this node is a cross-community bridge._
|
_High betweenness centrality (0.136) - this node is a cross-community bridge._
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -24,9 +24,9 @@
|
||||||
"semantic_hash": "03388a201be8750ddcddb14dfbc34411"
|
"semantic_hash": "03388a201be8750ddcddb14dfbc34411"
|
||||||
},
|
},
|
||||||
"package.json": {
|
"package.json": {
|
||||||
"mtime": 1792100566.4931674,
|
"mtime": 1792102753.0037653,
|
||||||
"seen": 1792100581.439811,
|
"seen": 1792102795.132633,
|
||||||
"ast_hash": "fceb2aeaea53cfa26e74f36ec2cf4e7c",
|
"ast_hash": "5b8d59a5fd4c3052fa6fb1dd3616838e",
|
||||||
"semantic_hash": ""
|
"semantic_hash": ""
|
||||||
},
|
},
|
||||||
"postcss.config.mjs": {
|
"postcss.config.mjs": {
|
||||||
|
|
@ -114,9 +114,9 @@
|
||||||
"semantic_hash": "20e25c727b4bb982e5a22d65efece143"
|
"semantic_hash": "20e25c727b4bb982e5a22d65efece143"
|
||||||
},
|
},
|
||||||
"src/app/components/WebLayout.tsx": {
|
"src/app/components/WebLayout.tsx": {
|
||||||
"mtime": 1792100566.4931674,
|
"mtime": 1792102753.0127654,
|
||||||
"seen": 1792100581.4398491,
|
"seen": 1792102795.1326752,
|
||||||
"ast_hash": "4c5f105a49587f804c8a59d17029e263",
|
"ast_hash": "19ff085bedc94e6cea703bfccfacd52d",
|
||||||
"semantic_hash": ""
|
"semantic_hash": ""
|
||||||
},
|
},
|
||||||
"src/app/components/WinWindow.tsx": {
|
"src/app/components/WinWindow.tsx": {
|
||||||
|
|
@ -240,9 +240,9 @@
|
||||||
"semantic_hash": "b81dea2175efa2b0b6c38451ab5a4bbd"
|
"semantic_hash": "b81dea2175efa2b0b6c38451ab5a4bbd"
|
||||||
},
|
},
|
||||||
"README.md": {
|
"README.md": {
|
||||||
"mtime": 1792100566.5121677,
|
"mtime": 1792102753.0337658,
|
||||||
"seen": 1792100581.439904,
|
"seen": 1792102795.1327267,
|
||||||
"ast_hash": "374e4ca7cda87fbbe327cd53df378839",
|
"ast_hash": "d42062ffe4714c5f5c00a7a7849bcb80",
|
||||||
"semantic_hash": ""
|
"semantic_hash": ""
|
||||||
},
|
},
|
||||||
"public/llms.txt": {
|
"public/llms.txt": {
|
||||||
|
|
@ -366,9 +366,9 @@
|
||||||
"semantic_hash": "6f69e16823f10d9c7af1625eff2b32e8"
|
"semantic_hash": "6f69e16823f10d9c7af1625eff2b32e8"
|
||||||
},
|
},
|
||||||
"AGENTS.md": {
|
"AGENTS.md": {
|
||||||
"mtime": 1792100572.9662693,
|
"mtime": 1792102785.3782775,
|
||||||
"seen": 1792100581.439899,
|
"seen": 1792102795.1327217,
|
||||||
"ast_hash": "13b85d5f3f9e7e69a7c3ec4862c4326d",
|
"ast_hash": "594842bd5f504ca50401180b54e743b8",
|
||||||
"semantic_hash": ""
|
"semantic_hash": ""
|
||||||
},
|
},
|
||||||
"public/freezmo_aero.png": {
|
"public/freezmo_aero.png": {
|
||||||
|
|
@ -382,5 +382,11 @@
|
||||||
"seen": 1792099263.7671065,
|
"seen": 1792099263.7671065,
|
||||||
"ast_hash": "78344ac481fd50f9366c30f041c8e135",
|
"ast_hash": "78344ac481fd50f9366c30f041c8e135",
|
||||||
"semantic_hash": ""
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"src/app/components/AeroMusic.tsx": {
|
||||||
|
"mtime": 1792102680.4096167,
|
||||||
|
"seen": 1792102795.1326532,
|
||||||
|
"ast_hash": "9e5d7d4591477a92d78eebf144627856",
|
||||||
|
"semantic_hash": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "dvorakmortonxd",
|
"name": "dvorakmortonxd",
|
||||||
"version": "2.2.21",
|
"version": "2.2.22",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dvorakmortonxd",
|
"name": "dvorakmortonxd",
|
||||||
"version": "2.2.21",
|
"version": "2.2.22",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^16.3.8",
|
"next": "^16.3.8",
|
||||||
"react": "^19.3.0",
|
"react": "^19.3.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "dvorakmortonxd",
|
"name": "dvorakmortonxd",
|
||||||
"version": "2.2.21",
|
"version": "2.2.22",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node scripts/dev-server.mjs",
|
"dev": "node scripts/dev-server.mjs",
|
||||||
|
|
|
||||||
211
src/app/components/AeroMusic.tsx
Normal file
211
src/app/components/AeroMusic.tsx
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
/* Frutiger Aero soundtrack: a random track from the "Frutiger Aero" playlist on
|
||||||
|
SoundCloud. A hidden SoundCloud iframe is driven through their Widget API so
|
||||||
|
the playlist autoplays, jumps to a random track and re-randomises whenever
|
||||||
|
playback moves on; a small glass chip shows what's playing and gives the
|
||||||
|
visitor a play/pause control. The iframe is only mounted by `WebLayout` while
|
||||||
|
aero mode is active, so unmounting it stops the music. */
|
||||||
|
|
||||||
|
const PLAYLIST_URL = "https://soundcloud.com/rustie-gamer/sets/frutiger-aero";
|
||||||
|
const API_SRC = "https://w.soundcloud.com/player/api.js";
|
||||||
|
const VOLUME = 35;
|
||||||
|
/* Ignore PLAY events for this long after we pick a track: a `skip()` can still
|
||||||
|
report the previous index for a moment, which would otherwise bounce us into
|
||||||
|
an endless re-pick. */
|
||||||
|
const PICK_GUARD_MS = 3000;
|
||||||
|
|
||||||
|
type SoundCloudSound = {
|
||||||
|
title?: string;
|
||||||
|
user?: { username?: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
type SoundCloudWidget = {
|
||||||
|
play: () => void;
|
||||||
|
pause: () => void;
|
||||||
|
skip: (index: number) => void;
|
||||||
|
setVolume: (volume: number) => void;
|
||||||
|
getSounds: (callback: (sounds: SoundCloudSound[]) => void) => void;
|
||||||
|
getCurrentSound: (callback: (sound: SoundCloudSound | null) => void) => void;
|
||||||
|
getCurrentSoundIndex: (callback: (index: number) => void) => void;
|
||||||
|
bind: (event: string, listener: () => void) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SoundCloudGlobal = {
|
||||||
|
Widget: ((element: HTMLIFrameElement) => SoundCloudWidget) & {
|
||||||
|
Events: { READY: string; PLAY: string; FINISH: string };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Track = { title: string; artist: string };
|
||||||
|
|
||||||
|
const PLACEHOLDER: Track = { title: "Frutiger Aero", artist: "SoundCloud playlist" };
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
SC?: SoundCloudGlobal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const WIDGET_SRC =
|
||||||
|
"https://w.soundcloud.com/player/?url=" +
|
||||||
|
encodeURIComponent(PLAYLIST_URL) +
|
||||||
|
"&color=%2364e3ff&auto_play=true&hide_related=true&show_comments=false" +
|
||||||
|
"&show_user=false&show_reposts=false&show_teaser=false&visual=false";
|
||||||
|
|
||||||
|
let apiPromise: Promise<SoundCloudGlobal> | null = null;
|
||||||
|
|
||||||
|
/** Loads SoundCloud's Widget API once; later mounts reuse the same promise. */
|
||||||
|
function loadWidgetApi(): Promise<SoundCloudGlobal> {
|
||||||
|
if (window.SC) return Promise.resolve(window.SC);
|
||||||
|
apiPromise ??= new Promise<SoundCloudGlobal>((resolve, reject) => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
script.dataset.soundcloudApi = "";
|
||||||
|
script.src = API_SRC;
|
||||||
|
script.async = true;
|
||||||
|
script.addEventListener("load", () => {
|
||||||
|
if (window.SC) resolve(window.SC);
|
||||||
|
else reject(new Error("SoundCloud widget API did not initialise"));
|
||||||
|
});
|
||||||
|
script.addEventListener("error", () =>
|
||||||
|
reject(new Error("SoundCloud widget API failed to load")),
|
||||||
|
);
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
return apiPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toTrack(sound: SoundCloudSound | null | undefined): Track {
|
||||||
|
if (!sound) return PLACEHOLDER;
|
||||||
|
return {
|
||||||
|
title: sound.title || PLACEHOLDER.title,
|
||||||
|
artist: sound.user?.username || "SoundCloud",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AeroMusic() {
|
||||||
|
const frameRef = useRef<HTMLIFrameElement>(null);
|
||||||
|
const widgetRef = useRef<SoundCloudWidget | null>(null);
|
||||||
|
const [track, setTrack] = useState<Track>(PLACEHOLDER);
|
||||||
|
const [playing, setPlaying] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
/* The index we deliberately jumped to, and when — see PICK_GUARD_MS. */
|
||||||
|
let chosen = -1;
|
||||||
|
let pickedAt = 0;
|
||||||
|
let syncTimer = 0;
|
||||||
|
|
||||||
|
loadWidgetApi()
|
||||||
|
.then((SC) => {
|
||||||
|
const frame = frameRef.current;
|
||||||
|
if (cancelled || !frame) return;
|
||||||
|
const widget = SC.Widget(frame);
|
||||||
|
widgetRef.current = widget;
|
||||||
|
|
||||||
|
/* The chip mirrors what the player is actually on, so a skip that gets
|
||||||
|
ignored can't leave it lying. Partial reads are ignored rather than
|
||||||
|
blanking the label. */
|
||||||
|
const syncTrack = () => {
|
||||||
|
widget.getCurrentSound((sound) => {
|
||||||
|
if (cancelled || !sound || !sound.title) return;
|
||||||
|
setTrack({ title: sound.title, artist: sound.user?.username || "SoundCloud" });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const playRandomTrack = () => {
|
||||||
|
widget.getSounds((sounds) => {
|
||||||
|
if (cancelled || !sounds || sounds.length === 0) return;
|
||||||
|
let index = Math.floor(Math.random() * sounds.length);
|
||||||
|
/* Don't just replay whatever is already on. */
|
||||||
|
if (sounds.length > 1 && index === chosen) index = (index + 1) % sounds.length;
|
||||||
|
chosen = index;
|
||||||
|
pickedAt = Date.now();
|
||||||
|
widget.skip(index);
|
||||||
|
widget.play();
|
||||||
|
/* Show it straight away, then settle on the truth once the player
|
||||||
|
has caught up. */
|
||||||
|
setTrack(toTrack(sounds[index]));
|
||||||
|
window.clearTimeout(syncTimer);
|
||||||
|
syncTimer = window.setTimeout(syncTrack, 900);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
widget.bind(SC.Widget.Events.READY, () => {
|
||||||
|
widget.setVolume(VOLUME);
|
||||||
|
playRandomTrack();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* The set plays on in its own order — grab every chance to re-randomise
|
||||||
|
instead of following the playlist track by track. */
|
||||||
|
widget.bind(SC.Widget.Events.PLAY, () => {
|
||||||
|
setPlaying(true);
|
||||||
|
syncTrack();
|
||||||
|
if (Date.now() - pickedAt < PICK_GUARD_MS) return;
|
||||||
|
widget.getCurrentSoundIndex((index) => {
|
||||||
|
if (cancelled || index === chosen) return;
|
||||||
|
playRandomTrack();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Fires at the end of the set (or of the single selected track). */
|
||||||
|
widget.bind(SC.Widget.Events.FINISH, playRandomTrack);
|
||||||
|
|
||||||
|
/* Cover the case where READY already fired before we bound it. */
|
||||||
|
widget.setVolume(VOLUME);
|
||||||
|
playRandomTrack();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* Offline, blocked or SoundCloud is down — the theme just stays silent. */
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
window.clearTimeout(syncTimer);
|
||||||
|
widgetRef.current?.pause();
|
||||||
|
widgetRef.current = null;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
const widget = widgetRef.current;
|
||||||
|
if (!widget) return;
|
||||||
|
if (playing) {
|
||||||
|
widget.pause();
|
||||||
|
setPlaying(false);
|
||||||
|
} else {
|
||||||
|
widget.play();
|
||||||
|
setPlaying(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<iframe
|
||||||
|
ref={frameRef}
|
||||||
|
className="web-aero-music-frame"
|
||||||
|
src={WIDGET_SRC}
|
||||||
|
title={`Frutiger Aero playlist on SoundCloud — ${track.title} by ${track.artist}`}
|
||||||
|
allow="autoplay"
|
||||||
|
loading="eager"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="web-aero-music"
|
||||||
|
onClick={toggle}
|
||||||
|
aria-pressed={playing}
|
||||||
|
title={playing ? "Pause the Frutiger Aero soundtrack" : "Play the Frutiger Aero soundtrack"}
|
||||||
|
>
|
||||||
|
<span className="web-aero-music-icon" aria-hidden="true">
|
||||||
|
{playing ? "❚❚" : "▶"}
|
||||||
|
</span>
|
||||||
|
<span className="web-aero-music-text">
|
||||||
|
<span className="web-aero-music-title">{track.title}</span>
|
||||||
|
<span className="web-aero-music-artist">{track.artist} · SoundCloud</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import ContributionHeatmap from "./ContributionHeatmap";
|
||||||
import FeatureWindow from "./FeatureWindow";
|
import FeatureWindow from "./FeatureWindow";
|
||||||
import { CodingProjectIndex, HardwareProjectIndex } from "./ProjectIndex";
|
import { CodingProjectIndex, HardwareProjectIndex } from "./ProjectIndex";
|
||||||
import Clock from "./Clock";
|
import Clock from "./Clock";
|
||||||
|
import AeroMusic from "./AeroMusic";
|
||||||
import { useTheme } from "./useTheme";
|
import { useTheme } from "./useTheme";
|
||||||
import { useColorScheme } from "./useColorScheme";
|
import { useColorScheme } from "./useColorScheme";
|
||||||
import { socials, friends } from "../data";
|
import { socials, friends } from "../data";
|
||||||
|
|
@ -421,12 +422,14 @@ export default function WebLayout() {
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<footer className="web-footer glass">
|
<footer className="web-footer glass">
|
||||||
<span className="web-footer-label">v2.2.21</span>
|
<span className="web-footer-label">v2.2.22</span>
|
||||||
© 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just
|
© 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just
|
||||||
follow the GPL3.0 License)
|
follow the GPL3.0 License)
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{aero && <AeroMusic />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2189,6 +2189,7 @@ html[data-theme="web"][data-aero="true"] .web-freezmo-ghost,
|
||||||
html[data-theme="web"][data-aero="true"] .web-freezmo-badge,
|
html[data-theme="web"][data-aero="true"] .web-freezmo-badge,
|
||||||
html[data-theme="web"][data-aero="true"] .web-switch,
|
html[data-theme="web"][data-aero="true"] .web-switch,
|
||||||
html[data-theme="web"][data-aero="true"] .web-footer-label,
|
html[data-theme="web"][data-aero="true"] .web-footer-label,
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music,
|
||||||
html[data-theme="web"][data-aero="true"] .fw,
|
html[data-theme="web"][data-aero="true"] .fw,
|
||||||
html[data-theme="web"][data-aero="true"] .fw-url,
|
html[data-theme="web"][data-aero="true"] .fw-url,
|
||||||
html[data-theme="web"][data-aero="true"] .fw-search,
|
html[data-theme="web"][data-aero="true"] .fw-search,
|
||||||
|
|
@ -2248,6 +2249,89 @@ html[data-theme="web"][data-aero="true"] .social-icon {
|
||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Frutiger Aero soundtrack ("Gelwave" by skeuoss) ----------
|
||||||
|
The SoundCloud iframe is park in the corner at 1px and fully transparent —
|
||||||
|
it is only there to play audio; the glass chip below is the real control. */
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music-frame {
|
||||||
|
position: fixed;
|
||||||
|
left: -8px;
|
||||||
|
bottom: -8px;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
border: 0;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music {
|
||||||
|
position: fixed;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
z-index: 40;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 11px;
|
||||||
|
padding: 8px 16px 8px 11px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
animation: aero-chip-in 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music-icon {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
flex: none;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 0.58rem;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
background: rgba(8, 28, 62, 0.3);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(190, 240, 255, 0.45),
|
||||||
|
inset 0 -2px 5px rgba(0, 140, 220, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 42vw;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music-title {
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music-artist {
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 0.64rem;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
opacity: 0.82;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes aero-chip-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(14px) scale(0.94);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
html[data-theme="web"][data-aero="true"] .heatmap-cell[data-level="1"],
|
html[data-theme="web"][data-aero="true"] .heatmap-cell[data-level="1"],
|
||||||
html[data-theme="web"][data-aero="true"] .heatmap-legend-cell[data-level="1"] {
|
html[data-theme="web"][data-aero="true"] .heatmap-legend-cell[data-level="1"] {
|
||||||
background-color: rgba(255, 255, 255, 0.26);
|
background-color: rgba(255, 255, 255, 0.26);
|
||||||
|
|
@ -2412,6 +2496,15 @@ html[data-theme="web"][data-aero="true"] .heatmap-legend-cell[data-level="4"] {
|
||||||
.web-side-foot {
|
.web-side-foot {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
/* Keep the Aero soundtrack chip out of the way on a phone — icon + title only. */
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music {
|
||||||
|
right: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
padding: 7px 13px 7px 9px;
|
||||||
|
}
|
||||||
|
html[data-theme="web"][data-aero="true"] .web-aero-music-artist {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 400px) {
|
@media (max-width: 400px) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue