Repository navigation
Conversation
DarhkVoyd
force-pushed
the
claude/exciting-keller-a18866
branch
from
May 14, 2026 21:37
f2054cb to
dfa60f6
Compare
DarhkVoyd
force-pushed
the
claude/exciting-keller-a18866
branch
from
May 17, 2026 21:32
d039cdb to
b3e383c
Compare
DarhkVoyd
force-pushed
the
claude/exciting-keller-a18866
branch
from
June 21, 2026 11:48
0074568 to
3aa7a07
Compare
clean up wait wait feat: Segments — multi-segment playback sequencer (#102) Replaces the single loop-segment system with a fully extensible Segments engine. Users can define multiple time-range markers on any video and configure exactly how they play back. Core capabilities - Multiple segments per video, each shown as a colored pentagon marker pair on the YouTube progress bar - Per-segment loop count (0 = infinite) and playback rate override - Per-iteration rate overrides: [1.0, 1.5] plays 1× first, 1.5× second - Advanced Sequence editor: compose steps with any custom segment order; duplicates allowed — [seg3, seg3, seg1] plays seg3 twice then seg1 - Segment merge: hold adjacent markers within 1.5% for 500 ms → merge with pulse animation Save tiers - Last-used: auto-updated volatile slot, never requires explicit save - Default slot: one-click quick save - Named configs: unlimited, each with an optional keyboard shortcut for instant activation (e.g. Ctrl+1) Keyboard shortcuts - Z — load last-used config / toggle off (replaces toggleLoopSegment) - Shift+Z — always start a fresh slate (full video, infinite loop) - Q / E — set start / end of the active segment (one containing playhead) - Shift+Q / Shift+E — nudge with acceleration (unchanged) - Named config combos — user-assignable per saved config New modules (SOLID boundaries) - utils/segments/types.ts — data types only - utils/segments/factories.ts — pure config factory functions - utils/segments/engine.ts — playback state machine, no DOM - utils/segments/markers.tsx — DOM marker controller, N colored pairs - utils/storage/segmentStore.ts — IndexedDB CRUD + transparent migration - components/SegmentButton.tsx — control bar button - components/SegmentPanel.tsx — below-video panel (simple + advanced) - components/Segments.tsx — coordinator; public API for watch.tsx Migration Existing SavedLoopSegment records are transparently converted to a single-segment SegmentConfig on first access. IndexedDB bumped to v4 with a new `segment_data` object store. Backward compat All existing preference keys (toggleLoopSegment, setLoopSegmentBegin, setLoopSegmentEnd, nudgeLoopSegment.*) are preserved unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> segment fixes marker update changes updates
DarhkVoyd
force-pushed
the
claude/exciting-keller-a18866
branch
from
June 21, 2026 11:49
3aa7a07 to
1774554
Compare
remove
DarhkVoyd
force-pushed
the
claude/exciting-keller-a18866
branch
from
June 21, 2026 11:50
1774554 to
9e63c09
Compare
Colocate docs UI under app/docs, consolidate site config into constants/site.ts, flatten route data files, and remove the old website/lib layer. update
DarhkVoyd
force-pushed
the
claude/exciting-keller-a18866
branch
from
June 23, 2026 06:15
cea95b3 to
111c312
Compare
…a files - Move hooks/ into core/ (useSyncStore, useLocalStorage, useCapabilityCache, useScope, useTheme) - Inline useVideoPreference into AudioMode.tsx (single caller, 3 lines) - Merge data/contract.ts + data/extension.ts into data/core.ts - Move data/extension.ts constants into core/ then into data/core.ts - Delete data/player.ts; export PlayerLayout/PlayerVisuals from data/profiles.ts - Fix stale ./contract import in data/store.ts - Rename useChromeStorageLocal → useLocalStorage, useStoreUpdater → useSyncStore
…E_IDB, consistent hooks - Centralise all chrome.storage.local keys into CHROME_STORAGE_LOCAL in data/core.ts - Add BROWSER_STORAGE_IDB to data/core.ts; drop scattered store name exports from indexedDb.ts - Rename useChromeLocal → useChromeStorageLocal, useSyncStore → useChromeStorageSync - Dissolve context/ directory; move StoreContext into core/storeContext.tsx - Fix popup/App.tsx to use getAllProfiles/getActiveProfile from core/profileStore instead of raw local reads - Replace all raw "toppings:*" string literals with CHROME_STORAGE_LOCAL constants
- CHROME_STORAGE_LOCAL → CHROME_STORAGE_LOCAL_KEY - BROWSER_STORAGE_IDB → BROWSER_STORAGE_IDB_STORE
The guard checked three conditions, two of which were unreachable: the recursion only descended into non-null plain objects, so `defaults` could never be nullish or a primitive. Recursing unconditionally lets the guard handle primitives and arrays itself, removing the duplicated type checks in the loop. Also iterate `defaults` keys instead of `stored` keys. Previously any key in storage passed through untouched, and `syncStorageWithDefaults` writes the merge back, so settings removed from DEFAULT_STORE would live in chrome.storage.sync forever. Safe here: DEFAULT_STORE is a static shape and every storage.sync.set in the repo writes that shape. Extracts isPlainObject as an exported guard and adds bun tests covering fallback, array replacement, unknown-key dropping, and type mismatches.
Root AGENTS.md now holds only what applies repo-wide and delegates project-specific rules to AGENTS/<scope>.md, so an agent reads the hub plus the one scope it touches. Previously the root carried a three-line repo layout and two sections that both said "read this file". The comment policy is the substance of this change and lives in the root so every agent reads it: code explains what and how through names and execution order, so a comment only earns its place when it answers why — a decision, constraint, or external cause the code cannot state. Applies on-touch rather than as a sweep mandate. AGENTS/web-ext.md is the first scoped file: commands, the four webpack entries, the React vs dom-chef split, the three storage layers and how mergeDefaults treats DEFAULT_STORE as schema, the MV3-to-MV2 manifest transform, and the background-dispatched navigation model.
…tions
Strips comments that restate the code — section banners, JSDoc repeating the
signature, and narration of the next line — from the twelve densest files.
What survives is the why: YouTube DOM constraints, ordering requirements,
magic-value encodings, and the reasons behind deliberate deviations.
Three comments were deleted as actively wrong rather than merely redundant:
- Segments.tsx claimed markers must not be rebuilt in the panel change
handler while the call passes skipMarkerRebuild = false, which rebuilds them.
- engine.ts documented getSegmentAt tie-breaking as nearest end time; the
code compares distance to the segment midpoint.
- markers.tsx carried two geometry blocks that contradicted each other, one
of which also contradicted the transform it sat above.
Comments dropped from key bindings ("// Z — ...") because those bindings are
user-configurable and the labels are already wrong for anyone who rebound them.
No behaviour change: bun run build passes at every step.
Second and final pass, covering profiles, audio mode, primitives, options routes, form components and the small utils. Same rule as the first pass: a comment stays only when it answers why — an external constraint, an ordering requirement, a magic-value encoding, or the reason a deliberate deviation exists. Notable keeps, rewritten rather than removed: the CSS.escape hazard in the audio-mode custom background, the createMediaElementSource routing constraint in the visualizer, the forced reflow in pageToast, the stuck tooltip on DOM-mutating clicks, and the search index's coupling to rendered UI text. Also drops a comment in watch.tsx claiming getAllProfiles returns only custom profiles. It returns presets plus custom, which means cycleProfilesShortcut prepends BUILT_IN_PRESETS to a list that already contains them and every preset appears twice in the cycle. The bug is left in place to be fixed on its own; only the comment hiding it is gone. web-ext comment density is now 330 of 13,256 lines (2.5%), down from 1,306 of 14,314 (9.1%). SegmentPanel.tsx, background/context.ts and utils/indexedDb.ts are untouched — they carry uncommitted work. bun run build passes; bun test utils/object.test.ts passes.
It belongs in the changelog, not in the pitch.
"Sponsor" reads corporate. The label is now a small on-brand gesture, like "buy me a coffee"; the key is renamed to match.
The Worker served two routes through eleven files of layers, barrels and constants, plus itty-router. It is now src/index.ts (routing, CORS, error JSON), src/playlist.ts (the feature) and src/http.ts (shared response plumbing), with no runtime dependencies. The contract with the extension is unchanged. Move to Bun and wrangler.jsonc, trim the config to what is used, add a deploy workflow mirroring the website's, and add backend/AGENTS.md with the contract and the types-regeneration gotcha.
Comment on lines
+16
to
+29
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: oven-sh/setup-bun@v2 | ||
| - run: bun install --frozen-lockfile | ||
| working-directory: backend | ||
| - run: bun run check | ||
| working-directory: backend | ||
| - uses: cloudflare/wrangler-action@v3 | ||
| with: | ||
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
| workingDirectory: backend | ||
| command: deploy |
The exported function now states the algorithm: page through the playlist, fetch each batch's durations, total them. Paging is an async generator, the YouTube call builds its URL with URLSearchParams, and the duration parser and sum are their own functions.
getPlaylistRuntime, videoIdsByPage, fetchDurationSeconds, fetchYouTube, toSeconds. The durations fetch returns a total, so the sum helper goes, and the ISO parser sits under its only caller.
…ror text Without .dev.vars the key was sent as "undefined" and the only clue was YouTube's bare 400. Now a missing key is a 500 naming the variable, and YouTube failures carry its own message.
wrangler dev regenerates worker-configuration.d.ts and drops secrets it cannot see, which broke the typecheck. The secret is now declared by hand and merges into Env whatever Wrangler generates.
youtubeErrorMessage parses Google's error envelope, so it stays beside fetchYouTube rather than in http.ts, which is about our own responses. The route regex is PLAYLIST_PATH, and the id is no longer decoded: playlist ids are [A-Za-z0-9_-].
It reads a message out of any JSON error body and falls back to the status text; nothing in it is YouTube-specific.
The live Worker currently catches the whole hostname, which is why the Pages site cannot be served there. Pinning the route in wrangler.jsonc leaves everything outside /api to Pages.
tsconfig listed mocha and chai types that were never installed, so tsc stopped before reporting anything. With that fixed it found 82 errors. 60 of them came from content scripts writing dom-chef JSX under React's JSX types: elements typed as ReactNode, handlers as SyntheticEvent. A local jsx-runtime (app/content_scripts/jsx) now types those files as HTMLElement with native events; each opts in with a jsxImportSource pragma, which also replaces the import React from "dom-chef" lines. Babel moves to the automatic runtime for the same reason. The rest were stale guards for store keys that are always present, readonly nav items, an untyped modifier list, and two dead functions.
Deploying toppings-website with
|
| Latest commit: |
b307bab
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://4e98bfa7.toppings-website-bq3.pages.dev |
| Branch Preview URL: | https://claude-exciting-keller-a1886.toppings-website-bq3.pages.dev |
scripts/build.ts bundles the four entries with Bun.build (IIFE, no code splitting), runs Tailwind through its PostCSS API from a real tailwind.config.ts, copies the static files and applies the same manifest transform for Firefox. CSS no longer goes through JS: the content stylesheet is listed in the manifest and the popup and options pages link theirs. Dev mode watches the source directories. scripts/release.ts zips dist with the system zip. The four dynamic imports become static, since nothing is code-split. Fifteen build dependencies go; 112 packages install instead of several hundred, and a production build takes about 300ms.
Profiles replaced Audio Mode, but the replacement was half-built: the Audio preset set watch.layout to no-video and applyWatchProfile ignored it, while 1,100 lines of the old feature stayed in the tree with no caller since the v3.0.3 restructure. A watch.layout primitive now collapses the player container for no-video and keeps playback running; the preset works. The old feature goes: the overlay UI, visualizer, custom image, per-video pins, the B key, the popup toggle, its options page, its CSS and the audioMode store subtree. watch.visuals and the unimplemented theatre layout go with it, from the types, the Profiles editor and profile import.
…l; drop aliases Bugs: the Shorts buttons had ids starting with '#', so the guard that stops double-injection never matched; the auto-scroll toggle changed memory only and never reached storage; the playback-rate menu items were created with a tw- prefixed marker class but queried without it; the double-tap circle reset its style to the string "null". The typing-target check that watch and shorts each carried is one function. Dead exports go (getActivePrimitives, createEmptyConfig, isSegmentsActive, getActiveConfig), and the toolbar icon uses whichever of chrome.action / browserAction exists instead of sniffing the UA. Constants that only renamed literals go with their files: HTTP_*, ERROR, NODE_ENV, EXTENSION_INSTALL_REASON, EXTENSION_MESSAGE_BODY, KEYBOARD_KEY, NUMBER, and the template-interpolating API client, which is now one fetchPlaylist function.
…mponents/ app/ keeps only the four surfaces. Content-script logic moves into features/ (playback, playlist, profiles, segments, shorts) next to the data and DOM strategies each feature owns. Shared plumbing is lib/ and the React components are ui/. data/core.ts splits into lib/youtube.ts, lib/protocol.ts and lib/storageKeys.ts; the two store files and the two version files merge. The four primitive barrels go; callers import the primitive they use. Cross-folder imports use the @/ root alias. No storage key, message type, primitive id or manifest field changes.
| import { useToast } from "@/ui/feedback/ToastProvider"; | ||
| import { useCapabilityCache } from "@/features/profiles/useCapabilityCache"; | ||
| import { setExtensionIcon } from "@/lib/browser"; | ||
| import { ThemePreference } from "@/ui/useTheme"; |
| @@ -0,0 +1,266 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import { Link } from "react-router-dom"; | |||
Comment on lines
+21
to
+26
| import { | ||
| BUILT_IN_PRESETS, | ||
| type Profile, | ||
| type ProfilePrimitiveConfig, | ||
| type ThumbnailMode, | ||
| } from "@/features/profiles/profiles"; |
| getActiveProfile, | ||
| setActiveProfileId, | ||
| } from "./profileStore"; | ||
| import { setSidebarVisible, resetSidebar } from "./primitives/watch/sidebar"; |
| setActiveProfileId, | ||
| } from "./profileStore"; | ||
| import { setSidebarVisible, resetSidebar } from "./primitives/watch/sidebar"; | ||
| import { setCommentsVisible, resetComments } from "./primitives/watch/comments"; |
| } from "./profileStore"; | ||
| import { setSidebarVisible, resetSidebar } from "./primitives/watch/sidebar"; | ||
| import { setCommentsVisible, resetComments } from "./primitives/watch/comments"; | ||
| import { setEndCardsVisible, resetEndCards } from "./primitives/watch/endCards"; |
| import { withStore } from "@/lib/indexedDb"; | ||
| import { BROWSER_STORAGE_IDB_STORE } from "@/lib/storageKeys"; | ||
| import type { VideoSegmentData, SegmentConfig, SegmentAutoLoadPin } from "./types"; | ||
| import { createFreshConfig } from "./factories"; |
One 1,696-line file becomes a public module (panel.tsx) and one file per concern: state, menu wiring, styles, formatting, mutations, header, config switcher, pin button, save menu, segment list, sequence editor, config manager. Sections re-render through state.render so none of them imports the module that composes them. The trigger-and-dropdown wiring that three menus copied is attachMenu; the panel's private toast is the shared page toast; mutations no longer take a config argument they ignored.
The extension replaced the standalone feature and its B key with Profiles. The feature list, description, greetings page, install page, FAQ and shortcut table follow; the 3.0.3 changelog stays as history and the next release notes the change.
The previous commit left the question without its comma and the old answer in place; the site did not build.
Member
Author
|
Superseded by #110. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Superseded by #109, which carries the same extension work as one commit on top of
main. This branch's history includes the website and backend commits that already landed via #107, which is why it conflicts. Safe to close once #109 is merged.