From ec46a1730ef2acdf56f00eb748c84bd89e39bc2e Mon Sep 17 00:00:00 2001 From: Simon Chan <1330321+yume-chan@users.noreply.github.com> Date: Fri, 15 Sep 2023 18:26:34 +0800 Subject: [PATCH] chore: remove deprecated packages --- .github/workflows/deploy.yml | 40 - CONTRIBUTING.md | 24 - README.md | 51 +- apps/demo/.eslintrc.json | 3 - apps/demo/.gitignore | 41 - apps/demo/README.md | 34 - apps/demo/next-env.d.ts | 5 - apps/demo/next.config.js | 79 - apps/demo/package.json | 64 - apps/demo/public/StreamSaver/mitm.html | 170 - apps/demo/public/StreamSaver/sw.js | 156 - apps/demo/public/favicon-256.png | Bin 5214 -> 0 bytes apps/demo/public/favicon.ico | Bin 25931 -> 0 bytes apps/demo/scripts/manifest.mjs | 29 - .../components/command-bar-spacer-item.tsx | 20 - apps/demo/src/components/command-bar.tsx | 16 - apps/demo/src/components/connect.tsx | 389 -- apps/demo/src/components/demo-mode-panel.tsx | 407 -- apps/demo/src/components/device-view.tsx | 161 - apps/demo/src/components/error-dialog.tsx | 33 - apps/demo/src/components/external-link.tsx | 25 - apps/demo/src/components/grid.tsx | 384 -- apps/demo/src/components/hex-viewer.tsx | 113 - apps/demo/src/components/index.ts | 12 - apps/demo/src/components/list-selection.ts | 150 - apps/demo/src/components/log-view.tsx | 16 - apps/demo/src/components/resize-observer.tsx | 49 - .../components/scrcpy/audio-decode-stream.ts | 119 - .../src/components/scrcpy/command-bar.tsx | 290 - .../src/components/scrcpy/fetch-server.ts | 79 - apps/demo/src/components/scrcpy/index.ts | 6 - apps/demo/src/components/scrcpy/input.ts | 224 - .../src/components/scrcpy/navigation-bar.tsx | 183 - apps/demo/src/components/scrcpy/recorder.ts | 456 -- apps/demo/src/components/scrcpy/settings.tsx | 607 -- apps/demo/src/components/scrcpy/state.tsx | 637 -- .../src/components/scrcpy/video-container.tsx | 180 - .../src/components/tabby-frame-manager.tsx | 178 - apps/demo/src/hooks/add-event-listener.ts | 40 - apps/demo/src/hooks/index.ts | 4 - apps/demo/src/hooks/layout-effect.ts | 4 - apps/demo/src/hooks/local-storage.ts | 34 - apps/demo/src/hooks/stable-callback.ts | 35 - apps/demo/src/pages/_app.tsx | 224 - apps/demo/src/pages/_document.tsx | 46 - apps/demo/src/pages/_offline.tsx | 10 - apps/demo/src/pages/audio.tsx | 81 - apps/demo/src/pages/bug-report.tsx | 165 - apps/demo/src/pages/chrome-devtools-frame.tsx | 124 - apps/demo/src/pages/chrome-devtools.tsx | 458 -- apps/demo/src/pages/device-info.tsx | 106 - apps/demo/src/pages/file-manager.tsx | 954 --- apps/demo/src/pages/framebuffer.tsx | 168 - apps/demo/src/pages/index.mdx | 58 - apps/demo/src/pages/install.tsx | 188 - apps/demo/src/pages/logcat.tsx | 833 --- apps/demo/src/pages/packet-log.tsx | 358 -- apps/demo/src/pages/power.tsx | 130 - apps/demo/src/pages/reverse.tsx | 50 - apps/demo/src/pages/scrcpy.tsx | 335 - apps/demo/src/pages/shell.tsx | 41 - apps/demo/src/pages/tabby-frame.tsx | 25 - apps/demo/src/pages/tcpip.tsx | 252 - apps/demo/src/state/global.ts | 64 - apps/demo/src/state/index.ts | 1 - apps/demo/src/styles/globals.css | 27 - apps/demo/src/utils/async-effect.ts | 42 - apps/demo/src/utils/file-size.ts | 88 - apps/demo/src/utils/file.ts | 76 - apps/demo/src/utils/icons.tsx | 189 - apps/demo/src/utils/index.ts | 6 - apps/demo/src/utils/styles.ts | 15 - apps/demo/src/utils/with-display-name.ts | 28 - apps/demo/streamsaver.d.ts | 41 - apps/demo/tsconfig.json | 32 - apps/demo/types/file-loader.d.ts | 4 - common/config/rush/pnpm-lock.yaml | 5714 +---------------- common/config/rush/repo-state.json | 2 +- .../adb-daemon-direct-sockets/.eslintrc.cjs | 11 - .../adb-daemon-direct-sockets/.npmignore | 16 - libraries/adb-daemon-direct-sockets/README.md | 5 - .../adb-daemon-direct-sockets/package.json | 46 - .../adb-daemon-direct-sockets/src/index.ts | 83 - .../tsconfig.build.json | 17 - .../adb-daemon-direct-sockets/tsconfig.json | 7 - libraries/adb-daemon-ws/.eslintrc.cjs | 11 - libraries/adb-daemon-ws/.npmignore | 16 - libraries/adb-daemon-ws/README.md | 9 - libraries/adb-daemon-ws/package.json | 46 - libraries/adb-daemon-ws/src/index.ts | 87 - libraries/adb-daemon-ws/tsconfig.build.json | 9 - libraries/adb-daemon-ws/tsconfig.json | 13 - libraries/b-tree/.eslintrc.cjs | 11 - libraries/b-tree/.npmignore | 16 - libraries/b-tree/CHANGELOG.json | 17 - libraries/b-tree/CHANGELOG.md | 14 - libraries/b-tree/LICENSE | 21 - libraries/b-tree/README.md | 10 - libraries/b-tree/jest.config.js | 14 - libraries/b-tree/package.json | 50 - libraries/b-tree/src/index.spec.ts | 98 - libraries/b-tree/src/index.ts | 418 -- libraries/b-tree/tsconfig.build.json | 3 - libraries/b-tree/tsconfig.json | 10 - libraries/b-tree/tsconfig.test.json | 8 - libraries/tabby-launcher/README.md | 5 - libraries/tabby-launcher/app/assets/logo.svg | 1 - .../tabby-launcher/app/src/app.module.ts | 42 - libraries/tabby-launcher/app/src/global.scss | 201 - libraries/tabby-launcher/app/src/preload.scss | 67 - libraries/tabby-launcher/app/src/toastr.scss | 28 - libraries/tabby-launcher/browser/net.js | 7 - .../tabby-launcher/browser/node-url.d.ts | 3 - libraries/tabby-launcher/browser/os.js | 7 - libraries/tabby-launcher/browser/process.js | 18 - .../tabby-launcher/browser/setImmediate.js | 3 - libraries/tabby-launcher/browser/tls.js | 3 - libraries/tabby-launcher/browser/url.js | 1 - libraries/tabby-launcher/browser/zlib.js | 39 - libraries/tabby-launcher/package.json | 70 - libraries/tabby-launcher/scripts/loader.mjs | 16 - libraries/tabby-launcher/src/entry.ts | 339 - libraries/tabby-launcher/tsconfig.json | 20 - libraries/tabby-launcher/webpack.config.mjs | 113 - libraries/tabby-tango/package.json | 66 - libraries/tabby-tango/src/buttonProvider.ts | 44 - .../src/components/terminalTab.component.ts | 44 - libraries/tabby-tango/src/icons/plus.svg | 1 - libraries/tabby-tango/src/index.ts | 36 - libraries/tabby-tango/src/profiles.ts | 41 - libraries/tabby-tango/src/session.ts | 65 - libraries/tabby-tango/src/state.ts | 6 - libraries/tabby-tango/tsconfig.json | 37 - libraries/tabby-tango/webpack.config.mjs | 11 - .../tabby-tango/webpack.plugin.config.mjs | 181 - rush.json | 26 - 136 files changed, 134 insertions(+), 18955 deletions(-) delete mode 100644 .github/workflows/deploy.yml delete mode 100644 apps/demo/.eslintrc.json delete mode 100644 apps/demo/.gitignore delete mode 100644 apps/demo/README.md delete mode 100644 apps/demo/next-env.d.ts delete mode 100644 apps/demo/next.config.js delete mode 100644 apps/demo/package.json delete mode 100644 apps/demo/public/StreamSaver/mitm.html delete mode 100644 apps/demo/public/StreamSaver/sw.js delete mode 100644 apps/demo/public/favicon-256.png delete mode 100644 apps/demo/public/favicon.ico delete mode 100644 apps/demo/scripts/manifest.mjs delete mode 100644 apps/demo/src/components/command-bar-spacer-item.tsx delete mode 100644 apps/demo/src/components/command-bar.tsx delete mode 100644 apps/demo/src/components/connect.tsx delete mode 100644 apps/demo/src/components/demo-mode-panel.tsx delete mode 100644 apps/demo/src/components/device-view.tsx delete mode 100644 apps/demo/src/components/error-dialog.tsx delete mode 100644 apps/demo/src/components/external-link.tsx delete mode 100644 apps/demo/src/components/grid.tsx delete mode 100644 apps/demo/src/components/hex-viewer.tsx delete mode 100644 apps/demo/src/components/index.ts delete mode 100644 apps/demo/src/components/list-selection.ts delete mode 100644 apps/demo/src/components/log-view.tsx delete mode 100644 apps/demo/src/components/resize-observer.tsx delete mode 100644 apps/demo/src/components/scrcpy/audio-decode-stream.ts delete mode 100644 apps/demo/src/components/scrcpy/command-bar.tsx delete mode 100644 apps/demo/src/components/scrcpy/fetch-server.ts delete mode 100644 apps/demo/src/components/scrcpy/index.ts delete mode 100644 apps/demo/src/components/scrcpy/input.ts delete mode 100644 apps/demo/src/components/scrcpy/navigation-bar.tsx delete mode 100644 apps/demo/src/components/scrcpy/recorder.ts delete mode 100644 apps/demo/src/components/scrcpy/settings.tsx delete mode 100644 apps/demo/src/components/scrcpy/state.tsx delete mode 100644 apps/demo/src/components/scrcpy/video-container.tsx delete mode 100644 apps/demo/src/components/tabby-frame-manager.tsx delete mode 100644 apps/demo/src/hooks/add-event-listener.ts delete mode 100644 apps/demo/src/hooks/index.ts delete mode 100644 apps/demo/src/hooks/layout-effect.ts delete mode 100644 apps/demo/src/hooks/local-storage.ts delete mode 100644 apps/demo/src/hooks/stable-callback.ts delete mode 100644 apps/demo/src/pages/_app.tsx delete mode 100644 apps/demo/src/pages/_document.tsx delete mode 100644 apps/demo/src/pages/_offline.tsx delete mode 100644 apps/demo/src/pages/audio.tsx delete mode 100644 apps/demo/src/pages/bug-report.tsx delete mode 100644 apps/demo/src/pages/chrome-devtools-frame.tsx delete mode 100644 apps/demo/src/pages/chrome-devtools.tsx delete mode 100644 apps/demo/src/pages/device-info.tsx delete mode 100644 apps/demo/src/pages/file-manager.tsx delete mode 100644 apps/demo/src/pages/framebuffer.tsx delete mode 100644 apps/demo/src/pages/index.mdx delete mode 100644 apps/demo/src/pages/install.tsx delete mode 100644 apps/demo/src/pages/logcat.tsx delete mode 100644 apps/demo/src/pages/packet-log.tsx delete mode 100644 apps/demo/src/pages/power.tsx delete mode 100644 apps/demo/src/pages/reverse.tsx delete mode 100644 apps/demo/src/pages/scrcpy.tsx delete mode 100644 apps/demo/src/pages/shell.tsx delete mode 100644 apps/demo/src/pages/tabby-frame.tsx delete mode 100644 apps/demo/src/pages/tcpip.tsx delete mode 100644 apps/demo/src/state/global.ts delete mode 100644 apps/demo/src/state/index.ts delete mode 100644 apps/demo/src/styles/globals.css delete mode 100644 apps/demo/src/utils/async-effect.ts delete mode 100644 apps/demo/src/utils/file-size.ts delete mode 100644 apps/demo/src/utils/file.ts delete mode 100644 apps/demo/src/utils/icons.tsx delete mode 100644 apps/demo/src/utils/index.ts delete mode 100644 apps/demo/src/utils/styles.ts delete mode 100644 apps/demo/src/utils/with-display-name.ts delete mode 100644 apps/demo/streamsaver.d.ts delete mode 100644 apps/demo/tsconfig.json delete mode 100644 apps/demo/types/file-loader.d.ts delete mode 100644 libraries/adb-daemon-direct-sockets/.eslintrc.cjs delete mode 100644 libraries/adb-daemon-direct-sockets/.npmignore delete mode 100644 libraries/adb-daemon-direct-sockets/README.md delete mode 100644 libraries/adb-daemon-direct-sockets/package.json delete mode 100644 libraries/adb-daemon-direct-sockets/src/index.ts delete mode 100644 libraries/adb-daemon-direct-sockets/tsconfig.build.json delete mode 100644 libraries/adb-daemon-direct-sockets/tsconfig.json delete mode 100644 libraries/adb-daemon-ws/.eslintrc.cjs delete mode 100644 libraries/adb-daemon-ws/.npmignore delete mode 100644 libraries/adb-daemon-ws/README.md delete mode 100644 libraries/adb-daemon-ws/package.json delete mode 100644 libraries/adb-daemon-ws/src/index.ts delete mode 100644 libraries/adb-daemon-ws/tsconfig.build.json delete mode 100644 libraries/adb-daemon-ws/tsconfig.json delete mode 100644 libraries/b-tree/.eslintrc.cjs delete mode 100644 libraries/b-tree/.npmignore delete mode 100644 libraries/b-tree/CHANGELOG.json delete mode 100644 libraries/b-tree/CHANGELOG.md delete mode 100644 libraries/b-tree/LICENSE delete mode 100644 libraries/b-tree/README.md delete mode 100644 libraries/b-tree/jest.config.js delete mode 100644 libraries/b-tree/package.json delete mode 100644 libraries/b-tree/src/index.spec.ts delete mode 100644 libraries/b-tree/src/index.ts delete mode 100644 libraries/b-tree/tsconfig.build.json delete mode 100644 libraries/b-tree/tsconfig.json delete mode 100644 libraries/b-tree/tsconfig.test.json delete mode 100644 libraries/tabby-launcher/README.md delete mode 100644 libraries/tabby-launcher/app/assets/logo.svg delete mode 100644 libraries/tabby-launcher/app/src/app.module.ts delete mode 100644 libraries/tabby-launcher/app/src/global.scss delete mode 100644 libraries/tabby-launcher/app/src/preload.scss delete mode 100644 libraries/tabby-launcher/app/src/toastr.scss delete mode 100644 libraries/tabby-launcher/browser/net.js delete mode 100644 libraries/tabby-launcher/browser/node-url.d.ts delete mode 100644 libraries/tabby-launcher/browser/os.js delete mode 100644 libraries/tabby-launcher/browser/process.js delete mode 100644 libraries/tabby-launcher/browser/setImmediate.js delete mode 100644 libraries/tabby-launcher/browser/tls.js delete mode 100644 libraries/tabby-launcher/browser/url.js delete mode 100644 libraries/tabby-launcher/browser/zlib.js delete mode 100644 libraries/tabby-launcher/package.json delete mode 100644 libraries/tabby-launcher/scripts/loader.mjs delete mode 100644 libraries/tabby-launcher/src/entry.ts delete mode 100644 libraries/tabby-launcher/tsconfig.json delete mode 100644 libraries/tabby-launcher/webpack.config.mjs delete mode 100644 libraries/tabby-tango/package.json delete mode 100644 libraries/tabby-tango/src/buttonProvider.ts delete mode 100644 libraries/tabby-tango/src/components/terminalTab.component.ts delete mode 100644 libraries/tabby-tango/src/icons/plus.svg delete mode 100644 libraries/tabby-tango/src/index.ts delete mode 100644 libraries/tabby-tango/src/profiles.ts delete mode 100644 libraries/tabby-tango/src/session.ts delete mode 100644 libraries/tabby-tango/src/state.ts delete mode 100644 libraries/tabby-tango/tsconfig.json delete mode 100644 libraries/tabby-tango/webpack.config.mjs delete mode 100644 libraries/tabby-tango/webpack.plugin.config.mjs diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 3a5f323d..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,40 +0,0 @@ -# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Deploy - -on: - push: - branches: [main] - -env: - BASE_PATH: /ya-webadb - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Use Node.js 18.x - uses: actions/setup-node@v3 - with: - node-version: 18.x - - - run: node common/scripts/install-run-rush.js install - - run: node common/scripts/install-run-rush.js build --verbose - - - run: npx next export - working-directory: ./apps/demo - - - run: touch apps/demo/out/.nojekyll - - - name: Deploy - uses: s0/git-publish-subdir-action@develop - env: - REPO: self - BRANCH: gh-pages - FOLDER: apps/demo/out - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5548099f..6095fcd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,30 +37,6 @@ $ rush install $ rush build:watch ``` -- Start demo's dev-server: - - ```sh - $ cd apps/demo - $ npm run dev - ``` - -Usually you need two terminals to run both 2 and 3 to test your changes. - -## Deploy Demo - -The demo is built with [Next.js](https://nextjs.org/), a full-stack React framework, which usually requires a Node.js environment to run. - -However, since the demo doesn't have any server-side code, the most simple deployment method is to use the [Static HTML Export](https://nextjs.org/docs/advanced-features/static-html-export) feature of Next.js. It generates pre-rendered, fully static HTML files, that can be deployed to any static website hosting services (e.g. GitHub Pages). - -To export static deployable HTML files, after running `rush build` command, run: - -```sh -cd apps/demo -npx next export -``` - -This will create an `out` folder containing exported HTML files and all required resource files. - ## Release new versions ```sh diff --git a/README.md b/README.md index a8824be1..79f6e961 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,9 @@ A library and a Web app that allow browsers to interact with Android devices via ADB (Android Debugging Protocol). -All features work on Chrome for Android, use a C-to-C (or OTG) cable or via WebSockify running in Termux (see [compatibility table](#compatibility) below). +All features work on Chrome for Android, use a C-to-C (or OTG) cable. -[🚀 Web App](https://tango-web-mu.vercel.app/) | [Old demo](https://yume-chan.github.io/ya-webadb) - -For USB connection, close Google ADB (Run `adb kill-server` in a terminal or close `adb.exe` from Task Manager) and all programs that may use ADB (e.g. Android Studio, Visual Studio, Godot Editor, etc.) before connecting. +[🚀 Web App](https://tango-web-mu.vercel.app/) | [Old Demo](https://tango-adb.github.io/old-demo/) ## Working Modes @@ -18,49 +16,30 @@ In this mode, Google ADB is not required for this library to communicate with An This mode is suitable for running on end-users' devices where Google ADB is not installed, or on mobile devices where Google ADB is not available. +Before connecting, make sure to close Google ADB (Run `adb kill-server` in a terminal or close `adb.exe` from Task Manager) and all programs that may use ADB (e.g. Android Studio, Visual Studio, Godot Editor, etc.). + ### Google ADB Client Mode In this mode, this library talks to a Google ADB server, which is either running on the same machine or on a remote machine. This allows other ADB-based tools to work alongside this library. ## Compatibility -| Connection | Chromium-based Browsers | Firefox | Node.js | -| ----------------------------------------- | -------------------------------- | --------- | ----------------------------- | -| USB cable | Supported using [WebUSB] API | No | Supported using `usb` package | -| Wireless through [WebSocket] 1 | Supported | Supported | Possible using `ws` package | -| Wireless through TCP | Waiting for [Direct Sockets] API | No | Possible using `net` module | +| Connection | Chromium-based Browsers | Node.js | +| -------------------------------- | -------------------------------- | ----------------------------- | +| Direct Connection over USB cable | Supported using [WebUSB] API | Supported using `usb` package | +| Direct Connection over TCP | Waiting for [Direct Sockets] API | Possible using `net` module | +| Google ADB client over TCP | Waiting for [Direct Sockets] API | Supported using `net` module | [webusb]: https://wicg.github.io/webusb/ -[websocket]: https://websockets.spec.whatwg.org/ [direct sockets]: https://wicg.github.io/direct-sockets/ -1 Requires WebSockify softwares, see [instruction](https://github.com/yume-chan/ya-webadb/discussions/245#discussioncomment-384030) for detail. +## API documentation -## Features +Currently the API is unstable and the documentation is lacking, but there are three sources you can refer to: -- 📁 File Management - - 📋 List - - ⬆ Upload - - ⬇ Download - - 🗑 Delete -- 📷 Screen Capture -- 📜 Terminal Emulator powered by [Tabby](https://github.com/Eugeny/tabby) - - Tabs and split panes - - Color themes - - Rich configuration -- ⚙ Enable ADB over WiFi -- 📦 Install APK -- 🎥 [Scrcpy](https://github.com/Genymobile/scrcpy) compatible client - - Screen mirroring - - Audio forwarding (Android >= 11) - - Recording - - Control device with mouse, touch and keyboard -- 🐛 Chrome Remote Debugging that supporting - - Google Chrome (stable, beta, dev, canary) - - Microsoft Edge (stable, beta, dev, canary) - - Opera (stable, beta) - - Vivaldi -- 🔌 Power and reboot to different modes +- Each package's `README.md` file +- The source code of old demo at https://github.com/tango-adb/old-demo (it's a React app) +- The work-in-progress documentation site at https://yume-chan.github.io/unofficial-adb-book/adb/installation ## Contribute @@ -86,6 +65,4 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) - [ADB](https://android.googlesource.com/platform/packages/modules/adb) from Google ([Apache License 2.0](./adb.NOTICE)) - [Scrcpy](https://github.com/Genymobile/scrcpy) from Romain Vimont ([Apache License 2.0](https://github.com/Genymobile/scrcpy/blob/master/LICENSE)) -- [Tabby](https://github.com/Eugeny/tabby) from Eugeny ([MIT License](https://github.com/Eugeny/tabby/blob/master/LICENSE)) -- [webm-muxer](https://github.com/Vanilagy/webm-muxer) from Vanilagy ([MIT License](https://github.com/Vanilagy/webm-muxer/blob/main/LICENSE)) - [web-streams-polyfill](https://github.com/MattiasBuelens/web-streams-polyfill) from Mattias Buelens ([MIT License](https://github.com/MattiasBuelens/web-streams-polyfill/blob/master/LICENSE)) diff --git a/apps/demo/.eslintrc.json b/apps/demo/.eslintrc.json deleted file mode 100644 index bffb357a..00000000 --- a/apps/demo/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/apps/demo/.gitignore b/apps/demo/.gitignore deleted file mode 100644 index 85533e07..00000000 --- a/apps/demo/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env.local -.env.development.local -.env.test.local -.env.production.local - -# vercel -.vercel - -public/manifest.json -public/fallback-*.js -public/sw.js -public/sw.js.map -public/workbox-*.js -public/workbox-*.js.map diff --git a/apps/demo/README.md b/apps/demo/README.md deleted file mode 100644 index c87e0421..00000000 --- a/apps/demo/README.md +++ /dev/null @@ -1,34 +0,0 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. - -[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. - -The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/demo/next-env.d.ts b/apps/demo/next-env.d.ts deleted file mode 100644 index 4f11a03d..00000000 --- a/apps/demo/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/demo/next.config.js b/apps/demo/next.config.js deleted file mode 100644 index c04d0214..00000000 --- a/apps/demo/next.config.js +++ /dev/null @@ -1,79 +0,0 @@ -const withMDX = require("@next/mdx")({ - extension: /\.mdx?$/, - options: { - // Disable MDX createElement hack - // because we don't need rendering custom elements - jsx: true, - }, -}); - -const withBundleAnalyzer = require("@next/bundle-analyzer")({ - enabled: process.env.ANALYZE === "true", -}); - -const basePath = process.env.BASE_PATH ?? ""; - -const withPwa = require("@yume-chan/next-pwa")({ - dest: "public", -}); - -function pipe(value, ...callbacks) { - for (const callback of callbacks) { - value = callback(value); - } - return value; -} - -module.exports = pipe( - /** @type {import('next').NextConfig} */ ({ - basePath, - pageExtensions: ["js", "jsx", "ts", "tsx", "md", "mdx"], - reactStrictMode: false, - productionBrowserSourceMaps: true, - experimental: { - // Workaround https://github.com/vercel/next.js/issues/33914 - esmExternals: "loose", - }, - publicRuntimeConfig: { - basePath, - }, - webpack(config) { - config.module.rules.push({ - test: /.*\.m?js$/, - // disable these modules because they generate a lot of warnings about - // non existing source maps - // we cannot filter these warnings via config.stats.warningsFilter - // because Next.js doesn't allow it - // https://github.com/vercel/next.js/pull/7550#issuecomment-512861158 - // https://github.com/vercel/next.js/issues/12861 - exclude: [/next/], - use: ["source-map-loader"], - enforce: "pre", - }); - - return config; - }, - // Enable Direct Sockets API - async headers() { - return [ - { - source: "/:path*", - headers: [ - { - key: "Cross-Origin-Opener-Policy", - value: "same-origin", - }, - { - key: "Cross-Origin-Embedder-Policy", - value: "credentialless", - }, - ], - }, - ]; - }, - poweredByHeader: false, - }), - withBundleAnalyzer, - withPwa, - withMDX -); diff --git a/apps/demo/package.json b/apps/demo/package.json deleted file mode 100644 index 4fc33917..00000000 --- a/apps/demo/package.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "name": "demo", - "version": "0.1.0", - "private": true, - "scripts": { - "postinstall": "fetch-scrcpy-server 2.1.1 && node scripts/manifest.mjs", - "dev": "next dev -p 5000", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "@fluentui/react": "^8.110.7", - "@fluentui/react-file-type-icons": "^8.9.3", - "@fluentui/react-hooks": "^8.6.29", - "@fluentui/react-icons": "^2.0.206", - "@fluentui/style-utilities": "^8.9.16", - "@griffel/react": "^1.5.10", - "@yume-chan/adb": "workspace:^0.0.21", - "@yume-chan/adb-credential-web": "workspace:^0.0.21", - "@yume-chan/adb-daemon-direct-sockets": "workspace:^0.0.9", - "@yume-chan/adb-daemon-webusb": "workspace:^0.0.21", - "@yume-chan/adb-daemon-ws": "workspace:^0.0.9", - "@yume-chan/adb-scrcpy": "workspace:^0.0.21", - "@yume-chan/android-bin": "workspace:^0.0.21", - "@yume-chan/aoa": "workspace:^0.0.21", - "@yume-chan/async": "^2.2.0", - "@yume-chan/b-tree": "workspace:^0.0.21", - "@yume-chan/event": "workspace:^0.0.21", - "@yume-chan/fetch-scrcpy-server": "workspace:^0.0.21", - "@yume-chan/pcm-player": "workspace:^0.0.21", - "@yume-chan/scrcpy": "workspace:^0.0.21", - "@yume-chan/scrcpy-decoder-tinyh264": "workspace:^0.0.21", - "@yume-chan/scrcpy-decoder-webcodecs": "workspace:^0.0.21", - "@yume-chan/stream-extra": "workspace:^0.0.21", - "@yume-chan/stream-saver": "^2.0.6", - "@yume-chan/struct": "workspace:^0.0.21", - "@yume-chan/tabby-launcher": "workspace:^1.0.197-nightly.1", - "@yume-chan/undici-browser": "5.22.1-mod.7", - "comlink": "^4.4.1", - "fflate": "^0.7.4", - "mobx": "^6.9.0", - "mobx-react-lite": "^3.4.3", - "next": "13.4.9", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "webm-muxer": "^3.1.1" - }, - "devDependencies": { - "@mdx-js/loader": "^2.3.0", - "@mdx-js/react": "^2.3.0", - "@next/bundle-analyzer": "^13.4.9", - "@next/mdx": "^13.4.9", - "@types/dom-webcodecs": "^0.1.8", - "@types/node": "^20.4.0", - "@types/react": "18.2.14", - "@yume-chan/next-pwa": "5.6.0-mod.2", - "eslint": "^8.44.0", - "eslint-config-next": "13.4.9", - "prettier": "^3.0.0", - "source-map-loader": "^4.0.1", - "typescript": "^5.1.6" - } -} diff --git a/apps/demo/public/StreamSaver/mitm.html b/apps/demo/public/StreamSaver/mitm.html deleted file mode 100644 index 23a0eab3..00000000 --- a/apps/demo/public/StreamSaver/mitm.html +++ /dev/null @@ -1,170 +0,0 @@ - - diff --git a/apps/demo/public/StreamSaver/sw.js b/apps/demo/public/StreamSaver/sw.js deleted file mode 100644 index d2b2ed99..00000000 --- a/apps/demo/public/StreamSaver/sw.js +++ /dev/null @@ -1,156 +0,0 @@ -/* global self ReadableStream Response */ - -self.addEventListener("install", () => { - self.skipWaiting(); -}); - -self.addEventListener("activate", (event) => { - const url = serviceWorker.scriptURL; - const baseUrl = url.substring(0, url.lastIndexOf("/")); - event.waitUntil( - (async () => { - const cache = await caches.open("StreamSaver"); - await cache.add(baseUrl + "/mitm.html"); - await clients.claim(); - })() - ); -}); - -const map = new Map(); - -// This should be called once per download -// Each event has a dataChannel that the data will be piped through -self.onmessage = (event) => { - // We send a heartbeat every x second to keep the - // service worker alive if a transferable stream is not sent - if (event.data === "ping") { - return; - } - - const data = event.data; - const downloadUrl = - data.url || - Math.random() + "/" + (typeof data === "string" ? data : data.filename); - const port = event.ports[0]; - const metadata = new Array(3); // [stream, data, port] - - metadata[1] = data; - metadata[2] = port; - - // Note to self: - // old streamsaver v1.2.0 might still use `readableStream`... - // but v2.0.0 will always transfer the stream through MessageChannel #94 - if (event.data.readableStream) { - metadata[0] = event.data.readableStream; - } else if (event.data.transferringReadable) { - port.onmessage = (evt) => { - port.onmessage = null; - metadata[0] = evt.data.readableStream; - }; - } else { - metadata[0] = createStream(port); - } - - map.set(downloadUrl, metadata); - port.postMessage({ download: downloadUrl }); -}; - -function createStream(port) { - // ReadableStream is only supported by chrome 52 - return new ReadableStream({ - start(controller) { - // When we receive data on the messageChannel, we write - port.onmessage = ({ data }) => { - if (data === "end") { - return controller.close(); - } - - if (data === "abort") { - controller.error("Aborted the download"); - return; - } - - controller.enqueue(data); - }; - }, - cancel(reason) { - console.log("user aborted", reason); - port.postMessage({ abort: true }); - }, - }); -} - -self.onfetch = async (event) => { - event.respondWith( - (async () => { - const url = event.request.url; - - const cache = await caches.open("StreamSaver"); - const response = await cache.match(event.request); - if (response) { - return response; - } - - // this only works for Firefox - if (url.endsWith("/ping")) { - return new Response("pong"); - } - - const hijacked = map.get(url); - if (!hijacked) return null; - map.delete(url); - - const [stream, data, port] = hijacked; - - // Not comfortable letting any user control all headers - // so we only copy over the length & disposition - const responseHeaders = new Headers({ - "Content-Type": "application/octet-stream; charset=utf-8", - - // To be on the safe side, The link can be opened in a iframe. - // but octet-stream should stop it. - "Content-Security-Policy": "default-src 'none'", - "X-Content-Security-Policy": "default-src 'none'", - "Cross-Origin-Embedder-Policy": "require-corp", - "X-WebKit-CSP": "default-src 'none'", - "X-XSS-Protection": "1; mode=block", - }); - - const headers = new Headers(data.headers || {}); - if (headers.has("Content-Length")) { - responseHeaders.set( - "Content-Length", - headers.get("Content-Length") - ); - } - if (headers.has("Content-Disposition")) { - responseHeaders.set( - "Content-Disposition", - headers.get("Content-Disposition") - ); - } - - // data, data.filename and size should not be used anymore - if (data.size) { - console.warn("Deprecated"); - responseHeaders.set("Content-Length", data.size); - } - - const fileName = typeof data === "string" ? data : data.filename; - if (fileName) { - console.warn("Deprecated"); - // Make filename RFC5987 compatible - fileName = encodeURIComponent(fileName) - .replace(/['()]/g, escape) - .replace(/\*/g, "%2A"); - responseHeaders.set( - "Content-Disposition", - "attachment; filename*=UTF-8''" + fileName - ); - } - - port.postMessage({ debug: "Download started" }); - return new Response(stream, { headers: responseHeaders }); - })() - ); -}; diff --git a/apps/demo/public/favicon-256.png b/apps/demo/public/favicon-256.png deleted file mode 100644 index 514ec6877c05ad3005918d854c9122f45c82d25b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5214 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 LrJugUA?W`A8`#=m diff --git a/apps/demo/scripts/manifest.mjs b/apps/demo/scripts/manifest.mjs deleted file mode 100644 index 33a57edc..00000000 --- a/apps/demo/scripts/manifest.mjs +++ /dev/null @@ -1,29 +0,0 @@ -import fs from "node:fs"; - -const baseUrl = (process.env.BASE_PATH ?? "") + "/"; - -fs.writeFileSync( - new URL("../public/manifest.json", import.meta.url), - JSON.stringify( - { - name: "Tango", - short_name: "Tango", - categories: ["utilities", "developer"], - description: "ADB in your browser", - scope: baseUrl, - start_url: baseUrl, - background_color: "#ffffff", - display: "standalone", - icons: [ - { - src: "favicon-256.png", - type: "image/png", - sizes: "256x256", - }, - ], - }, - undefined, - 4 - ), - "utf8" -); diff --git a/apps/demo/src/components/command-bar-spacer-item.tsx b/apps/demo/src/components/command-bar-spacer-item.tsx deleted file mode 100644 index ebf1c09f..00000000 --- a/apps/demo/src/components/command-bar-spacer-item.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { useEffect, useState } from "react"; - -export function CommandBarSpacerItem() { - const [container, setContainer] = useState(null); - - useEffect(() => { - if (!container) { - return; - } - - const parent = container.parentElement!; - const originalFlexGrow = parent.style.flexGrow; - parent.style.flexGrow = "1"; - return () => { - parent.style.flexGrow = originalFlexGrow; - }; - }, [container]); - - return
; -} diff --git a/apps/demo/src/components/command-bar.tsx b/apps/demo/src/components/command-bar.tsx deleted file mode 100644 index 0773eaaf..00000000 --- a/apps/demo/src/components/command-bar.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { CommandBar as FluentCommandBar, ICommandBarProps, StackItem } from '@fluentui/react'; -import { withDisplayName } from '../utils/with-display-name'; - -const ContainerStyles = { - root: { - borderBottom: '1px solid rgb(243, 242, 241)', - } -} as const; - -export const CommandBar = withDisplayName('CommandBar')((props: ICommandBarProps) => { - return ( - - - - ); -}); diff --git a/apps/demo/src/components/connect.tsx b/apps/demo/src/components/connect.tsx deleted file mode 100644 index 39e66c14..00000000 --- a/apps/demo/src/components/connect.tsx +++ /dev/null @@ -1,389 +0,0 @@ -import { - DefaultButton, - Dialog, - Dropdown, - IDropdownOption, - PrimaryButton, - ProgressIndicator, - Stack, - StackItem, -} from "@fluentui/react"; -import { - Adb, - AdbDaemonDevice, - AdbDaemonTransport, - AdbPacketData, - AdbPacketInit, -} from "@yume-chan/adb"; -import AdbWebCredentialStore from "@yume-chan/adb-credential-web"; -import AdbDaemonDirectSocketsDevice from "@yume-chan/adb-daemon-direct-sockets"; -import { - AdbDaemonWebUsbDeviceManager, - AdbDaemonWebUsbDeviceWatcher, -} from "@yume-chan/adb-daemon-webusb"; -import AdbDaemonWebSocketDevice from "@yume-chan/adb-daemon-ws"; -import { - Consumable, - InspectStream, - ReadableStream, - WritableStream, - pipeFrom, -} from "@yume-chan/stream-extra"; -import { observer } from "mobx-react-lite"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { GLOBAL_STATE } from "../state"; -import { CommonStackTokens, Icons } from "../utils"; - -const DropdownStyles = { dropdown: { width: "100%" } }; - -const CredentialStore = new AdbWebCredentialStore(); - -function ConnectCore(): JSX.Element | null { - const [selected, setSelected] = useState(); - const [connecting, setConnecting] = useState(false); - - const [usbSupported, setUsbSupported] = useState(true); - const [usbDeviceList, setUsbDeviceList] = useState([]); - const updateUsbDeviceList = useCallback(async () => { - const devices: AdbDaemonDevice[] = - await AdbDaemonWebUsbDeviceManager.BROWSER!.getDevices(); - setUsbDeviceList(devices); - return devices; - }, []); - - useEffect( - () => { - // Only run on client - const supported = !!AdbDaemonWebUsbDeviceManager.BROWSER; - setUsbSupported(supported); - - if (!supported) { - GLOBAL_STATE.showErrorDialog( - "Your browser does not support WebUSB standard, which is required for this site to work.\n\nLatest version of Google Chrome, Microsoft Edge, or other Chromium-based browsers are required." - ); - return; - } - - updateUsbDeviceList(); - - const watcher = new AdbDaemonWebUsbDeviceWatcher( - async (serial?: string) => { - const list = await updateUsbDeviceList(); - - if (serial) { - setSelected( - list.find((device) => device.serial === serial) - ); - return; - } - }, - globalThis.navigator.usb - ); - - return () => watcher.dispose(); - }, - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - [] - ); - - const [webSocketDeviceList, setWebSocketDeviceList] = useState< - AdbDaemonWebSocketDevice[] - >([]); - useEffect(() => { - const savedList = localStorage.getItem("ws-backend-list"); - if (!savedList) { - return; - } - - const parsed = JSON.parse(savedList) as { address: string }[]; - setWebSocketDeviceList( - parsed.map((x) => new AdbDaemonWebSocketDevice(x.address)) - ); - }, []); - - const addWebSocketDevice = useCallback(() => { - const address = window.prompt("Enter the address of WebSockify server"); - if (!address) { - return; - } - setWebSocketDeviceList((list) => { - const copy = list.slice(); - copy.push(new AdbDaemonWebSocketDevice(address)); - globalThis.localStorage.setItem( - "ws-backend-list", - JSON.stringify(copy.map((x) => ({ address: x.serial }))) - ); - return copy; - }); - }, []); - - const [tcpDeviceList, setTcpDeviceList] = useState< - AdbDaemonDirectSocketsDevice[] - >([]); - useEffect(() => { - if (!AdbDaemonDirectSocketsDevice.isSupported()) { - return; - } - - const savedList = localStorage.getItem("tcp-backend-list"); - if (!savedList) { - return; - } - - const parsed = JSON.parse(savedList) as { - address: string; - port: number; - }[]; - setTcpDeviceList( - parsed.map( - (x) => new AdbDaemonDirectSocketsDevice(x.address, x.port) - ) - ); - }, []); - - const addTcpDevice = useCallback(() => { - const host = window.prompt("Enter the address of device"); - if (!host) { - return; - } - - const port = window.prompt("Enter the port of device", "5555"); - if (!port) { - return; - } - - const portNumber = Number.parseInt(port, 10); - - setTcpDeviceList((list) => { - const copy = list.slice(); - copy.push(new AdbDaemonDirectSocketsDevice(host, portNumber)); - globalThis.localStorage.setItem( - "tcp-backend-list", - JSON.stringify( - copy.map((x) => ({ - address: x.host, - port: x.port, - })) - ) - ); - return copy; - }); - }, []); - - const handleSelectedChange = ( - e: React.FormEvent, - option?: IDropdownOption - ) => { - setSelected(option?.data as AdbDaemonDevice); - }; - - const addUsbDevice = useCallback(async () => { - const device = - await AdbDaemonWebUsbDeviceManager.BROWSER!.requestDevice(); - setSelected(device); - await updateUsbDeviceList(); - }, [updateUsbDeviceList]); - - const connect = useCallback(async () => { - if (!selected) { - return; - } - - setConnecting(true); - - let readable: ReadableStream; - let writable: WritableStream>; - try { - const streams = await selected.connect(); - - // Use `InspectStream`s to intercept and log packets - readable = streams.readable.pipeThrough( - new InspectStream((packet) => { - GLOBAL_STATE.appendLog("in", packet); - }) - ); - - writable = pipeFrom( - streams.writable, - new InspectStream((packet: Consumable) => { - GLOBAL_STATE.appendLog("out", packet.value); - }) - ); - } catch (e: any) { - GLOBAL_STATE.showErrorDialog(e); - setConnecting(false); - return; - } - - async function dispose() { - // Adb won't close the streams, - // so manually close them. - try { - readable.cancel(); - } catch {} - try { - await writable.close(); - } catch {} - GLOBAL_STATE.setDevice(undefined, undefined); - } - - try { - const device = new Adb( - await AdbDaemonTransport.authenticate({ - serial: selected.serial, - connection: { readable, writable }, - credentialStore: CredentialStore, - }) - ); - - device.disconnected.then( - async () => { - await dispose(); - }, - async (e) => { - GLOBAL_STATE.showErrorDialog(e); - await dispose(); - } - ); - - GLOBAL_STATE.setDevice(selected, device); - } catch (e: any) { - GLOBAL_STATE.showErrorDialog(e); - await dispose(); - } finally { - setConnecting(false); - } - }, [selected]); - - const disconnect = useCallback(async () => { - try { - await GLOBAL_STATE.adb!.close(); - } catch (e: any) { - GLOBAL_STATE.showErrorDialog(e); - } - }, []); - - const deviceList = useMemo( - () => - ([] as AdbDaemonDevice[]).concat( - usbDeviceList, - webSocketDeviceList, - tcpDeviceList - ), - [usbDeviceList, webSocketDeviceList, tcpDeviceList] - ); - - const deviceOptions = useMemo(() => { - return deviceList.map((device) => ({ - key: device.serial, - text: `${device.serial} ${device.name ? `(${device.name})` : ""}`, - data: device, - })); - }, [deviceList]); - - useEffect(() => { - setSelected((old) => { - if (old) { - const current = deviceList.find( - (device) => device.serial === old.serial - ); - if (current) { - return current; - } - } - - return deviceList.length ? deviceList[0] : undefined; - }); - }, [deviceList]); - - const addMenuProps = useMemo(() => { - const items = []; - - if (usbSupported) { - items.push({ - key: "usb", - text: "USB", - onClick: addUsbDevice, - }); - } - - items.push({ - key: "websocket", - text: "WebSocket", - onClick: addWebSocketDevice, - }); - - if (AdbDaemonDirectSocketsDevice.isSupported()) { - items.push({ - key: "direct-sockets", - text: "Direct Sockets TCP", - onClick: addTcpDevice, - }); - } - - return { - items, - }; - }, [usbSupported, addUsbDevice, addWebSocketDevice, addTcpDevice]); - - return ( - - - - {!GLOBAL_STATE.adb ? ( - - - - - - - - - ) : ( - - )} - - - - ); -} - -export const Connect = observer(ConnectCore); diff --git a/apps/demo/src/components/demo-mode-panel.tsx b/apps/demo/src/components/demo-mode-panel.tsx deleted file mode 100644 index 10eb3a13..00000000 --- a/apps/demo/src/components/demo-mode-panel.tsx +++ /dev/null @@ -1,407 +0,0 @@ -import { - Dropdown, - IDropdownOption, - Position, - Separator, - SpinButton, - Toggle, -} from "@fluentui/react"; -import { - DemoMode, - DemoModeMobileDataType, - DemoModeMobileDataTypes, - DemoModeSignalStrength, - DemoModeStatusBarMode, - DemoModeStatusBarModes, -} from "@yume-chan/android-bin"; -import { autorun, makeAutoObservable, reaction, runInAction } from "mobx"; -import { observer } from "mobx-react-lite"; -import { CSSProperties, useCallback } from "react"; -import { GLOBAL_STATE } from "../state"; - -const SignalStrengthOptions = Object.values(DemoModeSignalStrength).map( - (key) => ({ - key, - text: { - [DemoModeSignalStrength.Hidden]: "Hidden", - [DemoModeSignalStrength.Level0]: "Level 0", - [DemoModeSignalStrength.Level1]: "Level 1", - [DemoModeSignalStrength.Level2]: "Level 2", - [DemoModeSignalStrength.Level3]: "Level 3", - [DemoModeSignalStrength.Level4]: "Level 4", - }[key], - }) -); - -const MobileDataTypeOptions = DemoModeMobileDataTypes.map((key) => ({ - key, - text: { - "1x": "1X", - "3g": "3G", - "4g": "4G", - "4g+": "4G+", - "5g": "5G", - "5ge": "5GE", - "5g+": "5G+", - e: "EDGE", - // cspell: disable-next-line - g: "GPRS", - // cspell: disable-next-line - h: "HSPA", - // cspell: disable-next-line - "h+": "HSPA+", - lte: "LTE", - "lte+": "LTE+", - dis: "Disabled", - not: "Not default SIM", - null: "Unknown", - }[key], -})); - -const StatusBarModeOptions = DemoModeStatusBarModes.map((key) => ({ - key, - text: { - opaque: "Opaque", - translucent: "Translucent", - "semi-transparent": "Semi-transparent", - transparent: "Transparent", - warning: "Warning", - }[key], -})); - -class DemoModePanelState { - demoMode: DemoMode | undefined; - - allowed = false; - enabled = false; - features: Map = new Map(); - - constructor() { - makeAutoObservable(this); - - reaction( - () => GLOBAL_STATE.adb, - async (device) => { - if (device) { - runInAction(() => (this.demoMode = new DemoMode(device))); - const allowed = await this.demoMode!.getAllowed(); - runInAction(() => (this.allowed = allowed)); - if (allowed) { - const enabled = await this.demoMode!.getEnabled(); - runInAction(() => (this.enabled = enabled)); - } - } else { - this.demoMode = undefined; - this.allowed = false; - this.enabled = false; - this.features.clear(); - } - }, - { fireImmediately: true } - ); - - // Apply all features when enable - autorun(() => { - if (this.enabled) { - for (const group of FEATURES) { - for (const feature of group) { - feature.onChange( - this.features.get(feature.key) ?? feature.initial - ); - } - } - } - }); - } -} - -const state = new DemoModePanelState(); - -interface FeatureDefinition { - key: string; - label: string; - type: string; - min?: number; - max?: number; - step?: number; - options?: { key: string; text: string }[]; - initial: unknown; - onChange: (value: unknown) => void; -} - -const FEATURES: FeatureDefinition[][] = [ - [ - { - key: "batteryLevel", - label: "Battery Level", - type: "number", - min: 0, - max: 100, - step: 1, - initial: 100, - onChange: (value) => - state.demoMode!.setBatteryLevel(value as number), - }, - { - key: "batteryCharging", - label: "Battery Charging", - type: "boolean", - initial: false, - onChange: (value) => - state.demoMode!.setBatteryCharging(value as boolean), - }, - { - key: "powerSaveMode", - label: "Power Save Mode", - type: "boolean", - initial: false, - onChange: (value) => - state.demoMode!.setPowerSaveMode(value as boolean), - }, - ], - [ - { - key: "wifiSignalStrength", - label: "Wifi Signal Strength", - type: "select", - options: SignalStrengthOptions, - initial: DemoModeSignalStrength.Level4, - onChange: (value) => - state.demoMode!.setWifiSignalStrength( - value as DemoModeSignalStrength - ), - }, - { - key: "airplaneMode", - label: "Airplane Mode", - type: "boolean", - initial: false, - onChange: (value) => - state.demoMode!.setAirplaneMode(value as boolean), - }, - { - key: "mobileDataType", - label: "Mobile Data Type", - type: "select", - options: MobileDataTypeOptions, - initial: "lte", - onChange: (value) => - state.demoMode!.setMobileDataType( - value as DemoModeMobileDataType - ), - }, - { - key: "mobileSignalStrength", - label: "Mobile Signal Strength", - type: "select", - options: SignalStrengthOptions, - initial: DemoModeSignalStrength.Level4, - onChange: (value) => - state.demoMode!.setMobileSignalStrength( - value as DemoModeSignalStrength - ), - }, - ], - [ - { - key: "statusBarMode", - label: "Status Bar Mode", - type: "select", - options: StatusBarModeOptions, - initial: "transparent", - onChange: (value) => - state.demoMode!.setStatusBarMode( - value as DemoModeStatusBarMode - ), - }, - { - key: "vibrateMode", - label: "Vibrate Mode Indicator", - type: "boolean", - initial: false, - onChange: (value) => - state.demoMode!.setVibrateModeEnabled(value as boolean), - }, - { - key: "bluetoothConnected", - label: "Bluetooth Indicator", - type: "boolean", - initial: false, - onChange: (value) => - state.demoMode!.setBluetoothConnected(value as boolean), - }, - { - key: "locatingIcon", - label: "Locating Icon", - type: "boolean", - initial: false, - onChange: (value) => - state.demoMode!.setLocatingIcon(value as boolean), - }, - { - key: "alarmIcon", - label: "Alarm Icon", - type: "boolean", - initial: false, - onChange: (value) => state.demoMode!.setAlarmIcon(value as boolean), - }, - { - key: "notificationsVisibility", - label: "Notifications Visibility", - type: "boolean", - initial: true, - onChange: (value) => - state.demoMode!.setNotificationsVisibility(value as boolean), - }, - { - key: "hour", - label: "Clock Hour", - type: "number", - min: 0, - max: 23, - step: 1, - initial: 12, - onChange: (value) => - state.demoMode!.setTime( - value as number, - (state.features.get("minute") as number | undefined) ?? 34 - ), - }, - { - key: "minute", - label: "Clock Minute", - type: "number", - min: 0, - max: 59, - step: 1, - initial: 34, - onChange: (value) => - state.demoMode!.setTime( - (state.features.get("hour") as number | undefined) ?? 34, - value as number - ), - }, - ], -]; - -const FeatureBase = ({ feature }: { feature: FeatureDefinition }) => { - const handleChange = useCallback( - (e: unknown, value: unknown) => { - switch (feature.type) { - case "select": - value = (value as IDropdownOption).key; - break; - case "number": - value = parseFloat(value as string); - default: - break; - } - - feature.onChange(value); - runInAction(() => { - state.features.set(feature.key, value); - state.enabled = true; - }); - }, - [feature] - ); - - const value = state.features.get(feature.key) ?? feature.initial; - - switch (feature.type) { - case "boolean": - return ( - - ); - case "number": - return ( - - ); - case "select": - return ( - - ); - default: - return null; - } -}; - -const Feature = observer(FeatureBase); - -export interface DemoModePanelProps { - style?: CSSProperties; -} - -export const DemoModePanel = observer(({ style }: DemoModePanelProps) => { - const handleAllowedChange = useCallback( - async (e: unknown, value?: boolean) => { - await state.demoMode!.setAllowed(value!); - runInAction(() => { - state.allowed = value!; - state.enabled = false; - }); - }, - [] - ); - - const handleEnabledChange = useCallback( - async (e: unknown, value?: boolean) => { - await state.demoMode!.setEnabled(value!); - runInAction(() => (state.enabled = value!)); - }, - [] - ); - - return ( -
- - - - -
- Note: -
-
Device may not support all options.
- - {FEATURES.map((group, index) => ( -
- - - {group.map((feature) => ( - - ))} -
- ))} -
- ); -}); diff --git a/apps/demo/src/components/device-view.tsx b/apps/demo/src/components/device-view.tsx deleted file mode 100644 index b20cfd53..00000000 --- a/apps/demo/src/components/device-view.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { StackItem } from "@fluentui/react"; -import { makeStyles } from "@griffel/react"; -import { - CSSProperties, - ComponentType, - HTMLAttributes, - ReactNode, - useImperativeHandle, - useMemo, - useRef, - useState, -} from "react"; -import { forwardRef } from "../utils/with-display-name"; -import { ResizeObserver, Size } from "./resize-observer"; - -export interface DeviceViewProps extends HTMLAttributes { - width: number; - - height: number; - - BottomElement?: ComponentType<{ - className: string; - style: CSSProperties; - children: ReactNode; - }>; - - children?: ReactNode; -} - -export interface DeviceViewRef { - enterFullscreen(): void; -} - -const useClasses = makeStyles({ - outer: { - width: "100%", - height: "100%", - backgroundColor: "black", - }, - inner: { - position: "absolute", - transformOrigin: "top left", - }, - bottom: { - position: "absolute", - }, -}); - -export const DeviceView = forwardRef("DeviceView")( - ( - { width, height, BottomElement, children, ...props }: DeviceViewProps, - ref - ) => { - const classes = useClasses(); - - const [containerSize, setContainerSize] = useState({ - width: 0, - height: 0, - }); - const [bottomSize, setBottomSize] = useState({ - width: 0, - height: 0, - }); - - // Container size minus bottom element size - const usableSize = useMemo( - () => ({ - width: containerSize.width, - height: containerSize.height - bottomSize.height, - }), - [containerSize, bottomSize] - ); - - // Compute sizes after scaling - const childrenStyle = useMemo(() => { - let scale: number; - let childrenWidth: number; - let childrenHeight: number; - let childrenTop: number; - let childrenLeft: number; - - if (width === 0 || usableSize.width === 0) { - scale = 1; - childrenWidth = 0; - childrenHeight = 0; - childrenTop = 0; - childrenLeft = 0; - } else { - const videoRatio = width / height; - const containerRatio = usableSize.width / usableSize.height; - - if (videoRatio > containerRatio) { - scale = usableSize.width / width; - childrenWidth = usableSize.width; - childrenHeight = height * scale; - childrenTop = (usableSize.height - childrenHeight) / 2; - childrenLeft = 0; - } else { - scale = usableSize.height / height; - childrenWidth = width * scale; - childrenHeight = usableSize.height; - childrenTop = 0; - childrenLeft = (usableSize.width - childrenWidth) / 2; - } - } - - return { - scale, - width: childrenWidth, - height: childrenHeight, - top: childrenTop, - left: childrenLeft, - }; - }, [width, height, usableSize]); - - const containerRef = useRef(null); - useImperativeHandle( - ref, - () => ({ - enterFullscreen() { - containerRef.current!.requestFullscreen(); - }, - }), - [] - ); - - return ( - -
- - -
- {children} -
- - {!!width && !!BottomElement && ( - - - - )} -
-
- ); - } -); diff --git a/apps/demo/src/components/error-dialog.tsx b/apps/demo/src/components/error-dialog.tsx deleted file mode 100644 index 6c8d4e29..00000000 --- a/apps/demo/src/components/error-dialog.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { - Dialog, - DialogFooter, - DialogType, - PrimaryButton, -} from "@fluentui/react"; -import { observer } from "mobx-react-lite"; -import { PropsWithChildren } from "react"; -import { GLOBAL_STATE } from "../state"; - -export const ErrorDialogProvider = observer((props: PropsWithChildren<{}>) => { - return ( - <> - {props.children} - - - - ); -}); diff --git a/apps/demo/src/components/external-link.tsx b/apps/demo/src/components/external-link.tsx deleted file mode 100644 index d33024a8..00000000 --- a/apps/demo/src/components/external-link.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Link } from '@fluentui/react'; -import { ReactNode } from 'react'; -import { withDisplayName } from '../utils/with-display-name'; - -export interface ExternalLinkProps { - href: string; - spaceBefore?: boolean; - spaceAfter?: boolean; - children?: ReactNode; -} - -export const ExternalLink = withDisplayName('ExternalLink')(({ - href, - spaceBefore, - spaceAfter, - children, -}: ExternalLinkProps) => { - return ( - <> - {spaceBefore && ' '} - {children ?? href} - {spaceAfter && ' '} - - ); -}); diff --git a/apps/demo/src/components/grid.tsx b/apps/demo/src/components/grid.tsx deleted file mode 100644 index 220ea15a..00000000 --- a/apps/demo/src/components/grid.tsx +++ /dev/null @@ -1,384 +0,0 @@ -import { makeStyles, mergeClasses, shorthands } from "@griffel/react"; -import { - CSSProperties, - ComponentType, - HTMLAttributes, - useEffect, - useMemo, - useState, -} from "react"; -import { useStableCallback, withDisplayName } from "../utils"; -import { ResizeObserver, Size } from "./resize-observer"; - -const useClasses = makeStyles({ - container: { - display: "flex", - flexDirection: "column", - outlineStyle: "none", - ...shorthands.overflow("hidden"), - }, - header: { - position: "relative", - }, - body: { - position: "relative", - flexGrow: 1, - height: 0, - ...shorthands.overflow("auto"), - }, - placeholder: { - // make horizontal scrollbar visible - minHeight: "1px", - }, - row: { - position: "absolute", - top: 0, - left: 0, - right: 0, - willChange: "transform", - }, - cell: { - position: "absolute", - top: 0, - left: 0, - willChange: "transform", - }, -}); - -export interface GridCellProps { - className: string; - style: CSSProperties; - rowIndex: number; - columnIndex: number; -} - -export interface GridCellWrapperProps { - CellComponent: ComponentType; - rowIndex: number; - rowHeight: number; - columnIndex: number; - columnWidth: number; - columnOffset: number; -} - -const GridCellWrapper = withDisplayName("GridCellWrapper")( - ({ - CellComponent, - rowIndex, - rowHeight, - columnIndex, - columnWidth, - columnOffset, - }: GridCellWrapperProps) => { - const classes = useClasses(); - - const styles = useMemo( - () => ({ - width: columnWidth, - height: rowHeight, - transform: `translateX(${columnOffset}px)`, - }), - [rowHeight, columnWidth, columnOffset] - ); - - return ( - - ); - } -); - -export interface GridRowProps { - className: string; - style: CSSProperties; - rowIndex: number; - children: React.ReactNode; -} - -export interface GridColumn { - width: number; - minWidth?: number; - maxWidth?: number; - flexGrow?: number; - flexShrink?: number; - CellComponent: ComponentType; -} - -interface GridRowWrapperProps { - RowComponent: ComponentType; - rowIndex: number; - rowHeight: number; - columns: (GridColumn & { offset: number })[]; -} - -const GridRowWrapper = withDisplayName("GridRowWrapper")( - ({ RowComponent, rowIndex, rowHeight, columns }: GridRowWrapperProps) => { - const classes = useClasses(); - - const styles = useMemo( - () => ({ - height: rowHeight, - transform: `translateY(${rowIndex * rowHeight}px)`, - }), - [rowIndex, rowHeight] - ); - - return ( - - {columns.map((column, columnIndex) => ( - - ))} - - ); - } -); - -export interface GridHeaderProps { - className: string; - columnIndex: number; - style: CSSProperties; -} - -export interface GridProps extends HTMLAttributes { - rowCount: number; - rowHeight: number; - columns: GridColumn[]; - HeaderComponent: ComponentType; - RowComponent: ComponentType; -} - -export const Grid = withDisplayName("Grid")( - ({ - className, - rowCount, - rowHeight, - columns, - HeaderComponent, - RowComponent, - ...props - }: GridProps) => { - const classes = useClasses(); - - const [scrollLeft, setScrollLeft] = useState(0); - const [scrollTop, setScrollTop] = useState(0); - - const [bodyRef, setBodyRef] = useState(null); - const [bodySize, setBodySize] = useState({ width: 0, height: 0 }); - - const [autoScroll, setAutoScroll] = useState(true); - - const handleScroll = useStableCallback(() => { - if (bodyRef && bodyRef.scrollTop !== scrollTop) { - if (autoScroll) { - if ( - scrollTop < - bodyRef.scrollHeight - bodyRef.clientHeight && - bodyRef.scrollTop < scrollTop - ) { - setAutoScroll(false); - } - } else if ( - bodyRef.scrollTop + bodyRef.offsetHeight >= - bodyRef.scrollHeight - 10 - ) { - setAutoScroll(true); - } - - setScrollLeft(bodyRef.scrollLeft); - setScrollTop(bodyRef.scrollTop); - } - }); - - useEffect(() => { - if (bodyRef) { - setScrollLeft(bodyRef.scrollLeft); - setScrollTop(bodyRef.scrollTop); - } - }, [bodyRef]); - - const rowRange = useMemo(() => { - const start = Math.min(rowCount, Math.floor(scrollTop / rowHeight)); - const end = Math.min( - rowCount, - Math.ceil((scrollTop + bodySize.height) / rowHeight) - ); - return { start, end, offset: scrollTop - start * rowHeight }; - }, [scrollTop, bodySize.height, rowCount, rowHeight]); - - const columnMetadata = useMemo(() => { - if (bodySize.width === 0) { - return { - columns: [], - totalWidth: 0, - }; - } - - const result = []; - let requestedWidth = 0; - let columnsCanGrow = []; - let totalFlexGrow = 0; - let columnsCanShrink = []; - let totalFlexShrink = 0; - for (const column of columns) { - const copy = { ...column, offset: 0 }; - result.push(copy); - - requestedWidth += copy.width; - - if (copy.flexGrow !== undefined) { - columnsCanGrow.push(copy); - totalFlexGrow += copy.flexGrow; - } - - if (copy.flexShrink !== 0) { - if (copy.flexShrink === undefined) { - copy.flexShrink = 1; - } - if (copy.minWidth === undefined) { - copy.minWidth = 0; - } - columnsCanShrink.push(copy); - totalFlexShrink += copy.flexShrink; - } - } - - let extraWidth = bodySize.width - requestedWidth; - while (extraWidth > 1 && columnsCanGrow.length > 0) { - const growPerRatio = extraWidth / totalFlexGrow; - columnsCanGrow = columnsCanGrow.filter((column) => { - let canGrowFurther = true; - const initialWidth = column.width; - column.width += column.flexGrow! * growPerRatio; - if ( - column.maxWidth !== undefined && - column.width > column.maxWidth - ) { - column.width = column.maxWidth; - canGrowFurther = false; - } - extraWidth -= column.width - initialWidth; - return canGrowFurther; - }); - } - - while (extraWidth < -1 && columnsCanShrink.length > 0) { - const shrinkPerRatio = -extraWidth / totalFlexShrink; - columnsCanShrink = columnsCanShrink.filter((column) => { - let canShrinkFurther = true; - const initialWidth = column.width; - column.width -= column.flexShrink! * shrinkPerRatio; - if (column.width < column.minWidth!) { - column.width = column.minWidth!; - canShrinkFurther = false; - } - extraWidth += initialWidth - column.width; - return canShrinkFurther; - }); - } - - let offset = 0; - for (const column of result) { - column.offset = offset; - offset += column.width; - } - - return { - columns: result, - totalWidth: offset, - }; - }, [columns, bodySize.width]); - - useEffect(() => { - if (autoScroll && bodyRef) { - void bodyRef.offsetLeft; - bodyRef.scrollTop = bodyRef.scrollHeight; - } - }); - - const headers = useMemo( - () => - columnMetadata.columns.map((column, index) => ( - - )), - [columnMetadata, HeaderComponent, classes, rowHeight] - ); - - const headerStyle = useMemo( - () => ({ - height: rowHeight, - transform: `translateX(-${scrollLeft}px)`, - }), - [rowHeight, scrollLeft] - ); - - const placeholder = useMemo( - () => ( -
- ), - [classes, columnMetadata, rowCount, rowHeight] - ); - - return ( -
-
- {headers} -
-
- - {placeholder} - {Array.from( - { length: rowRange.end - rowRange.start }, - (_, rowIndex) => ( - - ) - )} -
-
- ); - } -); diff --git a/apps/demo/src/components/hex-viewer.tsx b/apps/demo/src/components/hex-viewer.tsx deleted file mode 100644 index 3140ed5c..00000000 --- a/apps/demo/src/components/hex-viewer.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { makeStyles, mergeClasses } from "@griffel/react"; -import { ReactNode, useMemo } from "react"; -import { withDisplayName } from "../utils"; - -const useClasses = makeStyles({ - root: { - width: "100%", - height: "100%", - overflowY: "auto", - }, - flex: { - display: "flex", - }, - cell: { - fontFamily: '"Cascadia Code", Consolas, monospace', - }, - lineNumber: { - textAlign: "right", - }, - hex: { - marginLeft: "40px", - fontVariantLigatures: "none", - }, -}); - -const PRINTABLE_CHARACTERS: [number, number][] = [ - [33, 126], - [161, 172], - [174, 255], -]; - -export function isPrintableCharacter(code: number) { - return PRINTABLE_CHARACTERS.some( - ([start, end]) => code >= start && code <= end - ); -} - -export function toCharacter(code: number) { - if (isPrintableCharacter(code)) { - return String.fromCharCode(code); - } - return "."; -} - -export function toText(data: Uint8Array) { - let result = ""; - for (const code of data) { - result += toCharacter(code); - } - return result; -} - -const PER_ROW = 16; - -export interface HexViewerProps { - className?: string; - data: Uint8Array; -} - -export const HexViewer = withDisplayName("HexViewer")( - ({ className, data }: HexViewerProps) => { - const classes = useClasses(); - - // Because ADB packets are usually small, - // so don't add virtualization now. - - const children = useMemo(() => { - const lineNumbers: ReactNode[] = []; - const hexRows: ReactNode[] = []; - const textRows: ReactNode[] = []; - for (let i = 0; i < data.length; i += PER_ROW) { - lineNumbers.push(
{i.toString(16)}
); - - let hex = ""; - for (let j = i; j < i + PER_ROW && j < data.length; j++) { - hex += data[j].toString(16).padStart(2, "0") + " "; - } - hexRows.push(
{hex}
); - - textRows.push( -
{toText(data.slice(i, i + PER_ROW))}
- ); - } - - return { - lineNumbers, - hexRows, - textRows, - }; - }, [data]); - - return ( -
-
-
- {children.lineNumbers} -
-
- {children.hexRows} -
-
- {children.textRows} -
-
-
- ); - } -); diff --git a/apps/demo/src/components/index.ts b/apps/demo/src/components/index.ts deleted file mode 100644 index ed4285e5..00000000 --- a/apps/demo/src/components/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export * from "./command-bar"; -export * from "./connect"; -export * from "./demo-mode-panel"; -export * from "./device-view"; -export * from "./error-dialog"; -export * from "./external-link"; -export * from "./grid"; -export * from "./hex-viewer"; -export * from "./list-selection"; -export * from "./log-view"; -export * from "./resize-observer"; -export * from "./tabby-frame-manager"; diff --git a/apps/demo/src/components/list-selection.ts b/apps/demo/src/components/list-selection.ts deleted file mode 100644 index c66761fe..00000000 --- a/apps/demo/src/components/list-selection.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { BTree, BTreeNode } from "@yume-chan/b-tree"; -import { - IAtom, - IObservableValue, - createAtom, - makeAutoObservable, - observable, - onBecomeUnobserved, -} from "mobx"; - -const IS_MAC = - typeof window != "undefined" && - /Mac|iPod|iPhone|iPad/.test(globalThis.navigator.platform); - -export function isModKey(e: { metaKey: boolean; ctrlKey: boolean }): boolean { - if (IS_MAC) { - return e.metaKey; - } else { - return e.ctrlKey; - } -} - -export class ObservableBTree implements Omit { - data: BTree; - hasMap: Map>; - keys: IAtom; - - constructor(order: number) { - this.data = new BTree(order); - this.hasMap = new Map(); - this.keys = createAtom("ObservableBTree.keys"); - } - - get order(): number { - return this.data.order; - } - - get size(): number { - this.keys.reportObserved(); - return this.data.size; - } - - has(value: number): boolean { - if (!this.hasMap.has(value)) { - const observableHasValue = observable.box(this.data.has(value)); - onBecomeUnobserved(observableHasValue, () => - this.hasMap.delete(value) - ); - this.hasMap.set(value, observableHasValue); - } - return this.hasMap.get(value)!.get(); - } - - add(value: number): boolean { - if (this.data.add(value)) { - this.hasMap.get(value)?.set(true); - this.keys.reportChanged(); - return true; - } - return false; - } - - delete(value: number): boolean { - if (this.data.delete(value)) { - this.hasMap.get(value)?.set(false); - this.keys.reportChanged(); - return true; - } - return false; - } - - clear(): void { - if (this.data.size === 0) { - return; - } - this.data.clear(); - for (const entry of this.hasMap) { - entry[1].set(false); - } - this.keys.reportChanged(); - } - - [Symbol.iterator](): Generator { - this.keys.reportObserved(); - return this.data[Symbol.iterator](); - } -} - -export class ObservableListSelection { - selected = new ObservableBTree(6); - rangeStart = 0; - selectedIndex: number | null = null; - - constructor() { - makeAutoObservable(this); - } - - get size() { - return this.selected.size; - } - - has(index: number) { - return this.selected.has(index); - } - - select(index: number, ctrlKey: boolean, shiftKey: boolean) { - if (this.rangeStart !== null && shiftKey) { - if (!ctrlKey) { - this.selected.clear(); - } - - let [start, end] = [this.rangeStart, index]; - if (start > end) { - [start, end] = [end, start]; - } - for (let i = start; i <= end; i += 1) { - this.selected.add(i); - } - this.selectedIndex = index; - return; - } - - if (ctrlKey) { - if (this.selected.has(index)) { - this.selected.delete(index); - this.selectedIndex = null; - } else { - this.selected.add(index); - this.selectedIndex = index; - } - this.rangeStart = index; - return; - } - - this.selected.clear(); - this.selected.add(index); - this.rangeStart = index; - this.selectedIndex = index; - } - - clear() { - this.selected.clear(); - this.rangeStart = 0; - this.selectedIndex = null; - } - - [Symbol.iterator]() { - return this.selected[Symbol.iterator](); - } -} diff --git a/apps/demo/src/components/log-view.tsx b/apps/demo/src/components/log-view.tsx deleted file mode 100644 index d89c7daf..00000000 --- a/apps/demo/src/components/log-view.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { PropsWithChildren, useEffect, useState } from 'react'; - -export function NoSsr({ children }: PropsWithChildren<{}>) { - const [showChild, setShowChild] = useState(false); - - // Wait until after client-side hydration to show - useEffect(() => { - setShowChild(true); - }, []); - - if (!showChild) { - return null; - } - - return <>{children}; -} diff --git a/apps/demo/src/components/resize-observer.tsx b/apps/demo/src/components/resize-observer.tsx deleted file mode 100644 index 2113ed09..00000000 --- a/apps/demo/src/components/resize-observer.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { makeStyles } from "@griffel/react"; -import { useEffect, useState } from 'react'; -import { useStableCallback, withDisplayName } from '../utils'; - -export interface Size { - width: number; - - height: number; -} - -export interface ResizeObserverProps { - onResize: (size: Size) => void; -} - -const useClasses = makeStyles({ - observer: { - position: 'absolute', - top: 0, - left: 0, - width: '100%', - height: '100%', - visibility: 'hidden', - } -}); - -export const ResizeObserver = withDisplayName('ResizeObserver')(({ - onResize, -}: ResizeObserverProps): JSX.Element | null => { - const classes = useClasses(); - - const [iframe, setIframe] = useState(null); - - const handleResize = useStableCallback(() => { - const { width, height } = iframe!.getBoundingClientRect(); - onResize({ width, height }); - }); - - useEffect(() => { - if (iframe) { - void iframe.offsetLeft; - iframe.contentWindow!.addEventListener('resize', handleResize); - handleResize(); - } - }, [iframe, handleResize]); - - return ( -