Files
paseo/.oxlintrc.json
T
Mohamed Boudra 345638dc7f fix(app): retire the docked explorer sidebar
Upgrading to 0.5.0-beta.1 left the old docked explorer sidebar showing
next to the new explorer pane, with no way to dismiss it.

The sidebar rendered off the persisted `desktop.fileExplorerOpen` flag.
#3287 rerouted the explorer toggle to the workspace-layout explorer pane,
so nothing on a wide layout ever wrote that flag false again, and no
migration cleared it. Assistant directory links and review attachments
could still set it. On Windows and Linux the sidebar's close button was
suppressed because the OS window controls own the top-right corner, so
there was no affordance at all.

The explorer is now one surface picked from two facts — is the layout
compact, and can the platform render split panes:

  compact                -> slide-over overlay (panel store)
  non-compact + splits   -> dedicated right pane (layout store)
  non-compact, no splits -> ordinary tabs in the focused pane

`workspace-tabs/explorer-surface.ts` owns that choice and every entry
point routes through it, so the toggle, Ctrl+E, Ctrl+., directory links,
review attachments and side file opens can no longer disagree about where
the explorer lives.

Pane splits stay gated on `isWeb`: they depend on dnd-kit and DOM-backed
accessibility helpers. Native tablets are non-compact without splits, so
they get explorer tabs rather than a second pane — deliberately trading
side-by-side agent and Changes for a surface that only renders what the
single-column tab row can actually reach.

Ctrl+. is handled by the workspace screen rather than the app shell. The
pathname alone cannot identify the active workspace: desktop cold-starts
at "/" and restores the remembered workspace from route params, so a key
derived from the pathname is null exactly when a workspace is mounted,
and the explorer reads as permanently closed.

`desktop.fileExplorerOpen` and `explorerWidth` are dropped by a migration
to panel-state v15 but stay declared in the persisted schema. That schema
is a strict object and the validated storage deletes the whole entry when
parsing fails, so removing the keys outright would wipe every upgrading
user's sidebar width, expanded paths and diff-collapse state.
2026-08-19 15:52:51 +02:00

329 lines
13 KiB
JSON

{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"options": {
"typeAware": false
},
"ignorePatterns": [".dev/**"],
"plugins": ["react", "react-perf", "unicorn", "typescript", "oxc", "import", "promise"],
"categories": {
"correctness": "error",
"suspicious": "error",
"perf": "error"
},
"rules": {
"react/react-in-jsx-scope": "off",
"no-await-in-loop": "off",
"no-unused-expressions": "error",
"no-useless-catch": "error",
"preserve-caught-error": "error",
"require-await": "off",
"no-async-promise-executor": "error",
"no-useless-escape": "error",
"no-empty-pattern": "error",
"no-self-assign": "error",
"no-shadow": "error",
"unicorn/consistent-function-scoping": "off",
"unicorn/no-array-sort": "off",
"unicorn/no-useless-spread": "error",
"unicorn/no-useless-fallback-in-spread": "error",
"unicorn/no-new-array": "error",
"unicorn/no-empty-file": "error",
"promise/always-return": "error",
"promise/no-multiple-resolved": "error",
"react/no-array-index-key": "error",
"react/jsx-no-useless-fragment": "error",
"react/jsx-no-constructed-context-values": "error",
"react/no-unescaped-entities": "error",
"react/button-has-type": "error",
"react/jsx-max-depth": ["error", { "max": 6 }],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
"react-perf/jsx-no-new-array-as-prop": "error",
"react-perf/jsx-no-new-function-as-prop": "error",
"react-perf/jsx-no-new-object-as-prop": "error",
"react-perf/jsx-no-jsx-as-prop": "error",
"oxc/no-map-spread": "error",
"oxc/no-async-endpoint-handlers": "error",
"oxc/only-used-in-recursion": "error",
"typescript/no-explicit-any": "error",
"typescript/prefer-as-const": "error",
"typescript/no-this-alias": "error",
"typescript/no-unnecessary-type-assertion": "error",
"typescript/consistent-type-definitions": ["error", "interface"],
"import/no-unassigned-import": [
"error",
{
"allow": [
"**/*.css",
"**/expo-router/entry",
"**/event-target-polyfill",
"**/dotenv/config",
"**/react-native",
"**/@/styles/unistyles",
"**/src/styles/unistyles",
"**/@/test/window-local-storage"
]
}
],
"no-nested-ternary": "error",
"no-unneeded-ternary": "error",
"complexity": ["error", { "max": 20 }],
"max-depth": ["error", { "max": 4 }],
"max-nested-callbacks": ["error", { "max": 3 }]
},
"overrides": [
{
"files": ["packages/app/src/**/*.{ts,tsx}"],
"rules": {
// React Native style arrays must read Unistyles proxies during render. Hoisting them to
// satisfy this allocation rule captures the temporary startup theme instead.
"react-perf/jsx-no-new-array-as-prop": "off",
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react-native",
"importNames": ["TextInput"],
"message": "Use EditingTextInput from @/components/ui/text-input. Raw TextInput bypasses editing-surface authority and IME safety."
},
{
"name": "@tanstack/react-query",
"importNames": ["useQuery", "useInfiniteQuery", "useQueries"],
"message": "App reads must go through useReplicaQuery/useFetchQuery from @/data/query. Grandfathered files may only leave the override burn-down list."
},
{
"name": "react-native-unistyles",
"importNames": ["useUnistyles"],
"message": "useUnistyles is banned by docs/unistyles.md. Grandfathered files may only leave the override burn-down list."
}
]
}
]
}
},
{
"files": [
"packages/app/src/data/**/*.{ts,tsx}",
"packages/app/src/**/*.test.{ts,tsx}",
"packages/app/src/**/*.spec.{ts,tsx}"
],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react-native",
"importNames": ["TextInput"],
"message": "Use EditingTextInput from @/components/ui/text-input. Raw TextInput bypasses editing-surface authority and IME safety."
},
{
"name": "react-native-unistyles",
"importNames": ["useUnistyles"],
"message": "useUnistyles is banned by docs/unistyles.md. Grandfathered files may only leave the override burn-down list."
}
]
}
]
}
},
// Raw query burn-down: 34 files total; the 28 files below still enforce the useUnistyles ban.
{
"files": [
"packages/app/src/assistant-file-links/use-file-link.ts",
"packages/app/src/components/message.tsx",
"packages/app/src/components/worktree-setup-callout-source.tsx",
"packages/app/src/desktop/hooks/use-daemon-status.ts",
"packages/app/src/desktop/hooks/use-install-status.ts",
"packages/app/src/desktop/settings/desktop-settings.ts",
"packages/app/src/git/pull-request-panel/use-data.ts",
"packages/app/src/git/use-github-search-query.ts",
"packages/app/src/git/use-pr-status-query.ts",
"packages/app/src/git/use-status-query.ts",
"packages/app/src/hooks/use-archive-agent.ts",
"packages/app/src/hooks/use-agent-autocomplete.ts",
"packages/app/src/hooks/use-agent-commands-query.ts",
"packages/app/src/hooks/use-agent-history.ts",
"packages/app/src/hooks/use-branch-switcher.ts",
"packages/app/src/hooks/use-changes-preferences/index.ts",
"packages/app/src/hooks/use-draft-agent-features.ts",
"packages/app/src/hooks/use-form-preferences.ts",
"packages/app/src/hooks/use-is-local-daemon.ts",
"packages/app/src/hooks/use-keyboard-shortcut-overrides.ts",
"packages/app/src/hooks/use-preferred-editor.ts",
"packages/app/src/hooks/use-settings/index.ts",
"packages/app/src/panels/terminal-panel.tsx",
"packages/app/src/projects/icons.ts",
"packages/app/src/provider-usage/use-provider-usage.ts",
"packages/app/src/screens/project-settings-screen.tsx",
"packages/app/src/screens/workspace/use-workspace-checkout-status.ts",
"packages/app/src/workspace/desktop-open-targets.ts"
],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react-native",
"importNames": ["TextInput"],
"message": "Use EditingTextInput from @/components/ui/text-input. Raw TextInput bypasses editing-surface authority and IME safety."
},
{
"name": "react-native-unistyles",
"importNames": ["useUnistyles"],
"message": "useUnistyles is banned by docs/unistyles.md. Grandfathered files may only leave the override burn-down list."
}
]
}
]
}
},
// useUnistyles burn-down: 74 files total; the 68 files below still enforce the raw-query ban.
{
"files": [
"packages/app/src/app/_layout.tsx",
"packages/app/src/app/pair-scan.tsx",
"packages/app/src/components/adaptive-modal-sheet.tsx",
"packages/app/src/components/add-host-method-modal.tsx",
"packages/app/src/components/add-host-modal.tsx",
"packages/app/src/components/agent-list.tsx",
"packages/app/src/components/agent-status-dot.tsx",
"packages/app/src/components/attachment-lightbox.tsx",
"packages/app/src/desktop/browser/pane/index.electron.tsx",
"packages/app/src/desktop/browser/pane/index.tsx",
"packages/app/src/desktop/browser/pane/index.web.tsx",
"packages/app/src/components/command-center.tsx",
"packages/app/src/components/compact-explorer-sidebar.tsx",
"packages/app/src/components/context-window-meter.tsx",
"packages/app/src/components/dictation-controls.tsx",
"packages/app/src/components/download-toast.tsx",
"packages/app/src/components/draggable-list.native.tsx",
"packages/app/src/components/headers/back-header.tsx",
"packages/app/src/components/headers/menu-header.tsx",
"packages/app/src/components/headers/screen-header.tsx",
"packages/app/src/components/host-status-dot.tsx",
"packages/app/src/components/hosts/host-picker.tsx",
"packages/app/src/components/icons/paseo-logo.tsx",
"packages/app/src/components/left-sidebar.tsx",
"packages/app/src/components/pair-link-modal.tsx",
"packages/app/src/components/plan-card.tsx",
"packages/app/src/components/provider-diagnostic-sheet.tsx",
"packages/app/src/components/question-form-card.tsx",
"packages/app/src/components/quitting-overlay.tsx",
"packages/app/src/components/realtime-voice-overlay.tsx",
"packages/app/src/components/resize-handle.tsx",
"packages/app/src/components/rewind/rewind-menu.tsx",
"packages/app/src/components/settings-textarea.tsx",
"packages/app/src/components/sidebar-callout.tsx",
"packages/app/src/components/split-container.tsx",
"packages/app/src/components/split-drop-zone.tsx",
"packages/app/src/components/terminal-pane.tsx",
"packages/app/src/components/toast-host.tsx",
"packages/app/src/components/tool-call-sheet.tsx",
"packages/app/src/components/ui/alert.tsx",
"packages/app/src/components/ui/autocomplete.tsx",
"packages/app/src/components/ui/combobox.tsx",
"packages/app/src/components/ui/context-menu.tsx",
"packages/app/src/components/ui/dropdown-menu.tsx",
"packages/app/src/components/ui/external-link.tsx",
"packages/app/src/components/volume-meter.tsx",
"packages/app/src/components/web-desktop-scrollbar.tsx",
"packages/app/src/components/welcome-screen.tsx",
"packages/app/src/composer/agent-controls/index.tsx",
"packages/app/src/composer/agent-controls/mode-control.tsx",
"packages/app/src/constants/layout.ts",
"packages/app/src/desktop/components/desktop-permission-row.tsx",
"packages/app/src/desktop/components/desktop-permissions-section.tsx",
"packages/app/src/desktop/components/desktop-updates-section.tsx",
"packages/app/src/desktop/components/integrations-section.tsx",
"packages/app/src/desktop/updates/update-callout-source.tsx",
"packages/app/src/git/actions-split-button.tsx",
"packages/app/src/hooks/use-web-scrollbar-style.web.ts",
"packages/app/src/hosts/host-chooser.tsx",
"packages/app/src/screens/open-project-screen.tsx",
"packages/app/src/screens/projects-screen.tsx",
"packages/app/src/screens/sessions-screen.tsx",
"packages/app/src/screens/settings-screen.tsx",
"packages/app/src/screens/settings/host-page.tsx",
"packages/app/src/screens/settings/providers-section.tsx",
"packages/app/src/screens/settings/settings-group.tsx",
"packages/app/src/screens/startup-splash-screen.tsx",
"packages/app/src/screens/workspace/workspace-route-state-views.tsx"
],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react-native",
"importNames": ["TextInput"],
"message": "Use EditingTextInput from @/components/ui/text-input. Raw TextInput bypasses editing-surface authority and IME safety."
},
{
"name": "@tanstack/react-query",
"importNames": ["useQuery", "useInfiniteQuery", "useQueries"],
"message": "App reads must go through useReplicaQuery/useFetchQuery from @/data/query. Grandfathered files may only leave the override burn-down list."
}
]
}
]
}
},
// Both burn-downs: 6 overlapping files. They may only shrink out of this exemption.
{
"files": [
"packages/app/src/components/file-explorer-pane.tsx",
"packages/app/src/components/file-pane.tsx",
"packages/app/src/components/import-session-sheet.tsx",
"packages/app/src/components/project-picker-modal.tsx",
"packages/app/src/desktop/components/pair-device-section.tsx",
"packages/app/src/screens/new-workspace-screen.tsx"
],
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react-native",
"importNames": ["TextInput"],
"message": "Use EditingTextInput from @/components/ui/text-input. Raw TextInput bypasses editing-surface authority and IME safety."
}
]
}
]
}
},
{
"files": [
"packages/app/src/components/ui/text-input/**/*.{ts,tsx}",
"packages/app/src/agent-stream/scroll-keyboard-dismiss/use-scroll-keyboard-dismiss.ts"
],
"rules": {
"no-restricted-imports": "off"
}
},
{
"files": ["**/e2e/**/fixtures.ts"],
"rules": {
"no-empty-pattern": "off"
}
}
]
}