chore: Phase 1 hygiene — drop unused imports/exports/types (behavior-neutral)

Machine-baseline harvest (ruff + knip), all tsc/parse-green, no runtime change:
- backend: remove 3 unused imports (channels/playlists/youtube) via ruff; drop the
  unused `job` binding in unshare_download (keep the _own_job ownership guard call).
- frontend: remove `export` from 23 internally-used-only symbols (knip "unused
  exports") to shrink the public surface; delete 2 genuinely-dead declarations
  (PlexBrowseResult — leftover from the removed /browse route; WIDGET_TITLES —
  hardcoded English titles superseded by i18n).

Held back for a decision (unused here = possibly-unwired, NOT dead — flagged, not
removed): e2ee.lock()/clearDevice() (security primitives never wired to logout / a
"forget device" feature) and loadDefaultViewFilters (App reimplements it inline — a
DRY issue). See siftlode-ops/CODE-HYGIENE.md.
This commit is contained in:
npeter83 2026-07-11 04:47:08 +02:00
parent bf8d4b94a0
commit c2a2c98f16
19 changed files with 26 additions and 42 deletions

View file

@ -8,7 +8,7 @@ import { LS, setAccountRaw } from "./storage";
// WITHOUT statically importing the — now lazy-loaded — admin page, which would otherwise pull
// it back into the main bundle and defeat the code-split.
export const ADMIN_USERS_TAB_KEY = LS.adminUsersTab;
export const ADMIN_USERS_ACCESS_TAB = "access";
const ADMIN_USERS_ACCESS_TAB = "access";
export function focusAccessRequestsTab(): void {
setAccountRaw(ADMIN_USERS_TAB_KEY, ADMIN_USERS_ACCESS_TAB);

View file

@ -78,7 +78,7 @@ export interface VideoDetail {
duration_seconds: number | null;
}
export interface ChannelLink {
interface ChannelLink {
title: string | null;
url: string;
}
@ -611,7 +611,7 @@ export interface SystemConfigData {
}
// Admin: result of testing the Plex server connection (also drives the library-picker).
export interface PlexSection {
interface PlexSection {
key: string;
title: string;
type: string; // "movie" | "show"
@ -656,13 +656,6 @@ export interface PlexCard {
show_id?: string | null; // episode: the show's rating_key (for grouping a playlist by show/season)
summary?: string | null; // episode
}
export interface PlexBrowseResult {
kind: "movie" | "show";
total: number;
offset: number;
limit: number;
items: PlexCard[];
}
// Unified cross-library browse (movies + shows mixed). `episodes` is populated only on a search that
// also matches episodes (the grouped "Episodes" section).
export interface PlexUnifiedResult {
@ -823,7 +816,7 @@ export function plexFilterCount(f: PlexFilters): number {
// Serialize the shared PlexFilters metadata fields onto a query string. Used by both the library grid
// and the facets request so the two endpoints always agree on the filter set (paging/sort/sort_dir are
// each caller's own concern and stay out of here).
export function appendPlexFilters(u: URLSearchParams, f: PlexFilters): void {
function appendPlexFilters(u: URLSearchParams, f: PlexFilters): void {
if (f.genres?.length) u.set("genres", f.genres.join(","));
if (f.genreMode === "all") u.set("genre_mode", "all");
if (f.contentRatings?.length) u.set("content_ratings", f.contentRatings.join(","));
@ -903,7 +896,7 @@ export interface DownloadProfile {
sort_order: number;
}
export interface DownloadAsset {
interface DownloadAsset {
id: number;
status: string;
title: string | null;

View file

@ -5,7 +5,7 @@ import { createStore } from "./store";
// server can't carry out an action (a definitive 4xx/5xx response). Distinct from toasts
// (transient, e.g. connection blips) and from silent handling. Module-level so the non-React
// api layer can raise it; an <ErrorDialog> mounted at the app root renders the current one.
export interface AppError {
interface AppError {
title: string;
message: string;
}

View file

@ -5,7 +5,7 @@
import { getActiveAccount, type Message, type MessageUser } from "./api";
// A pushed message plus both parties, so the dock can open/flash the right window.
export interface IncomingMessage {
interface IncomingMessage {
message: Message;
from?: MessageUser;
to?: MessageUser;

View file

@ -17,7 +17,7 @@ export async function partnerPub(partnerId: number): Promise<string> {
}
// Live "is secure messaging unlocked on this device" — shared so the page and the dock agree.
export function useUnlocked(): boolean {
function useUnlocked(): boolean {
return useSyncExternalStore(subscribeUnlock, isUnlocked, isUnlocked);
}

View file

@ -6,7 +6,7 @@ import { LS, readAccount, readAccountMerged, writeAccount } from "./storage";
export type NotifLevel = "info" | "success" | "warning" | "error" | "fatal";
export interface NotifAction {
interface NotifAction {
label: string;
onClick: () => void;
}
@ -34,7 +34,7 @@ export type ChannelSubscribedMeta = {
};
// Admin nudge that pending access requests are waiting — carries a durable inbox link to the
// Users page (where Access requests live). No payload; the panel provides the navigation.
export type AccessRequestsMeta = {
type AccessRequestsMeta = {
kind: "access-requests";
};
export type NotifMeta =
@ -224,7 +224,7 @@ export function notify(input: NotifyInput): number {
}
/** Back-compat: a simple info toast with an optional inline action (e.g. Undo). */
export function toast(message: string, action?: NotifAction): number {
function toast(message: string, action?: NotifAction): number {
return notify({ message, action });
}

View file

@ -14,10 +14,10 @@
import { getAccountRaw, LS, setAccountRaw } from "./storage";
export const ONBOARD_ACTIVE = "siftlode.onboarding.active"; // sessionStorage (not in LS, which is localStorage)
const ONBOARD_ACTIVE = "siftlode.onboarding.active"; // sessionStorage (not in LS, which is localStorage)
// Per-account (see accountKey): a fresh account should still get the onboarding nudge even if a
// different account in the same browser dismissed it.
export const ONBOARD_DISMISSED = LS.onboardingDismissed;
const ONBOARD_DISMISSED = LS.onboardingDismissed;
export function shouldAutoOpenOnboarding(me: {
can_read: boolean;

View file

@ -8,15 +8,7 @@ import { LS, readAccount, writeAccount } from "./storage";
export type WidgetId = "savedviews" | "date" | "language" | "topic" | "tags";
export const ALL_WIDGETS: WidgetId[] = ["savedviews", "date", "tags", "language", "topic"];
export const WIDGET_TITLES: Record<WidgetId, string> = {
savedviews: "Saved views",
date: "Upload date",
language: "Language",
topic: "Topic",
tags: "Tags",
};
const ALL_WIDGETS: WidgetId[] = ["savedviews", "date", "tags", "language", "topic"];
export interface SidebarLayout {
order: WidgetId[];

View file

@ -129,7 +129,7 @@ export function writeJSON(key: string, value: unknown): void {
* Validation is deferred to the caller (clamp at render) so it can be used before an
* async-loaded option list is known, keeping hook order stable. Generalizes the per-component
* "persisted active tab" pattern (Settings, Stats, admin pages, the channel filter). */
export function usePersistedState(
function usePersistedState(
key: string,
fallback = "",
): [string, (value: string) => void] {

View file

@ -1,6 +1,6 @@
import { LS, readAccountMerged, writeAccount } from "./storage";
export type Mode = "dark" | "light";
type Mode = "dark" | "light";
export type Scheme = "midnight" | "forest" | "slate" | "youtube";
export const SCHEMES: { id: Scheme; name: string; swatch: string }[] = [

View file

@ -89,7 +89,7 @@ export function hasFilterParams(params: URLSearchParams): boolean {
// The single source of truth for valid page ids; `Page` and the runtime validator both
// derive from it, so adding a page is a one-line change (no parallel allowlists to keep in
// sync). "feed" is the default/fallback.
export const PAGES = [
const PAGES = [
"feed",
"channels",
"stats",