From bd6a605d616393828445623eae463d48a3cc10d7 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Tue, 15 Sep 2026 23:17:50 -0700 Subject: [PATCH] Group six more clusters out of web/src/lib Takes the flat module count from 66 to 42, continuing what admin/ and calendar/ started. lib/mailbox/ archiveDate, emptyFolder, folderMove, labelTree, mailboxName, mailboxRoute lib/sieve/ sieve, sieveApply, sieveFolders lib/input/ keyboard, swipe, touch, listSelection, dropUpload lib/notify/ notify, webpush, webpushEnable lib/sw/ swCache, swFacts, staleBuild lib/text/ html, markdown, text, emlName FOUR THINGS THE FILENAMES GET WRONG, each checked by reading the file rather than trusting what it is called: - appFolder is not a mailbox. It is the `ihasmail` folder in JMAP *Files*, where the client keeps signature images and synced settings. It stays flat. - format holds no formatting of text. It re-exports the date and clock formatters, so it belongs with dates/datetime, not with text/. - preview is the file viewer deciding what it can show without downloading, and source is where to point someone asking for this instance's AGPL source. Neither is about text. - notify is not Web Push. It is the tab title, the favicon badge and the new-mail sound -- in-app notification, which is why it sits with webpush rather than under sw/ with the service worker's own concerns. threadScroll stays flat too: it decides where a conversation opens, which is view state rather than a gesture, and input/ is honest only if everything in it interprets something the reader did. No behavior change. Almost every reference was on the @/ alias; eight relative imports in files that did not move, or that moved away from a sibling, needed rewriting by hand. --- web/src/App.tsx | 10 +++++----- web/src/lib/__tests__/describeRules.test.ts | 2 +- web/src/lib/__tests__/htmlAlternative.test.ts | 2 +- web/src/lib/__tests__/keyboardFocus.test.ts | 2 +- web/src/lib/__tests__/keyboardKeyless.test.ts | 2 +- web/src/lib/__tests__/keyboardSequence.test.ts | 2 +- web/src/lib/__tests__/shareTarget.test.ts | 2 +- web/src/lib/__tests__/swipeNav.test.ts | 2 +- web/src/lib/calendar/appointment.ts | 2 +- web/src/lib/filenode.ts | 2 +- .../lib/{ => input}/__tests__/dropUpload.test.ts | 2 +- .../lib/{ => input}/__tests__/listSelection.test.ts | 2 +- web/src/lib/{ => input}/__tests__/swipe.test.ts | 0 web/src/lib/{ => input}/__tests__/touch.test.ts | 0 web/src/lib/{ => input}/dropUpload.ts | Bin web/src/lib/{ => input}/keyboard.ts | 0 web/src/lib/{ => input}/listSelection.ts | 0 web/src/lib/{ => input}/swipe.ts | 0 web/src/lib/{ => input}/touch.ts | 0 .../lib/{ => mailbox}/__tests__/archiveDate.test.ts | 2 +- .../lib/{ => mailbox}/__tests__/emptyFolder.test.ts | 2 +- .../lib/{ => mailbox}/__tests__/folderMove.test.ts | 0 .../lib/{ => mailbox}/__tests__/labelTree.test.ts | 2 +- .../lib/{ => mailbox}/__tests__/mailboxName.test.ts | 2 +- .../{ => mailbox}/__tests__/mailboxRoute.test.ts | 2 +- web/src/lib/{ => mailbox}/archiveDate.ts | 0 web/src/lib/{ => mailbox}/emptyFolder.tsx | 0 web/src/lib/{ => mailbox}/folderMove.ts | 0 web/src/lib/{ => mailbox}/labelTree.ts | 0 web/src/lib/{ => mailbox}/mailboxName.ts | 0 web/src/lib/{ => mailbox}/mailboxRoute.ts | 0 web/src/lib/{ => notify}/__tests__/webpush.test.ts | 2 +- web/src/lib/{ => notify}/notify.ts | 2 +- web/src/lib/{ => notify}/webpush.ts | 0 web/src/lib/{ => notify}/webpushEnable.ts | 6 +++--- web/src/lib/shareTarget.ts | 2 +- web/src/lib/{ => sieve}/__tests__/sieve.test.ts | 0 .../lib/{ => sieve}/__tests__/sieveApply.test.ts | 0 .../lib/{ => sieve}/__tests__/sieveFolders.test.ts | 0 web/src/lib/{ => sieve}/sieve.ts | 2 +- web/src/lib/{ => sieve}/sieveApply.ts | 2 +- web/src/lib/{ => sieve}/sieveFolders.ts | 0 web/src/lib/signatureHtml.ts | 2 +- web/src/lib/{ => sw}/__tests__/staleBuild.test.ts | 2 +- web/src/lib/{ => sw}/__tests__/swFacts.test.ts | 4 ++-- web/src/lib/{ => sw}/staleBuild.ts | 4 ++-- web/src/lib/{ => sw}/swCache.ts | 0 web/src/lib/{ => sw}/swFacts.ts | 4 ++-- web/src/lib/templatePlaceholders.ts | 2 +- web/src/lib/{ => text}/__tests__/emlName.test.ts | 2 +- web/src/lib/{ => text}/__tests__/html.test.ts | 0 web/src/lib/{ => text}/__tests__/markdown.test.ts | 2 +- web/src/lib/{ => text}/__tests__/text.test.ts | 0 web/src/lib/{ => text}/emlName.ts | 0 web/src/lib/{ => text}/html.ts | 0 web/src/lib/{ => text}/markdown.ts | 0 web/src/lib/{ => text}/text.ts | 0 web/src/main.tsx | 2 +- web/src/store/__tests__/sieve-overwrite.test.ts | 2 +- web/src/store/compose.ts | 6 +++--- web/src/store/files.ts | 2 +- web/src/store/mail/index.ts | 10 +++++----- web/src/store/mail/types.ts | 2 +- web/src/store/session.ts | 4 ++-- web/src/store/settings.ts | 2 +- web/src/store/sieve.ts | 2 +- web/src/ui/filepreview.tsx | 2 +- web/src/views/SearchBar.tsx | 2 +- web/src/views/Shortcuts.tsx | 2 +- web/src/views/admin/TenantSheet.tsx | 2 +- web/src/views/admin/TenantsAdmin.tsx | 2 +- web/src/views/calendar/CalendarView.tsx | 4 ++-- web/src/views/compose/Composer.tsx | 4 ++-- web/src/views/compose/RichEditor.tsx | 2 +- web/src/views/files/FilesTree.tsx | 2 +- web/src/views/files/FilesView.tsx | 2 +- web/src/views/mail/FilterFromMessage.tsx | 4 ++-- web/src/views/mail/MailView.tsx | 6 +++--- web/src/views/mail/MailboxPicker.tsx | 2 +- web/src/views/mail/MailboxTree.tsx | 10 +++++----- web/src/views/mail/MessageList.tsx | 12 ++++++------ web/src/views/mail/MessageView.tsx | 6 +++--- web/src/views/mail/ThreadView.tsx | 2 +- web/src/views/settings/AppearanceSettings.tsx | 2 +- web/src/views/settings/FiltersSettings.tsx | 2 +- web/src/views/settings/FoldersSettings.tsx | 2 +- web/src/views/settings/IdentitiesSettings.tsx | 4 ++-- web/src/views/settings/LabelsSettings.tsx | 2 +- web/src/views/settings/NotificationsSettings.tsx | 6 +++--- web/src/views/settings/RuleDialog.tsx | 2 +- web/src/views/settings/ShortcutsSettings.tsx | 2 +- web/src/views/settings/TemplatesSettings.tsx | 2 +- .../__tests__/filters-unsaved-guard.test.tsx | 2 +- .../settings/__tests__/rule-dialog-header.test.tsx | 2 +- .../settings/__tests__/rule-drag-reorder.test.tsx | 2 +- 95 files changed, 104 insertions(+), 104 deletions(-) rename web/src/lib/{ => input}/__tests__/dropUpload.test.ts (97%) rename web/src/lib/{ => input}/__tests__/listSelection.test.ts (98%) rename web/src/lib/{ => input}/__tests__/swipe.test.ts (100%) rename web/src/lib/{ => input}/__tests__/touch.test.ts (100%) rename web/src/lib/{ => input}/dropUpload.ts (100%) rename web/src/lib/{ => input}/keyboard.ts (100%) rename web/src/lib/{ => input}/listSelection.ts (100%) rename web/src/lib/{ => input}/swipe.ts (100%) rename web/src/lib/{ => input}/touch.ts (100%) rename web/src/lib/{ => mailbox}/__tests__/archiveDate.test.ts (99%) rename web/src/lib/{ => mailbox}/__tests__/emptyFolder.test.ts (96%) rename web/src/lib/{ => mailbox}/__tests__/folderMove.test.ts (100%) rename web/src/lib/{ => mailbox}/__tests__/labelTree.test.ts (99%) rename web/src/lib/{ => mailbox}/__tests__/mailboxName.test.ts (98%) rename web/src/lib/{ => mailbox}/__tests__/mailboxRoute.test.ts (97%) rename web/src/lib/{ => mailbox}/archiveDate.ts (100%) rename web/src/lib/{ => mailbox}/emptyFolder.tsx (100%) rename web/src/lib/{ => mailbox}/folderMove.ts (100%) rename web/src/lib/{ => mailbox}/labelTree.ts (100%) rename web/src/lib/{ => mailbox}/mailboxName.ts (100%) rename web/src/lib/{ => mailbox}/mailboxRoute.ts (100%) rename web/src/lib/{ => notify}/__tests__/webpush.test.ts (99%) rename web/src/lib/{ => notify}/notify.ts (99%) rename web/src/lib/{ => notify}/webpush.ts (100%) rename web/src/lib/{ => notify}/webpushEnable.ts (98%) rename web/src/lib/{ => sieve}/__tests__/sieve.test.ts (100%) rename web/src/lib/{ => sieve}/__tests__/sieveApply.test.ts (100%) rename web/src/lib/{ => sieve}/__tests__/sieveFolders.test.ts (100%) rename web/src/lib/{ => sieve}/sieve.ts (99%) rename web/src/lib/{ => sieve}/sieveApply.ts (99%) rename web/src/lib/{ => sieve}/sieveFolders.ts (100%) rename web/src/lib/{ => sw}/__tests__/staleBuild.test.ts (99%) rename web/src/lib/{ => sw}/__tests__/swFacts.test.ts (98%) rename web/src/lib/{ => sw}/staleBuild.ts (98%) rename web/src/lib/{ => sw}/swCache.ts (100%) rename web/src/lib/{ => sw}/swFacts.ts (97%) rename web/src/lib/{ => text}/__tests__/emlName.test.ts (96%) rename web/src/lib/{ => text}/__tests__/html.test.ts (100%) rename web/src/lib/{ => text}/__tests__/markdown.test.ts (97%) rename web/src/lib/{ => text}/__tests__/text.test.ts (100%) rename web/src/lib/{ => text}/emlName.ts (100%) rename web/src/lib/{ => text}/html.ts (100%) rename web/src/lib/{ => text}/markdown.ts (100%) rename web/src/lib/{ => text}/text.ts (100%) diff --git a/web/src/App.tsx b/web/src/App.tsx index 51466e5..3645092 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -16,12 +16,12 @@ import { LoginPage } from "@/views/Login"; import { AppShell } from "@/views/AppShell"; import { MailView } from "@/views/mail/MailView"; import { ComposerDock } from "@/views/compose/ComposerDock"; -import { setUnreadBadge } from "@/lib/notify"; -import { publishWorkerFacts } from "@/lib/swFacts"; +import { setUnreadBadge } from "@/lib/notify/notify"; +import { publishWorkerFacts } from "@/lib/sw/swFacts"; import { PAINTED_FROM_CACHE, useSettings, syncedPart } from "@/store/settings"; import { armSettingsSync, loadRemoteSettings, queueSettingsPush, settingsAlreadyLoadedFor, settingsSyncAvailable } from "@/lib/settingsSync"; import { loadSettingsPolicy } from "@/lib/settingsPolicy"; -import { listenForVerification, renewWebPush } from "@/lib/webpushEnable"; +import { listenForVerification, renewWebPush } from "@/lib/notify/webpushEnable"; import { plural, t, useLanguageVersion, whenLanguageReady } from "@/lib/i18n"; import { confirmLeaveUnsaved, hasUnsavedChanges } from "@/lib/unsavedChanges"; import { BASE_PATH, withBase } from "@/lib/basePath"; @@ -260,7 +260,7 @@ function AuthedApp() { }); const appName = useSession((s) => s.session?.ihasmail?.appName) || DEFAULT_APP_NAME; useEffect(() => { - void import("@/lib/notify").then((m) => { + void import("@/lib/notify/notify").then((m) => { m.setBaseTitle(appName); setUnreadBadge(inboxUnread); }); @@ -284,7 +284,7 @@ function AuthedApp() { // Request notification permission lazily when enabled const notif = useSettings((s) => s.settings.desktopNotifications); useEffect(() => { - if (notif) void import("@/lib/notify").then((m) => m.requestNotificationPermission()); + if (notif) void import("@/lib/notify/notify").then((m) => m.requestNotificationPermission()); }, [notif]); // Nothing worth painting until the account's settings are in force; see the diff --git a/web/src/lib/__tests__/describeRules.test.ts b/web/src/lib/__tests__/describeRules.test.ts index a09d79f..e33cbff 100644 --- a/web/src/lib/__tests__/describeRules.test.ts +++ b/web/src/lib/__tests__/describeRules.test.ts @@ -6,7 +6,7 @@ * the joining is Intl's rather than a hardcoded " and ". */ import { describe, expect, it } from "vitest"; -import { describeRule as describeSieve } from "../sieve"; +import { describeRule as describeSieve } from "../sieve/sieve"; import { describeRule as describeRecurrence, weekdayOptions } from "../calendar/recurrence"; import { setUiLanguageForFormatting } from "../datetime"; import { setCatalog } from "../i18n"; diff --git a/web/src/lib/__tests__/htmlAlternative.test.ts b/web/src/lib/__tests__/htmlAlternative.test.ts index 8e9ccb3..d0d6f8c 100644 --- a/web/src/lib/__tests__/htmlAlternative.test.ts +++ b/web/src/lib/__tests__/htmlAlternative.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { hasHtmlAlternative } from "../html"; +import { hasHtmlAlternative } from "../text/html"; /* * The rule: `htmlBody` is derived, so its presence proves nothing. Only the diff --git a/web/src/lib/__tests__/keyboardFocus.test.ts b/web/src/lib/__tests__/keyboardFocus.test.ts index 8497b3b..84f9273 100644 --- a/web/src/lib/__tests__/keyboardFocus.test.ts +++ b/web/src/lib/__tests__/keyboardFocus.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { isTextEntry, keyboard } from "@/lib/keyboard"; +import { isTextEntry, keyboard } from "@/lib/input/keyboard"; /* * Shortcuts after a click on a checkbox (#260). diff --git a/web/src/lib/__tests__/keyboardKeyless.test.ts b/web/src/lib/__tests__/keyboardKeyless.test.ts index be127f3..d542a8f 100644 --- a/web/src/lib/__tests__/keyboardKeyless.test.ts +++ b/web/src/lib/__tests__/keyboardKeyless.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { comboOf, keyboard } from "@/lib/keyboard"; +import { comboOf, keyboard } from "@/lib/input/keyboard"; /* * A "keydown" that carries no key. Chrome's password autofill dispatches one diff --git a/web/src/lib/__tests__/keyboardSequence.test.ts b/web/src/lib/__tests__/keyboardSequence.test.ts index dbb1dc0..ababc0c 100644 --- a/web/src/lib/__tests__/keyboardSequence.test.ts +++ b/web/src/lib/__tests__/keyboardSequence.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { keyboard } from "@/lib/keyboard"; +import { keyboard } from "@/lib/input/keyboard"; /* * Two-key sequences against the single keys they start with. diff --git a/web/src/lib/__tests__/shareTarget.test.ts b/web/src/lib/__tests__/shareTarget.test.ts index acd651d..d126833 100644 --- a/web/src/lib/__tests__/shareTarget.test.ts +++ b/web/src/lib/__tests__/shareTarget.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { collectShare, shareBody, SHARE_MAX_AGE_MS } from "@/lib/shareTarget"; -import { SW_CACHE_NAME } from "@/lib/swCache"; +import { SW_CACHE_NAME } from "@/lib/sw/swCache"; /** * The handoff, from the tab's side. The worker's half cannot be exercised here diff --git a/web/src/lib/__tests__/swipeNav.test.ts b/web/src/lib/__tests__/swipeNav.test.ts index 03041bc..98db357 100644 --- a/web/src/lib/__tests__/swipeNav.test.ts +++ b/web/src/lib/__tests__/swipeNav.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { navSwipeThreshold, swipeNavDirection, swipeThreshold, lockAxis } from "@/lib/touch"; +import { navSwipeThreshold, swipeNavDirection, swipeThreshold, lockAxis } from "@/lib/input/touch"; describe("navSwipeThreshold", () => { it("asks for more travel than a row swipe does, at every width", () => { diff --git a/web/src/lib/calendar/appointment.ts b/web/src/lib/calendar/appointment.ts index 5162eee..2a272fc 100644 --- a/web/src/lib/calendar/appointment.ts +++ b/web/src/lib/calendar/appointment.ts @@ -3,7 +3,7 @@ import { useCalendar, type EventDraft } from "@/store/calendar"; import { useMail } from "@/store/mail"; import { uniqueAddresses } from "../address"; import { toLocalDateOnly } from "../dates"; -import { htmlToText } from "../text"; +import { htmlToText } from "../text/text"; /** * How much of a message body is copied into an event description. diff --git a/web/src/lib/filenode.ts b/web/src/lib/filenode.ts index 0ed68e9..6ba9290 100644 --- a/web/src/lib/filenode.ts +++ b/web/src/lib/filenode.ts @@ -9,7 +9,7 @@ * so a node has one shape and there is nothing left to detect. */ import type { FileNode, Id } from "@/jmap/types"; -import { descendantIds } from "./folderMove"; +import { descendantIds } from "./mailbox/folderMove"; /** Properties to request for a node. */ export function fileNodeProps(): string[] { diff --git a/web/src/lib/__tests__/dropUpload.test.ts b/web/src/lib/input/__tests__/dropUpload.test.ts similarity index 97% rename from web/src/lib/__tests__/dropUpload.test.ts rename to web/src/lib/input/__tests__/dropUpload.test.ts index f818b3e..fc9c671 100644 --- a/web/src/lib/__tests__/dropUpload.test.ts +++ b/web/src/lib/input/__tests__/dropUpload.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { foldersNeeded, hasDirectory, planUpload } from "@/lib/dropUpload"; +import { foldersNeeded, hasDirectory, planUpload } from "@/lib/input/dropUpload"; /** * Dropping a folder in, reduced to the two things the DataTransfer entry API diff --git a/web/src/lib/__tests__/listSelection.test.ts b/web/src/lib/input/__tests__/listSelection.test.ts similarity index 98% rename from web/src/lib/__tests__/listSelection.test.ts rename to web/src/lib/input/__tests__/listSelection.test.ts index 203d8eb..513a134 100644 --- a/web/src/lib/__tests__/listSelection.test.ts +++ b/web/src/lib/input/__tests__/listSelection.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { rowClick, type RowClick } from "@/lib/listSelection"; +import { rowClick, type RowClick } from "@/lib/input/listSelection"; const IDS = ["a", "b", "c", "d", "e"]; const click = (over: Partial[0]> = {}): RowClick => diff --git a/web/src/lib/__tests__/swipe.test.ts b/web/src/lib/input/__tests__/swipe.test.ts similarity index 100% rename from web/src/lib/__tests__/swipe.test.ts rename to web/src/lib/input/__tests__/swipe.test.ts diff --git a/web/src/lib/__tests__/touch.test.ts b/web/src/lib/input/__tests__/touch.test.ts similarity index 100% rename from web/src/lib/__tests__/touch.test.ts rename to web/src/lib/input/__tests__/touch.test.ts diff --git a/web/src/lib/dropUpload.ts b/web/src/lib/input/dropUpload.ts similarity index 100% rename from web/src/lib/dropUpload.ts rename to web/src/lib/input/dropUpload.ts diff --git a/web/src/lib/keyboard.ts b/web/src/lib/input/keyboard.ts similarity index 100% rename from web/src/lib/keyboard.ts rename to web/src/lib/input/keyboard.ts diff --git a/web/src/lib/listSelection.ts b/web/src/lib/input/listSelection.ts similarity index 100% rename from web/src/lib/listSelection.ts rename to web/src/lib/input/listSelection.ts diff --git a/web/src/lib/swipe.ts b/web/src/lib/input/swipe.ts similarity index 100% rename from web/src/lib/swipe.ts rename to web/src/lib/input/swipe.ts diff --git a/web/src/lib/touch.ts b/web/src/lib/input/touch.ts similarity index 100% rename from web/src/lib/touch.ts rename to web/src/lib/input/touch.ts diff --git a/web/src/lib/__tests__/archiveDate.test.ts b/web/src/lib/mailbox/__tests__/archiveDate.test.ts similarity index 99% rename from web/src/lib/__tests__/archiveDate.test.ts rename to web/src/lib/mailbox/__tests__/archiveDate.test.ts index ee9304c..f7dac1f 100644 --- a/web/src/lib/__tests__/archiveDate.test.ts +++ b/web/src/lib/mailbox/__tests__/archiveDate.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { archiveSegments, archivePath, groupByArchivePath } from "@/lib/archiveDate"; +import { archiveSegments, archivePath, groupByArchivePath } from "@/lib/mailbox/archiveDate"; /** * The dates below are written as local-time strings on purpose. The segments diff --git a/web/src/lib/__tests__/emptyFolder.test.ts b/web/src/lib/mailbox/__tests__/emptyFolder.test.ts similarity index 96% rename from web/src/lib/__tests__/emptyFolder.test.ts rename to web/src/lib/mailbox/__tests__/emptyFolder.test.ts index 9fd8d6b..d301524 100644 --- a/web/src/lib/__tests__/emptyFolder.test.ts +++ b/web/src/lib/mailbox/__tests__/emptyFolder.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { canEmpty, emptyLabel } from "@/lib/emptyFolder"; +import { canEmpty, emptyLabel } from "@/lib/mailbox/emptyFolder"; import type { MailboxRole } from "@/jmap/types"; /** diff --git a/web/src/lib/__tests__/folderMove.test.ts b/web/src/lib/mailbox/__tests__/folderMove.test.ts similarity index 100% rename from web/src/lib/__tests__/folderMove.test.ts rename to web/src/lib/mailbox/__tests__/folderMove.test.ts diff --git a/web/src/lib/__tests__/labelTree.test.ts b/web/src/lib/mailbox/__tests__/labelTree.test.ts similarity index 99% rename from web/src/lib/__tests__/labelTree.test.ts rename to web/src/lib/mailbox/__tests__/labelTree.test.ts index 8416a58..ff7773d 100644 --- a/web/src/lib/__tests__/labelTree.test.ts +++ b/web/src/lib/mailbox/__tests__/labelTree.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { labelTree, visibleLabels, descendantKeywords } from "@/lib/labelTree"; +import { labelTree, visibleLabels, descendantKeywords } from "@/lib/mailbox/labelTree"; import type { Label } from "@/store/settings"; const L = (keyword: string, over: Partial