diff --git a/_WIP_Advanced/Create Your Own Template.md b/_WIP_Advanced/Create Your Own Template.md deleted file mode 100644 index 1515b07..0000000 --- a/_WIP_Advanced/Create Your Own Template.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sort: 1 ---- - -## TODO diff --git a/_WIP_Advanced/Usage Via NodeJS API.md b/_WIP_Advanced/Usage Via NodeJS API.md deleted file mode 100644 index 2076f29..0000000 --- a/_WIP_Advanced/Usage Via NodeJS API.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sort: 3 ---- - -## TODO diff --git a/_WIP_Advanced/assets/new-template.png b/_WIP_Advanced/assets/new-template.png deleted file mode 100644 index b3af500..0000000 Binary files a/_WIP_Advanced/assets/new-template.png and /dev/null differ diff --git a/_WIP_Configuration Files/Babel.md b/_WIP_Configuration Files/Babel.md deleted file mode 100644 index 8e17bee..0000000 --- a/_WIP_Configuration Files/Babel.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -sort: 6 ---- - -To allow developers to use the latest language features without worrying about browser or environment constraints, `Extension` relies on [Babel](#) to transpile cutting-edge JavaScript and TypeScript code, ensuring broad compatibility and enabling developers. - -## Feature Support - -Here’s how each Babel plugin and preset contributes to this feature: - -- [@babel/plugin-proposal-decorators](https://www.npmjs.com/package/@babel/plugin-proposal-decorators): Allows developers to use decorators in their code, providing a convenient and readable way to annotate and modify classes and properties at design time. -- [@babel/plugin-syntax-dynamic-import](https://www.npmjs.com/package/@babel/plugin-syntax-dynamic-import): Enables the use of dynamic import() syntax, facilitating code-splitting and lazy-loading of modules, which can significantly improve the performance of web applications. -- [@babel/preset-env](https://www.npmjs.com/package/@babel/preset-env): Automatically determines the Babel plugins and polyfills needed based on the target environment's feature support, ensuring that the output code runs efficiently across all platforms. -- [@babel/preset-react](https://www.npmjs.com/package/@babel/preset-react): Transforms JSX into standard JavaScript, making it compatible with the browser and enabling the use of React's powerful UI library and its ecosystem. -- [@babel/preset-typescript](https://www.npmjs.com/package/@babel/preset-typescript): Provides support for TypeScript, allowing developers to leverage type-safe programming, interfaces, and other TypeScript features, which are compiled down to standard JavaScript. -- [babel-plugin-react-require](https://www.npmjs.com/package/babel-plugin-react-require): Automatically injects the React import statement in files that use JSX, ensuring that the React namespace is always available where needed. -- [babel-plugin-transform-react-remove-prop-types](https://www.npmjs.com/package/babel-plugin-transform-react-remove-prop-types): Removes PropTypes from the production build of React components, reducing the bundle size and improving the performance of the production app. - -This comprehensive setup empowers developers to write modern, efficient, and clean extension code across various modern web technologies, including [[React]] and [[TypeScript]]. - -## Next Steps - -- Add a custom [[Babel]] config (`babel.config.js`) to your extension. -- To maintain compatibility across diverse browser vendors, see [[Polyfill]]. -- Learn about [[React]] in Extension. -- Learn about [[TypeScript]] in Extension. - ---- diff --git a/_WIP_Configuration Files/PostCSS.md b/_WIP_Configuration Files/PostCSS.md deleted file mode 100644 index c85fd8a..0000000 --- a/_WIP_Configuration Files/PostCSS.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sort: 3 ---- - ---- diff --git a/_WIP_Configuration Files/_Eslint.md b/_WIP_Configuration Files/_Eslint.md deleted file mode 100644 index db54e06..0000000 --- a/_WIP_Configuration Files/_Eslint.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 4 ---- - -TODO - ---- diff --git a/_WIP_Configuration Files/_Prettier.md b/_WIP_Configuration Files/_Prettier.md deleted file mode 100644 index ff0dbb5..0000000 --- a/_WIP_Configuration Files/_Prettier.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 5 ---- - -TODO - ---- diff --git a/_WIP_Configuration Files/_Webpack.md b/_WIP_Configuration Files/_Webpack.md deleted file mode 100644 index abcbc73..0000000 --- a/_WIP_Configuration Files/_Webpack.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 7 ---- - -TODO - ---- diff --git a/_WIP_Configuration Files/_extension.config.js.md b/_WIP_Configuration Files/_extension.config.js.md deleted file mode 100644 index 99abd1e..0000000 --- a/_WIP_Configuration Files/_extension.config.js.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 8 ---- - -TODO - ---- diff --git a/_WIP_Contribute/Architecture Overview.md b/_WIP_Contribute/Architecture Overview.md deleted file mode 100644 index 4b6b118..0000000 --- a/_WIP_Contribute/Architecture Overview.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 2 ---- - -TODO - ---- diff --git a/_WIP_Contribute/Help Ensuring Quality.md b/_WIP_Contribute/Help Ensuring Quality.md deleted file mode 100644 index ff0dbb5..0000000 --- a/_WIP_Contribute/Help Ensuring Quality.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 5 ---- - -TODO - ---- diff --git a/_WIP_Contribute/Help Me Fixing Stuff.md b/_WIP_Contribute/Help Me Fixing Stuff.md deleted file mode 100644 index db54e06..0000000 --- a/_WIP_Contribute/Help Me Fixing Stuff.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 4 ---- - -TODO - ---- diff --git a/_WIP_Contribute/Run This Project Locally.md b/_WIP_Contribute/Run This Project Locally.md deleted file mode 100644 index 4abc087..0000000 --- a/_WIP_Contribute/Run This Project Locally.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 3 ---- - -TODO - ---- diff --git a/_WIP_Contribute/What This Project Is All About.md b/_WIP_Contribute/What This Project Is All About.md deleted file mode 100644 index 1e361cf..0000000 --- a/_WIP_Contribute/What This Project Is All About.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -sort: 1 ---- - -TODO - ---- diff --git a/_WIP_Contribute/assets/new-template.png b/_WIP_Contribute/assets/new-template.png deleted file mode 100644 index b3af500..0000000 Binary files a/_WIP_Contribute/assets/new-template.png and /dev/null differ diff --git a/_WIP_Development/_Modern CSS/Less.md b/_WIP_Development/_Modern CSS/Less.md deleted file mode 100644 index d83a538..0000000 --- a/_WIP_Development/_Modern CSS/Less.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -sort: 2 ---- - -#### Demo - - - -```sh -npx extension dev @Extension/templates/less -``` - -#### In an HTML file - -```html - - - - - - New Extension - - - - - -

Hello, Extension.

- - -``` - -#### In a JavaScript File - -```js -// ./NewTabApp.jsx - -import "./css/globals.css"; - -export default function MyNewTabPage() { - return

Hello, Extension.

; -} -``` - -#### In a content_script file - -```js -// ./content_script.jsx - -// There is no HTML in a content_script file, so -// we import our global CSS file via dynamic import. -import("./css/globals.css"); - -export default function MyNewTabPage() { - return

Hello, Extension!

; -} -``` - -## Next Steps - -- Learn how to apply styles to the multiple extension contexts in [[Working With Stylesheets]]. -- Learn how to enable [[PostCSS]] capabilities in your extension. -- Configure [[Tailwind]] in your extension. -- Using [[Stylelint]] with Extension to lint your stylesheets. - ---- diff --git a/_WIP_Development/_Modern CSS/Sass.md b/_WIP_Development/_Modern CSS/Sass.md deleted file mode 100644 index b36e673..0000000 --- a/_WIP_Development/_Modern CSS/Sass.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -sort: 1 ---- - -From [https://sass-lang.com/](https://sass-lang.com/): - -> Sass is the most mature, stable, and powerful professional grade CSS extension language in the world - -`Extension` supports both `.scss` and `sass` extensions. It also supports component-level Sass via CSS Modules and the `.module.scss` or `.module.sass` extension. - -## Starter Scss Template - -`Extension` comes with a default Sass Module template for new projects, which you can use as a starting point for your next Sass Extension. This is the easiest way to have Sass integrated with `Extension`. - - - -### Try it yourself - -```sh -npx extension dev @Extension/templates/sass -``` - -## Usage With An Existing Extension - -### Installation & Configuration - -```sh -# Install required dependency -npm install -D sass -``` - -### Usage - -#### In An HTML File - -```html - - - - - - New Extension - - - - - -

Hello, Extension.

- - -``` - -#### In A JavaScript File - -```js -// ./NewTabApp.jsx - -import styles from './styles/button.module.scss' - -export default function MyNewTabPage({ Component, pageProps }) { - return ( -

Hello, Extension.

- ) -} -``` - - - -#### As A Module - -```scss -/* styles/button.module.scss */ - -$primary-color: #64ff00; - -:export { - primaryColor: $primary-color; -} -``` - -```js -// ./NewTabApp.jsx - -import styles from './styles/button.module.scss' - -export default function MyNewTabPage({ Component, pageProps }) { - return ( -

Hello, Extension.

- ) -} -``` - -Now, add `css/button.scss` as an import for the file you want Sass to work. For example: - -## Next Steps - -- Ensure the semantics and code quality of your CSS files by using [[Stylelint]]. - ---- diff --git a/_WIP_Features/API Resolution.md b/_WIP_Features/API Resolution.md deleted file mode 100644 index cba09d2..0000000 --- a/_WIP_Features/API Resolution.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -sort: 5 ---- - -## Support for API File Paths - -```diff -- # During development. -- chrome.action.setIcon({path: "../../images/popup.html"}); - -+ # During production. -+ chrome.action.setIcon({path: "/assets/popup.html"}); -``` - -`Extension` comes with a built-in API resolver. This allows developers to specify the real path of their extension file requests without worrying about whether the output path will be resolved after compilation, allowing your extension to efficiently communicate and interact with web pages and other extension features. Allowing automatic path resolution. - ---- diff --git a/_WIP_Features/HTML Fields.md b/_WIP_Features/HTML Fields.md deleted file mode 100644 index 65af140..0000000 --- a/_WIP_Features/HTML Fields.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 2 ---- - - - -TODO diff --git a/_WIP_Features/Icon Fields.md b/_WIP_Features/Icon Fields.md deleted file mode 100644 index 3b97253..0000000 --- a/_WIP_Features/Icon Fields.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 3 ---- - - - -TODO diff --git a/_WIP_Features/JSON Fields.md b/_WIP_Features/JSON Fields.md deleted file mode 100644 index 2fc2a74..0000000 --- a/_WIP_Features/JSON Fields.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 6 ---- - - - -TODO diff --git a/_WIP_Features/Locales Files.md b/_WIP_Features/Locales Files.md deleted file mode 100644 index f66dbf4..0000000 --- a/_WIP_Features/Locales Files.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 7 ---- - - - -TODO diff --git a/_WIP_Features/Migrating Your Extension.md b/_WIP_Features/Migrating Your Extension.md deleted file mode 100644 index 92d75c3..0000000 --- a/_WIP_Features/Migrating Your Extension.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -sort: 2 ---- - - - -## TODO diff --git a/_WIP_Features/Page Reload And HMR.md b/_WIP_Features/Page Reload And HMR.md deleted file mode 100644 index 28cd30f..0000000 --- a/_WIP_Features/Page Reload And HMR.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -sort: 11 ---- - - - -TODO - ---- diff --git a/_WIP_Features/Script Fields.md b/_WIP_Features/Script Fields.md deleted file mode 100644 index 0efdf85..0000000 --- a/_WIP_Features/Script Fields.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 3 ---- - - diff --git a/_WIP_Features/Static Assets.md b/_WIP_Features/Static Assets.md deleted file mode 100644 index 802761d..0000000 --- a/_WIP_Features/Static Assets.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 5 ---- - - - -TODO diff --git a/_WIP_Features/The Manifest File.md b/_WIP_Features/The Manifest File.md deleted file mode 100644 index 1880a58..0000000 --- a/_WIP_Features/The Manifest File.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 1 ---- - - - -TODO diff --git a/_WIP_Features/Web Accessible Resources.md b/_WIP_Features/Web Accessible Resources.md deleted file mode 100644 index 8ec7ec2..0000000 --- a/_WIP_Features/Web Accessible Resources.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -templateEngineOverride: njk,md -sort: 8 ---- - - - -fonts, csv diff --git a/_WIP_Integrations/Add To This Page.md b/_WIP_Integrations/Add To This Page.md deleted file mode 100644 index b5cd0f6..0000000 --- a/_WIP_Integrations/Add To This Page.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sort: 5 ---- - -## TODO diff --git a/_WIP_Integrations/Authentication.md b/_WIP_Integrations/Authentication.md deleted file mode 100644 index ba654c1..0000000 --- a/_WIP_Integrations/Authentication.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sort: 4 ---- - -## TODO diff --git a/_WIP_Integrations/Stripe.md b/_WIP_Integrations/Stripe.md deleted file mode 100644 index 1515b07..0000000 --- a/_WIP_Integrations/Stripe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sort: 1 ---- - -## TODO diff --git a/_WIP_Integrations/Supabase.md b/_WIP_Integrations/Supabase.md deleted file mode 100644 index ede3808..0000000 --- a/_WIP_Integrations/Supabase.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sort: 2 ---- - -## TODO diff --git a/_WIP_Integrations/assets/new-template.png b/_WIP_Integrations/assets/new-template.png deleted file mode 100644 index b3af500..0000000 Binary files a/_WIP_Integrations/assets/new-template.png and /dev/null differ diff --git a/assets/blog/extension-2-0-0-beta.png b/assets/blog/extension-2-0-0-beta.png new file mode 100644 index 0000000..2d16e19 Binary files /dev/null and b/assets/blog/extension-2-0-0-beta.png differ diff --git a/components.json b/components.json index 5e86a46..038beb5 100644 --- a/components.json +++ b/components.json @@ -11,7 +11,7 @@ "prefix": "" }, "aliases": { - "components": "@/components", - "utils": "@/lib/utils" + "components": "theme/components", + "utils": "theme/lib/utils" } } diff --git a/docs/en/blog/announcing-2-0-0-beta.mdx b/docs/en/blog/announcing-2-0-0-beta.mdx new file mode 100644 index 0000000..41c335c --- /dev/null +++ b/docs/en/blog/announcing-2-0-0-beta.mdx @@ -0,0 +1,21 @@ +# Announcing Extension.js 2.0.0-beta + +> October 18, 2024 + +![](../../../assets/blog/extension-2-0-0-beta.png) + +Extension.js is now officially in beta. + +**Version 2.0.0-beta** + +- Fixed SASS Modules not working in the content script. +- Add support to Svelte templates. +- Add preview command. +- Updated documentation (this one!) + +The beta release is focused on making all the existing features stable enough to document and test. New features are locked until the next stable release and the documentation is now up-to-date. This is the last beta release before the release candidate. + +Thanks for reading. Talk soon. + +Cezar Augusto
+Creator and Lead Developer, Extension.js diff --git a/docs/en/blog/index.mdx b/docs/en/blog/index.mdx index e021927..6379250 100644 --- a/docs/en/blog/index.mdx +++ b/docs/en/blog/index.mdx @@ -1,5 +1,9 @@ # Latest News +## [Announcing v2.0.0-beta](/blog/announcing-2-0-0-beta) + +> October 18, 2024 + ## [Announcing v2.0.0-alpha](/blog/announcing-2-0-0-alpha) > September 9, 2024 diff --git a/docs/en/docs/_meta.json b/docs/en/docs/_meta.json index 1b5e84a..0a63933 100644 --- a/docs/en/docs/_meta.json +++ b/docs/en/docs/_meta.json @@ -10,7 +10,32 @@ }, { "type": "dir", - "name": "guides", - "label": "Guides" + "name": "features", + "label": "Features" + }, + { + "type": "dir", + "name": "browsers", + "label": "Browsers" + }, + { + "type": "dir", + "name": "languages-and-frameworks", + "label": "Languages and Frameworks" + }, + { + "type": "dir", + "name": "integrations", + "label": "Configuration Support" + }, + { + "type": "dir", + "name": "development", + "label": "Development" + }, + { + "type": "dir", + "name": "commands", + "label": "Commands Reference" } ] diff --git a/docs/en/docs/browsers/_meta.json b/docs/en/docs/browsers/_meta.json new file mode 100644 index 0000000..4bdc9e8 --- /dev/null +++ b/docs/en/docs/browsers/_meta.json @@ -0,0 +1,22 @@ +[ + { + "name": "available", + "label": "Browsers Available" + }, + { + "name": "preferences", + "label": "Browser Preferences" + }, + { + "name": "flags", + "label": "Browser Flags" + }, + { + "name": "profile", + "label": "Browser Profile" + }, + { + "name": "unsupported", + "label": "Running Browsers From Binary" + } +] diff --git a/docs/en/docs/browsers/available.mdx b/docs/en/docs/browsers/available.mdx new file mode 100644 index 0000000..0eb891b --- /dev/null +++ b/docs/en/docs/browsers/available.mdx @@ -0,0 +1,38 @@ +import { AvatarBrowsers } from "../../../../theme/components/avatar-group/browsers"; + + + +# Browsers Available + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull-request. The link that documents this feature can be found at the bottom of the page. + +Extension.js provides support for a variety of browsers, enabling you to easily develop and test your extensions across multiple platforms, detecting browser binaries and launching the selected browser with your extension loaded. The plugin also supports custom profiles, browser preferences, flags, and more. + +## How Does It Work? + +To launch your extension in a specific browser, Extension.js uses the browser's binary path to open a new instance with your extension loaded. For supported browsers (Chrome, Edge, Firefox) you can specify the browser to use by passing the `--browser` flag with the desired browser name. For other browsers, you can specify the path to the browser binary using the `--chromium-binary` or `--gecko-binary` flags, depending on the engine used by the browser. + +## Supported Browsers + +The following browsers are officially supported by Extension.js: + +| Browser | Usage | +| ------------------ | ------------------------------------------------------------- | +| **Chrome** | `extension dev --browser=chrome` | +| **Edge** | `extension dev --browser=edge` | +| **Firefox** | `extension dev --browser=firefox` | +| **Chromium-based** | `extension dev --chromium-binary=chromium-based=browser-path` | +| **Gecko-based** | `extension dev --gecko-binary=gecko-based-browser-path` | + +Any browser based on the Chromium engine (e.g., Brave or Opera) is supported with the same configuration options as Chrome. + +## Best Practices + +- **Use Custom Profiles**: Keep separate profiles for each browser during development for isolated testing. +- **Polyfill for Compatibility**: Ensure your extension works across older browsers by using the `--polyfill` flag. +- **Browser Flags**: Use browser flags to enable or disable features for specific testing scenarios, like `--disable-extensions` for clean testing environments. + +## Next Steps + +- [Customize your browser configuration](./flags.mdx) using the `--browser-args` flag. +- [Learn more about unsupported browsers](./unsupported.mdx) and how to run your extension on them. diff --git a/docs/en/docs/browsers/flags.mdx b/docs/en/docs/browsers/flags.mdx new file mode 100644 index 0000000..a1fce41 --- /dev/null +++ b/docs/en/docs/browsers/flags.mdx @@ -0,0 +1,64 @@ +import { AvatarBrowsers } from "../../../../theme/components/avatar-group/browsers.tsx"; + + + +# Browser Flags + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull-request. The link that documents this feature can be found at the bottom of the page. + +Browser flags allow you to pass custom command-line flags to the browser during launch. +This can be useful for enabling experimental features, disabling security policies, or customizing the browser's behavior in development mode. + +## How Does It Work? + +When you run your extension in development mode, you can specify browser flags to customize the browser's behavior. Extension.js will hook into the browser's launch process and pass the specified flags to the browser binary. + +## How to Use Browser Flags + +You can configure browser flags in the `extension.config.js` file by using the `browserFlags` key. +These flags will be passed to the browser when it launches during the development process. + +### Example Configuration + +Here’s an example of how you can use browser flags in your `extension.config.js`: + +```js +module.exports = { + browser: { + chrome: { + browserFlags: ["--disable-web-security", "--auto-open-devtools-for-tabs"], + }, + firefox: { + browserFlags: ["--devtools", "--new-instance"], + }, + }, +}; +``` + +In this example: + +- For **Chrome**, `--disable-web-security` disables web security features like CORS, and `--auto-open-devtools-for-tabs` opens DevTools for each new tab. +- For **Firefox**, `--devtools` opens DevTools by default, and `--new-instance` launches a new browser instance for testing purposes. + +## Available Flags for Supported Browsers + +The available flags vary between browsers. Below are links to where you can find more information on the supported flags for each browser. + +| Browser | Usage | More Information | +| -------------- | ---------------------------------------- | -------------------------------------------------------------------------------------- | +| Chrome | `extension dev --browser=chrome` | [Chrome Flags](https://peter.sh/experiments/chromium-command-line-switches/) | +| Edge | `extension dev --browser=edge` | [Edge Flags](https://docs.microsoft.com/en-us/deployedge/microsoft-edge-policies) | +| Firefox | `extension dev --browser=firefox` | [Firefox Flags](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options) | +| Chromium-based | `extension dev --browser=chromium-based` | [Chromium Flags](https://peter.sh/experiments/chromium-command-line-switches/) | +| Gecko-based | `extension dev --browser=gecko-based` | Firefox-based browsers share the same flags as Firefox. | + +If your target browser is not listed here, it may not have publicly disclosed its available flags. If you are aware of resources for browser flags for unsupported browsers, please update this documentation. + +## Best Practices + +- **Test in Different Browsers**: Customize browser flags for each supported browser to test behavior across different environments. This helps in ensuring compatibility across all target platforms. + +## Next Steps + +- Learn more about [Browser Preferences](./preferences.mdx). +- Explore [Browser-Specific Environment Files](../features/environment-variables.mdx). diff --git a/docs/en/docs/browsers/preferences.mdx b/docs/en/docs/browsers/preferences.mdx new file mode 100644 index 0000000..efc928b --- /dev/null +++ b/docs/en/docs/browsers/preferences.mdx @@ -0,0 +1,90 @@ +import { AvatarBrowsers } from "../../../../theme/components/avatar-group/browsers"; + + + +# Browser Preferences + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull-request. The link that documents this feature can be found at the bottom of the page. + +Extension.js allows you to customize browser preferences for Firefox and other Gecko-based browsers. +These preferences can be configured through the `preferences` property in the `extension.config.js` file, +which then adds the custom preferences to the browser's user profile during development. + +## How Does It Work? + +Browser preferences are settings that can be configured to customize the behavior of the browser. Extension.js will hook into the browser's profile directory and modify the preferences file to set the custom preferences you define in the configuration file. + +## How to Set Custom Preferences + +To set custom preferences, add a `preferences` key to the browser configuration in your `extension.config.js`. +These preferences will be passed to the browser during the development process, modifying its behavior according to your configuration. + +### Example Configuration + +Here is an example of how you can set custom preferences for Firefox and Gecko-based browsers in your `extension.config.js`: + +```js +module.exports = { + browser: { + firefox: { + preferences: { + "browser.startup.homepage": "https://developer.mozilla.org", + "devtools.theme": "dark", + "dom.webnotifications.enabled": false, + }, + }, + }, +}; +``` + +In this example: + +- `browser.startup.homepage` sets the default homepage for Firefox. +- `devtools.theme` sets the theme of the developer tools to dark mode. +- `dom.webnotifications.enabled` disables web notifications. + +## Using Preferences in the Configuration File + +Below is the interface for configuring browser preferences within your `extension.config.js`: + +```ts +interface DevOptions { + browser: "firefox" | "gecko-based"; + preferences?: Record; +} +``` + +> Note from the Author: The `preferences` object can contain any key-value pair that is valid for the browser's preferences. While it is theoretically possible to set any preference on Chromium-based browsers, there is no guarantee that all preferences will work as expected. + +This allows you to customize preferences like the homepage, DevTools configuration, or disable certain browser features like web notifications. + +### Example with Custom Profile + +You can also specify a custom profile for Firefox using the `profile` option along with `preferences`: + +```js +module.exports = { + browser: { + firefox: { + profile: "path/to/custom-profile", + preferences: { + "browser.startup.homepage": "https://example.com", + }, + }, + }, +}; +``` + +## More Detailed Preferences + +For a comprehensive list of available Firefox preferences, you can explore the [Firefox source code](https://searchfox.org/mozilla-central/source/) where many default preferences are defined in `all.js` or `firefox.js`. + +## Best Practices + +- **Use Custom Profiles**: It is a good idea to use separate profiles for different browsers during development to keep testing environments isolated. +- **Set Browser-Specific Preferences**: Customize browser preferences to optimize your development environment, such as disabling web notifications or setting custom homepages. + +## Next Steps + +- Learn more about [browser-specific environment files](../features/environment-variables.mdx). +- Explore how to [configure other browser preferences](./available.mdx). diff --git a/docs/en/docs/browsers/profile.mdx b/docs/en/docs/browsers/profile.mdx new file mode 100644 index 0000000..3e9a901 --- /dev/null +++ b/docs/en/docs/browsers/profile.mdx @@ -0,0 +1,58 @@ +import { AvatarEmoji } from "../../../../theme/components/avatar-emoji"; + + + +# Browser Profile + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull request. The link that documents this feature can be found at the bottom of the page. + +Browser profiles allow you to load a custom browser profile during extension development, which can be useful for replicating real-user behavior, settings, extensions, and session data. You can either use the default profile provided by Extension.js or define your own path using the `--profile` flag in the CLI. + +## How Does It Work? + +You can specify a custom browser profile to load during the browser launch process. Extension.js will hook into the browser's profile directory and load the specified profile, allowing you to test your extension in a real-world environment. + +## How to Use Browser Profiles + +### Using the Default Profile + +By default, **Extension.js** launches a browser with a fresh, clean profile. This ensures there are no session conflicts or unwanted settings during testing. + +### Using a Custom Profile + +To use a custom browser profile, you can specify the profile path in the `extension.config.js` file or use the `--profile` flag during the development process. + +**Example in `extension.config.js`:** + +```js +module.exports = { + browser: { + chrome: { + profile: "path/to/custom/profile", + }, + firefox: { + profile: "path/to/custom/firefox-profile", + }, + }, +}; +``` + +### CLI Option: Specifying a Custom Profile Path + +You can also define a profile path directly in the CLI using the `--profile` flag: + +```bash +extension dev --browser=chrome --profile=/path/to/custom/profile +``` + +In this case, the **Chrome** browser will launch with the specified profile from `/path/to/custom/profile`. This is useful for loading existing profiles with user-specific settings like extensions, bookmarks, or cookies. + +## Best Practices + +- **Separate Profiles for Different Browsers**: It's a good practice to maintain separate profiles for each browser to ensure clean, isolated testing environments. +- **Real-World Testing**: Use real browser profiles from actual user sessions to test how your extension interacts with real-world data and settings. +- **Automated Testing**: Define and automate the use of custom profiles to run tests in various user environments. + +## Next Steps + +- For more detailed documentation and to contribute to this feature, check the [Browser Profiles on GitHub](https://github.com/extension-js/extension.js/tree/main/docs/browser-profile). diff --git a/docs/en/docs/browsers/unsupported.mdx b/docs/en/docs/browsers/unsupported.mdx new file mode 100644 index 0000000..cd66dca --- /dev/null +++ b/docs/en/docs/browsers/unsupported.mdx @@ -0,0 +1,90 @@ +import { AvatarBrowsers } from "../../../../theme/components/avatar-group/browsers"; + + + +# Running Browsers From Binary + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull request. The link that documents this feature can be found at the bottom of the page. + +While Extension.js natively supports browsers like Chrome, Edge, and Firefox, you can also run unsupported browsers by specifying the path to the browser binary using the `--chromiumBinary` or `--geckoBinary` flags. Additionally, you can add custom binary paths via the `extension.config.js` file for persistent configuration across runs. + +## How Does It Work? + +If you need to run a browser that is not fully supported by Extension.js, you can specify the path to a custom browser binary using the `--chromiumBinary` or `--geckoBinary` flags. Extension.js will launch the specified browser binary with your extension loaded, allowing you to test your extension in a custom browser environment. + +**Example Using CLI Flags:** + +```bash +extension dev --chromiumBinary=/path/to/custom-browser +``` + +or for Gecko-based browsers like Firefox: + +```bash +extension dev --geckoBinary=/path/to/custom-firefox +``` + +**Example Using `extension.config.js`:** + +```js +module.exports = { + browser: { + firefox: { + geckoBinary: "/path/to/custom-firefox", + }, + "chromium-based": { + chromiumBinary: "/path/to/custom-browser", + }, + }, +}; +``` + +In these scenarios, the browser specified in the `--chromiumBinary` or `--geckoBinary` flag (or in `extension.config.js`) will be launched instead of the default Chrome, Edge, or Firefox binaries. + +## Available Browsers + +Here is a short list of browsers you can run using these binary flags, along with their official websites for reference: + +| **Browser Name** | **Type** | **CLI Flag** | **Official Website** | +| ----------------------------- | ---------------------- | ------------------ | --------------------------------------------------------- | +| **Brave** | Chromium-Based Browser | `--chromiumBinary` | [brave.com](https://brave.com) | +| **Opera** | Chromium-Based Browser | `--chromiumBinary` | [opera.com](https://www.opera.com) | +| **Vivaldi** | Chromium-Based Browser | `--chromiumBinary` | [vivaldi.com](https://vivaldi.com) | +| **Waterfox** | Gecko-Based Browser | `--geckoBinary` | [waterfox.net](https://www.waterfox.net) | +| **Firefox Developer Edition** | Gecko-Based Browser | `--geckoBinary` | [firefox.com](https://www.mozilla.org/firefox/developer/) | + +## Example Configuration + +Below is an example configuration using the `--geckoBinary` flag to run a custom Firefox instance with specific preferences and flags: + +```bash +extension dev --geckoBinary=/path/to/custom-firefox --profile=/path/to/firefox/profile +``` + +Or via `extension.config.js`: + +```js +module.exports = { + browser: { + firefox: { + geckoBinary: "/path/to/custom-firefox", + profile: "/path/to/firefox/profile", + preferences: { + homepage: "https://example.com", + }, + browserFlags: ["--safe-mode"], + }, + }, +}; +``` + +## Best Practices + +- **Set Up Profiles**: Utilize custom profiles for different users or configurations. +- **Preferences**: Use the `preferences` option to customize browser settings. +- **Flags**: Leverage browser flags for specialized environments or behaviors. +- **Binary Paths**: Run custom or unsupported browsers by specifying their binary paths. + +## Next Steps + +- Check out the [browser preferences](./preferences.mdx) to further configure your environment. diff --git a/docs/en/docs/guides/commands/_meta.json b/docs/en/docs/commands/_meta.json similarity index 57% rename from docs/en/docs/guides/commands/_meta.json rename to docs/en/docs/commands/_meta.json index 5cea2d6..fae2438 100644 --- a/docs/en/docs/guides/commands/_meta.json +++ b/docs/en/docs/commands/_meta.json @@ -1,7 +1,6 @@ [ - { "name": "reference", "label": "Reference" }, { "name": "create", "label": "create" }, { "name": "dev", "label": "dev" }, - { "name": "start", "label": "start" }, + { "name": "preview", "label": "preview" }, { "name": "build", "label": "build" } ] diff --git a/docs/en/docs/commands/build.mdx b/docs/en/docs/commands/build.mdx new file mode 100644 index 0000000..3b4a570 --- /dev/null +++ b/docs/en/docs/commands/build.mdx @@ -0,0 +1,90 @@ +import { PackageManagerTabs } from "@theme"; +import { AvatarImage } from "../../../../theme/components/avatar-image"; + + + +# Build Command + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull-request. The link that documents this feature can be found at the bottom of the page. + +The `build` command in Extension.js is responsible for compiling your extension for production. +This command creates optimized builds for different browsers, ensuring compatibility and performance. +The `build/` folder will contain subfolders for each targeted browser, each with the necessary code transforms and optimizations. + +## Usage + +To use the `build` command, run: + + + +## Build Output + +After running the `build` command, Extension.js generates optimized files for the specified browser. +You will find the output inside the `build/` folder, with separate subfolders for each browser (Chrome, Edge, etc.). +These folders contain the bundled JavaScript, CSS, HTML, and other necessary assets for the extension to work in the targeted environment. + +**Example Output Structure:** + +```plaintext +build/ +├── chrome/ +│ ├── manifest.json +│ ├── background.js +│ ├── content.js +├── edge/ +│ ├── manifest.json +│ ├── background.js +│ ├── content.js +``` + +## Arguments And Flags + +| Flag | Argument | What it does | Defaults to | +| ------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| [path or url] | The extension path or the remote extension URL | If a path is defined, builds the local extension. If a URL is provided, pulls the extension from remote source and builds it as a local extension | `process.cwd()` | +| -b, --browser | The browser that will run the extension | Specifies the browser to build for (`chrome`, `edge`, `all`) | `"chrome"` | + +## Features + +- **Cross-Browser Builds:** You can build for multiple browsers in one go by specifying the `--browser` flag. + If you set the flag to `"all"`, Extension.js will generate builds for all supported browsers. +- **Optimized for Production:** The `build` command optimizes the extension for production by applying minification and other performance improvements. +- **Custom Output:** You can customize the output filename and format for your zip file by setting the `--zipFilename` option. + +## Example Build Command + +To build for Chrome and generate a zip file, run the following: + + + +## Build Process Overview + +During the build process, the following steps take place: + +- **Manifest Handling:** The manifest file is processed, ensuring that all required fields for the targeted browser are present. +- **Asset Bundling:** JavaScript, CSS, and other assets are bundled and minified for optimal performance. +- **Browser-Specific Transforms:** Depending on the target browser, Extension.js applies the necessary code transformations. +- **Zip Generation:** If the `--zip` flag is passed, the build output will be packaged into a zip file for easy distribution to the browser’s extension store. + +## Best Practices + +- **Target All Browsers:** For cross-browser compatibility, always build for multiple browsers by using the `--browser=all` option. +- **Check Build Logs:** After the build, review the logs to ensure there are no warnings or errors. The build logs will highlight any issues with compatibility or missing assets. +- **Optimize Your Manifest:** Ensure that your `manifest.json` is optimized and fully compatible with all targeted browsers to avoid build issues. +- **Custom Build Artifacts:** You can define custom names for your build artifacts using the `--zipFilename` option, which is useful for organizing builds. + +## Next Steps + +- Learn more about the [browsers available](../browsers/available.mdx). diff --git a/docs/en/docs/commands/create.mdx b/docs/en/docs/commands/create.mdx new file mode 100644 index 0000000..1887e0e --- /dev/null +++ b/docs/en/docs/commands/create.mdx @@ -0,0 +1,95 @@ +import { PackageManagerTabs } from "@theme"; +import { AvatarImage } from "../../../../theme/components/avatar-image"; + + + +# Create Command + +The `create` command is responsible for setting up a new browser extension project using Extension.js. +This command accepts flags like `--template` for initializing your extension with various templates, +and allows you to define the directory or path where the extension will be created. +The command is designed to simplify the setup process for developers, +providing a streamlined way to get started with cross-browser extension development. + +## Usage + +You can run the `create` command using the following syntax. +Replace `` with your project name or path and set any required options: + + [options]", + pnpm: "pnpx extension@latest create [options]", + yarn: "yarn dlx extension@latest create [options]", + }} +/> + +**Example Usage with Templates** + + + +This example demonstrates creating a new extension using the `react` template. +You can substitute `react` with any available template from the [Templates](../getting-started/templates) section. + +## Path Option + +The `create` command can also accept a specific directory where you want to create the extension. +If no path is provided, it defaults to the current working directory. + + + +In this example, the extension is created in the `path/to/my-extension` directory. + +## Arguments and Flags + +The `create` command accepts several arguments and flags to customize the behavior of the extension creation: + +| Flag | Argument | What it does | Defaults to | +| ---------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `[path or name]` | The extension path or name | If a path is defined, loads the local extension. If a name is provided, loads the extension in the current working directory. | `process.cwd()` | +| -t, --template | Name of the template used to bootstrap your extension | Bootstrap your extension using a template | [new](../getting-started/templates#template-new) | + +- **Path or Name:** If a path is provided, the extension is created at that location. If a name is provided, it is created in the current working directory. +- **`--template`:** Specifies the template to use when bootstrapping the extension (e.g., `react`, `typescript`). + +## Example Commands + +This command creates a new extension named my-new-extension using the React template. + + + +## Available Templates + +Users can choose from a variety of templates when creating their extension projects, including: + +- React: For building new tab or content scripts using React. +- TypeScript: Initializes an extension with TypeScript support. +- Custom: You can also specify external templates by providing a URL to a GitHub repo or any npm package. + +## Best Practices + +- Use meaningful names: When naming your extension or path, use meaningful names that reflect the project's purpose. +- Customizing Templates: You can always customize your extension after it's created. For example, if you want to add TypeScript support to a React template, it's easy to do so afterward. +- Starting from a Template: Using a template is the fastest way to get started with browser extension development in Extension.js. + +## Next Steps + +Once your extension is created, you can start developing by running extension dev. For more details on development workflows, check out the Development Guide. diff --git a/docs/en/docs/commands/dev.mdx b/docs/en/docs/commands/dev.mdx new file mode 100644 index 0000000..80b2e62 --- /dev/null +++ b/docs/en/docs/commands/dev.mdx @@ -0,0 +1,50 @@ +import { PackageManagerTabs } from "@theme"; +import { AvatarImage } from "../../../../theme/components/avatar-image"; + + + +# Dev Command + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull-request. The link that documents this feature can be found at the bottom of the page. + +The `dev` command in Extension.js is responsible for running your extension in development mode. +This command will start a new browser instance with your extension bundled and ready to run, +while also enabling live-reload as you modify the extension files during development. + +## Usage + +You can run the `dev` command with the following syntax: + + + +## Description + +The `dev` command is essential for a fast development workflow. It watches for changes in your extension's files, +recompiles them, and reloads the extension in real time. If you provide a URL, Extension.js will download and run the extension as if it were local. + +It supports multiple browsers, and you can specify which one to use by passing the `--browser` flag. +Setting the `browser` to `"all"` will open the extension in all available browsers. +If a browser is not installed, the command will throw a compiler error. + +## Arguments and Flags + +Below is a breakdown of the available flags for the `dev` command: + +| Flag | Argument | What it does | Defaults to | +| ------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| [path or url] | The extension path or the remote extension URL | If a path is defined, it loads the local extension. If a URL is provided, it pulls the extension from a remote source and loads it as a local extension | `process.cwd()` | +| -b, --browser | The browser that will run the extension | Changes the browser (`chrome`, `edge`, `all`) | `"chrome"` | + +## Best Practices + +- **Browser Specific Configurations:** You can use the `--browser` flag to run your extension in different browsers during the development process. + +## Next Steps + +- Learn more about the [Create Command](./create). diff --git a/docs/en/docs/commands/preview.mdx b/docs/en/docs/commands/preview.mdx new file mode 100644 index 0000000..daaeead --- /dev/null +++ b/docs/en/docs/commands/preview.mdx @@ -0,0 +1,63 @@ +import { PackageManagerTabs } from "@theme"; +import { AvatarImage } from "../../../../theme/components/avatar-image"; + + + +# Preview Command + +> **Warning**: This feature is a work in progress and may be incomplete or subject to change. If you see an error or something that could be improved, please make a pull-request. The link that documents this feature can be found at the bottom of the page. + + + +**The `preview` command allows you to run your extension in a production-like environment without making a final build.** It simulates how the extension will behave in a production browser environment. This is helpful for testing before officially deploying or building the extension. + +## Features + +- **Production-like Environment:** The `preview` command runs the extension in a browser with production-like settings, providing a realistic preview of the extension's final behavior. +- **Support for Remote Extensions:** If the path is a URL, `Extension.js` will download and preview the extension from that remote source, just like local extensions. +- **Browser Selection:** You can specify which browser (`chrome`, `edge`, etc.) should preview the extension. +- **Minimal Setup:** The preview command avoids full production builds, offering a faster way to check production behavior. + +## Arguments And Flags + +| Flag | Argument | What it does | Defaults to | +| ------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| [path or url] | The extension path or the remote extension URL | If a path is defined, previews the local extension. If a URL is provided, pulls the extension from remote source and previews it as a local extension | `process.cwd()` | +| -b, --browser | The browser that will run the extension | Specifies the browser (`chrome`, `edge`, `all`) | `"chrome"` | + +## Example Preview Command + +To preview your extension for Edge, run the following command: + + + +## Preview Process Overview + +When running the `preview` command, Extension.js follows these steps: + +1. **Manifest Validation:** Ensures that the `manifest.json` file exists in the project directory. +2. **Production-like Bundle:** Bundles the extension files without creating a full production build. The files are optimized but not stored permanently. +3. **Browser Launch:** Launches a browser instance and previews the extension, simulating a production environment. +4. **Real-Time Feedback:** Displays progress messages and possible warnings/errors during the preview. + +## Best Practices + +- **Test Before Building:** Always use `preview` to test how your extension will behave in production before using the `build` command for the final package. +- **Use Remote URLs:** If you want to test an extension stored in a remote repository, simply provide the repository URL as the argument. +- **Set Custom Ports:** If you're running multiple browser instances, use custom ports with the `--port` flag to avoid conflicts. + +## Next Steps + +- After previewing, consider [Building Your Extension](./build.mdx) for the final production package. diff --git a/docs/en/docs/development/_meta.json b/docs/en/docs/development/_meta.json new file mode 100644 index 0000000..ade876a --- /dev/null +++ b/docs/en/docs/development/_meta.json @@ -0,0 +1,10 @@ +[ + { "name": "manifest-json", "label": "Manifest" }, + { "name": "html", "label": "HTML" }, + { "name": "scripts", "label": "Scripts" }, + { "name": "css", "label": "CSS" }, + { "name": "locales", "label": "Locales" }, + { "name": "json", "label": "JSON" }, + { "name": "icons", "label": "Icons" }, + { "name": "web_accessible_resources", "label": "Web Accessible Resources" } +] diff --git a/docs/en/docs/development/css.mdx b/docs/en/docs/development/css.mdx new file mode 100644 index 0000000..7105bd8 --- /dev/null +++ b/docs/en/docs/development/css.mdx @@ -0,0 +1,100 @@ +import { PackageManagerTabs } from "@theme"; +import { AvatarImage } from "../../../../theme/components/avatar-image"; + + + +# CSS + +In an extension, CSS files play a key role in defining the visual style of popups, options pages, new tab pages, and injected content. Extension.js ensures that CSS files defined in the `manifest.json` or included in HTML files are properly processed, supporting popular pre-processors like Sass, Less, and standard CSS. + +## CSS File Support + +Extension.js offers first-class support for CSS and its pre-processor variants (e.g., `.scss`, `.sass`, `.less`). These styles can be used in both `content_scripts` and HTML pages with their original extensions, making development flexible and modern. Additionally, hot-module replacement (HMR) is supported for CSS during development, ensuring rapid feedback and live updates. + +The following manifest fields are supported for CSS files: + +| Manifest Field | File Type Expected | HMR Support | +| ----------------------------- | ------------------------- | ----------- | +| `content_scripts.css` | .css, .scss, .sass, .less | Yes | +| `background.page` | .css, .scss, .sass, .less | Yes | +| `action.default_popup` | .css, .scss, .sass, .less | Yes | +| `chrome_url_overrides.newtab` | .css, .scss, .sass, .less | Yes | +| `options_ui.page` | .css, .scss, .sass, .less | Yes | +| `devtools_page` | .css, .scss, .sass, .less | Yes | + +## Sample CSS in `manifest.json` + +Here’s a basic example of adding CSS files to `content_scripts` in the `manifest.json`: + +```json +{ + "manifest_version": 3, + "name": "My Extension", + "version": "1.0", + "content_scripts": [ + { + "matches": [""], + "css": ["./styles/content-style.less"], + "js": ["./scripts/content-script.js"] + } + ] +} +``` + +## Sample CSS in HTML Files + +CSS can be directly imported in HTML files, with full support for pre-processors like Less and Sass. Simply include the styles with their original extensions, and Extension.js will handle the compilation and injection of these styles: + +```html + + + + + + My Extension Page + + + +

Welcome to My Extension

+

This is a sample extension page.

+ + +``` + +## Output Path + +The output path for CSS files follows the same logic as scripts or HTML. The CSS will be bundled into `[feature]/index.css` for styles imported in HTML files, based on where it's used. For example, for a `newtab` override, the resulting files would look like this: + +```plaintext +chrome_url_overrides/ +├── index.html +├── index.js +└── index.css +``` + +For the CSS that represents content scripts in the `manifest.json` file, the output path will be: + +```plaintext +content_scripts/ +├── index.js +└── index.css +``` + +## How Extension.js Handles CSS + +The plugin for CSS file support in Extension.js ensures that the following steps occur during compilation: + +1. **Add CSS Entries:** CSS entries from the manifest and additional styles from HTML or `/styles` folder are added to the compilation. +2. **Inject Styles for Content Scripts:** During development, CSS files are dynamically injected into `content_scripts` to benefit from HMR. +3. **Enable HMR for CSS:** HMR is enabled for CSS files across all relevant extension contexts. +4. **Support for Pre-processors:** Files with `.scss`, `.sass`, and `.less` extensions are fully supported and compiled during the build process. +5. **Support for PostCSS:** PostCSS is supported for additional transformations and optimizations on CSS files. + +## Best Practices + +- Ensure your manifest fields correctly reference CSS files to take advantage of live-reloading and asset handling. +- Leverage the power of CSS pre-processors like Sass and Less for more organized and maintainable styles. + +## Next Steps + +- Learn more about handling additional CSS and static files via [Special Folders](../features/special-folders.mdx). diff --git a/docs/en/docs/development/html.mdx b/docs/en/docs/development/html.mdx new file mode 100644 index 0000000..103a9a9 --- /dev/null +++ b/docs/en/docs/development/html.mdx @@ -0,0 +1,99 @@ +import { PackageManagerTabs } from "@theme"; +import { AvatarImage } from "../../../../theme/components/avatar-image"; + + + +# HTML + +In an extension, HTML files serve as the foundation for user interfaces such as popups, options pages, and new tab pages. Extension.js ensures that HTML files defined in the `manifest.json` file are correctly processed, with full support for styles, scripts, and static assets. + +## HTML File Support + +Extension.js offers first-class support for HTML files, ensuring hot-module replacement (HMR) where applicable. It handles asset injection (images, CSS, JS), live-reloading, and updates. + +The following manifest fields use HTML files as entry points: + +| Manifest Field | File Type Expected | HMR Support | +| ------------------------------------ | ------------------ | ----------- | +| `action.default_popup` | .html | Yes | +| `background.page` | .html | Yes | +| `chrome_settings_overrides.homepage` | .html | Yes | +| `chrome_url_overrides.newtab` | .html | Yes | +| `chrome_url_overrides.history` | .html | Yes | +| `chrome_url_overrides.bookmarks` | .html | Yes | +| `devtools_page` | .html | Yes | +| `options_ui.page` | .html | Yes | +| `page_action.default_popup` | .html | Yes | +| `sandbox.pages` | .html | Yes | +| `side_panel.default_panel` | .html | Yes | +| `sidebar_action.default_panel` | .html | Yes | + +## Sample HTML File + +Here’s a basic example of an HTML file used in an extension: + +```html + + + + + + My Extension Page + + + +
+

Welcome to My Extension

+

This is a sample extension page.

+
+ + + +``` + +## Handling HTML Files Not Declared in `manifest.json` + +If you need to include additional HTML files that are not declared in the `manifest.json`, such as sandboxed iframes or extra pages, you can place these files in the `/pages` folder. Extension.js will process the HTML files in this folder as it would for any HTML entry point defined in the manifest. + +**Example Usage:** +Place your HTML file in the `/pages` folder, and it will be bundled and available for use, with full support for HMR, asset injection, and other processing steps. + +```plaintext +pages/ +└── extra-page.html +``` + +This allows you to add extra pages to your extension without cluttering the manifest or needing to specify them directly there. + +For more details, refer to the [Special Folders documentation](../features/special-folders.mdx). + +## Output Path + +The output path for HTML files is always structured as `[feature]/index[ext]`. For example, if you're working on a new tab override, the resulting files would look like this: + +```plaintext +chrome_url_overrides/ +├── index.html +├── index.css +└── index.js +``` + +## How Extension.js Handles HTML + +The plugin for HTML file support in Extension.js ensures that the following steps occur during compilation: + +1. **Emit HTML File:** The original HTML file is included in the compilation process. +2. **Inject Assets:** Static assets like ``, ``, and ` - -``` - -```js -// Index.jsx -import React from "react"; -import ReactDOM from "react-dom/client"; -import NewTabApp from "./NewTabApp"; - -const root = ReactDOM.createRoot(document.getElementById("root")); - -root.render( - - - , -); -``` - -```js -// MyExtension.jsx - -export default function MyExtension() { - return

Hello, Extension!

; -} -``` - -#### In A `content_script` File - -Scripts defined as `content_scripts` inherit the HTML from the tab they are allowed to run. If you don't know beforehand the HTML element you want to inject React into, you can create one and render it as any other pre-made HTML element. - -```js -import React from "react"; -import ReactDOM from "react-dom/client"; - -// Styles are set as dynamic imports in content_scripts. -import("./content.css"); -import MyExtension from "./MyExtension"; - -setTimeout(initial, 1000); - -function initial() { - // Create a new div element and append it to the document's body - const rootDiv = document.createElement("div"); - rootDiv.id = "extension-root"; - document.body.appendChild(rootDiv); - - // Use `createRoot` to create a root, then render the component - // Note that `createRoot` takes the container DOM node, not the React element - const root = ReactDOM.createRoot(rootDiv); - root.render(); -} -``` - -## Next Steps - -- Learn more about [Manifest Capabilities](../manifest-json/manifest-capabilities). - ---- diff --git a/docs/en/docs/guides/development/modern-javascript/typescript.mdx b/docs/en/docs/guides/development/modern-javascript/typescript.mdx deleted file mode 100644 index 45dd61c..0000000 --- a/docs/en/docs/guides/development/modern-javascript/typescript.mdx +++ /dev/null @@ -1,139 +0,0 @@ -import { PackageManagerTabs } from "@theme"; - -# TypeScript - -TypeScript is JavaScript with syntax for types. - -`Extension` offers built-in support for TypeScript files. - -## Starter TypeScript Template - -`Extension` comes with a default TypeScript template for new projects, which you can use as a starting point for your next TypeScript-based Extension. This is the easiest way to have TypeScript integrated with `Extension`. - -![TypeScript Extension Template](../../../../../../assets/guides/development/ts-template.png) - -#### Try it yourself - - - -## Usage With An Existing Extension - -### Installation - -1. Install TypeScript as a devDependency: - - - -2. Generates the TypeScript config file `tsconfig.json`: - - - -### Configuration - -#### Automatic TypeScript Support - -If your extension has TypeScript installed but no `tsconfig.json` file, `Extension` will attempt to automate the integration process by creating a `tsconfig.json` file at the project root, with the following defaults: - -```json5 -compilerOptions: { - // Allow JavaScript files to be imported inside your project, - // instead of just .ts and .tsx files - "allowJs": true, - - // Allow default imports from modules with no default export - "allowSyntheticDefaultImports": true, - - // Enables emit interoperability between CommonJS and ES Modules - "esModuleInterop": true, - - // Issue an error if a program tries to include a file by a casing - // different from the casing on disk. - "forceConsistentCasingInFileNames": true, - - // Controls how JSX constructs are emitted in JavaScript files. - // This only affects output of JS files that started in .tsx files. - // "react-jsx" if the user extension has React as a dependency or devDependency. - "jsx": "preserve", - - // Include typings for latest ECMAScript features and DOM APIs - "lib": ["dom", "dom.iterable", "esnext"], - - // Use Node's module resolution algorithm; useful if using npm packages - "moduleResolution": "node", - - // Use ES modules, which are the standard in modern browsers - "module": "esnext", - - // Allow importing '.json' files - "resolveJsonModule": true, - - // Enable all strict type-checking options - "strict": true, - - // Use the latest ECMAScript version for the target output - "target": "esnext", - - // Ensure each file can be safely transpiled without relying - // on other imports - "isolatedModules": false - }, - exclude: ["node_modules", "dist"] -} -``` - -#### Automatic Type Injection - -In order to handle the multiple import capabilities such as `import()` of JavaScript modules and file assets, `Extension` injects a `extension.d.ts` file at the root of your project folder. - -![TypeScript Extension Type Definition](../../../../../../assets/guides/development/extension-type-definition.png) - -> [!tip] -> Ensure the `extension.d.ts` file is added to your `.gitignore` file as it is auto-generated and only useful during `development` mode. - -#### `tsconfig.json` Overrides - -`Extension` applies some defaults to your extension `tsconfig.json` file. The options below overrides any user-defined rules. - -```json5 -{ - compilerOptions: { - // Generate source maps for debugging. False during production. - sourceMap: true, - - // Skip type checking of all declaration files (*.d.ts) - skipLibCheck: true, - - // Whether to embed the source map content in the .js files. - inlineSourceMap: false, - - // Generates a source map for .d.ts files which map back to the - // original .ts source file. - declarationMap: false, - - // Do not emit compiler output files like JavaScript source code, - // source-maps or declarations. - noEmit: true, - - // Tells TypeScript to save information about the project graph - // from the last compilation to files stored on disk. - incremental: true, - - // This setting lets you specify a file for storing incremental - // compilation information as a part of composite projects which - // enables faster building of larger TypeScript codebases. - tsBuildInfoFile: "./node_modules/.cache/tsbuildinfo", - }, - exclude: ["node_modules", "dist"], -} -``` - -## Next Steps - -- Learn how `Extension` works with extensions using [CSS Pre-Processors](../modern-css/overview). -- Learn how `Extension` works with extensions using [React](./react). diff --git a/docs/en/docs/guides/development/special-folders.mdx b/docs/en/docs/guides/development/special-folders.mdx deleted file mode 100644 index 46395bd..0000000 --- a/docs/en/docs/guides/development/special-folders.mdx +++ /dev/null @@ -1,47 +0,0 @@ -# Special Folders - -During development, sometimes we need a standalone script or page that adds extra functionality to an extension. It could be an iframe in a sandboxed page, or a script to be used as a content_script via `chrome.scripting.executeScript`. Given these features can be a core part of your extension@latest development, they need the same support as any other file handled by the manifest file, like support for TypeScript, pre-processors and all other features provided by Extension. - -This is the use case of the special folders. - -Special folders are folders that allows you to work with files outside the scope of the manifest file. Since `Extension` uses the manifest to generate the assets, files outside its scope are not bundled by default -- unless they are in a special folder. - -| Folder Name | Description | -| ----------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| `pages/` | Allows adding complementary HTML pages to the compilation process, handling its resources as any other HTML file from the manifest. | -| `scripts/` | Allows adding script files to the compilation process that are not present in the manifest file or HTML pages. | -| `public/` | Allows adding static assets to the output directory without bundling. These files are not processed by Extension. | - -## Handling HTML Pages - -To work with complementary HTML files (such as internal page links or ` -The `extension` package has a default `create` command that generates the foundation of your next extension project (as shown in the demo above). You can also use the `--template` flag to specify a template. +Extension.js runs via the `extension` package. The package has a `create` command that generates the foundation of your next extension project (as shown in the demo above). You can also use the `--template` flag to specify a template. =18.0'} + '@csstools/convert-colors@2.0.0': + resolution: {integrity: sha512-P7BVvddsP2Wl5v3drJ3ArzpdfXMqoZ/oHOV/yFiGFb3JQr9Z9UXZ9tnHAKJsO89lfprR1F9ExW3Yij21EjEBIA==} + engines: {node: '>=6.0.0'} + + '@floating-ui/core@1.6.8': + resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} + + '@floating-ui/dom@1.6.11': + resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==} + + '@floating-ui/react-dom@2.1.2': + resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.8': + resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -410,6 +468,294 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@radix-ui/primitive@1.1.0': + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + + '@radix-ui/react-arrow@1.1.0': + resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.0': + resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.0': + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.0': + resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-direction@1.1.0': + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.1': + resolution: {integrity: sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-icons@1.3.0': + resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-popper@1.2.0': + resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.2': + resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.0': + resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.1': + resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.0': + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.0': + resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.1.0': + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tabs@1.1.0': + resolution: {integrity: sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle@1.1.0': + resolution: {integrity: sha512-gwoxaKZ0oJ4vIgzsfESBuSgJNdc0rv12VhHgcqN0TEJmmZixXG/2XpsLK8kzNWYcnaoRIEEQc0bEi3dIvdUpjw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.1.3': + resolution: {integrity: sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.0': + resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.1.0': + resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.0': + resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@remix-run/router@1.15.3': resolution: {integrity: sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==} engines: {node: '>=14.0.0'} @@ -577,13 +923,6 @@ packages: peerDependencies: '@rspress/runtime': ^1.29.0 - '@rspress/plugin-rss@1.29.0': - resolution: {integrity: sha512-Jkk5Tg3KnQYBBIh8W1ymcAR9O8BZtgxgNGcgsIKWwnzKQz0mip5uC0Wr0cDehL6xfetiLbnVnvVbmDUpLZPdyQ==} - engines: {node: '>=14.17.6'} - peerDependencies: - react: '>=17.0.0' - rspress: ^1.29.0 - '@rspress/runtime@1.29.0': resolution: {integrity: sha512-aV+y7tyP3wQD7oUN2LdvECX/88gi0UiinTTEDJd+nortoMCBlGFBkzWtfO2QBWRBLpQYXvO5DM9KG6B7yK7oZg==} engines: {node: '>=14.17.6'} @@ -595,6 +934,9 @@ packages: resolution: {integrity: sha512-qRyy8xaJN4NM/I6iLS795rH91laV1uCQCVJ8H3Nu5OF4HrCFxR+yBLPQ/73dhRx6k9bEQVkGG6VfSehF1/LXyQ==} engines: {node: '>=14.17.6'} + '@rstack-dev/doc-ui@1.5.2': + resolution: {integrity: sha512-LPJNTRhv0eVxhsu6GR9zVK5szQwLgLmBHcOjrYkxhy6tHmDr++0ZXG9c61iAOH8A5gEbygXAmnLDsxbHD9Hosw==} + '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -868,10 +1210,20 @@ packages: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} + class-variance-authority@0.7.0: + resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + clear-module@4.1.2: resolution: {integrity: sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==} engines: {node: '>=8'} + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1040,6 +1392,19 @@ packages: electron-to-chromium@1.4.823: resolution: {integrity: sha512-4h+oPeAiGQOHFyUJOqpoEcPj/xxlicxBzOErVeYVMMmAiXUXsGpsFd0QXBMaUUbnD8hhSfLf9uw+MlsoIA7j5w==} + embla-carousel-react@8.3.0: + resolution: {integrity: sha512-P1FlinFDcIvggcErRjNuVqnUR8anyo8vLMIH8Rthgofw7Nj8qTguCa2QjFAbzxAUTQTPNNjNL7yt0BGGinVdFw==} + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 + + embla-carousel-reactive-utils@8.3.0: + resolution: {integrity: sha512-EYdhhJ302SC4Lmkx8GRsp0sjUhEN4WyFXPOk0kGu9OXZSRMmcBlRgTvHcq8eKJE1bXWBsOi1T83B+BSSVZSmwQ==} + peerDependencies: + embla-carousel: 8.3.0 + + embla-carousel@8.3.0: + resolution: {integrity: sha512-Ve8dhI4w28qBqR8J+aMtv7rLK89r1ZA5HocwFz6uMB/i5EiC7bGI7y+AM80yAVUJw3qqaZYK7clmZMUR8kM3UA==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1153,10 +1518,6 @@ packages: fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - feed@4.2.2: - resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} - engines: {node: '>=0.4.0'} - fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -1199,6 +1560,20 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} + framer-motion@11.9.0: + resolution: {integrity: sha512-nCfGxvsQecVLjjYDu35G2F5ls+ArE3FBfhxV0RSiisMaUKqteq5DMBFNRKwMyVj+VqKTNhawt+BV480YCHKFlQ==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fs-extra@11.2.0: resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} engines: {node: '>=14.14'} @@ -1537,6 +1912,11 @@ packages: resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} engines: {node: 14 || >=16.14} + lucide-react@0.446.0: + resolution: {integrity: sha512-BU7gy8MfBMqvEdDPH79VhOXSEgyG8TSPOKWaExWGCQVqnGH7wGgDngPbofu+KdtVjPQBWbEmnfMTq90CTiiDRg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + markdown-extensions@1.1.1: resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==} engines: {node: '>=0.10.0'} @@ -1756,6 +2136,12 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-themes@0.3.0: + resolution: {integrity: sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==} + peerDependencies: + react: ^16.8 || ^17 || ^18 + react-dom: ^16.8 || ^17 || ^18 + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -1930,12 +2316,23 @@ packages: react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + react-gist@1.2.4: + resolution: {integrity: sha512-uDgPJ08IJsl/7auK3CS53JKGC1P/X7tNCzo9k0r6D/5l+ilsxE1+SAaylJCh0aeyZoJOoxQikKU+MHsAvA9iFw==} + peerDependencies: + react: 0.14.x || ^15.0.0-rc || ^16.0.0-rc || ^17.0.0-rc + react-helmet-async@1.3.0: resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==} peerDependencies: react: ^16.6.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0 + react-icon-cloud@4.1.4: + resolution: {integrity: sha512-hc8yGNU98V6ObPdeNIt75M016xGMxbTWqB4l6exo1uwE5bvFU095unMbX2K3YBKYhGKEV3c7fSmq3jD3cRWX+A==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -1970,6 +2367,12 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' + react-tweet@3.2.1: + resolution: {integrity: sha512-dktP3RMuwRB4pnSDocKpSsW5Hq1IXRW6fONkHhxT5EBIXsKZzdQuI70qtub1XN2dtZdkJWWxfBm/Q+kN+vRYFA==} + peerDependencies: + react: '>= 18.0.0' + react-dom: '>= 18.0.0' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -2199,9 +2602,6 @@ packages: resolution: {integrity: sha512-WGXA6jcaoBo5Uhw0HX/s6z/sl3zyYQ7ZOnLOJzqwpctFcFmU4L07zn51e2VSkXXFpQZFAdMZNqOGz/7h/fvcRA==} engines: {node: '>=16.0.0'} - sax@1.3.0: - resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} - scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -2312,6 +2712,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + swr@2.2.5: + resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + tailwind-merge@2.5.2: resolution: {integrity: sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==} @@ -2430,6 +2835,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -2507,10 +2917,6 @@ packages: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} - xml-js@1.6.11: - resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} - hasBin: true - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -2764,6 +3170,25 @@ snapshots: '@cspell/url@8.14.2': {} + '@csstools/convert-colors@2.0.0': {} + + '@floating-ui/core@1.6.8': + dependencies: + '@floating-ui/utils': 0.2.8 + + '@floating-ui/dom@1.6.11': + dependencies: + '@floating-ui/core': 1.6.8 + '@floating-ui/utils': 0.2.8 + + '@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.6.11 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/utils@0.2.8': {} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -2876,6 +3301,243 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@radix-ui/primitive@1.1.0': {} + + '@radix-ui/react-arrow@1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-collection@1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-context@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-context@1.1.1(@types/react@18.3.5)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-direction@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-dismissable-layer@1.1.1(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-icons@1.3.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@radix-ui/react-id@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-popper@1.2.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-portal@1.1.2(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-presence@1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-presence@1.1.1(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-primitive@2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-roving-focus@1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-slot@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-tabs@1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-context': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-toggle@1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-tooltip@1.1.3(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-popper': 1.2.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.2(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.1(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.5)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-use-size@1.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/react-visually-hidden@1.1.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.5 + + '@radix-ui/rect@1.1.0': {} + '@remix-run/router@1.15.3': {} '@rsbuild/core@1.0.1-rc.0': @@ -3073,13 +3735,6 @@ snapshots: '@rspress/runtime': 1.29.0 medium-zoom: 1.1.0 - '@rspress/plugin-rss@1.29.0(react@18.3.1)(rspress@1.29.0(webpack@5.91.0))': - dependencies: - '@rspress/shared': 1.29.0 - feed: 4.2.2 - react: 18.3.1 - rspress: 1.29.0(webpack@5.91.0) - '@rspress/runtime@1.29.0': dependencies: '@rspress/shared': 1.29.0 @@ -3119,6 +3774,14 @@ snapshots: react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rspack-plugin-virtual-module: 0.1.12 + '@rstack-dev/doc-ui@1.5.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + framer-motion: 11.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - react + - react-dom + '@selderee/plugin-htmlparser2@0.11.0': dependencies: domhandler: 5.0.3 @@ -3400,11 +4063,19 @@ snapshots: chrome-trace-event@1.0.3: {} + class-variance-authority@0.7.0: + dependencies: + clsx: 2.0.0 + clear-module@4.1.2: dependencies: parent-module: 2.0.0 resolve-from: 5.0.0 + client-only@0.0.1: {} + + clsx@2.0.0: {} + clsx@2.1.1: {} color-convert@2.0.1: @@ -3610,6 +4281,18 @@ snapshots: electron-to-chromium@1.4.823: {} + embla-carousel-react@8.3.0(react@18.3.1): + dependencies: + embla-carousel: 8.3.0 + embla-carousel-reactive-utils: 8.3.0(embla-carousel@8.3.0) + react: 18.3.1 + + embla-carousel-reactive-utils@8.3.0(embla-carousel@8.3.0): + dependencies: + embla-carousel: 8.3.0 + + embla-carousel@8.3.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -3721,10 +4404,6 @@ snapshots: dependencies: format: 0.2.2 - feed@4.2.2: - dependencies: - xml-js: 1.6.11 - fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -3764,6 +4443,13 @@ snapshots: dependencies: fetch-blob: 3.2.0 + framer-motion@11.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + tslib: 2.6.2 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 @@ -4153,6 +4839,10 @@ snapshots: lru-cache@10.2.0: {} + lucide-react@0.446.0(react@18.3.1): + dependencies: + react: 18.3.1 + markdown-extensions@1.1.1: {} markdown-table@3.0.3: {} @@ -4617,6 +5307,11 @@ snapshots: neo-async@2.6.2: {} + next-themes@0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + node-domexception@1.0.0: {} node-fetch@3.3.0: @@ -4784,6 +5479,10 @@ snapshots: react-fast-compare@3.2.2: {} + react-gist@1.2.4(react@18.3.1): + dependencies: + react: 18.3.1 + react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.0 @@ -4794,6 +5493,11 @@ snapshots: react-fast-compare: 3.2.2 shallowequal: 1.1.0 + react-icon-cloud@4.1.4(react@18.3.1): + dependencies: + '@csstools/convert-colors': 2.0.0 + react: 18.3.1 + react-is@16.13.1: {} react-lazy-with-preload@2.2.1: {} @@ -4830,6 +5534,14 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-tweet@3.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@swc/helpers': 0.5.13 + clsx: 2.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + swr: 2.2.5(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -5058,8 +5770,6 @@ snapshots: sass-embedded-win32-ia32: 1.77.8 sass-embedded-win32-x64: 1.77.8 - sax@1.3.0: {} - scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -5165,6 +5875,12 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + swr@2.2.5(react@18.3.1): + dependencies: + client-only: 0.0.1 + react: 18.3.1 + use-sync-external-store: 1.2.2(react@18.3.1) + tailwind-merge@2.5.2: {} tailwindcss-animate@1.0.7(tailwindcss@3.4.10): @@ -5306,6 +6022,10 @@ snapshots: dependencies: punycode: 2.3.1 + use-sync-external-store@1.2.2(react@18.3.1): + dependencies: + react: 18.3.1 + util-deprecate@1.0.2: {} uvu@0.5.6: @@ -5414,10 +6134,6 @@ snapshots: xdg-basedir@5.1.0: {} - xml-js@1.6.11: - dependencies: - sax: 1.3.0 - xtend@4.0.2: {} yaml-front-matter@4.1.1: diff --git a/project-words.txt b/project-words.txt index b74ca2a..2484fab 100644 --- a/project-words.txt +++ b/project-words.txt @@ -6,3 +6,10 @@ omnibox OPENAI Rspress sakura +stylelintrc +minifications +recompiles +shadcn +shadcnui +vivaldi +X \ No newline at end of file diff --git a/rspress.config.ts b/rspress.config.ts index c44c937..5a0fbdb 100644 --- a/rspress.config.ts +++ b/rspress.config.ts @@ -7,13 +7,13 @@ import { defineConfig } from "rspress/config"; export default defineConfig({ root: path.join(__dirname, "docs"), - title: "Extension • Make it very easy to develop cross-browser extensions", + title: "Extension.js • Make it very easy to develop cross-browser extensions", description: "Make it very easy to develop cross-browser extensions", lang: "en", logo: { light: - "https://github.com/user-attachments/assets/c35a8c33-77da-44f3-9ad0-1b44a9b7a151", - dark: "https://github.com/user-attachments/assets/91d4de96-07cd-44fc-a2ce-3da862fffdc1", + "https://github.com/user-attachments/assets/c51916f5-454b-4502-b171-ab1f26fc2dd2", + dark: "https://github.com/user-attachments/assets/5690527e-1981-43dd-b267-4a7fc3752bc3", }, icon: "https://github.com/user-attachments/assets/58c169c3-03a5-4088-9bdb-61775917428f", markdown: { diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 7814660..e1f91df 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -5,6 +5,7 @@ module.exports = { "./components/**/*.{js,jsx,tx,tsx}", ], prefix: "", + darkMode: "class", // Enable class-based dark mode theme: { container: { center: true, @@ -63,10 +64,20 @@ module.exports = { from: { height: "var(--radix-accordion-content-height)" }, to: { height: "0" }, }, + marquee: { + from: { transform: "translateX(0)" }, + to: { transform: "translateX(calc(-100% - var(--gap)))" }, + }, + "marquee-vertical": { + from: { transform: "translateY(0)" }, + to: { transform: "translateY(calc(-100% - var(--gap)))" }, + }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", + marquee: "marquee var(--duration) linear infinite", + "marquee-vertical": "marquee-vertical var(--duration) linear infinite", }, }, }, diff --git a/theme/components/Landingpage/Hero/index.tsx b/theme/components-home/Landingpage/Hero/index.tsx similarity index 95% rename from theme/components/Landingpage/Hero/index.tsx rename to theme/components-home/Landingpage/Hero/index.tsx index 6f7372d..8693a76 100644 --- a/theme/components/Landingpage/Hero/index.tsx +++ b/theme/components-home/Landingpage/Hero/index.tsx @@ -12,7 +12,7 @@ const Hero = memo(() => { }, [tUrl, navigate]); return ( -
+
{
diff --git a/theme/components/Landingpage/index.tsx b/theme/components-home/Landingpage/index.tsx similarity index 72% rename from theme/components/Landingpage/index.tsx rename to theme/components-home/Landingpage/index.tsx index 2a72152..649b05f 100644 --- a/theme/components/Landingpage/index.tsx +++ b/theme/components-home/Landingpage/index.tsx @@ -2,7 +2,7 @@ import Hero from "./Hero"; const LandingPage = () => { return ( -
+
); diff --git a/theme/components/Announcement/close.tsx b/theme/components-home/announcement/close.tsx similarity index 100% rename from theme/components/Announcement/close.tsx rename to theme/components-home/announcement/close.tsx diff --git a/theme/components/Announcement/index.tsx b/theme/components-home/announcement/index.tsx similarity index 82% rename from theme/components/Announcement/index.tsx rename to theme/components-home/announcement/index.tsx index 538723b..74f1275 100644 --- a/theme/components/Announcement/index.tsx +++ b/theme/components-home/announcement/index.tsx @@ -3,7 +3,7 @@ import { usePageData } from "rspress/runtime"; import { useLang } from "rspress/runtime"; const LOCAL_STORAGE_KEY = "extensionjs-announcement-closed"; -const ANNOUNCEMENT_URL = "/blog/announcing-2-0-0-alpha"; +const ANNOUNCEMENT_URL = "/blog/announcing-2-0-0-beta"; function CloseIcon() { return ( @@ -17,7 +17,7 @@ function CloseIcon() { stroke-width="2" stroke-linecap="round" stroke-linejoin="round" - className="lucide lucide-x text-background w-4 h-4" + className="lucide lucide-x" > Close icon @@ -46,12 +46,12 @@ export function Announcement() { > {lang === "en" - ? "Announcing Extension.js 2.0.0-alpha" - : "Announcing Extension.js 2.0.0-alpha"} + ? "Announcing Extension.js 2.0.0-beta" + : "Announcing Extension.js 2.0.0-beta"} diff --git a/theme/components/avatar-emoji/index.tsx b/theme/components/avatar-emoji/index.tsx new file mode 100644 index 0000000..960d0b9 --- /dev/null +++ b/theme/components/avatar-emoji/index.tsx @@ -0,0 +1,53 @@ +import type React from "react"; + +interface AvatarEmojiProps { + emoji: string; + size?: number; // Optional prop for the width + altText?: string; // Optional prop for the alt text +} + +const emojiLinks: Record = { + fire: "🔥", + folder: "🗂️", + first: "🥇", + remote: "🎮", + manifest: "📜", + package: "📦", + icons: "🧩", + config: "🎛️", + error: "✋", + speed: "⚡️", + path: "🛣️", + user: "👤", + flag: "🏴‍☠️", + star: "⭐️", +}; + +const AvatarEmoji: React.FC = ({ + emoji, + size = 120, + altText, +}) => { + const emojiSymbol = emojiLinks[emoji] || ""; // Retrieve the emoji from the emojiLinks object + + if (!emojiSymbol) { + return

Emoji not found

; // Return a fallback if the emoji doesn't exist + } + + return ( +
+ + {emojiSymbol} + +
+ ); +}; + +export { AvatarEmoji }; diff --git a/theme/components/avatar-group/browsers.tsx b/theme/components/avatar-group/browsers.tsx new file mode 100644 index 0000000..8b82ff2 --- /dev/null +++ b/theme/components/avatar-group/browsers.tsx @@ -0,0 +1,66 @@ +import type React from "react"; + +interface AvatarBrowsersProps { + browsers: string[]; + size?: number; +} + +const browserIcons: Record = { + arc: "https://github.com/cezaraugusto/extension.js/assets/4672033/6ce53a31-c6f6-4a1c-b927-e9ec7fd2df78", + brave: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/brave/brave.svg", + chrome: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome.svg", + chromium: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chromium/chromium.svg", + edge: "https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge.svg", + firefox: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg", + opera: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera.svg", + safari: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari/safari.svg", + vivaldi: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/vivaldi/vivaldi.svg", + gecko: + "https://github.com/user-attachments/assets/b133bb25-c6c5-40fc-ae2e-0c4a2ba23b60", + waterfox: + "https://github.com/user-attachments/assets/9e3ab324-d49d-415e-a07f-1640bbe5196d", +}; + +const AvatarBrowsers: React.FC = ({ + browsers, + size = 120, +}) => { + const displayedBrowsers = browsers.slice(0, 3); // Display up to 3 browsers + const remainingCount = browsers.length - displayedBrowsers.length; // Remaining browsers count + + return ( +
+ {displayedBrowsers.map((browser) => ( + {`${browser} + ))} + {remainingCount > 0 && ( +
+ +{remainingCount} +
+ )} +
+ ); +}; + +export { AvatarBrowsers }; diff --git a/theme/components/avatar-group/config.tsx b/theme/components/avatar-group/config.tsx new file mode 100644 index 0000000..393dc54 --- /dev/null +++ b/theme/components/avatar-group/config.tsx @@ -0,0 +1,59 @@ +import type React from "react"; + +interface AvatarBrowsersProps { + browsers: string[]; +} + +const browserIcons: Record = { + arc: "https://github.com/cezaraugusto/extension.js/assets/4672033/6ce53a31-c6f6-4a1c-b927-e9ec7fd2df78", + brave: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/brave/brave.svg", + chrome: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome.svg", + chromium: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chromium/chromium.svg", + edge: "https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge.svg", + firefox: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg", + opera: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera.svg", + safari: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari/safari.svg", + vivaldi: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/vivaldi/vivaldi.svg", + gecko: + "https://github.com/user-attachments/assets/b133bb25-c6c5-40fc-ae2e-0c4a2ba23b60", + waterfox: + "https://github.com/user-attachments/assets/9e3ab324-d49d-415e-a07f-1640bbe5196d", +}; + +const AvatarBrowsers: React.FC = ({ browsers }) => { + const displayedBrowsers = browsers.slice(0, 3); // Display up to 3 browsers + const remainingCount = browsers.length - displayedBrowsers.length; // Remaining browsers count + + return ( +
+ {displayedBrowsers.map((browser) => ( + {`${browser} + ))} + {remainingCount > 0 && ( +
+ +{remainingCount} +
+ )} +
+ ); +}; + +export { AvatarBrowsers }; diff --git a/theme/components/avatar-group/css.tsx b/theme/components/avatar-group/css.tsx new file mode 100644 index 0000000..393dc54 --- /dev/null +++ b/theme/components/avatar-group/css.tsx @@ -0,0 +1,59 @@ +import type React from "react"; + +interface AvatarBrowsersProps { + browsers: string[]; +} + +const browserIcons: Record = { + arc: "https://github.com/cezaraugusto/extension.js/assets/4672033/6ce53a31-c6f6-4a1c-b927-e9ec7fd2df78", + brave: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/brave/brave.svg", + chrome: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome.svg", + chromium: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chromium/chromium.svg", + edge: "https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge.svg", + firefox: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg", + opera: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera.svg", + safari: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari/safari.svg", + vivaldi: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/vivaldi/vivaldi.svg", + gecko: + "https://github.com/user-attachments/assets/b133bb25-c6c5-40fc-ae2e-0c4a2ba23b60", + waterfox: + "https://github.com/user-attachments/assets/9e3ab324-d49d-415e-a07f-1640bbe5196d", +}; + +const AvatarBrowsers: React.FC = ({ browsers }) => { + const displayedBrowsers = browsers.slice(0, 3); // Display up to 3 browsers + const remainingCount = browsers.length - displayedBrowsers.length; // Remaining browsers count + + return ( +
+ {displayedBrowsers.map((browser) => ( + {`${browser} + ))} + {remainingCount > 0 && ( +
+ +{remainingCount} +
+ )} +
+ ); +}; + +export { AvatarBrowsers }; diff --git a/theme/components/avatar-group/javascript.tsx b/theme/components/avatar-group/javascript.tsx new file mode 100644 index 0000000..393dc54 --- /dev/null +++ b/theme/components/avatar-group/javascript.tsx @@ -0,0 +1,59 @@ +import type React from "react"; + +interface AvatarBrowsersProps { + browsers: string[]; +} + +const browserIcons: Record = { + arc: "https://github.com/cezaraugusto/extension.js/assets/4672033/6ce53a31-c6f6-4a1c-b927-e9ec7fd2df78", + brave: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/brave/brave.svg", + chrome: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome.svg", + chromium: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chromium/chromium.svg", + edge: "https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge.svg", + firefox: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg", + opera: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera.svg", + safari: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari/safari.svg", + vivaldi: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/vivaldi/vivaldi.svg", + gecko: + "https://github.com/user-attachments/assets/b133bb25-c6c5-40fc-ae2e-0c4a2ba23b60", + waterfox: + "https://github.com/user-attachments/assets/9e3ab324-d49d-415e-a07f-1640bbe5196d", +}; + +const AvatarBrowsers: React.FC = ({ browsers }) => { + const displayedBrowsers = browsers.slice(0, 3); // Display up to 3 browsers + const remainingCount = browsers.length - displayedBrowsers.length; // Remaining browsers count + + return ( +
+ {displayedBrowsers.map((browser) => ( + {`${browser} + ))} + {remainingCount > 0 && ( +
+ +{remainingCount} +
+ )} +
+ ); +}; + +export { AvatarBrowsers }; diff --git a/theme/components/avatar-group/technologies.tsx b/theme/components/avatar-group/technologies.tsx new file mode 100644 index 0000000..393dc54 --- /dev/null +++ b/theme/components/avatar-group/technologies.tsx @@ -0,0 +1,59 @@ +import type React from "react"; + +interface AvatarBrowsersProps { + browsers: string[]; +} + +const browserIcons: Record = { + arc: "https://github.com/cezaraugusto/extension.js/assets/4672033/6ce53a31-c6f6-4a1c-b927-e9ec7fd2df78", + brave: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/brave/brave.svg", + chrome: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome.svg", + chromium: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/chromium/chromium.svg", + edge: "https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge.svg", + firefox: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg", + opera: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera.svg", + safari: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari/safari.svg", + vivaldi: + "https://raw.githubusercontent.com/alrra/browser-logos/main/src/vivaldi/vivaldi.svg", + gecko: + "https://github.com/user-attachments/assets/b133bb25-c6c5-40fc-ae2e-0c4a2ba23b60", + waterfox: + "https://github.com/user-attachments/assets/9e3ab324-d49d-415e-a07f-1640bbe5196d", +}; + +const AvatarBrowsers: React.FC = ({ browsers }) => { + const displayedBrowsers = browsers.slice(0, 3); // Display up to 3 browsers + const remainingCount = browsers.length - displayedBrowsers.length; // Remaining browsers count + + return ( +
+ {displayedBrowsers.map((browser) => ( + {`${browser} + ))} + {remainingCount > 0 && ( +
+ +{remainingCount} +
+ )} +
+ ); +}; + +export { AvatarBrowsers }; diff --git a/theme/components/avatar-image/index.tsx b/theme/components/avatar-image/index.tsx new file mode 100644 index 0000000..f9c6bcc --- /dev/null +++ b/theme/components/avatar-image/index.tsx @@ -0,0 +1,95 @@ +import type React from "react"; + +interface AvatarImageProps { + icon: string; + size?: number; // Optional prop for the width + altText?: string; // Optional prop for the alt text +} + +const iconLinks: Record = { + babel: + "https://github.com/user-attachments/assets/73fc8a2c-6562-483d-83f3-4b690ddb2d63", + css: "https://github.com/user-attachments/assets/3e211ed0-cd60-4e17-9a0b-e8b497b0d80b", + cssModules: + "https://github.com/user-attachments/assets/96193b79-54e2-4387-8a89-52e8100d2df4", + env: "https://github.com/user-attachments/assets/75bd0de7-2b00-41db-8407-e06a96bf5b7c", + eslint: + "https://github.com/user-attachments/assets/7f1691f3-b94f-4204-90bc-6914af23b276", + extension: + "https://github.com/user-attachments/assets/cf9f43b8-9eac-470e-943f-a2e9e983fcb9", + html5: + "https://github.com/user-attachments/assets/4892720f-ddd3-4d89-9481-4a3db60ee2e2", + javascript: + "https://github.com/user-attachments/assets/508dd752-7978-4d49-a1ee-65e3a04f78f6", + less: "https://github.com/user-attachments/assets/74465239-2a9e-4b24-bbbf-0031e1e7938a", + node: "https://github.com/user-attachments/assets/1e8a9e62-9aee-4f21-af3c-7a3a9a5d7e5c", + postcss: + "https://github.com/user-attachments/assets/e15b417f-bb2b-4b86-8137-5e538e7e80c3", + preact: + "https://github.com/user-attachments/assets/6ee218ce-5edb-4229-a4f7-76b6b89165e5", + prettier: + "https://github.com/user-attachments/assets/72f43840-4709-4687-a128-a4609b791a00", + react: + "https://github.com/user-attachments/assets/69713bf5-c54d-4af0-a952-30526de21020", + sass: "https://github.com/user-attachments/assets/f25e2156-b219-4cf4-bd94-938ddf2fbfe7", + stylelint: + "https://github.com/user-attachments/assets/f2776268-45f6-4746-a966-2d94309e7cb3", + tailwindcss: + "https://github.com/user-attachments/assets/f388f8b1-63e8-494f-b7a8-3775366e816c", + typescript: + "https://github.com/user-attachments/assets/b954925d-670c-431e-ae18-0a389d6779a8", + vue: "https://github.com/user-attachments/assets/e0cb6661-e94a-4152-bae9-421bca06f65c", + wasm: "https://github.com/user-attachments/assets/aabf918f-d1a3-4703-b295-dea7804d638e", + github: + "https://github.com/user-attachments/assets/3732ca6d-bea7-4e74-9776-a11c48e70066", + shadcnui: + "https://github.com/user-attachments/assets/07ca973c-ca41-487f-91ac-a567ea9f5036", + image: + "https://github.com/user-attachments/assets/d6a95760-1039-4864-9fd8-241da127f1bb", + json: "https://github.com/user-attachments/assets/2ef7d509-57f8-4d5b-a093-0d0246d9b8fb", + locale: + "https://github.com/user-attachments/assets/132a4c05-e3af-4588-a14d-7e70c2893352", + html: "https://github.com/user-attachments/assets/198f81c1-7150-4196-812c-443176d2e141", + discord: + "https://github.com/user-attachments/assets/5dddc96d-6e92-4a87-8de5-37d02a435695", + x: "https://github.com/user-attachments/assets/8d5aac76-af7e-4a42-9d1f-1b1d2b2ab1e8", + package: + "https://github.com/user-attachments/assets/7b1fc9ae-9663-4992-b74c-8398ff7b0079", + webpack: + "https://raw.githubusercontent.com/webpack/media/master/logo/icon.png", + chromium: + "https://raw.githubusercontent.com/alrra/browser-logos/refs/heads/main/src/chromium/chromium.svg", +}; + +const AvatarImage: React.FC = ({ + icon, + size = 120, + altText, +}) => { + const imagePath = iconLinks[icon] || ""; // Retrieve the icon path from the iconLinks object + + if (!imagePath) { + return

Icon not found

; // Return a fallback if the icon doesn't exist + } + + return ( + {altText + ); +}; + +export { AvatarImage }; diff --git a/theme/components/carousel-templates/data.ts b/theme/components/carousel-templates/data.ts new file mode 100644 index 0000000..e614cfb --- /dev/null +++ b/theme/components/carousel-templates/data.ts @@ -0,0 +1,547 @@ +import type { Template } from "./types"; + +const DEFAULT_TEMPLATE: Template = { + name: "init", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: undefined, + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, +}; + +const JS_TEMPLATES: Template[] = [ + { + name: "action", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["action"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content-esm", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content-css-modules", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content-less", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "less", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content-less-modules", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "less", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content-main-world", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content-sass", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "sass", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "content-sass-modules", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "sass", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "declarative_net_request", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: undefined, + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "action-locales", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["action"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "new", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "new-esm", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "new-less", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "less", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "new-sass", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "sass", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "sidebar", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["sidebar"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "special-folders-pages", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: undefined, + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "special-folders-scripts", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: undefined, + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, + { + name: "storage", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: undefined, + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: undefined, + }, +]; + +const WASM_TEMPLATES: Template[] = []; + +const TS_TEMPLATES: Template[] = [ + { + name: "content-typescript", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, + { + name: "content-env", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, + { + name: "new-typescript", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, + { + name: "new-env", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, +]; + +const CUSTOM_TEMPLATES: Template[] = [ + { + name: "action-chatgpt", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["action"], + uiFramework: "react", + css: "css", + hasBackground: false, + hasEnv: true, + configFiles: ["postcss.config.js", "tailwind.config.js", "tsconfig.json"], + }, + { + name: "new-crypto", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json", "extension.config.js"], + }, + { + name: "new-node-apis", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json", "extension.config.js"], + }, + { + name: "content-react-svgr", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: "react", + css: "css", + hasBackground: true, + hasEnv: false, + configFiles: [ + "extension.config.js", + "tsconfig.json", + "postcss.config.js", + "tailwind.config.js", + ], + }, + { + name: "new-react-router", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: "react", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, +]; + +const FRAMEWORK_TEMPLATES: Template[] = [ + { + name: "new-react", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: "react", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, + { + name: "content-react", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: "react", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["postcss.config.js", "tailwind.config.js", "tsconfig.json"], + }, + { + name: "new-preact", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: "preact", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, + { + name: "content-preact", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: "preact", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["postcss.config.js", "tailwind.config.js", "tsconfig.json"], + }, + { + name: "content-extension-config", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: "react", + css: "css", + hasBackground: true, + hasEnv: false, + configFiles: [ + "extension.config.js", + "tsconfig.json", + "postcss.config.js", + "tailwind.config.js", + ], + }, + { + name: "new-vue", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: "vue", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, + { + name: "content-vue", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: "vue", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["postcss.config.js", "tailwind.config.js", "tsconfig.json"], + }, + { + name: "new-svelte", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: "svelte", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json"], + }, +]; + +const CONFIG_TEMPLATES: Template[] = [ + // { + // name: 'config-babel', + // screenshot: 'https://placekitten.com/200/300', + // description: 'An Extension.js template', // uiContext: ['newTab'], + // uiFramework: undefined, + // css: 'css', + // hasBackground: false, + // hasEnv: false, + // configFiles: ['babel.config.json'] + // }, + { + name: "new-config-eslint", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json", "eslint.config.mjs"], + }, + { + name: "new-config-lint", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: [ + "tsconfig.json", + ".stylelintrc.json", + "eslint.config.mjs", + ".prettierrc", + ".stylelintrc.json", + ], + }, + { + name: "new-config-prettier", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tsconfig.json", ".prettierrc"], + }, + { + name: "new-config-stylelint", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: undefined, + css: "sass", + hasBackground: false, + hasEnv: false, + configFiles: [".stylelintrc.json"], + }, + { + name: "content-tailwind", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["content"], + uiFramework: undefined, + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["tailwind.config.js", "postcss.config.js"], + }, + { + name: "new-tailwind", + screenshot: "https://placekitten.com/200/300", + description: "An Extension.js template", + uiContext: ["newTab"], + uiFramework: "react", + css: "css", + hasBackground: false, + hasEnv: false, + configFiles: ["postcss.config.js", "tailwind.config.js", "tsconfig.json"], + }, +]; + +const ALL_TEMPLATES: Template[] = [ + DEFAULT_TEMPLATE, + ...JS_TEMPLATES, + ...WASM_TEMPLATES, + ...TS_TEMPLATES, + ...CUSTOM_TEMPLATES, + ...FRAMEWORK_TEMPLATES, + ...CONFIG_TEMPLATES, +]; + +const ALL_TEMPLATES_BUT_DEFAULT = ALL_TEMPLATES.filter( + (template) => template.name !== "init", +); + +const SUPPORTED_BROWSERS: string[] = ["chrome", "edge", "firefox"]; + +export { + SUPPORTED_BROWSERS, + DEFAULT_TEMPLATE, + JS_TEMPLATES, + WASM_TEMPLATES, + TS_TEMPLATES, + CUSTOM_TEMPLATES, + FRAMEWORK_TEMPLATES, + CONFIG_TEMPLATES, + ALL_TEMPLATES, + ALL_TEMPLATES_BUT_DEFAULT, +}; diff --git a/theme/components/carousel-templates/details.tsx b/theme/components/carousel-templates/details.tsx new file mode 100644 index 0000000..6d3e6bd --- /dev/null +++ b/theme/components/carousel-templates/details.tsx @@ -0,0 +1,68 @@ +import { Card, CardContent } from "../ui/card"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableRow, +} from "../ui/table"; +import type { Template } from "./types"; + +export function TemplateDetailsCard({ template }: { template: Template }) { + const toUpperCase = (str: string) => + str.charAt(0).toUpperCase() + str.slice(1); + + return ( + + + + Context + + {template.uiContext ? toUpperCase(template.uiContext[0]) : "None"} + + + + Framework + + {toUpperCase(template.uiFramework || "None")} + + + + CSS + + {toUpperCase(template.css)} + + + + Background? + + {template.hasEnv ? "Yes" : "No"} + + + + Env Vars? + + {template.hasEnv ? "Yes" : "No"} + + + + Config Files + {template.configFiles && template.configFiles.length > 0 ? ( + +
    + {template.configFiles.map((configFile) => ( +
  • {configFile.toLowerCase()}
  • + ))} +
+
+ ) : ( + None + )} +
+
+ + {template.name} - Technical Details + +
+ ); +} diff --git a/theme/components/carousel-templates/get-toggle.tsx b/theme/components/carousel-templates/get-toggle.tsx new file mode 100644 index 0000000..4cb7637 --- /dev/null +++ b/theme/components/carousel-templates/get-toggle.tsx @@ -0,0 +1,7 @@ +import { FontBoldIcon } from "@radix-ui/react-icons"; + +import { Toggle } from "../../components/ui/toggle"; + +export function GetToggle() { + return Select; +} diff --git a/theme/components/carousel-templates/index.tsx b/theme/components/carousel-templates/index.tsx new file mode 100644 index 0000000..4c5e82b --- /dev/null +++ b/theme/components/carousel-templates/index.tsx @@ -0,0 +1,105 @@ +import { TabsCommandLine } from "@components/tabs-command-line"; +import { useState } from "react"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "../ui/carousel"; +import { ALL_TEMPLATES } from "./data"; +import { TemplateTabs } from "./template-tabs"; + +interface CarouselTemplates { + activeIndex?: number; + setActiveIndex?: (index: number) => void; + single?: boolean; + showControls?: boolean; + showCommands?: boolean; +} + +export function CarouselTemplatesControls({ + activeIndex, +}: { + activeIndex?: number; +}) { + return ( +
+ + {/* */} + +
+ ); +} + +export function CarouselTemplates({ + activeIndex, + setActiveIndex, + single, + showControls = true, + showCommands = true, +}: CarouselTemplates) { + const [localActiveIndex, localSetActiveIndex] = useState(0); + + const handleSlideClick = (index: number) => { + if (!setActiveIndex) { + localSetActiveIndex(index); + return; + } + + setActiveIndex(index); + }; + + return ( + +
+ + {ALL_TEMPLATES.map((template, index) => { + const isActive = + (activeIndex != null ? activeIndex : localActiveIndex) === index; + const activeClass = isActive + ? "bg-[var(--rp-c-brand-tint)] rounded-md" + : ""; + + return ( + + handleSlideClick(index)} + /> + + ); + })} + + {showControls && ( + + )} + {showCommands && ( + + )} + + ); +} diff --git a/theme/components/carousel-templates/template-tabs.tsx b/theme/components/carousel-templates/template-tabs.tsx new file mode 100644 index 0000000..bb7f86b --- /dev/null +++ b/theme/components/carousel-templates/template-tabs.tsx @@ -0,0 +1,35 @@ +import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"; +import { TemplateDetailsCard } from "./details"; +import { TemplateCard } from "./template"; +import type { Template } from "./types"; + +export function TemplateTabs({ + template, + className, + setActiveTemplate, +}: { + template: Template; + className?: string; + setActiveTemplate: () => void; +}) { + return ( + + + + Overview + Details + + + + + + + + + + ); +} diff --git a/theme/components/carousel-templates/template.tsx b/theme/components/carousel-templates/template.tsx new file mode 100644 index 0000000..09e804e --- /dev/null +++ b/theme/components/carousel-templates/template.tsx @@ -0,0 +1,53 @@ +import { Button } from "@components/ui/button"; +import { Toggle } from "@components/ui/toggle"; +import { Badge } from "../ui/badge"; +import { CardDescription, CardTitle } from "../ui/card"; +import type { Template } from "./types"; + +export function GetToggle({ + setActiveTemplate, +}: { + setActiveTemplate: () => void; +}) { + return ( + + + + ); +} + +export function TemplateCard({ + template, + setActiveTemplate, +}: { + template: Template; + setActiveTemplate: () => void; +}) { + return ( +
+ Prototype screenshot +
+
+ {template.name} + {template.description} +
+
+
+
+ + {template.uiContext || "manifest.json"} + + +
+
+
+ ); +} diff --git a/theme/components/carousel-templates/types.ts b/theme/components/carousel-templates/types.ts new file mode 100644 index 0000000..3e1579f --- /dev/null +++ b/theme/components/carousel-templates/types.ts @@ -0,0 +1,27 @@ +export type UIContext = + | "sidebar" + | "newTab" + | "content" + | "action" + | "devTools"; +export type ConfigFiles = + | "postcss.config.js" + | "tailwind.config.js" + | "tsconfig.json" + | ".stylelintrc.json" + | "extension.config.js" + | "babel.config.json" + | ".prettierrc" + | "eslint.config.mjs"; + +export interface Template { + name: string; + description: string; + screenshot: string; + uiContext: UIContext[] | undefined; + uiFramework: "react" | "preact" | "vue" | "svelte" | undefined; + css: "css" | "sass" | "less" | "stylus"; + hasBackground: boolean; + hasEnv: boolean; + configFiles: ConfigFiles[] | undefined; +} diff --git a/theme/components/tabs-command-line/index.tsx b/theme/components/tabs-command-line/index.tsx new file mode 100644 index 0000000..8792a5f --- /dev/null +++ b/theme/components/tabs-command-line/index.tsx @@ -0,0 +1,136 @@ +import { Check, Copy } from "lucide-react"; +import React, { useState } from "react"; +import { Button } from "../../components/ui/button"; +import { Card, CardContent } from "../../components/ui/card"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "../../components/ui/tabs"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "../../components/ui/tooltip"; + +interface TabsCommandLineProps { + command: { + npm: string; + pnpm: string; + yarn: string; + }; +} + +export function TabsCommandLine({ command }: TabsCommandLineProps) { + const [selectedTab, setSelectedTab] = useState<"npm" | "pnpm" | "yarn">( + "npm", + ); + const [copiedTab, setCopiedTab] = useState(null); + + const getCommand = (packageManager: "npm" | "pnpm" | "yarn"): string => { + switch (packageManager) { + case "npm": + return `npx ${command.npm}`; + case "pnpm": + return `pnpm dlx ${command.pnpm}`; + case "yarn": + return `yarn dlx ${command.yarn}`; + default: + return `npx ${command.npm}`; + } + }; + + const copyToClipboard = (text: string, tab: string) => { + navigator.clipboard.writeText(text); + setCopiedTab(tab); + setTimeout(() => setCopiedTab(null), 2000); + }; + + const tabs = [ + { + key: "npm", + label: "npm", + iconUrl: + "https://github.com/user-attachments/assets/135f149f-ce5e-4ce2-9fee-3eb529b5481a", + }, + { + key: "pnpm", + label: "pnpm", + iconUrl: + "https://github.com/user-attachments/assets/42ed278a-12da-4816-b7e3-da867144fc62", + }, + { + key: "yarn", + label: "Yarn", + iconUrl: + "https://github.com/user-attachments/assets/461cee19-7d40-4d25-b70e-42501cf5fc0f", + }, + ]; + + return ( + + + setSelectedTab(value as "npm" | "pnpm" | "yarn") + } + className="w-full" + > + + {tabs.map((tab) => ( + + The package manager iconUrl + {tab.label} + + ))} + + {tabs.map((tab) => ( + +
+
+                {getCommand(tab.key as "npm" | "pnpm" | "yarn")}
+              
+ + + + + + + {copiedTab === tab.key ? "Copied!" : "Copy to clipboard"} + + + +
+
+ ))} +
+
+ ); +} diff --git a/theme/components/three-arrows/index.tsx b/theme/components/three-arrows/index.tsx new file mode 100644 index 0000000..e0dc903 --- /dev/null +++ b/theme/components/three-arrows/index.tsx @@ -0,0 +1,51 @@ +const ThreeArrows = () => ( +
+ + Arrow right + + + + Arrow right + + + + Arrow right + + +
+); + +export { ThreeArrows }; diff --git a/theme/components/ui/badge.tsx b/theme/components/ui/badge.tsx new file mode 100644 index 0000000..510e924 --- /dev/null +++ b/theme/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import { type VariantProps, cva } from "class-variance-authority"; +import type * as React from "react"; + +import { cn } from "../../lib/utils"; + +const badgeVariants = cva( + "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ); +} + +export { Badge, badgeVariants }; diff --git a/theme/components/ui/button.tsx b/theme/components/ui/button.tsx new file mode 100644 index 0000000..a89d44c --- /dev/null +++ b/theme/components/ui/button.tsx @@ -0,0 +1,57 @@ +import { Slot } from "@radix-ui/react-slot"; +import { type VariantProps, cva } from "class-variance-authority"; +import * as React from "react"; + +import { cn } from "../../lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/theme/components/ui/card-hover-effect.tsx b/theme/components/ui/card-hover-effect.tsx new file mode 100644 index 0000000..add06c6 --- /dev/null +++ b/theme/components/ui/card-hover-effect.tsx @@ -0,0 +1,110 @@ +import { AnimatePresence, motion } from "framer-motion"; +import { useState } from "react"; +import { cn } from "../../lib/utils"; + +export const HoverEffect = ({ + items, + className, +}: { + items: { + title: string; + description: string; + link: string; + }[]; + className?: string; +}) => { + const [hoveredIndex, setHoveredIndex] = useState(null); + + return ( + + ); +}; + +export const Card = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +
+
+
{children}
+
+
+ ); +}; +export const CardTitle = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +

+ {children} +

+ ); +}; +export const CardDescription = ({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) => { + return ( +

+ {children} +

+ ); +}; diff --git a/theme/components/ui/card.tsx b/theme/components/ui/card.tsx new file mode 100644 index 0000000..20313da --- /dev/null +++ b/theme/components/ui/card.tsx @@ -0,0 +1,83 @@ +import * as React from "react"; + +import { cn } from "../../lib/utils"; + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/theme/components/ui/carousel.tsx b/theme/components/ui/carousel.tsx new file mode 100644 index 0000000..933bb2a --- /dev/null +++ b/theme/components/ui/carousel.tsx @@ -0,0 +1,260 @@ +import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons"; +import useEmblaCarousel, { + type UseEmblaCarouselType, +} from "embla-carousel-react"; +import * as React from "react"; + +import { cn } from "../../lib/utils"; +import { Button } from "./button"; + +type CarouselApi = UseEmblaCarouselType[1]; +type UseCarouselParameters = Parameters; +type CarouselOptions = UseCarouselParameters[0]; +type CarouselPlugin = UseCarouselParameters[1]; + +type CarouselProps = { + opts?: CarouselOptions; + plugins?: CarouselPlugin; + orientation?: "horizontal" | "vertical"; + setApi?: (api: CarouselApi) => void; +}; + +type CarouselContextProps = { + carouselRef: ReturnType[0]; + api: ReturnType[1]; + scrollPrev: () => void; + scrollNext: () => void; + canScrollPrev: boolean; + canScrollNext: boolean; +} & CarouselProps; + +const CarouselContext = React.createContext(null); + +function useCarousel() { + const context = React.useContext(CarouselContext); + + if (!context) { + throw new Error("useCarousel must be used within a "); + } + + return context; +} + +const Carousel = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + orientation = "horizontal", + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref, + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === "horizontal" ? "x" : "y", + }, + plugins, + ); + const [canScrollPrev, setCanScrollPrev] = React.useState(false); + const [canScrollNext, setCanScrollNext] = React.useState(false); + + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return; + } + + setCanScrollPrev(api.canScrollPrev()); + setCanScrollNext(api.canScrollNext()); + }, []); + + const scrollPrev = React.useCallback(() => { + api?.scrollPrev(); + }, [api]); + + const scrollNext = React.useCallback(() => { + api?.scrollNext(); + }, [api]); + + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault(); + scrollPrev(); + } else if (event.key === "ArrowRight") { + event.preventDefault(); + scrollNext(); + } + }, + [scrollPrev, scrollNext], + ); + + React.useEffect(() => { + if (!api || !setApi) { + return; + } + + setApi(api); + }, [api, setApi]); + + React.useEffect(() => { + if (!api) { + return; + } + + onSelect(api); + api.on("reInit", onSelect); + api.on("select", onSelect); + + return () => { + api?.off("select", onSelect); + }; + }, [api, onSelect]); + + return ( + +
+ {children} +
+
+ ); + }, +); +Carousel.displayName = "Carousel"; + +const CarouselContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { carouselRef, orientation } = useCarousel(); + + return ( +
+
+
+ ); +}); +CarouselContent.displayName = "CarouselContent"; + +const CarouselItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { orientation } = useCarousel(); + + return ( +
+ ); +}); +CarouselItem.displayName = "CarouselItem"; + +const CarouselPrevious = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollPrev, canScrollPrev } = useCarousel(); + + return ( + + ); +}); +CarouselPrevious.displayName = "CarouselPrevious"; + +const CarouselNext = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel(); + + return ( + + ); +}); +CarouselNext.displayName = "CarouselNext"; + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +}; diff --git a/theme/components/ui/hero-video-dialog.tsx b/theme/components/ui/hero-video-dialog.tsx new file mode 100644 index 0000000..830731b --- /dev/null +++ b/theme/components/ui/hero-video-dialog.tsx @@ -0,0 +1,140 @@ +import { AnimatePresence, motion } from "framer-motion"; +import { Play, XIcon } from "lucide-react"; +import { useState } from "react"; + +import { cn } from "../../lib/utils"; + +type AnimationStyle = + | "from-bottom" + | "from-center" + | "from-top" + | "from-left" + | "from-right" + | "fade" + | "top-in-bottom-out" + | "left-in-right-out"; + +interface HeroVideoProps { + animationStyle?: AnimationStyle; + videoSrc: string; + thumbnailSrc: string; + thumbnailAlt?: string; + className?: string; +} + +const animationVariants = { + "from-bottom": { + initial: { y: "100%", opacity: 0 }, + animate: { y: 0, opacity: 1 }, + exit: { y: "100%", opacity: 0 }, + }, + "from-center": { + initial: { scale: 0.5, opacity: 0 }, + animate: { scale: 1, opacity: 1 }, + exit: { scale: 0.5, opacity: 0 }, + }, + "from-top": { + initial: { y: "-100%", opacity: 0 }, + animate: { y: 0, opacity: 1 }, + exit: { y: "-100%", opacity: 0 }, + }, + "from-left": { + initial: { x: "-100%", opacity: 0 }, + animate: { x: 0, opacity: 1 }, + exit: { x: "-100%", opacity: 0 }, + }, + "from-right": { + initial: { x: "100%", opacity: 0 }, + animate: { x: 0, opacity: 1 }, + exit: { x: "100%", opacity: 0 }, + }, + fade: { + initial: { opacity: 0 }, + animate: { opacity: 1 }, + exit: { opacity: 0 }, + }, + "top-in-bottom-out": { + initial: { y: "-100%", opacity: 0 }, + animate: { y: 0, opacity: 1 }, + exit: { y: "100%", opacity: 0 }, + }, + "left-in-right-out": { + initial: { x: "-100%", opacity: 0 }, + animate: { x: 0, opacity: 1 }, + exit: { x: "100%", opacity: 0 }, + }, +}; + +export default function HeroVideoDialog({ + animationStyle = "from-center", + videoSrc, + thumbnailSrc, + thumbnailAlt = "Video thumbnail", + className, +}: HeroVideoProps) { + const [isVideoOpen, setIsVideoOpen] = useState(false); + const selectedAnimation = animationVariants[animationStyle]; + + return ( +
+
setIsVideoOpen(true)} + onKeyUp={() => setIsVideoOpen(true)} + onKeyDown={() => setIsVideoOpen(true)} + onKeyPress={() => setIsVideoOpen(true)} + > + {thumbnailAlt} +
+
+
+ +
+
+
+
+ + {isVideoOpen && ( + setIsVideoOpen(false)} + exit={{ opacity: 0 }} + className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-md" + > + + + + +
+