feat(frontend): the folders are layers — an import only goes down, a module is reached through its index.ts
`local/layers` (eslint.config.js) resolves every import to its file and refuses one that goes up (lib/ → components/ui/ → features/ → modules/ → shell/) or that reaches into another module's own files. A module's index.ts is its public face: the stores, mounted at the root and read app-wide, which features/ may read too; a page is still opened lazily by import(). Its contract test probes every edge, and three mutants of the rule (off, no face check, a door for everyone) each turn it red. Six modules got that face; the shared channel UI (about, tags, tag manager) moved to features/channel, the filter chips and the saved-views context to features/filters, and the prefs store's controller type moved next to the store instead of importing it from its own page. The header's per-module controls and the eagerly loaded rails keep eight inline suppressions, each naming why: they go when the header gets slots and every rail loads lazily. `chunkGuard` fails the production build when a lazily imported module lands in a boot chunk (the entry, the shell, what they import) — e.g. a page re-exported from an index.ts, which makes its import() a silent no-op. Proved on a build with that mutant; today's build has none.
This commit is contained in:
@@ -18,7 +18,7 @@ import { compareText } from "../../lib/collate";
|
||||
import { formatDate } from "../../lib/format";
|
||||
import { navItemAttrs } from "../../lib/listNav";
|
||||
import { useConfirm } from "../../components/ui/confirmContext";
|
||||
import { folderIcon, folderLabel, takesUploads } from "../files/fileFolders";
|
||||
import { folderIcon, folderLabel, takesUploads } from "../files";
|
||||
import { type ActionMenuItem } from "../../components/ui/ActionMenu";
|
||||
import { Badge } from "../../components/ui/Badge";
|
||||
import { Button } from "../../components/ui/Button";
|
||||
|
||||
@@ -20,7 +20,7 @@ import { DownloadFormats } from "./ProfileEditor";
|
||||
import DiagnosticsSettings from "./DiagnosticsSettings";
|
||||
import ApiTokens from "./ApiTokens";
|
||||
import { type SettingsTabId } from "../../lib/settingsTab";
|
||||
import { SCHEMES, type Scheme, type ThemePrefs } from "../../lib/theme";
|
||||
import { SCHEMES, type Scheme } from "../../lib/theme";
|
||||
import { api, HttpError, type Me } from "../../lib/api";
|
||||
import { displayName, roleKey } from "../../lib/api/account";
|
||||
import { notifyActionError } from "../../lib/actionErrors";
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
START_ON_HOME_PREF,
|
||||
} from "../../features/navigation/navigationInit";
|
||||
import { usePref } from "../../lib/usePref";
|
||||
import { usePrefs } from "../../features/prefs/prefsContext";
|
||||
import { usePrefs, type PrefsController } from "../../features/prefs/prefsContext";
|
||||
import Tabs from "../../components/ui/Tabs";
|
||||
import { useTabPanel } from "../../components/ui/tabPanel";
|
||||
import { useMe } from "../../lib/useMe";
|
||||
@@ -49,25 +49,6 @@ import { Slider } from "../../components/ui/NativeControls";
|
||||
import { Input, Textarea } from "../../components/ui/Input";
|
||||
import { SURFACE } from "../../components/ui/surfaces";
|
||||
|
||||
// The Settings page edits server-persisted preferences. Each change applies locally for instant
|
||||
// preview AND auto-saves (debounced) — no draft, no Save button; a small indicator flashes the
|
||||
// outcome. PrefsProvider owns the state + the save; this controller
|
||||
// is how the panel reads/writes it.
|
||||
export type PrefsSaveState = SaveState;
|
||||
export interface PrefsController {
|
||||
theme: ThemePrefs;
|
||||
setTheme: (t: ThemePrefs) => void;
|
||||
perf: boolean;
|
||||
setPerf: (v: boolean) => void;
|
||||
hints: boolean;
|
||||
setHints: (v: boolean) => void;
|
||||
railLabels: boolean;
|
||||
setRailLabels: (v: boolean) => void;
|
||||
notif: NotifSettings;
|
||||
setNotif: (n: NotifSettings) => void;
|
||||
saveState: PrefsSaveState;
|
||||
}
|
||||
|
||||
type TabId = SettingsTabId;
|
||||
const TABS: { id: TabId; icon: typeof Monitor }[] = [
|
||||
{ id: "appearance", icon: Monitor },
|
||||
@@ -138,7 +119,7 @@ export default function SettingsPanel() {
|
||||
|
||||
// A small transient indicator of the auto-save outcome. Renders nothing while idle; it sits at the
|
||||
// bottom of the settings card and fades after a success (the state resets to "idle" in App).
|
||||
function SaveIndicator({ state }: { state: PrefsSaveState }) {
|
||||
function SaveIndicator({ state }: { state: SaveState }) {
|
||||
const { t } = useTranslation();
|
||||
if (state === "idle") return null;
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user