style: 完成所有文件的lint

This commit is contained in:
claude-code-best
2026-05-01 21:39:30 +08:00
parent d136872cc9
commit 6182015005
1333 changed files with 68255 additions and 77882 deletions

View File

@@ -1,35 +1,24 @@
import { feature } from 'bun:bundle'
import React, {
useContext,
useEffect,
useEffectEvent,
useState,
useSyncExternalStore,
} from 'react'
import { MailboxProvider } from '../context/mailbox.js'
import { useSettingsChange } from '../hooks/useSettingsChange.js'
import { logForDebugging } from '../utils/debug.js'
import { feature } from 'bun:bundle';
import React, { useContext, useEffect, useEffectEvent, useState, useSyncExternalStore } from 'react';
import { MailboxProvider } from '../context/mailbox.js';
import { useSettingsChange } from '../hooks/useSettingsChange.js';
import { logForDebugging } from '../utils/debug.js';
import {
createDisabledBypassPermissionsContext,
isBypassPermissionsModeDisabled,
} from '../utils/permissions/permissionSetup.js'
import { applySettingsChange } from '../utils/settings/applySettingsChange.js'
import type { SettingSource } from '../utils/settings/constants.js'
import { createStore } from './store.js'
} from '../utils/permissions/permissionSetup.js';
import { applySettingsChange } from '../utils/settings/applySettingsChange.js';
import type { SettingSource } from '../utils/settings/constants.js';
import { createStore } from './store.js';
// DCE: voice context is ant-only. External builds get a passthrough.
/* eslint-disable @typescript-eslint/no-require-imports */
const VoiceProvider: (props: { children: React.ReactNode }) => React.ReactNode =
feature('VOICE_MODE')
? require('../context/voice.js').VoiceProvider
: ({ children }) => children
const VoiceProvider: (props: { children: React.ReactNode }) => React.ReactNode = feature('VOICE_MODE')
? require('../context/voice.js').VoiceProvider
: ({ children }) => children;
/* eslint-enable @typescript-eslint/no-require-imports */
import {
type AppState,
type AppStateStore,
getDefaultAppState,
} from './AppStateStore.js'
import { type AppState, type AppStateStore, getDefaultAppState } from './AppStateStore.js';
// TODO: Remove these re-exports once all callers import directly from
// ./AppStateStore.js. Kept for back-compat during migration so .ts callers
@@ -42,40 +31,29 @@ export {
IDLE_SPECULATION_STATE,
type SpeculationResult,
type SpeculationState,
} from './AppStateStore.js'
} from './AppStateStore.js';
export const AppStoreContext = React.createContext<AppStateStore | null>(null)
export const AppStoreContext = React.createContext<AppStateStore | null>(null);
type Props = {
children: React.ReactNode
initialState?: AppState
onChangeAppState?: (args: { newState: AppState; oldState: AppState }) => void
}
children: React.ReactNode;
initialState?: AppState;
onChangeAppState?: (args: { newState: AppState; oldState: AppState }) => void;
};
const HasAppStateContext = React.createContext<boolean>(false)
const HasAppStateContext = React.createContext<boolean>(false);
export function AppStateProvider({
children,
initialState,
onChangeAppState,
}: Props): React.ReactNode {
export function AppStateProvider({ children, initialState, onChangeAppState }: Props): React.ReactNode {
// Don't allow nested AppStateProviders.
const hasAppStateContext = useContext(HasAppStateContext)
const hasAppStateContext = useContext(HasAppStateContext);
if (hasAppStateContext) {
throw new Error(
'AppStateProvider can not be nested within another AppStateProvider',
)
throw new Error('AppStateProvider can not be nested within another AppStateProvider');
}
// Store is created once and never changes -- stable context value means
// the provider never triggers re-renders. Consumers subscribe to slices
// via useSyncExternalStore in useAppState(selector).
const [store] = useState(() =>
createStore<AppState>(
initialState ?? getDefaultAppState(),
onChangeAppState,
),
)
const [store] = useState(() => createStore<AppState>(initialState ?? getDefaultAppState(), onChangeAppState));
// Check on mount if bypass mode should be disabled
// This handles the race condition where remote settings load BEFORE this component mounts,
@@ -83,30 +61,21 @@ export function AppStateProvider({
// On subsequent sessions, the cached remote-settings.json is read during initial setup,
// but on the first session the remote fetch may complete before React mounts.
useEffect(() => {
const { toolPermissionContext } = store.getState()
if (
toolPermissionContext.isBypassPermissionsModeAvailable &&
isBypassPermissionsModeDisabled()
) {
logForDebugging(
'Disabling bypass permissions mode on mount (remote settings loaded before mount)',
)
const { toolPermissionContext } = store.getState();
if (toolPermissionContext.isBypassPermissionsModeAvailable && isBypassPermissionsModeDisabled()) {
logForDebugging('Disabling bypass permissions mode on mount (remote settings loaded before mount)');
store.setState(prev => ({
...prev,
toolPermissionContext: createDisabledBypassPermissionsContext(
prev.toolPermissionContext,
),
}))
toolPermissionContext: createDisabledBypassPermissionsContext(prev.toolPermissionContext),
}));
}
}, [])
}, []);
// Listen for external settings changes and sync to AppState.
// This ensures file watcher changes propagate through the app --
// shared with the headless/SDK path via applySettingsChange.
const onSettingsChange = useEffectEvent((source: SettingSource) =>
applySettingsChange(source, store.setState),
)
useSettingsChange(onSettingsChange)
const onSettingsChange = useEffectEvent((source: SettingSource) => applySettingsChange(source, store.setState));
useSettingsChange(onSettingsChange);
return (
<HasAppStateContext.Provider value={true}>
@@ -116,18 +85,16 @@ export function AppStateProvider({
</MailboxProvider>
</AppStoreContext.Provider>
</HasAppStateContext.Provider>
)
);
}
function useAppStore(): AppStateStore {
// eslint-disable-next-line react-hooks/rules-of-hooks
const store = useContext(AppStoreContext)
const store = useContext(AppStoreContext);
if (!store) {
throw new ReferenceError(
'useAppState/useSetAppState cannot be called outside of an <AppStateProvider />',
)
throw new ReferenceError('useAppState/useSetAppState cannot be called outside of an <AppStateProvider />');
}
return store
return store;
}
/**
@@ -147,22 +114,22 @@ function useAppStore(): AppStateStore {
* ```
*/
export function useAppState<T>(selector: (state: AppState) => T): T {
const store = useAppStore()
const store = useAppStore();
const get = () => {
const state = store.getState()
const selected = selector(state)
const state = store.getState();
const selected = selector(state);
if (process.env.USER_TYPE === 'ant' && state === selected) {
throw new Error(
`Your selector in \`useAppState(${selector.toString()})\` returned the original state, which is not allowed. You must instead return a property for optimised rendering.`,
)
);
}
return selected
}
return selected;
};
return useSyncExternalStore(store.subscribe, get, get)
return useSyncExternalStore(store.subscribe, get, get);
}
/**
@@ -170,30 +137,26 @@ export function useAppState<T>(selector: (state: AppState) => T): T {
* Returns a stable reference that never changes -- components using only
* this hook will never re-render from state changes.
*/
export function useSetAppState(): (
updater: (prev: AppState) => AppState,
) => void {
return useAppStore().setState
export function useSetAppState(): (updater: (prev: AppState) => AppState) => void {
return useAppStore().setState;
}
/**
* Get the store directly (for passing getState/setState to non-React code).
*/
export function useAppStateStore(): AppStateStore {
return useAppStore()
return useAppStore();
}
const NOOP_SUBSCRIBE = () => () => {}
const NOOP_SUBSCRIBE = () => () => {};
/**
* Safe version of useAppState that returns undefined if called outside of AppStateProvider.
* Useful for components that may be rendered in contexts where AppStateProvider isn't available.
*/
export function useAppStateMaybeOutsideOfProvider<T>(
selector: (state: AppState) => T,
): T | undefined {
const store = useContext(AppStoreContext)
export function useAppStateMaybeOutsideOfProvider<T>(selector: (state: AppState) => T): T | undefined {
const store = useContext(AppStoreContext);
return useSyncExternalStore(store ? store.subscribe : NOOP_SUBSCRIBE, () =>
store ? selector(store.getState()) : undefined,
)
);
}

View File

@@ -1,112 +1,125 @@
import { describe, expect, test } from "bun:test";
import { createStore } from "../store";
import { describe, expect, test } from 'bun:test'
import { createStore } from '../store'
describe("createStore", () => {
test("returns object with getState, setState, subscribe", () => {
const store = createStore({ count: 0 });
expect(typeof store.getState).toBe("function");
expect(typeof store.setState).toBe("function");
expect(typeof store.subscribe).toBe("function");
});
describe('createStore', () => {
test('returns object with getState, setState, subscribe', () => {
const store = createStore({ count: 0 })
expect(typeof store.getState).toBe('function')
expect(typeof store.setState).toBe('function')
expect(typeof store.subscribe).toBe('function')
})
test("getState returns initial state", () => {
const store = createStore({ count: 0, name: "test" });
expect(store.getState()).toEqual({ count: 0, name: "test" });
});
test('getState returns initial state', () => {
const store = createStore({ count: 0, name: 'test' })
expect(store.getState()).toEqual({ count: 0, name: 'test' })
})
test("setState updates state via updater function", () => {
const store = createStore({ count: 0 });
store.setState(prev => ({ count: prev.count + 1 }));
expect(store.getState().count).toBe(1);
});
test('setState updates state via updater function', () => {
const store = createStore({ count: 0 })
store.setState(prev => ({ count: prev.count + 1 }))
expect(store.getState().count).toBe(1)
})
test("setState does not notify when state unchanged (Object.is)", () => {
const store = createStore({ count: 0 });
let notified = false;
store.subscribe(() => { notified = true; });
store.setState(prev => prev);
expect(notified).toBe(false);
});
test('setState does not notify when state unchanged (Object.is)', () => {
const store = createStore({ count: 0 })
let notified = false
store.subscribe(() => {
notified = true
})
store.setState(prev => prev)
expect(notified).toBe(false)
})
test("setState notifies subscribers on change", () => {
const store = createStore({ count: 0 });
let notified = false;
store.subscribe(() => { notified = true; });
store.setState(prev => ({ count: prev.count + 1 }));
expect(notified).toBe(true);
});
test('setState notifies subscribers on change', () => {
const store = createStore({ count: 0 })
let notified = false
store.subscribe(() => {
notified = true
})
store.setState(prev => ({ count: prev.count + 1 }))
expect(notified).toBe(true)
})
test("subscribe returns unsubscribe function", () => {
const store = createStore({ count: 0 });
const unsub = store.subscribe(() => {});
expect(typeof unsub).toBe("function");
});
test('subscribe returns unsubscribe function', () => {
const store = createStore({ count: 0 })
const unsub = store.subscribe(() => {})
expect(typeof unsub).toBe('function')
})
test("unsubscribe stops notifications", () => {
const store = createStore({ count: 0 });
let count = 0;
const unsub = store.subscribe(() => { count++; });
store.setState(prev => ({ count: prev.count + 1 }));
unsub();
store.setState(prev => ({ count: prev.count + 1 }));
expect(count).toBe(1);
});
test('unsubscribe stops notifications', () => {
const store = createStore({ count: 0 })
let count = 0
const unsub = store.subscribe(() => {
count++
})
store.setState(prev => ({ count: prev.count + 1 }))
unsub()
store.setState(prev => ({ count: prev.count + 1 }))
expect(count).toBe(1)
})
test("multiple subscribers all get notified", () => {
const store = createStore({ count: 0 });
let a = 0, b = 0;
store.subscribe(() => { a++; });
store.subscribe(() => { b++; });
store.setState(prev => ({ count: prev.count + 1 }));
expect(a).toBe(1);
expect(b).toBe(1);
});
test('multiple subscribers all get notified', () => {
const store = createStore({ count: 0 })
let a = 0,
b = 0
store.subscribe(() => {
a++
})
store.subscribe(() => {
b++
})
store.setState(prev => ({ count: prev.count + 1 }))
expect(a).toBe(1)
expect(b).toBe(1)
})
test("onChange callback is called on state change", () => {
let captured: any = null;
test('onChange callback is called on state change', () => {
let captured: any = null
const store = createStore({ count: 0 }, ({ newState, oldState }) => {
captured = { newState, oldState };
});
store.setState(prev => ({ count: prev.count + 5 }));
expect(captured).not.toBeNull();
expect(captured.oldState.count).toBe(0);
expect(captured.newState.count).toBe(5);
});
captured = { newState, oldState }
})
store.setState(prev => ({ count: prev.count + 5 }))
expect(captured).not.toBeNull()
expect(captured.oldState.count).toBe(0)
expect(captured.newState.count).toBe(5)
})
test("onChange is not called when state unchanged", () => {
let called = false;
const store = createStore({ count: 0 }, () => { called = true; });
store.setState(prev => prev);
expect(called).toBe(false);
});
test('onChange is not called when state unchanged', () => {
let called = false
const store = createStore({ count: 0 }, () => {
called = true
})
store.setState(prev => prev)
expect(called).toBe(false)
})
test("works with complex state objects", () => {
const store = createStore({ items: [] as number[], name: "test" });
store.setState(prev => ({ ...prev, items: [1, 2, 3] }));
expect(store.getState().items).toEqual([1, 2, 3]);
expect(store.getState().name).toBe("test");
});
test('works with complex state objects', () => {
const store = createStore({ items: [] as number[], name: 'test' })
store.setState(prev => ({ ...prev, items: [1, 2, 3] }))
expect(store.getState().items).toEqual([1, 2, 3])
expect(store.getState().name).toBe('test')
})
test("works with primitive state", () => {
const store = createStore(0);
store.setState(() => 42);
expect(store.getState()).toBe(42);
});
test('works with primitive state', () => {
const store = createStore(0)
store.setState(() => 42)
expect(store.getState()).toBe(42)
})
test("updater receives previous state", () => {
const store = createStore({ value: 10 });
test('updater receives previous state', () => {
const store = createStore({ value: 10 })
store.setState(prev => {
expect(prev.value).toBe(10);
return { value: prev.value * 2 };
});
expect(store.getState().value).toBe(20);
});
expect(prev.value).toBe(10)
return { value: prev.value * 2 }
})
expect(store.getState().value).toBe(20)
})
test("sequential setState calls produce final state", () => {
const store = createStore({ count: 0 });
store.setState(prev => ({ count: prev.count + 1 }));
store.setState(prev => ({ count: prev.count + 1 }));
store.setState(prev => ({ count: prev.count + 1 }));
expect(store.getState().count).toBe(3);
});
});
test('sequential setState calls produce final state', () => {
const store = createStore({ count: 0 })
store.setState(prev => ({ count: prev.count + 1 }))
store.setState(prev => ({ count: prev.count + 1 }))
store.setState(prev => ({ count: prev.count + 1 }))
expect(store.getState().count).toBe(3)
})
})