From a372be7df6d88ba0df6355ea0e6e2fb7968c5f3b Mon Sep 17 00:00:00 2001 From: koji Date: Wed, 26 Aug 2026 22:32:30 -0400 Subject: [PATCH 1/2] add oxlint and oxfmt --- .oxfmtrc.json | 9 + .oxlintrc.json | 121 ++ .prettierrc.json | 6 - docs/Architecture.md | 2 +- docs/Instllation.md | 8 +- docs/README.md | 21 +- .../2026-08-25-protocol-visualizer-website.md | 267 ++- ...8-25-protocol-visualizer-website-design.md | 34 +- eslint.config.js | 26 - index.html | 9 +- package.json | 15 +- pnpm-lock.yaml | 1529 +++++------------ src/App.test.tsx | 23 +- src/components/BeakerLogo/BeakerLogo.test.tsx | 4 +- src/components/BeakerLogo/BeakerLogo.tsx | 18 +- src/components/Disclaimer/Disclaimer.tsx | 15 +- src/components/Features/Features.tsx | 4 +- src/components/Footer/Footer.test.tsx | 13 +- src/components/Footer/Footer.tsx | 7 +- src/components/Header/Header.test.tsx | 21 +- src/components/Hero/Hero.module.css | 10 +- src/components/Hero/Hero.test.tsx | 12 +- src/components/Install/Install.test.tsx | 19 +- src/components/Install/Install.tsx | 31 +- .../RequestAccessButton.module.css | 4 +- .../RequestAccess/RequestAccessButton.tsx | 15 +- src/components/Screenshot/Screenshot.test.tsx | 4 +- src/components/Screenshot/Screenshot.tsx | 4 +- .../ThemeToggle/ThemeToggle.module.css | 4 +- .../ThemeToggle/ThemeToggle.test.tsx | 8 +- src/components/ThemeToggle/ThemeToggle.tsx | 28 +- src/components/TryCta/TryCta.module.css | 6 +- src/components/TryCta/TryCta.test.tsx | 10 +- src/config/links.test.ts | 8 +- src/hooks/useTheme.ts | 3 +- src/styles/tokens.css | 8 +- 36 files changed, 1046 insertions(+), 1280 deletions(-) create mode 100644 .oxfmtrc.json create mode 100644 .oxlintrc.json delete mode 100644 .prettierrc.json delete mode 100644 eslint.config.js diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..70ddfca --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,9 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "singleQuote": true, + "semi": false, + "printWidth": 80, + "trailingComma": "all", + "sortPackageJson": false, + "ignorePatterns": [] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..034bdd3 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,121 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [], + "categories": { + "correctness": "off" + }, + "env": { + "builtin": true + }, + "ignorePatterns": ["dist"], + "overrides": [ + { + "files": ["**/*.{ts,tsx}"], + "rules": { + "constructor-super": "off", + "for-direction": "error", + "getter-return": "off", + "no-async-promise-executor": "error", + "no-case-declarations": "error", + "no-class-assign": "off", + "no-compare-neg-zero": "error", + "no-cond-assign": "error", + "no-const-assign": "off", + "no-constant-binary-expression": "error", + "no-constant-condition": "error", + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-class-members": "off", + "no-dupe-else-if": "error", + "no-dupe-keys": "off", + "no-duplicate-case": "error", + "no-empty": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-empty-static-block": "error", + "no-ex-assign": "error", + "no-extra-boolean-cast": "error", + "no-fallthrough": "error", + "no-func-assign": "off", + "no-global-assign": "error", + "no-import-assign": "off", + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-loss-of-precision": "error", + "no-misleading-character-class": "error", + "no-new-native-nonconstructor": "off", + "no-nonoctal-decimal-escape": "error", + "no-obj-calls": "off", + "no-prototype-builtins": "error", + "no-redeclare": "off", + "no-regex-spaces": "error", + "no-self-assign": "error", + "no-setter-return": "off", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-this-before-super": "off", + "no-unassigned-vars": "error", + "no-unexpected-multiline": "error", + "no-unreachable": "off", + "no-unsafe-finally": "error", + "no-unsafe-negation": "off", + "no-unsafe-optional-chaining": "error", + "no-unused-labels": "error", + "no-unused-private-class-members": "error", + "no-unused-vars": "error", + "no-useless-backreference": "error", + "no-useless-catch": "error", + "no-useless-escape": "error", + "no-with": "off", + "preserve-caught-error": "error", + "require-yield": "error", + "use-isnan": "error", + "valid-typeof": "error", + "no-var": "error", + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "no-array-constructor": "error", + "no-unused-expressions": "error", + "typescript/ban-ts-comment": "error", + "typescript/no-duplicate-enum-values": "error", + "typescript/no-empty-object-type": "error", + "typescript/no-explicit-any": "error", + "typescript/no-extra-non-null-assertion": "error", + "typescript/no-misused-new": "error", + "typescript/no-namespace": "error", + "typescript/no-non-null-asserted-optional-chain": "error", + "typescript/no-require-imports": "error", + "typescript/no-this-alias": "error", + "typescript/no-unnecessary-type-constraint": "error", + "typescript/no-unsafe-declaration-merging": "error", + "typescript/no-unsafe-function-type": "error", + "typescript/no-wrapper-object-types": "error", + "typescript/prefer-as-const": "error", + "typescript/prefer-namespace-keyword": "error", + "typescript/triple-slash-reference": "error", + "react/rules-of-hooks": "error", + "react/exhaustive-deps": "warn", + "react/static-components": "error", + "react/use-memo": "error", + "react/preserve-manual-memoization": "error", + "react/incompatible-library": "warn", + "react/immutability": "error", + "react/globals": "error", + "react/refs": "error", + "react/set-state-in-effect": "error", + "react/error-boundaries": "error", + "react/purity": "error", + "react/set-state-in-render": "error", + "react/unsupported-syntax": "warn", + "react/only-export-components": "off" + }, + "plugins": ["typescript", "react"], + "env": { + "es2022": true, + "browser": true + } + } + ] +} diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 4d1655f..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "singleQuote": true, - "semi": false, - "printWidth": 100, - "trailingComma": "all" -} diff --git a/docs/Architecture.md b/docs/Architecture.md index 9b10089..846b1e7 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -1,6 +1,6 @@ # Overall Architecture and Data Flow -This extension is designed to operate through the collaboration of three components: the **VSCode Extension Host (backend)**, **React Webview (frontend)**, and **Python Subprocess (simulation environment)**. +This extension is designed to operate through the collaboration of three components: the **VSCode Extension Host (backend)**, **React Webview (frontend)**, and **Python Subprocess (simulation environment)**. ``` [ VSCode Editor ] ➔ (Click the "Reanalyze" button) diff --git a/docs/Instllation.md b/docs/Instllation.md index 74c9531..9efcdc4 100644 --- a/docs/Instllation.md +++ b/docs/Instllation.md @@ -1,4 +1,3 @@ - # Setup Protocol Vizualizer ## 1. Prerequisites @@ -6,13 +5,13 @@ The following are required to use the extension: - **Python 3.8 or later** must be installed, and the `python3` command must be available in PATH -- The **`opentrons`** Python package must be installed +- The **`opentrons`** Python package must be installed ```bash pip install opentrons ``` -**Note:** +**Note:** If you need to use Protocol Vizualizer with an OT-2, you will need to use opentrons==9.0.0 pacakge since opentrons 9.1.0+ is not compatible with OT-2. ```bash @@ -20,7 +19,8 @@ pip install opentrons==9.0.0 ``` ## 2. Installation (from `.vsix`) -Download `protocol-viz-x.x.x.vsix` from GitHub Releases or another source, then install it in VSCode using one of the following methods. + +Download `protocol-viz-x.x.x.vsix` from GitHub Releases or another source, then install it in VSCode using one of the following methods. **Method A: From the Command Palette** diff --git a/docs/README.md b/docs/README.md index 6d2beb7..99ccc25 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,7 +9,7 @@ A VSCode extension (prototype) for simulating Opentrons Python protocols directl The following are required to use the extension: - **Python 3.8 or later** must be installed, and the `python3` command must be available in PATH -- The **`opentrons`** Python package must be installed +- The **`opentrons`** Python package must be installed ```bash pip install opentrons @@ -24,6 +24,7 @@ pip install opentrons==9.0.0 If you want to use this extension with Flex and OT-2, you will need to create a virtual environment and install opentrons packages separately. Then switch an environment on VSCode's Python Interpreter via `Command Palette`. ### Create a Virtual Environments + The following uses `python3` to create virtual environments because it does not require to install any packages or software. You can use any virtual environment manager you prefer such as `conda` or `uv`. ```bash @@ -38,42 +39,48 @@ source .OT2/bin/activate pip install opentrons ``` - --- ## 2. Usage + 1. Open an Opentrons protocol `.py` file in VSCode. 2. Start Protocol Visualizer. Currently, there are 3 ways to start it: - 1. From the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`), run **`Opentrons: Open Protocol Visualizer`**. - 2. Click the **beaker** icon in the status bar. - 3. Set a new shortcut via Command Pallets `Open Keyboard Shortcuts` and start it with your custom shortcut. -3. When the Visualizer panel opens on the right side of the screen, and `If your protocol uses Runtime parameters`, Protocol Visualizer will generate UI inputs for these parameters. - +3. From the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`), run **`Opentrons: Open Protocol Visualizer`**. +4. Click the **beaker** icon in the status bar. +5. Set a new shortcut via Command Pallets `Open Keyboard Shortcuts` and start it with your custom shortcut. +6. When the Visualizer panel opens on the right side of the screen, and `If your protocol uses Runtime parameters`, Protocol Visualizer will generate UI inputs for these parameters. `Once the analysis is complete, you are ready to edit your protocol.` ### 2-1 Custom labware + This extension supports custom labware definitions. You need to place your custom labware definition files in the same directory as your protocol file. ### 2-2 Auto-analysis + Protocol Visualizer starts analysis automatically when you save changes to your protocol file. ### 2-3 Pop out button + This extension is using VSCode's Auxiliary Window, so you can pop out the Visualizer panel to a separate window. Also you can do the same thing with dragging the Protocol Visualizer tab to somewhere of your screen. ### 2-4 Runtime parameters + For Runtime parameters, you will need to click `Analyze` button to apply your changes to your protocol. `If you change a Runtime parameter, Protocol Visualizer will not change your protocol.` Runtime parameters function creates a temporary protocol file that applyes your changes as a default parameter value for the analysis and it will be removed whne the visualization is done. ### 2-5 Step Jumper + The step jumper that is located under `Protocol Steps` allows you to jump to a specific step in your protocol. Input the step number and hit `Enter` to jump to that step. --- ## 3. Errors and bugs + If you encounter any errors or bugs, please report them to the #visualization-extension channel or Koji Kanao via DM/email. --- ## 4. License + MIT License © 2026 Koji Kanao diff --git a/docs/superpowers/plans/2026-08-25-protocol-visualizer-website.md b/docs/superpowers/plans/2026-08-25-protocol-visualizer-website.md index c2ef190..a036030 100644 --- a/docs/superpowers/plans/2026-08-25-protocol-visualizer-website.md +++ b/docs/superpowers/plans/2026-08-25-protocol-visualizer-website.md @@ -30,6 +30,7 @@ ### Task 1: Scaffold project and tooling **Files:** + - Create: `package.json` (overwrite existing stub) - Create: `tsconfig.json` - Create: `vite.config.ts` @@ -44,6 +45,7 @@ - Create: `src/test/setup.ts` **Interfaces:** + - Consumes: nothing. - Produces: runnable Vite app whose default export is `App` from `src/App.tsx` returning `ReactNode`; test scripts `npm run lint`, `npm run test`, `npm run build`. Later tasks replace `App.tsx` content but keep the default export shape. @@ -238,6 +240,7 @@ import '@testing-library/jest-dom/vitest' - [ ] **Step 8: Install dependencies** Run: + ```bash npm install react@^19 react-dom@^19 npm install -D vite @vitejs/plugin-react typescript@~5.9 eslint @eslint/js typescript-eslint eslint-plugin-react-hooks eslint-plugin-react-refresh globals prettier vitest jsdom @testing-library/react @testing-library/jest-dom @types/react@^19 @types/react-dom@^19 @@ -262,6 +265,7 @@ git commit -m "chore: scaffold Vite + React 19 + TypeScript + Vitest tooling" ### Task 2: Theme system (tokens, useTheme, ThemeToggle, FOUC script) **Files:** + - Create: `src/styles/tokens.css` - Create: `src/hooks/useTheme.ts` - Test: `src/hooks/useTheme.test.ts` @@ -274,6 +278,7 @@ git commit -m "chore: scaffold Vite + React 19 + TypeScript + Vitest tooling" - Modify: `src/App.tsx` (own theme state via useTheme) **Interfaces:** + - Consumes: nothing new. - Produces: - `export type Theme = 'dark' | 'light'` from `src/hooks/useTheme.ts` @@ -399,7 +404,8 @@ function getInitialTheme(): Theme { // localStorage unavailable (e.g. private mode) — fall through } try { - if (window.matchMedia('(prefers-color-scheme: light)').matches) return 'light' + if (window.matchMedia('(prefers-color-scheme: light)').matches) + return 'light' } catch { // matchMedia unavailable — default below } @@ -447,13 +453,17 @@ describe('ThemeToggle', () => { it('offers switching to light when current theme is dark', () => { const onToggle = vi.fn() render() - expect(screen.getByRole('button', { name: 'Switch to light theme' })).toBeInTheDocument() + expect( + screen.getByRole('button', { name: 'Switch to light theme' }), + ).toBeInTheDocument() }) it('offers switching to dark when current theme is light', () => { const onToggle = vi.fn() render() - expect(screen.getByRole('button', { name: 'Switch to dark theme' })).toBeInTheDocument() + expect( + screen.getByRole('button', { name: 'Switch to dark theme' }), + ).toBeInTheDocument() }) it('calls onToggleTheme when clicked', () => { @@ -491,7 +501,16 @@ const LABELS: Record = { function SunIcon() { return ( -