Compare commits
3 commits
b08490e80e
...
3286721f08
| Author | SHA1 | Date | |
|---|---|---|---|
| 3286721f08 | |||
| 737437a593 | |||
| 4d5fba3977 |
8 changed files with 170 additions and 46 deletions
3
.env
Executable file
3
.env
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
DATABASE_URL="file:./db/custom.db"
|
||||||
|
OVE_DIRECTORY_URL="https://s3374dir.openvisengine.com"
|
||||||
|
OVE_DIRECTORY_API_KEY="+7MkRB+hkV0UBhPqM1haTQ0f8JGKqkuXwoBTdTSAouU="
|
||||||
3
.env.ove
Executable file
3
.env.ove
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
DATABASE_URL="file:./db/custom.db"
|
||||||
|
OVE_DIRECTORY_URL="https://s3374dir.openvisengine.com"
|
||||||
|
OVE_DIRECTORY_API_KEY="+7MkRB+hkV0UBhPqM1haTQ0f8JGKqkuXwoBTdTSAouU="
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
// stamp. (v7.10: the engine-health dot is gone -- compile errors already
|
// stamp. (v7.10: the engine-health dot is gone -- compile errors already
|
||||||
// toast, so the always-green "engine ok" badge was pure noise.)
|
// toast, so the always-green "engine ok" badge was pure noise.)
|
||||||
//
|
//
|
||||||
|
// v7.15.2: the right-hand group carries the project's Facebook page link
|
||||||
|
// (facebook.com/openvisengine) beside the version stamp -- icon-only, opens
|
||||||
|
// in a new tab; the project identity corner.
|
||||||
|
//
|
||||||
// v7.9 credit block: "© {year} · Site Created & Deployed by Sum'ting White" +
|
// v7.9 credit block: "© {year} · Site Created & Deployed by Sum'ting White" +
|
||||||
// a "View my portfolio at zcreations.dev" link, centered in the footer's
|
// a "View my portfolio at zcreations.dev" link, centered in the footer's
|
||||||
// blank area. Flex choreography: from xl the credit sits INLINE between the
|
// blank area. Flex choreography: from xl the credit sits INLINE between the
|
||||||
|
|
@ -14,6 +18,7 @@
|
||||||
|
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import { Facebook } from 'lucide-react'
|
||||||
import { useAvaStore } from '@/lib/store'
|
import { useAvaStore } from '@/lib/store'
|
||||||
import { APP_NAME, APP_VERSION } from '@/lib/version'
|
import { APP_NAME, APP_VERSION } from '@/lib/version'
|
||||||
|
|
||||||
|
|
@ -76,8 +81,19 @@ export function FooterBar() {
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* version stamp (v7.10: the engine-health dot beside it is gone) */}
|
{/* version stamp (v7.10: the engine-health dot beside it is gone) +
|
||||||
|
the project's Facebook page (v7.15.2) */}
|
||||||
<div className="ml-auto flex shrink-0 items-center gap-3 sm:gap-4 xl:ml-0">
|
<div className="ml-auto flex shrink-0 items-center gap-3 sm:gap-4 xl:ml-0">
|
||||||
|
<a
|
||||||
|
href="https://www.facebook.com/openvisengine/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
aria-label="OVE on Facebook (opens in a new tab)"
|
||||||
|
title="OVE on Facebook -- facebook.com/openvisengine"
|
||||||
|
className="inline-flex size-6 items-center justify-center rounded-xs text-zinc-500 transition-colors hover:bg-zinc-800 hover:text-emerald-300 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
|
||||||
|
>
|
||||||
|
<Facebook className="size-3.5" aria-hidden />
|
||||||
|
</a>
|
||||||
<span
|
<span
|
||||||
className="text-zinc-600"
|
className="text-zinc-600"
|
||||||
title={`${APP_NAME} -- version ${APP_VERSION}`}
|
title={`${APP_NAME} -- version ${APP_VERSION}`}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,18 @@
|
||||||
// OVE — sticky app header: logo, audio source controls (segmented), the
|
// OVE — sticky app header: logo, audio source controls (segmented), the
|
||||||
// demo-track chip, What's New!, theme tick switch, BPM readout and the beat
|
// demo-track chip, What's New!, BPM readout and the beat indicator.
|
||||||
// indicator. (v7.6.1: the Set Beat button moved out — tap-tempo lives in the
|
// (v7.6.1: the Set Beat button moved out — tap-tempo lives in the Beat tab,
|
||||||
// Beat tab, and Space works anywhere.)
|
// and Space works anywhere. v7.15.2: the light/dark tick switch moved out
|
||||||
|
// too — it lives in the Settings tab's Appearance section now.)
|
||||||
|
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useRef, useSyncExternalStore } from 'react'
|
import { useRef } from 'react'
|
||||||
import { AudioWaveform, Disc3, FileAudio, Link2, Mic, Moon, Music2, Square, Sun } from 'lucide-react'
|
import { AudioWaveform, Disc3, FileAudio, Link2, Mic, Music2, Square } from 'lucide-react'
|
||||||
import { useTheme } from 'next-themes'
|
|
||||||
import { Switch } from '@/components/ui/switch'
|
|
||||||
import { BeatDot } from '@/components/ava/beat-dot'
|
import { BeatDot } from '@/components/ava/beat-dot'
|
||||||
import { LinkDialog } from '@/components/ava/link-dialog'
|
import { LinkDialog } from '@/components/ava/link-dialog'
|
||||||
import { WhatsNew } from '@/components/ava/whats-new-dialog'
|
import { WhatsNew } from '@/components/ava/whats-new-dialog'
|
||||||
import { useAudioSource } from '@/components/ava/audio-source'
|
import { useAudioSource } from '@/components/ava/audio-source'
|
||||||
import { useAvaStore } from '@/lib/store'
|
import { useAvaStore } from '@/lib/store'
|
||||||
import { getStoredDarkBase } from '@/lib/theme'
|
|
||||||
import { APP_NAME, APP_SHORT } from '@/lib/version'
|
import { APP_NAME, APP_SHORT } from '@/lib/version'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
|
@ -142,35 +140,10 @@ function BpmReadout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Light-switch tick: left position (unchecked) keeps the dark theme, flipping
|
* v7.15.2: the ThemeToggle (Moon/Sun tick switch) moved to the Settings
|
||||||
* right (checked) switches the page to the white theme. The dark side is
|
* tab's Appearance section — see settings-tab.tsx. Same mechanics exactly
|
||||||
* whichever dark base the visitor picked on the Settings tab (royal purple
|
* (flipping to dark returns to the visitor's stored dark base).
|
||||||
* by default); both choices persist per visitor. The useSyncExternalStore
|
|
||||||
* mount guard returns false on the server / first client render and true
|
|
||||||
* after hydration, so the checkbox never causes a hydration mismatch.
|
|
||||||
*/
|
*/
|
||||||
const emptySubscribe = () => () => {}
|
|
||||||
function ThemeToggle() {
|
|
||||||
const { resolvedTheme, setTheme } = useTheme()
|
|
||||||
const mounted = useSyncExternalStore(emptySubscribe, () => true, () => false)
|
|
||||||
const isLight = mounted && resolvedTheme === 'light'
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="flex h-11 items-center gap-1.5 rounded-lg border border-zinc-800 bg-zinc-900/60 px-2.5"
|
|
||||||
title={isLight ? 'Theme: light -- flip left for dark' : 'Theme: dark -- flip right for light'}
|
|
||||||
>
|
|
||||||
<Moon className="size-3.5 shrink-0 text-zinc-500" aria-hidden />
|
|
||||||
<Switch
|
|
||||||
checked={isLight}
|
|
||||||
onCheckedChange={(v) => setTheme(v ? 'light' : getStoredDarkBase())}
|
|
||||||
aria-label={isLight ? 'Switch to dark theme' : 'Switch to light theme'}
|
|
||||||
className="data-[state=checked]:bg-emerald-400 data-[state=unchecked]:bg-zinc-600"
|
|
||||||
/>
|
|
||||||
<Sun className="size-3.5 shrink-0 text-zinc-500" aria-hidden />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AppHeader() {
|
export function AppHeader() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -194,10 +167,10 @@ export function AppHeader() {
|
||||||
<SourceControls />
|
<SourceControls />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* what's new + theme switch + BPM readout + beat indicator -- always visible */}
|
{/* what's new + BPM readout + beat indicator -- always visible
|
||||||
|
(v7.15.2: the theme tick switch moved to the Settings tab) */}
|
||||||
<div className="order-2 ml-auto flex items-center gap-2.5 md:order-3 sm:gap-3">
|
<div className="order-2 ml-auto flex items-center gap-2.5 md:order-3 sm:gap-3">
|
||||||
<WhatsNew />
|
<WhatsNew />
|
||||||
<ThemeToggle />
|
|
||||||
<BpmReadout />
|
<BpmReadout />
|
||||||
<BeatDot size={44} />
|
<BeatDot size={44} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// OVE — Settings tab (v6.5): appearance controls — base theme + highlight
|
// OVE — Settings tab (v6.5): appearance controls — light/dark flip (v7.15.2,
|
||||||
// color pickers. Everything is per-visitor (localStorage only, nothing on
|
// moved here from the header), base theme + highlight color pickers.
|
||||||
// the server) and applies instantly. The Native tab that previously lived
|
// Everything is per-visitor (localStorage only, nothing on the server) and
|
||||||
// in this slot is parked (see control-panel.tsx) and returns unchanged
|
// applies instantly. The Native tab that previously lived in this slot is
|
||||||
// when the project nears completion.
|
// parked (see control-panel.tsx) and returns unchanged when the project
|
||||||
|
// nears completion.
|
||||||
//
|
//
|
||||||
// v7.7: the Music Services log in / "log out of all" section is PARKED
|
// v7.7: the Music Services log in / "log out of all" section is PARKED
|
||||||
// (MUSIC_SERVICES_UI below). Stream-a-Link already plays for everyone
|
// (MUSIC_SERVICES_UI below). Stream-a-Link already plays for everyone
|
||||||
|
|
@ -14,10 +15,11 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useSyncExternalStore, useState, useEffect } from 'react'
|
import { useSyncExternalStore, useState, useEffect } from 'react'
|
||||||
import { Check, LogIn, LogOut, Music4, Palette, RotateCcw } from 'lucide-react'
|
import { Check, LogIn, LogOut, Moon, Music4, Palette, RotateCcw, Sun } from 'lucide-react'
|
||||||
import { useTheme } from 'next-themes'
|
import { useTheme } from 'next-themes'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { ServiceLoginDialog } from '@/components/ava/service-login-dialog'
|
import { ServiceLoginDialog } from '@/components/ava/service-login-dialog'
|
||||||
import { SERVICES, type ServiceId } from '@/lib/music-services'
|
import { SERVICES, type ServiceId } from '@/lib/music-services'
|
||||||
import {
|
import {
|
||||||
|
|
@ -31,6 +33,7 @@ import {
|
||||||
DEFAULT_BASE,
|
DEFAULT_BASE,
|
||||||
THEME_BASES,
|
THEME_BASES,
|
||||||
applyAccent,
|
applyAccent,
|
||||||
|
getStoredDarkBase,
|
||||||
isAccentId,
|
isAccentId,
|
||||||
storeDarkBase,
|
storeDarkBase,
|
||||||
type AccentId,
|
type AccentId,
|
||||||
|
|
@ -89,6 +92,14 @@ export function SettingsTab() {
|
||||||
if (id === 'outline' && accent !== 'blue') applyAccent('blue')
|
if (id === 'outline' && accent !== 'blue') applyAccent('blue')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** v7.15.2 — the header tick switch's flip, verbatim: checking = the white
|
||||||
|
* theme; unchecking returns to the visitor's stored dark base (kept fresh
|
||||||
|
* by pickBase below). The mount guard lives in `isLight`, so the SSR
|
||||||
|
* render shows the unchecked position without a hydration mismatch. */
|
||||||
|
function flipLight(v: boolean) {
|
||||||
|
setTheme(v ? 'light' : getStoredDarkBase())
|
||||||
|
}
|
||||||
|
|
||||||
function pickAccent(id: AccentId) {
|
function pickAccent(id: AccentId) {
|
||||||
applyAccent(id)
|
applyAccent(id)
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +168,38 @@ export function SettingsTab() {
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
|
{/* v7.15.2 — the light/dark tick switch, moved from the header. First
|
||||||
|
control in Appearance: the quick flip. The base cards below remain
|
||||||
|
the full palette choice; this row only toggles between the white
|
||||||
|
theme and whichever dark base the visitor picked (default outline). */}
|
||||||
|
<section aria-label="Light and dark mode">
|
||||||
|
<h4 className="text-xs font-semibold uppercase tracking-[0.14em] text-zinc-500">Light / dark mode</h4>
|
||||||
|
<div className="mt-3 flex items-center justify-between gap-4 rounded-xl border border-zinc-800 bg-zinc-900/40 px-4 py-3">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="text-sm font-semibold">
|
||||||
|
{mounted ? (isLight ? 'Light theme' : 'Dark theme') : 'Theme'}
|
||||||
|
</p>
|
||||||
|
<p className="mt-0.5 text-[11px] leading-snug text-zinc-500">
|
||||||
|
Quick flip between the white theme and the dark side — flipping back to dark returns
|
||||||
|
to the base picked below (or the last one used).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="flex shrink-0 items-center gap-2"
|
||||||
|
title={isLight ? 'Theme: light -- flip left for dark' : 'Theme: dark -- flip right for light'}
|
||||||
|
>
|
||||||
|
<Moon className="size-4 shrink-0 text-zinc-500" aria-hidden />
|
||||||
|
<Switch
|
||||||
|
checked={isLight}
|
||||||
|
onCheckedChange={flipLight}
|
||||||
|
aria-label={isLight ? 'Switch to dark theme' : 'Switch to light theme'}
|
||||||
|
className="data-[state=checked]:bg-emerald-400 data-[state=unchecked]:bg-zinc-600"
|
||||||
|
/>
|
||||||
|
<Sun className="size-4 shrink-0 text-zinc-500" aria-hidden />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section aria-label="Base theme">
|
<section aria-label="Base theme">
|
||||||
<h4 className="text-xs font-semibold uppercase tracking-[0.14em] text-zinc-500">Base theme</h4>
|
<h4 className="text-xs font-semibold uppercase tracking-[0.14em] text-zinc-500">Base theme</h4>
|
||||||
<div className="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2">
|
<div className="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,20 @@ export interface ReleaseNote {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RELEASE_NOTES: ReleaseNote[] = [
|
export const RELEASE_NOTES: ReleaseNote[] = [
|
||||||
|
{
|
||||||
|
version: '7.15.2',
|
||||||
|
name: 'Facebook page + theme switch in Settings',
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
kind: 'added',
|
||||||
|
text: "OVE has a Facebook page now -- the footer carries the link (the Facebook icon beside the version stamp), so that is where news and showcase clips will land.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'improved',
|
||||||
|
text: 'The light/dark tick switch moved from the header to the Settings tab, as the first row of the Appearance section -- every look-and-feel control now lives in one place. It flips exactly as before: back to dark it returns to the base picked below it, and both choices persist.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
version: '7.15.1',
|
version: '7.15.1',
|
||||||
name: 'Silence holds the beat grid',
|
name: 'Silence holds the beat grid',
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,22 @@
|
||||||
// warns in the console when the two drift apart.
|
// warns in the console when the two drift apart.
|
||||||
//
|
//
|
||||||
// History:
|
// History:
|
||||||
|
// 7.15.2 — Facebook page + theme switch relocation (user request): the
|
||||||
|
// project now has its own Facebook page, and the footer's
|
||||||
|
// right-hand group carries the link (facebook.com/openvisengine)
|
||||||
|
// beside the version stamp -- an icon-only button (lucide
|
||||||
|
// Facebook glyph) that opens in a new tab, same hover/focus
|
||||||
|
// treatment as the portfolio link. The light/dark tick switch
|
||||||
|
// (v6.3) moved out of the header into the Settings tab's
|
||||||
|
// Appearance section, as a "Light / dark mode" row ABOVE the
|
||||||
|
// base-theme cards -- the header is one control lighter and all
|
||||||
|
// appearance controls now live in one place. Mechanics are
|
||||||
|
// identical: checking = the white theme, unchecking returns to
|
||||||
|
// the visitor's stored dark base (kept fresh by the base cards),
|
||||||
|
// both choices persist per visitor, and the mount guard keeps
|
||||||
|
// the SSR render mismatch-free. The row states the current side
|
||||||
|
// ("Light theme" / "Dark theme") and explains the return-to-base
|
||||||
|
// behavior in its caption.
|
||||||
// 7.15.1 — the silence hold (user request): "if silence is detected,
|
// 7.15.1 — the silence hold (user request): "if silence is detected,
|
||||||
// it needs to place an immediate override and hold on the beat
|
// it needs to place an immediate override and hold on the beat
|
||||||
// detection / prediction ... if silence is no longer detected,
|
// detection / prediction ... if silence is no longer detected,
|
||||||
|
|
@ -532,4 +548,4 @@
|
||||||
|
|
||||||
export const APP_NAME = 'Open Visualization Engine'
|
export const APP_NAME = 'Open Visualization Engine'
|
||||||
export const APP_SHORT = 'OVE'
|
export const APP_SHORT = 'OVE'
|
||||||
export const APP_VERSION = '7.15.1'
|
export const APP_VERSION = '7.15.2'
|
||||||
|
|
|
||||||
56
worklog.md
56
worklog.md
|
|
@ -2500,3 +2500,59 @@ BROWSER E2E (agent-browser, ALL PASS; probe track = generated 13 s WAV, 8 s of 1
|
||||||
|
|
||||||
Stage Summary:
|
Stage Summary:
|
||||||
- v7.15.1 shipped: silence places an immediate HOLD on beat detection + prediction (level < 0.006 for 300 ms; sticky until 0.015) -- nothing fires, learning/confidence/beatphase freeze; decisive audio releases instantly and the grid resumes from the phase the audio went quiet (pause/resume keeps perfect time); taps override; waveform/band data never gated; scripts get silent; the indicator shows HOLD. Headless 26/26 + browser E2E green; pushed to zai-dev.
|
- v7.15.1 shipped: silence places an immediate HOLD on beat detection + prediction (level < 0.006 for 300 ms; sticky until 0.015) -- nothing fires, learning/confidence/beatphase freeze; decisive audio releases instantly and the grid resumes from the phase the audio went quiet (pause/resume keeps perfect time); taps override; waveform/band data never gated; scripts get silent; the indicator shows HOLD. Headless 26/26 + browser E2E green; pushed to zai-dev.
|
||||||
|
|
||||||
|
---
|
||||||
|
Task ID: 45
|
||||||
|
Agent: main (Z.ai Code)
|
||||||
|
Task: v7.15.2 (user request): "So I created a facebook page just for this project that I would like to add to the footer, it is 'https://www.facebook.com/openvisengine/'. Also the light/dark theme switch needs to be moved to the 'Settings' tab." Delivered on the zai-dev staging branch (remote: gitzcreations/openvisengine).
|
||||||
|
|
||||||
|
Approach:
|
||||||
|
- The Facebook link belongs with the PROJECT identity, not the maker credit block (that corner is the site creator's) -- so it went into the footer's right-hand group beside the version stamp: icon-only (lucide Facebook glyph, size-3.5 in a size-6 hit area), target=_blank + rel=noopener noreferrer, aria-label + title for a11y, same hover/focus treatment as the portfolio link (zinc-500 -> emerald-300, focus-visible ring).
|
||||||
|
- The theme tick switch (v6.3 header resident) is a stateless control whose logic (flip to light / flip back to the STORED dark base) lives entirely in setTheme + getStoredDarkBase() -- so moving it is pure relocation. New home: the Settings tab's Appearance section, as a "Light / dark mode" row ABOVE the base-theme cards (the quick flip first, the full palette choice below), in a bordered card that states the current side ("Light theme" / "Dark theme") and explains the return-to-base behavior in its caption. Mechanics are byte-identical: checked = 'light', unchecked = getStoredDarkBase(); the existing mounted/isLight guard keeps the SSR render mismatch-free.
|
||||||
|
- pickBase() already refreshes the stored dark base on every dark card pick, so the switch's return target always tracks the visitor's latest dark choice -- verified with a purple -> light -> dark round trip.
|
||||||
|
|
||||||
|
Work Log:
|
||||||
|
- footer-bar.tsx: Facebook icon link in the right group (v7.15.2 comment block; Facebook import from lucide-react).
|
||||||
|
- header.tsx: ThemeToggle component + its now-unused imports removed (useSyncExternalStore, Moon/Sun, useTheme, Switch, getStoredDarkBase); file header + right-group comments updated (v7.15.2 note pointing at settings-tab.tsx); the header right group is now What's New! + BPM readout + beat indicator.
|
||||||
|
- settings-tab.tsx: "Light / dark mode" section (first in Appearance) with the Moon/Sun tick switch; flipLight() = the header toggle's onCheckedChange verbatim; Moon, Sun, Switch, getStoredDarkBase imports; file header comment updated.
|
||||||
|
- version.ts: APP_VERSION 7.15.2 + history entry. release-notes.ts: 7.15.2 "Facebook page + theme switch in Settings" (1 added + 1 improved, first-person voice, no em dashes).
|
||||||
|
|
||||||
|
BROWSER E2E (agent-browser, ALL PASS):
|
||||||
|
- Fresh load: title "OVE -- Open Visualization Engine (v7.15.2)"; footer shows "version 7.15.2".
|
||||||
|
- Footer link: present in <footer>, href https://www.facebook.com/openvisengine/, target=_blank, rel=noopener noreferrer, aria-label "OVE on Facebook (opens in a new tab)", title set, lucide-facebook icon rendered, in the same group as the version stamp; visible at 1280 and 375 widths.
|
||||||
|
- Header: no [role=switch], zero lucide-moon/lucide-sun icons (the toggle is gone); header right group = What's New! + BPM + beat dot.
|
||||||
|
- Settings tab: section[aria-label="Light and dark mode"] first in Appearance, heading "Light / dark mode", status "Dark theme", caption present, switch unchecked with aria-label "Switch to light theme", Moon+Sun icons, Base theme section follows.
|
||||||
|
- Flip test: click -> html.light + "Light theme" + aria "Switch to dark theme"; click again -> html.outline (the stored dark base restored, not just any dark) + "Dark theme".
|
||||||
|
- Stored-base tracking: pick Royal Purple card -> html.purple; flip light -> light; flip back -> html.purple (the round trip returns to the NEWLY stored base, proving pickBase/storeDarkBase feeds the switch). Default look (outline + blue accent) restored afterwards.
|
||||||
|
- What's New: dialog opens with 7.15.2 first ("Facebook page + theme switch in Settings", both items present).
|
||||||
|
- Responsive: 1280x800 footer sticks to the viewport bottom (docH 800 = innerH 800); 375x812 no horizontal overflow (scrollW 375 = clientW), footer flush at the document bottom (footerBottomDoc 1301.26 ~= docH 1301 -- pushed naturally, header sticky).
|
||||||
|
- Console: 0 errors / 0 warnings. Test browser closed.
|
||||||
|
|
||||||
|
Stage Summary:
|
||||||
|
- v7.15.2 shipped: the footer's right-hand group carries the project's Facebook page link (facebook.com/openvisengine, icon-only, new tab) beside the version stamp; the light/dark tick switch moved from the header to the Settings tab's Appearance section (first row, above the base-theme cards) with identical mechanics -- checked = white theme, unchecked = the visitor's stored dark base, both persist. Browser E2E green across link, relocation, flip behavior, stored-base tracking, What's New, responsiveness and the sticky footer.
|
||||||
|
|
||||||
|
---
|
||||||
|
Task ID: 46
|
||||||
|
Agent: main (Z.ai Code)
|
||||||
|
Task: v7.15.2 ops restore (user report): "Why is the Directory Tab showing 'OVE-Directory is not configured on this server.' We have set this up in a previous version, did it get corrupted or lost?" + credentials cards supplied (Google Drive: API Reference + Connection Card).
|
||||||
|
|
||||||
|
Diagnosis (nothing was corrupted in the repo -- the SANDBOX was reprovisioned):
|
||||||
|
- The directory config lives in .env (OVE_DIRECTORY_URL + OVE_DIRECTORY_API_KEY), which has been deliberately UNTRACKED (gitignored) since the GitLab sync. The sandbox disk was reset when this session booted (05:11): the bootstrap rewrote .env to just DATABASE_URL, and the previous untracked .env with the key was lost with the old disk. Same wipe took the untracked db/ (custom.db) and secrets/ (house-account music-service cookies).
|
||||||
|
- The app kept working after the wipe because the running next-server held OPEN file handles to the deleted db inode -- every API call queried a file that no longer existed on disk. Any restart would have broken the database too.
|
||||||
|
- The directory VPS itself was never down: upstream GET /status answered 200 (service ok, uptime ~25 h, 2 scripts stored). Only the local proxy config was missing.
|
||||||
|
|
||||||
|
Recovery:
|
||||||
|
- .env restored from the credentials cards: OVE_DIRECTORY_URL=https://s3374dir.openvisengine.com + OVE_DIRECTORY_API_KEY (from the card; value intentionally not recorded here -- .env stays untracked/gitignored, never in the repo).
|
||||||
|
- Database RECOVERED from the deleted inode: cat /proc/<pid>/fd/<n> > db/custom.db off the running next-server's handles (single-file journal mode, no WAL sidecars) -- 307,200 bytes, SQLite header valid, PRAGMA integrity_check ok, contents intact: 106 presets, 8 folders, 1 settings row. The recovered snapshot now lives on disk, so restarts are safe.
|
||||||
|
- secrets/ house-account cookies: NOT recoverable (no open handles; they were files read per-request). Graceful degradation as designed -- the house cookie is only a FALLBACK for stream-a-link; everything else works. Needs the operator's cookie files restored by hand if the house account is wanted again.
|
||||||
|
- Dev server restarted via the platform's .zscripts/dev.sh (setsid-detached so the tree survives command boundaries): first manual attempt died with a corrupted Turbopack cache (the platform's git checkouts poisoned .next task data -- cleared with rm -rf .next) and a second attempt died with the tool session's teardown (pipe children not session-detached). Clean dev.sh start: health check passed. dev.pid refreshed.
|
||||||
|
|
||||||
|
BROWSER E2E (agent-browser, ALL PASS):
|
||||||
|
- GET /api/presets: full 106-preset library from the RECOVERED on-disk db (fresh process, not the deleted inode).
|
||||||
|
- GET /api/ove-directory/status: upstream envelope verbatim (service ok, 2 scripts, uptime ~25 h) -- NOT_CONFIGURED is gone.
|
||||||
|
- GET /api/ove-directory/scripts: authenticated proxy returns both community scripts ("Spectrum Bars" avg 3.3 / 4 votes, "Thick Spectrum Bars" avg 5.0 / 2 votes).
|
||||||
|
- Directory tab in the browser: active, no configuration error, both script names visible.
|
||||||
|
- Console: 0 errors / 0 warnings; canvas renders; footer version 7.15.2. Test browser closed.
|
||||||
|
|
||||||
|
Stage Summary:
|
||||||
|
- Directory tab restored: the sandbox reprovision (not corruption) had wiped the untracked .env holding OVE_DIRECTORY_URL/OVE_DIRECTORY_API_KEY; credentials re-entered from the project's cards, the deleted custom.db was recovered live from the running process's file handles before restart (106 presets/folders/settings intact), Turbopack cache cleared after the platform's git-checkout poisoning, dev server restarted via dev.sh. House-account cookies (secrets/) remain lost by design's graceful fallback -- flagged to the operator for manual restore if wanted.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue