authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-05 22:07:29-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-05 22:07:29-07:00
log1200f530bdc8be25ca1c85affa42b6d9c259601c
tree738528b79a38ede7314410aab0973da8370695b3
parent226abe5f916ff828d8be2c19f1148b10feebfe43
signature Signed by PGP key B5690EEEBB952194

refactor(web): keep app utilities private and remove dead helpers (#10227)


22 files changed, 27 insertions(+), 52 deletions(-)

apps/web/src/browser/browserViewportActions.ts+1-1
...@@ -4,7 +4,7 @@ type BrowserViewportHandler = (setting: PreviewViewportSetting) => Promise<void>...@@ -4,7 +4,7 @@ type BrowserViewportHandler = (setting: PreviewViewportSetting) => Promise<void>
44
5export const BROWSER_VIEWPORT_COMMIT_TIMEOUT_MS = 15_000;5export const BROWSER_VIEWPORT_COMMIT_TIMEOUT_MS = 15_000;
66
7export class BrowserViewportCommitTimeoutError extends Error {7class BrowserViewportCommitTimeoutError extends Error {
8 override readonly name = "BrowserViewportCommitTimeoutError";8 override readonly name = "BrowserViewportCommitTimeoutError";
99
10 constructor(readonly tabId: string) {10 constructor(readonly tabId: string) {
apps/web/src/browser/webviewCrashRecovery.ts+2-2
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1export const WEBVIEW_CRASH_RECOVERY_WINDOW_MS = 30_000;1export const WEBVIEW_CRASH_RECOVERY_WINDOW_MS = 30_000;
2export const WEBVIEW_CRASH_RECOVERY_MAX_ATTEMPTS = 3;2const WEBVIEW_CRASH_RECOVERY_MAX_ATTEMPTS = 3;
3export const WEBVIEW_CRASH_RECOVERY_BASE_DELAY_MS = 250;3const WEBVIEW_CRASH_RECOVERY_BASE_DELAY_MS = 250;
44
5export interface WebviewCrashRecoveryState {5export interface WebviewCrashRecoveryState {
6 readonly attempts: number;6 readonly attempts: number;
apps/web/src/browserFaviconLogic.ts+1-1
...@@ -9,7 +9,7 @@ export type BrowserFaviconEntry = {...@@ -9,7 +9,7 @@ export type BrowserFaviconEntry = {
9};9};
1010
11export const BROWSER_FAVICON_MAX_ENTRIES = 40;11export const BROWSER_FAVICON_MAX_ENTRIES = 40;
12export const BROWSER_FAVICON_MAX_KEY_LENGTH = 4_096;12const BROWSER_FAVICON_MAX_KEY_LENGTH = 4_096;
13const BROWSER_FAVICON_MAX_FUTURE_SKEW_MS = 5 * 60 * 1_000;13const BROWSER_FAVICON_MAX_FUTURE_SKEW_MS = 5 * 60 * 1_000;
14export const BROWSER_FAVICON_MAX_ALIASES_PER_ENTRY = 4;14export const BROWSER_FAVICON_MAX_ALIASES_PER_ENTRY = 4;
15const BROWSER_FAVICON_MAX_ALIAS_LENGTH = 255;15const BROWSER_FAVICON_MAX_ALIAS_LENGTH = 255;
apps/web/src/browserHistoryStore.ts+2-2
...@@ -14,7 +14,7 @@ export type BrowserHistoryEntry = { url: string; lastVisitedAt: number; title?:...@@ -14,7 +14,7 @@ export type BrowserHistoryEntry = { url: string; lastVisitedAt: number; title?:
1414
15export const BROWSER_HISTORY_MAX_ENTRIES_PER_PROJECT = 50;15export const BROWSER_HISTORY_MAX_ENTRIES_PER_PROJECT = 50;
16export const BROWSER_HISTORY_MAX_PROJECTS = 20;16export const BROWSER_HISTORY_MAX_PROJECTS = 20;
17export const BROWSER_HISTORY_MAX_URL_LENGTH = 2048;17const BROWSER_HISTORY_MAX_URL_LENGTH = 2048;
18export const BROWSER_HISTORY_MAX_TITLE_LENGTH = 512;18export const BROWSER_HISTORY_MAX_TITLE_LENGTH = 512;
19const MAX_VALID_DATE_MS = 8_640_000_000_000_000;19const MAX_VALID_DATE_MS = 8_640_000_000_000_000;
2020
...@@ -35,7 +35,7 @@ export function normalizeHistoryUrl(raw: string): string | null {...@@ -35,7 +35,7 @@ export function normalizeHistoryUrl(raw: string): string | null {
35 return parsed.href.length > BROWSER_HISTORY_MAX_URL_LENGTH ? null : parsed.href;35 return parsed.href.length > BROWSER_HISTORY_MAX_URL_LENGTH ? null : parsed.href;
36}36}
3737
38export function titleLookupKey(normalized: string, environmentHostname?: string | null): string {38function titleLookupKey(normalized: string, environmentHostname?: string | null): string {
39 const parsed = new URL(visitLookupKey(normalized, environmentHostname));39 const parsed = new URL(visitLookupKey(normalized, environmentHostname));
40 if (parsed.pathname !== "/" && parsed.pathname.endsWith("/"))40 if (parsed.pathname !== "/" && parsed.pathname.endsWith("/"))
41 parsed.pathname = parsed.pathname.slice(0, -1);41 parsed.pathname = parsed.pathname.slice(0, -1);
apps/web/src/clientPersistenceStorage.ts+1-1
...@@ -2,7 +2,7 @@ import { ClientSettingsSchema, type ClientSettings } from "@t3tools/contracts";...@@ -2,7 +2,7 @@ import { ClientSettingsSchema, type ClientSettings } from "@t3tools/contracts";
22
3import { getLocalStorageItem, setLocalStorageItem } from "./hooks/useLocalStorage";3import { getLocalStorageItem, setLocalStorageItem } from "./hooks/useLocalStorage";
44
5export const CLIENT_SETTINGS_STORAGE_KEY = "t3code:client-settings:v1";5const CLIENT_SETTINGS_STORAGE_KEY = "t3code:client-settings:v1";
66
7function hasWindow(): boolean {7function hasWindow(): boolean {
8 return typeof window !== "undefined";8 return typeof window !== "undefined";
apps/web/src/composerDraftStore.ts+2-4
...@@ -769,7 +769,7 @@ const EMPTY_THREAD_DRAFT = Object.freeze<ComposerThreadDraftState>({...@@ -769,7 +769,7 @@ const EMPTY_THREAD_DRAFT = Object.freeze<ComposerThreadDraftState>({
769 * slice — adding a new field to the interface (e.g. `elementContexts`) only769 * slice — adding a new field to the interface (e.g. `elementContexts`) only
770 * has to be reflected here, not in every stub.770 * has to be reflected here, not in every stub.
771 */771 */
772export function createEmptyThreadDraft(): ComposerThreadDraftState {772function createEmptyThreadDraft(): ComposerThreadDraftState {
773 return {773 return {
774 prompt: "",774 prompt: "",
775 images: [],775 images: [],
...@@ -4050,9 +4050,7 @@ export function useThreadHasUnsentDraft(threadRef: ScopedThreadRef): boolean {...@@ -4050,9 +4050,7 @@ export function useThreadHasUnsentDraft(threadRef: ScopedThreadRef): boolean {
4050 );4050 );
4051}4051}
40524052
4053export function useComposerDraftModelState(4053function useComposerDraftModelState(threadRef: ComposerThreadTarget): ComposerDraftModelState {
4054 threadRef: ComposerThreadTarget,
4055): ComposerDraftModelState {
4056 return useComposerDraftStore(4054 return useComposerDraftStore(
4057 useShallow((state) => {4055 useShallow((state) => {
4058 const draft = getComposerDraftState(state, threadRef);4056 const draft = getComposerDraftState(state, threadRef);
apps/web/src/desktopAppActivation.ts+1-1
...@@ -44,7 +44,7 @@ function failure(...@@ -44,7 +44,7 @@ function failure(
44 return { version: 1, requestId, ok: false, code, message };44 return { version: 1, requestId, ok: false, code, message };
45}45}
4646
47export function desktopPlatformToEnvironmentOs(47function desktopPlatformToEnvironmentOs(
48 platform: DesktopAppActivationRequest["platform"],48 platform: DesktopAppActivationRequest["platform"],
49): ExecutionEnvironmentPlatformOs {49): ExecutionEnvironmentPlatformOs {
50 return platform === "win32" ? "windows" : platform;50 return platform === "win32" ? "windows" : platform;
apps/web/src/keybindings.ts-8
...@@ -377,14 +377,6 @@ export function isDiffToggleShortcut(...@@ -377,14 +377,6 @@ export function isDiffToggleShortcut(
377 return matchesCommandShortcut(event, keybindings, "diff.toggle", options);377 return matchesCommandShortcut(event, keybindings, "diff.toggle", options);
378}378}
379379
380export function isPreviewRefreshShortcut(
381 event: ShortcutEventLike,
382 keybindings: ResolvedKeybindingsConfig,
383 options?: ShortcutMatchOptions,
384): boolean {
385 return matchesCommandShortcut(event, keybindings, "preview.refresh", options);
386}
387
388export function isOpenFavoriteEditorShortcut(380export function isOpenFavoriteEditorShortcut(
389 event: ShortcutEventLike,381 event: ShortcutEventLike,
390 keybindings: ResolvedKeybindingsConfig,382 keybindings: ResolvedKeybindingsConfig,
apps/web/src/lib/attachmentUploadQueue.ts+1-1
...@@ -445,7 +445,7 @@ export function startAttachmentUpload(input: {...@@ -445,7 +445,7 @@ export function startAttachmentUpload(input: {
445 * persisted draft upload survives cancellation (an environment switch cancels445 * persisted draft upload survives cancellation (an environment switch cancels
446 * the old job, and the draft still references that server copy).446 * the old job, and the draft still references that server copy).
447 */447 */
448export function cancelAttachmentUpload(imageId: string): void {448function cancelAttachmentUpload(imageId: string): void {
449 const job = jobsByImageId.get(imageId);449 const job = jobsByImageId.get(imageId);
450 if (!job) {450 if (!job) {
451 return;451 return;
apps/web/src/lib/diffRendering.ts+2-2
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1import { parsePatchFiles } from "@pierre/diffs/utils/parsePatchFiles";1import { parsePatchFiles } from "@pierre/diffs/utils/parsePatchFiles";
2import type { FileDiffMetadata } from "@pierre/diffs/types";2import type { FileDiffMetadata } from "@pierre/diffs/types";
33
4export const DIFF_THEME_NAMES = {4const DIFF_THEME_NAMES = {
5 light: "pierre-light",5 light: "pierre-light",
6 dark: "pierre-dark",6 dark: "pierre-dark",
7} as const;7} as const;
...@@ -81,7 +81,7 @@ interface RenderablePatchOptions {...@@ -81,7 +81,7 @@ interface RenderablePatchOptions {
81 compactPartialHunkOffsets?: boolean;81 compactPartialHunkOffsets?: boolean;
82}82}
8383
84export function compactPartialHunkOffsets(file: FileDiffMetadata): FileDiffMetadata {84function compactPartialHunkOffsets(file: FileDiffMetadata): FileDiffMetadata {
85 if (!file.isPartial) return file;85 if (!file.isPartial) return file;
8686
87 let splitLineStart = 0;87 let splitLineStart = 0;
apps/web/src/lib/previewAnnotation.ts+1-1
...@@ -111,7 +111,7 @@ async function previewAnnotationScreenshotFile(...@@ -111,7 +111,7 @@ async function previewAnnotationScreenshotFile(
111}111}
112112
113/** Upper bound on turning a picked element's crop into a composer attachment. */113/** Upper bound on turning a picked element's crop into a composer attachment. */
114export const PREVIEW_ANNOTATION_CAPTURE_TIMEOUT_MS = 5_000;114const PREVIEW_ANNOTATION_CAPTURE_TIMEOUT_MS = 5_000;
115115
116export type PreviewAnnotationCapture =116export type PreviewAnnotationCapture =
117 /** The crop is ready to attach. */117 /** The crop is ready to attach. */
apps/web/src/lib/storage.ts+1-1
...@@ -26,7 +26,7 @@ export function createMemoryStorage(): StateStorage {...@@ -26,7 +26,7 @@ export function createMemoryStorage(): StateStorage {
26 };26 };
27}27}
2828
29export function isStateStorage(29function isStateStorage(
30 storage: Partial<StateStorage> | null | undefined,30 storage: Partial<StateStorage> | null | undefined,
31): storage is StateStorage {31): storage is StateStorage {
32 return (32 return (
apps/web/src/lib/terminalContext.ts+2-5
...@@ -65,7 +65,7 @@ export function filterTerminalContextsWithText<T extends { text: string }>(...@@ -65,7 +65,7 @@ export function filterTerminalContextsWithText<T extends { text: string }>(
65 return contexts.filter((context) => hasTerminalContextText(context));65 return contexts.filter((context) => hasTerminalContextText(context));
66}66}
6767
68export function normalizeTerminalContextSelection(68function normalizeTerminalContextSelection(
69 selection: TerminalContextSelection,69 selection: TerminalContextSelection,
70): TerminalContextSelection | null {70): TerminalContextSelection | null {
71 const text = normalizeTerminalContextText(selection.text);71 const text = normalizeTerminalContextText(selection.text);
...@@ -85,10 +85,7 @@ export function normalizeTerminalContextSelection(...@@ -85,10 +85,7 @@ export function normalizeTerminalContextSelection(
85 };85 };
86}86}
8787
88export function formatTerminalContextRange(selection: {88function formatTerminalContextRange(selection: { lineStart: number; lineEnd: number }): string {
89 lineStart: number;
90 lineEnd: number;
91}): string {
92 return selection.lineStart === selection.lineEnd89 return selection.lineStart === selection.lineEnd
93 ? `line ${selection.lineStart}`90 ? `line ${selection.lineStart}`
94 : `lines ${selection.lineStart}-${selection.lineEnd}`;91 : `lines ${selection.lineStart}-${selection.lineEnd}`;
apps/web/src/lib/windowControlsOverlay.ts+1-1
...@@ -43,7 +43,7 @@ export function syncDocumentWindowControlsOverlayClass(): () => void {...@@ -43,7 +43,7 @@ export function syncDocumentWindowControlsOverlayClass(): () => void {
43 };43 };
44}44}
4545
46export function getElectronPlatformClassNames(46function getElectronPlatformClassNames(
47 platform: string,47 platform: string,
48):48):
49 | readonly [typeof ELECTRON_CLASS_NAME]49 | readonly [typeof ELECTRON_CLASS_NAME]
apps/web/src/logicalProject.ts-1
...@@ -4,7 +4,6 @@ export {...@@ -4,7 +4,6 @@ export {
4 deriveLogicalProjectKeyFromSettings,4 deriveLogicalProjectKeyFromSettings,
5 derivePhysicalProjectKey,5 derivePhysicalProjectKey,
6 derivePhysicalProjectKeyFromPath,6 derivePhysicalProjectKeyFromPath,
7 deriveProjectGroupLabel,
8 deriveProjectGroupingOverrideKey,7 deriveProjectGroupingOverrideKey,
9 getProjectOrderKey,8 getProjectOrderKey,
10 resolveProjectGroupingMode,9 resolveProjectGroupingMode,
apps/web/src/portDiscoveryState.ts+1-1
...@@ -43,7 +43,7 @@ export function boundConfiguredLocalServerUrls(...@@ -43,7 +43,7 @@ export function boundConfiguredLocalServerUrls(
43 return bounded;43 return bounded;
44}44}
4545
46export function useDiscoveredPorts(46function useDiscoveredPorts(
47 environmentId: EnvironmentId | null,47 environmentId: EnvironmentId | null,
48 configuredUrls?: ReadonlyArray<string>,48 configuredUrls?: ReadonlyArray<string>,
49): ReadonlyArray<DiscoveredLocalServer> {49): ReadonlyArray<DiscoveredLocalServer> {
apps/web/src/projectIconOptions.ts+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1import { iconNames, type IconName } from "lucide-react/dynamic";1import { iconNames, type IconName } from "lucide-react/dynamic";
2export { PROJECT_ICON_COLORS, projectIconColorClassName } from "./projectIconColors";2export { PROJECT_ICON_COLORS, projectIconColorClassName } from "./projectIconColors";
33
4export const POPULAR_PROJECT_ICONS = [4const POPULAR_PROJECT_ICONS = [
5 "folder-code",5 "folder-code",
6 "code-2",6 "code-2",
7 "terminal",7 "terminal",
apps/web/src/reviewCommentContext.ts-6
...@@ -180,12 +180,6 @@ export function parseReviewCommentMessageSegments(...@@ -180,12 +180,6 @@ export function parseReviewCommentMessageSegments(
180 return segments;180 return segments;
181}181}
182182
183export function hasReviewCommentMessageSegments(value: string): boolean {
184 return parseReviewCommentMessageSegments(value).some(
185 (segment) => segment.kind === "review-comment",
186 );
187}
188
189export function formatReviewCommentFence(language: string, contents: string): string {183export function formatReviewCommentFence(language: string, contents: string): string {
190 const longestBacktickRun = Math.max(184 const longestBacktickRun = Math.max(
191 0,185 0,
apps/web/src/rightPanelStore.ts+2-2
...@@ -14,7 +14,7 @@ import { createJSONStorage, persist } from "zustand/middleware";...@@ -14,7 +14,7 @@ import { createJSONStorage, persist } from "zustand/middleware";
1414
15import { resolveStorage } from "./lib/storage";15import { resolveStorage } from "./lib/storage";
1616
17export const RIGHT_PANEL_KINDS = [17const RIGHT_PANEL_KINDS = [
18 "diff",18 "diff",
19 "files",19 "files",
20 "file",20 "file",
...@@ -193,7 +193,7 @@ export function pullRequestSurfaceId(target: {...@@ -193,7 +193,7 @@ export function pullRequestSurfaceId(target: {
193 return `pull-request:${scope}${encodeURIComponent(target.projectId)}:${encodeURIComponent(target.repository)}:${target.number}`;193 return `pull-request:${scope}${encodeURIComponent(target.projectId)}:${encodeURIComponent(target.repository)}:${target.number}`;
194}194}
195195
196export function pullRequestSurface(target: {196function pullRequestSurface(target: {
197 environmentId?: string;197 environmentId?: string;
198 projectId: string;198 projectId: string;
199 repository: string;199 repository: string;
apps/web/src/test/reactHookHarness.ts+1-1
...@@ -34,7 +34,7 @@ import type { Dispatch, SetStateAction } from "react";...@@ -34,7 +34,7 @@ import type { Dispatch, SetStateAction } from "react";
34 * Call `beginRender()` before each component invocation and `reset()` in34 * Call `beginRender()` before each component invocation and `reset()` in
35 * `beforeEach` to drop persisted state between tests.35 * `beforeEach` to drop persisted state between tests.
36 */36 */
37export function createReactHookHarness() {37function createReactHookHarness() {
38 let cursor = 0;38 let cursor = 0;
39 let slots: unknown[] = [];39 let slots: unknown[] = [];
40 const nextIndex = () => cursor++;40 const nextIndex = () => cursor++;
apps/web/src/themePalette.ts+3-8
...@@ -22,15 +22,10 @@ export { EMBER_THEME, GROVE_THEME, IRIS_THEME, OCEAN_THEME, T3_CHAT_THEME, THEME...@@ -22,15 +22,10 @@ export { EMBER_THEME, GROVE_THEME, IRIS_THEME, OCEAN_THEME, T3_CHAT_THEME, THEME
22export type { ThemeAppearance, ThemeColorRole, ThemeColors, ThemeDefinition, ThemeVariants };22export type { ThemeAppearance, ThemeColorRole, ThemeColors, ThemeDefinition, ThemeVariants };
2323
24export const T3_CHAT_THEME_ID = "t3-chat" as const;24export const T3_CHAT_THEME_ID = "t3-chat" as const;
25export const T3_CHAT_THEME_LABEL = "T3 Chat";25const GROVE_THEME_ID = "grove" as const;
26export const GROVE_THEME_ID = "grove" as const;
27export const GROVE_THEME_LABEL = "Grove";
28export const OCEAN_THEME_ID = "ocean" as const;26export const OCEAN_THEME_ID = "ocean" as const;
29export const OCEAN_THEME_LABEL = "Ocean";27const EMBER_THEME_ID = "ember" as const;
30export const EMBER_THEME_ID = "ember" as const;28const IRIS_THEME_ID = "iris" as const;
31export const EMBER_THEME_LABEL = "Ember";
32export const IRIS_THEME_ID = "iris" as const;
33export const IRIS_THEME_LABEL = "Iris";
34export const THEME_FILE_VERSION = 1 as const;29export const THEME_FILE_VERSION = 1 as const;
35export const CUSTOM_THEMES_STORAGE_KEY = "t3code:themes:v1";30export const CUSTOM_THEMES_STORAGE_KEY = "t3code:themes:v1";
36export const THEME_FOLLOW_SYSTEM_STORAGE_KEY = "t3code:theme-follow-system";31export const THEME_FOLLOW_SYSTEM_STORAGE_KEY = "t3code:theme-follow-system";
apps/web/src/versionSkew.ts+1-1
...@@ -12,7 +12,7 @@ export interface VersionMismatch {...@@ -12,7 +12,7 @@ export interface VersionMismatch {
12 readonly hint: string;12 readonly hint: string;
13}13}
1414
15export const VERSION_MISMATCH_DISMISSALS_STORAGE_KEY = "t3code:version-mismatch-dismissals:v1";15const VERSION_MISMATCH_DISMISSALS_STORAGE_KEY = "t3code:version-mismatch-dismissals:v1";
1616
17// Runtime failures retain their identity until the next attempt. Dismiss only17// Runtime failures retain their identity until the next attempt. Dismiss only
18// that attempt, across chat remounts, without clearing the error in Settings.18// that attempt, across chat remounts, without clearing the error in Settings.