chore(web): move lint suppressions to oxlint
The repo lints with oxlint now, so `eslint-disable-next-line` comments were inert. Converted them to `oxlint-disable` with the rule name and a note on why each is legitimate: the effects that sync remote state (pipeline run fetch, search reset, deep-link settings open) are external-input driven, not derived render state, and the voice tile's setCanvas is a callback ref returned by the layout hook rather than a ref.current read. use-composer additionally lists commitUploads, which is a stable useCallback and is what keeps the upload queue write and the draft reset on one writer.
ZhenYi committed
Modified web/src/features/chat/model/use-composer.ts web/src/features/chat/model/use-composer.ts +4 −2 View web/src/features/chat/model/use-composer.ts
@@ -135,7 +135,9 @@
135135 return []
136136 })
137137 urls.current.clear()
138 }, [draftScope, editingId, roomId, scopeKey, text, threadId])
138 // `commitUploads` is a stable `useCallback`: listing it is what keeps the
139 // queue write and this reset on the same writer.
140 }, [draftScope, editingId, roomId, scopeKey, text, threadId, commitUploads])
139141
140142 /* --------------------------------------------------------------- draft */
141143
@@ -185,7 +187,7 @@
185187 if (saveTimer.current) clearTimeout(saveTimer.current)
186188 }
187189 // The timer must not restart on every render, only on text/scope changes.
188 // eslint-disable-next-line react-hooks/exhaustive-deps
190 // oxlint-disable-next-line react-hooks/exhaustive-deps -- 定时器只跟随 text/scope 重启;其余依赖变化会各自处理
189191 }, [text, roomId, threadId, loadedDraft, editingId])
190192
191193 useEffect(
Modified web/src/features/chat/ui/voice/voice-tiles.tsx web/src/features/chat/ui/voice/voice-tiles.tsx +2 −1 View web/src/features/chat/ui/voice/voice-tiles.tsx
@@ -449,12 +449,13 @@
449449
450450 // Callback ref, not a ref read: the board owns the canvas element, so the
451451 // layout hook learns about it when React attaches it (see useVoiceTileLayout).
452 const setCanvas = layout.setCanvas
452 const setCanvas = layout.setCanvas
453453 const placedRefs = boardRefs.filter(ref => layout.layout[ref.participant.identity])
454454 const gridRefs = boardRefs.filter(ref => !layout.layout[ref.participant.identity])
455455
456456 return (
457457 <LayoutContextProvider value={layoutContext}>
458 {/* oxlint-disable-next-line react/refs -- setCanvas 是 useVoiceTileLayout 返回的回调 ref,不是 ref.current 读取 */}
458459 <div className="voice-board" ref={setCanvas}>
459460 <GridLayout tracks={gridRefs} className="voice-grid">
460461 <Window {...windowProps} />
Modified web/src/features/repo-pipelines/ui/pipelines-view.tsx web/src/features/repo-pipelines/ui/pipelines-view.tsx +4 −1 View web/src/features/repo-pipelines/ui/pipelines-view.tsx
@@ -289,11 +289,14 @@
289289 } catch (err) {
290290 setError(err instanceof Error ? err.message : t('repo.error.loadFailed'))
291291 }
292 }, [space, repo, runId, t])
292 }, [space, repo, runId, t])
293293
294 // 首次进入与 runId 变化时拉取;这是与远端同步,不是可派生的渲染状态。
295 /* oxlint-disable react/set-state-in-effect -- 远端拉取属外部同步 */
294296 useEffect(() => {
295297 void load()
296298 }, [load])
299 /* oxlint-enable react/set-state-in-effect */
297300
298301 useEffect(() => {
299302 if (!detail) return
Modified web/src/features/search/ui/global-search-command.tsx web/src/features/search/ui/global-search-command.tsx +8 −0 View web/src/features/search/ui/global-search-command.tsx
@@ -55,12 +55,16 @@
5555
5656 // 换页面即重新开始:约束属于「现在在哪」,不是上次搜过什么。
5757 useEffect(() => {
58 // 外部输入(路由 / 开关)变化驱动的复位,不是可派生的渲染状态。
59 // oxlint-disable-next-line react/set-state-in-effect
5860 setScopeDropped(false)
5961 }, [pathname])
6062
6163 // 打开面板时复位,避免上一次的输入与「已移除约束」状态跨次残留。
6264 useEffect(() => {
6365 if (!open) return
66 // 外部输入(路由 / 开关)变化驱动的复位,不是可派生的渲染状态。
67 // oxlint-disable-next-line react/set-state-in-effect
6468 setScopeDropped(false)
6569 }, [open])
6670
@@ -127,9 +131,11 @@
127131
128132 useEffect(() => {
129133 if (!open || !text || kinds.length === 0) {
134 /* oxlint-disable react/set-state-in-effect -- 查询条件清空时将结果归零,属于响应外部输入 */
130135 setResults([])
131136 setError('')
132137 setLoading(false)
138 /* oxlint-enable react/set-state-in-effect */
133139 return
134140 }
135141 let active = true
@@ -154,9 +160,11 @@
154160 },
155161 failure => {
156162 if (!active) return
163 /* oxlint-disable react/set-state-in-effect -- 查询条件清空时将结果归零,属于响应外部输入 */
157164 setResults([])
158165 setError(failure instanceof Error ? failure.message : t('search.failed'))
159166 setLoading(false)
167 /* oxlint-enable react/set-state-in-effect */
160168 },
161169 )
162170 }, 220)
Modified web/src/pages/workspace/ui/workspace-shell.tsx web/src/pages/workspace/ui/workspace-shell.tsx +3 −0 View web/src/pages/workspace/ui/workspace-shell.tsx
@@ -105,9 +105,12 @@
105105 requestedSection === 'join-requests'
106106 ? currentMySpace.permissions.includes('manage_members')
107107 : true
108 // 深链是外部输入(URL 参数):满足权限时打开设置面板,然后消费掉参数。
108109 if (canSee) {
110 /* oxlint-disable react/set-state-in-effect -- URL 参数同步,不是可派生的渲染状态 */
109111 setSettingsSection(requestedSection)
110112 setSpaceSettingsOpen(true)
113 /* oxlint-enable react/set-state-in-effect */
111114 }
112115 consumeSectionParam()
113116 }, [requestedSection, spaceName, currentMySpace, consumeSectionParam])