├── .eslintrc.cjs
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── LICENSE
├── README.default.md
├── README.md
├── bin
└── test-pr.sh
├── components.json
├── index.html
├── jsconfig.json
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
└── favicon.ico
├── src
├── artifacts
│ ├── index.tsx
│ └── signup.tsx
├── components
│ ├── layout.tsx
│ └── ui
│ │ ├── accordion.tsx
│ │ ├── alert-dialog.tsx
│ │ ├── alert.tsx
│ │ ├── aspect-ratio.tsx
│ │ ├── avatar.tsx
│ │ ├── badge.tsx
│ │ ├── breadcrumb.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── card.tsx
│ │ ├── carousel.tsx
│ │ ├── chart.tsx
│ │ ├── checkbox.tsx
│ │ ├── collapsible.tsx
│ │ ├── command.tsx
│ │ ├── context-menu.tsx
│ │ ├── dialog.tsx
│ │ ├── drawer.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── form.tsx
│ │ ├── hover-card.tsx
│ │ ├── input-otp.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── menubar.tsx
│ │ ├── navigation-menu.tsx
│ │ ├── pagination.tsx
│ │ ├── popover.tsx
│ │ ├── progress.tsx
│ │ ├── radio-group.tsx
│ │ ├── resizable.tsx
│ │ ├── scroll-area.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── skeleton.tsx
│ │ ├── slider.tsx
│ │ ├── sonner.tsx
│ │ ├── switch.tsx
│ │ ├── table.tsx
│ │ ├── tabs.tsx
│ │ ├── textarea.tsx
│ │ ├── toast.tsx
│ │ ├── toaster.tsx
│ │ ├── toggle-group.tsx
│ │ ├── toggle.tsx
│ │ ├── tooltip.tsx
│ │ └── use-toast.ts
├── index.css
├── lib
│ └── utils.ts
├── main.tsx
├── styled-jsx.d.ts
└── vite-env.d.ts
├── tailwind.config.mjs
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:react-hooks/recommended',
8 | ],
9 | ignorePatterns: ['dist', '.eslintrc.cjs'],
10 | parser: '@typescript-eslint/parser',
11 | plugins: ['react-refresh'],
12 | rules: {
13 | 'react-refresh/only-export-components': [
14 | 'warn',
15 | { allowConstantExport: true },
16 | ],
17 | '@typescript-eslint/no-unused-vars': ['error', {
18 | argsIgnorePattern: '^_',
19 | varsIgnorePattern: '^_',
20 | }],
21 | },
22 | overrides: [
23 | {
24 | files: ['**/components/ui/**/*.tsx'],
25 | rules: {
26 | 'react-refresh/only-export-components': 'off'
27 | }
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | node-version: [18.x, 20.x]
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v4
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 | cache: 'npm'
25 |
26 | - name: Install dependencies
27 | run: npm ci
28 |
29 | - name: Run linting
30 | run: npm run lint
31 |
32 | - name: Build
33 | run: npm run build
34 |
35 | - name: Upload build artifacts
36 | uses: actions/upload-artifact@v4
37 | with:
38 | name: dist
39 | path: dist/
40 | if-no-files-found: error
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Cláudio Silva
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.default.md:
--------------------------------------------------------------------------------
1 | # React + TypeScript + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
10 | ## Expanding the ESLint configuration
11 |
12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13 |
14 | - Configure the top-level `parserOptions` property like this:
15 |
16 | ```js
17 | export default {
18 | // other rules...
19 | parserOptions: {
20 | ecmaVersion: 'latest',
21 | sourceType: 'module',
22 | project: ['./tsconfig.json', './tsconfig.node.json'],
23 | tsconfigRootDir: __dirname,
24 | },
25 | }
26 | ```
27 |
28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Claude Artifact Runner
2 |
3 | A template project for easily converting Claude AI’s Artifacts into React applications, ready to run out of the box or extend as needed.
4 |
5 | ## TL/DR
6 |
7 | 1. You created a fancy web app using Claude's Artifacts feature.
8 | 2. You want to run it outside of Claude's website, or use it as a base for a larger project.
9 | 3. Clone, install and run this project.
10 | 4. Save your artifact(s) to the correct folder.
11 | 5. Done!
12 | 6. Optional step: continue developing your project into a full-fledged web application.
13 | 7. Optional step: generate a release build and publish your finished application to a web server or cloud service.
14 |
15 | ## Use Cases
16 |
17 | 1. Run Artifacts on your local machine, on a web server or on a cloud service.
18 | 2. Use Artifacts as a starting point for a new project and then extend it with custom code.
19 | 3. Create a new web application from scratch and add some Artifacts to it.
20 | 4. Or forget about Claude's Artifacts and just use the project as a starting point for your manually coded web application.
21 |
22 | ## Table of Contents
23 |
24 | - [Why is this needed?](#why-is-this-needed)
25 | - [What this project is not](#what-this-project-is-not)
26 | - [What this project actually is](#what-this-project-actually-is)
27 | - [Limitations](#limitations)
28 | - [What's included?](#whats-included)
29 | - [Prerequisites](#prerequisites)
30 | - [Getting started](#getting-started)
31 | - [Running a single Artifact](#running-a-single-artifact)
32 | - [Creating a multi-page application](#creating-a-multi-page-application)
33 | - [Developing a more complex application](#developing-a-more-complex-application)
34 | - [Project structure](#project-structure)
35 | - [Building for production](#building-for-production)
36 | - [Deploying your application](#deploying-your-application)
37 | - [Local test deployment](#local-test-deployment)
38 | - [Traditional web hosting](#traditional-web-hosting)
39 | - [Cloud hosting platforms](#cloud-hosting-platforms)
40 | - [Netlify](#netlify)
41 | - [Vercel](#vercel)
42 | - [GitHub Pages](#github-pages)
43 | - [Cloudflare Pages](#cloudflare-pages)
44 | - [Troubleshooting](#troubleshooting)
45 | - [Contributing](#contributing)
46 | - [License](#license)
47 | - [Acknowledgements](#acknowledgements)
48 |
49 | ## Why is this needed?
50 |
51 | After all, doesn't Claude already provide both Copy and Download buttons for you to get the generated code?
52 |
53 | Well, if you've created a small web application or component and want to use it outside of Claude's website, you'll be disappointed to find out that Claude will only provide you with a single file containing the main logic of the app, not the full project with all necessary files to run it independently.
54 |
55 | > Note that the code loaded by your web browser to run the Artifact is not the same as the code you copy or download from the interface; it is a transpiled, minimized and bundled version that includes all the necessary libraries (e.g., React) required to run it.
56 |
57 | If you're unfamiliar with the technologies used on the project, you'll have a hard time assembling and configuring all the required libraries and tooling required to make a running standalone app.
58 |
59 | Even if you're an experienced developer, you may just want to save time and effort and get the Artifacts running as easily and as soon as possible.
60 |
61 | This template project provides the fastest and easiest way to get your Artifacts up and running on your machine. It includes all the necessary dependencies and configurations to seamlessly transition your Claude-generated Artifacts into a fully functional web application in no time.
62 |
63 | ## What this project is not
64 |
65 | **This is not an Artifact viewer.**
66 |
67 | "What??" - you might say.
68 |
69 | Well, you **can** use it to view Artifacts, but it's not really intended for that.
70 |
71 | If this was just a viewer, it would most probably just display a simple interface with a text box and a panel, where you could paste the Artifact code. Then it would compile it at runtime and display it in the panel.
72 |
73 | But that would not be very useful, would it?
74 |
75 | ## What this project actually is
76 |
77 | This project converts Artifacts into actual, production-ready standalone web applications.
78 |
79 | This means it has no UI of its own. Your Artifacts will be the application's UI.
80 |
81 | You can also blend AI-generated Artifacts with your own custom code, incorporate code generated by other AI tools, such as **v0.dev**, or even build applications without Artifacts at all.
82 |
83 | Built with modern web development essentials including TypeScript, Tailwind CSS, Shadcn UI, and file-based routing, this pre-configured setup lets you focus on development rather than configuration. As a standard React application, you have the freedom to extend it with any features you need, from backend services like Supabase to web frameworks such as Express or Fastify.
84 |
85 | You'll be able to deploy your app anywhere, whether locally for your own use, in a company intranet or in a public-facing production environment, at the webhosting or cloud provider of your choice.
86 |
87 | ## Limitations
88 |
89 | This project is meant for running Artifacts that are interactive web apps, usually made in React, and for which Claude writes Javascript or Typescript code.
90 |
91 | **Mermaid diagrams, SVGs, and other document-type Artifacts are out of the project’s scope.**
92 |
93 | Also, Claude's Artifacts run client-side only (i.e. in the browser). As such, they are limited in their capabilities.
94 |
95 | If you need a full-stack application (with database, APIs, etc.), I'll be honest, this is not the best project for that, as it does not provide a backend.
96 |
97 | Nevertheless, it does provide a good starting point. You can still add a server-side framework to create a full-stack application, or use a cloud Backend service like Supabase or Firebase.
98 |
99 | ## What's included?
100 |
101 | These are the libraries and frameworks this project provides, identical* to those available on Claude's Artifacts environment:
102 |
103 | 1. **React 18** for building user interfaces.
104 | 2. **TypeScript** to support Artifacts written in type-safe Javascript.
105 | 3. **Vite** for fast development and building.
106 | 4. **Shadcn UI** for pre-built, customizable components.
107 | 5. **Tailwind CSS** for compact and expressive embedded styling.
108 | 6. **Recharts** for creating dynamic, customizable charts and data visualizations.
109 | 7. **Lucide React** for a comprehensive library of open-source icons designed for React applications.
110 |
111 | > \* Note that the actual versions of the packages currently in use in the Artifacts environment may differ from the ones installed by this project, as Anthropic may update them from time to time.
112 | > If a component generated by Claude fails to run properly because of an outdated package, please let me know.
113 |
114 | ## Prerequisites
115 |
116 | Before you begin, ensure you have the following installed:
117 | - Node.js
118 | minimum supported version is 16 (lts/gallium), tested up to version 23.2, version 22.11 is recommended
119 | - npm (usually comes with Node.js)
120 |
121 | ## Getting started
122 |
123 | 1. Clone the repository:
124 | ```
125 | git clone https://github.com/claudio-silva/claude-artifact-runner.git
126 | cd claude-artifact-runner
127 | ```
128 |
129 | 2. Install dependencies:
130 | ```
131 | npm install
132 | ```
133 |
134 | 3. Start the development server:
135 | ```
136 | npm run dev
137 | ```
138 |
139 | 4. Open your browser and visit `http://localhost:5173` to see the default app running.
140 |
141 | The default app is composed of two demo components: a login form and a signup form. You can navigate between them by clicking on the link at the bottom of the form.
142 |
143 | > These demo pages/components are just for demonstration purposes and can be easily replaced with your own components, either generated by Claude or created by yourself.
144 | > **This will NOT be the UI of your application.**
145 |
146 | ## Running a single Artifact
147 |
148 | If you just want to run a single Artifact, you can follow these simple steps:
149 |
150 | 1. Follow the "Getting started" steps. Leave the browser open at the initial page.
151 | 2. Delete the files in the `src/artifacts/` directory.
152 | 3. Download your Artifact from Claude.ai
153 | 4. Move the file to the `src/artifacts/` directory and rename it to `index.tsx`.
154 | 5. You'll immediately see your Artifact running on the open browser tab.
155 |
156 | Note that you'll be viewing the app in development mode. To generate the final app, ready for production, you'll need to build it first. See the instructions further below.
157 |
158 | ## Creating a multi-page application
159 |
160 | If you want to create a multi-page application, you can follow these steps:
161 |
162 | 1. Follow the previous section's steps.
163 | 2. Generate more Artifacts using Claude and download them to the `src/artifacts/` directory.
164 | 5. Give each file it a unique name, such as `your-component.tsx`. Each component you add will be a new page accessible at `http://localhost:5173/your-component-name` (without the `.tsx` extension).
165 | 6. If you have `npm run dev` running in the background, the new pages will be ready to display immediately. If a page is already open when being updated, it will refresh automatically.
166 | 7. You can link Artifacts to each other to build a multi-page application.
167 | > **It's easy:** on each Artifact page, just add links that navigate to the other Artifacts, by specifying their names without the `.tsx` extension.
168 | E.g. `Go to My Component`
169 | 8. You can also use the `useNavigate` hook to navigate to a specific page.
170 | 9. Finally, to create a release build and publish your finished application, follow the instructions further below.
171 |
172 | ## Developing a more complex application
173 |
174 | If you intend to create a more advanced application, you'll probably want to customize its visual appearance by changing styles, adding images and new components, creating a base layout with a top header and a sidebar with the main navigation menu, etc.
175 |
176 | This section is intended for advanced users only, and is not required for simple applications.
177 |
178 | ### Customization
179 |
180 | - For styling:
181 | - Prefer using Tailwind classes directly in components.
182 | - Use `src/index.css` only for Tailwind configuration and critical global styles.
183 | - For component-specific styles, use CSS Modules (*.module.css).
184 | - For complex styling needs (such as dynamic styles), consider styled-components or other CSS-in-JS solutions.
185 | - Modify `tailwind.config.mjs` to customize the Tailwind CSS theme.
186 | - Place static assets (such as images) in the `public` folder.
187 | - Update `main.tsx` to change the overall layout of the app (e.g. adding a navigation bar).
188 | - Add or modify components in the `src/components/` directory.
189 | > **Note:** Shadcn UI components installed via `npx` are automatically placed in `src/components/ui`. All components come pre-installed by default, but if you remove some and later want to reinstall any, you may simply run `npx shadcn-ui@latest add `.
190 |
191 | ### Removing unneeded components / libraries
192 |
193 | The **Recharts** library and ALL **Shadcn UI** components come pre-installed, so that all code that Claude may generate will run *out-of-the-box*.
194 |
195 | If you just want to run the Artifact locally, you may leave things as they are, but if you want to deploy the application or use it as a base for a larger project, you may want to optimize the application's bundle size.
196 |
197 | To do that, you may remove the pre-installed components or libraries that are not required by your application.
198 |
199 | #### Unneeded Shadcn UI components:
200 | Just delete the component's files from `src/components/ui`.
201 |
202 | #### Unneeded packages (ex: Recharts):
203 | Use `npm remove` to uninstall them.
204 |
205 | ## Project structure
206 |
207 | | Directory/File | Description |
208 | |--------------------------------------------|-------------------------------------------------------|
209 | | `dist/` | Compiled output |
210 | | `public/` | Standalone static assets |
211 | | `src/` | Contains the source code for the application |
212 | | `src/artifacts/` | **The Artifacts generated by Claude should be placed here** |
213 | | `src/assets/` | Static assets for the build system |
214 | | `src/components/` | Bundled Shadcn UI components |
215 | | `src/lib/utils.ts` | Utility functions and helpers |
216 | | `src/index.css` | Tailwind styles |
217 | | `src/main.tsx` | Entry point of the application |
218 | | `src/vite-env.d.ts` | Type definitions for Vite |
219 | | `.eslintrc.cjs` | ESLint configuration |
220 | | `components.json` | Shadcn UI components configuration |
221 | | `index.html` | Entry HTML file |
222 | | `jsconfig.json` | JSON configuration |
223 | | `postcss.config.js` | PostCSS configuration |
224 | | `tailwind.config.mjs` | Tailwind CSS configuration |
225 | | `tsconfig.app.json`, `tsconfig.json`, `tsconfig.node.json` | TypeScript configuration |
226 | | `package.json` | All the required packages are registered here |
227 | | `vite.config.ts` | Vite configuration |
228 |
229 | ## Building for production
230 |
231 | To create a production build, run:
232 |
233 | ```
234 | npm run build
235 | ```
236 |
237 | This will generate optimized files in the `dist/` directory, ready for deployment.
238 |
239 | ## Deploying your application
240 |
241 | After running `npm run build`, you'll have a `dist` folder containing the built files (typically an HTML file, a JavaScript file, and a CSS file).
242 |
243 | Here are several ways to deploy these files:
244 |
245 | ### Local test deployment
246 |
247 | For local testing of the production build, you can use the `serve` package:
248 |
249 | 1. Install `serve` globally:
250 | ```
251 | npm install -g serve
252 | ```
253 |
254 | 2. Navigate to your project directory and run:
255 | ```
256 | serve -s dist
257 | ```
258 |
259 | 3. Open a browser and go to `http://localhost:3000` (or the URL provided in the terminal).
260 |
261 | ### Traditional web hosting
262 |
263 | If you want to deploy to a shared or dedicated web server:
264 |
265 | 1. Upload the contents of the `dist` folder to your web server's public HTML directory (often called `public_html`, `www`, or `htdocs`).
266 |
267 | Remember to update any necessary configuration files (like `vite.config.ts`) before building your app if it is not being served from the root of your domain.
268 |
269 | For example, for `vite.config.ts`, you may configure it like this:
270 | ```javascript
271 | export default {
272 | base: '/subdirectory/', // Set this to the path your app is served from
273 | // other configurations
274 | };
275 | ```
276 |
277 | ### Cloud hosting platforms
278 |
279 | Here are some popular free cloud hosting platforms and how to deploy your app to them:
280 |
281 | > Remember to run `npm run build` before deploying to ensure you're uploading the latest version of your app.
282 |
283 | #### Netlify
284 |
285 | 1. Install the Netlify CLI:
286 | ```
287 | npm install -g netlify-cli
288 | ```
289 |
290 | 2. Run the following command in your project directory:
291 | ```
292 | netlify deploy
293 | ```
294 |
295 | 3. Follow the prompts. When asked for the publish directory, enter `dist`.
296 |
297 | 4. For production deployment, use:
298 | ```
299 | netlify deploy --prod
300 | ```
301 |
302 | #### Vercel
303 |
304 | 1. Install the Vercel CLI:
305 | ```
306 | npm install -g vercel
307 | ```
308 |
309 | 2. Run the following command in your project directory:
310 | ```
311 | vercel
312 | ```
313 |
314 | 3. Follow the prompts. Vercel will automatically detect that it's a Vite project and use the correct settings.
315 |
316 | #### GitHub Pages
317 |
318 | 1. If you haven't already, create a GitHub repository for your project.
319 |
320 | 2. Install the `gh-pages` package:
321 | ```
322 | npm install gh-pages --save-dev
323 | ```
324 |
325 | 3. Add these scripts to your `package.json`:
326 | ```json
327 | "scripts": {
328 | "predeploy": "npm run build",
329 | "deploy": "gh-pages -d dist"
330 | }
331 | ```
332 |
333 | 4. Run:
334 | ```
335 | npm run deploy
336 | ```
337 |
338 | 5. Set up GitHub Pages in your repository settings to use the `gh-pages` branch.
339 |
340 | #### Cloudflare Pages
341 |
342 | You can deploy to Cloudflare Pages either through the Cloudflare dashboard or using the `wrangler` CLI tool. Here's how to do it using `wrangler`, which is often the most straightforward method:
343 |
344 | 1. **Install Wrangler:**
345 | ```
346 | npm install -g wrangler
347 | ```
348 |
349 | 2. **Login to Cloudflare:**
350 | ```
351 | wrangler login
352 | ```
353 |
354 | 3. **Deploy your project:**
355 | ```
356 | wrangler pages deploy dist
357 | ```
358 |
359 | This command will prompt you to create a new project if one doesn't exist, and then deploy your `dist` folder to Cloudflare Pages.
360 |
361 | 4. **Configure your project (optional):**
362 | If you need more control over your deployment, you can create a `wrangler.toml` file in your project root:
363 |
364 | ```toml
365 | name = "my-react-app"
366 | compatibility_date = "2024-07-16" # Replace with the current date
367 |
368 | [site]
369 | bucket = "./dist"
370 | ```
371 |
372 | Note: The `account_id` and `workers_dev` fields are typically not needed for Cloudflare Pages deployments.
373 |
374 | 5. **Custom domain and production settings:**
375 | To use a custom domain or configure production settings, you can use the Cloudflare Pages dashboard. There, you can set up your domain, configure environment variables, and manage other deployment settings.
376 |
377 | ## Troubleshooting
378 |
379 | If you encounter any issues, try the following:
380 |
381 | 1. Clear your browser cache and restart the development server.
382 | 2. Delete the `node_modules` folder and run `npm install` again.
383 | 3. Make sure your Node.js version is compatible with the project requirements.
384 | 4. Check for any error messages in the console and search for solutions online.
385 |
386 | If problems persist, please open an issue on this project's GitHub repository.
387 |
388 | ## Contributing
389 |
390 | Contributions are welcome! Please feel free to submit a Pull Request.
391 |
392 | ## License
393 |
394 | This project is open source and available under the [MIT License](LICENSE).
395 |
396 | ## Acknowledgements
397 |
398 | I found [Claude-React-Jumpstart](https://github.com/Bklieger/Claude-React-Jumpstart) when looking for a way to run Artifacts outside of [claude.ai](https://claude.ai).
399 |
400 | However, it did not fully meet my needs, so I decided to make my own project, as I wanted something that:
401 | * was completely pre-configured (no need to install or configure additional stuff),
402 | * was ready to go with a single `npm install`, and
403 | * included all components and libraries needed to fully replicate the Artifacts environment.
404 |
405 | I would also like to thank [IntranetFactory](https://github.com/IntranetFactory) for contributing the routing solution for handling multiple Artifacts.
406 |
--------------------------------------------------------------------------------
/bin/test-pr.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Exit immediately if a command exits with a non-zero status.
4 | set -e
5 |
6 | # Colors for output
7 | RED='\033[0;31m'
8 | GREEN='\033[0;32m'
9 | YELLOW='\033[1;33m'
10 | BLUE='\033[0;34m'
11 | NC='\033[0m' # No Color
12 |
13 | # --- Helper Functions ---
14 |
15 | print_status() {
16 | echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
17 | }
18 |
19 | print_success() {
20 | echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
21 | }
22 |
23 | print_error() {
24 | echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
25 | }
26 |
27 | print_info() {
28 | echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
29 | }
30 |
31 | cleanup() {
32 | print_status "Running cleanup..."
33 | # Ensure we are back on the main branch if possible
34 | current_branch=$(git rev-parse --abbrev-ref HEAD || echo "unknown")
35 | if [[ "$current_branch" != "main" && "$current_branch" != "unknown" ]]; then
36 | print_status "Switching back to main branch..."
37 | git checkout main || print_error "Failed to checkout main branch during cleanup."
38 | fi
39 |
40 | # Restore package files if backups exist
41 | if [[ -f package.json.backup && -f package-lock.json.backup ]]; then
42 | print_status "Restoring package files..."
43 | mv package.json.backup package.json
44 | mv package-lock.json.backup package-lock.json
45 | else
46 | print_status "No backup files found to restore."
47 | fi
48 | print_status "Cleanup finished."
49 | }
50 |
51 | # Trap EXIT signal to ensure cleanup runs
52 | trap cleanup EXIT
53 |
54 | run_single_test() {
55 | local pr_number=$1
56 | local test_passed=true
57 |
58 | print_info "--- Testing PR #$pr_number ---"
59 |
60 | print_status "Checking out PR #$pr_number..."
61 | if ! gh pr checkout $pr_number; then
62 | print_error "Failed to checkout PR #$pr_number. Skipping."
63 | git checkout main # Attempt to return to main
64 | return 1
65 | fi
66 |
67 | print_status "Installing dependencies for PR #$pr_number..."
68 | if ! npm ci; then # Use npm ci for cleaner installs
69 | print_error "npm ci failed for PR #$pr_number."
70 | test_passed=false
71 | fi
72 |
73 | if $test_passed; then
74 | print_status "Running linting for PR #$pr_number..."
75 | if ! npm run lint; then
76 | print_error "Linting failed for PR #$pr_number."
77 | test_passed=false
78 | fi
79 | fi
80 |
81 | if $test_passed; then
82 | print_status "Running build for PR #$pr_number..."
83 | if ! npm run build; then
84 | print_error "Build failed for PR #$pr_number."
85 | test_passed=false
86 | fi
87 | fi
88 |
89 | if $test_passed; then
90 | print_success "PR #$pr_number passed tests!"
91 | else
92 | print_error "PR #$pr_number failed tests."
93 | fi
94 |
95 | print_status "Returning to main branch..."
96 | git checkout main
97 |
98 | # Decide return status (0 for success, 1 for failure)
99 | if $test_passed; then
100 | return 0
101 | else
102 | return 1
103 | fi
104 | }
105 |
106 | # --- Main Execution ---
107 |
108 | # Check prerequisites
109 | if ! command -v gh &> /dev/null; then
110 | print_error "GitHub CLI ('gh') could not be found. Please install it."
111 | exit 1
112 | fi
113 |
114 | if ! gh auth status &> /dev/null; then
115 | print_error "Not logged into GitHub CLI ('gh auth status' failed). Please log in."
116 | exit 1
117 | fi
118 |
119 | if [ ! -f "package.json" ]; then
120 | print_error "Script must be run from the project root directory (where package.json exists)."
121 | exit 1
122 | fi
123 |
124 | print_status "Starting PR tester..."
125 |
126 | # Stash any local changes to prevent conflicts
127 | print_status "Stashing any local changes..."
128 | if ! git stash push -m "Temporary stash for PR testing" > /dev/null; then
129 | print_error "Failed to stash local changes. Please commit or stash your changes manually before running this script."
130 | exit 1
131 | fi
132 |
133 | # Ask user what type of PRs to test
134 | print_info "What type of PRs would you like to test?"
135 | echo "1) All open PRs"
136 | echo "2) Only Dependabot PRs"
137 | read -p "Enter your choice (1 or 2): " pr_type_choice
138 |
139 | # Set the author filter based on user choice
140 | author_filter=""
141 | if [ "$pr_type_choice" = "2" ]; then
142 | author_filter="--author app/dependabot"
143 | print_status "Testing only Dependabot PRs..."
144 | else
145 | print_status "Testing all open PRs..."
146 | fi
147 |
148 | # Create backups *once*
149 | print_status "Creating backups of package files..."
150 | cp package.json package.json.backup
151 | cp package-lock.json package-lock.json.backup
152 |
153 | # Fetch open PRs based on user choice
154 | print_status "Fetching open PRs..."
155 | # Get PRs into an array using standard array assignment
156 | pr_options=()
157 | while IFS= read -r line; do
158 | pr_options+=("$line")
159 | done < <(gh pr list $author_filter --state open --json number,title --jq '.[] | "#\(.number): \(.title)"')
160 |
161 | if [ ${#pr_options[@]} -eq 0 ]; then
162 | print_error "No open PRs found."
163 | # Restore stashed changes
164 | git stash pop > /dev/null
165 | # Cleanup will run automatically on exit
166 | exit 1
167 | fi
168 |
169 | # Store the number of PRs for later use
170 | num_prs=${#pr_options[@]}
171 |
172 | # Add options for Test All and Quit
173 | pr_options+=("Test All" "Quit")
174 |
175 | # Present the menu
176 | print_info "Please select a PR to test:"
177 | PS3="Enter your choice: "
178 | select opt in "${pr_options[@]}"; do
179 | case $opt in
180 | "Test All")
181 | print_status "Selected: Test All"
182 | all_passed=true
183 | # Extract numbers using standard array assignment
184 | pr_numbers=()
185 | while IFS= read -r number; do
186 | pr_numbers+=("$number")
187 | done < <(gh pr list $author_filter --state open --json number --jq '.[].number')
188 |
189 | for pr_num in "${pr_numbers[@]}"; do
190 | if ! run_single_test $pr_num; then
191 | all_passed=false
192 | print_error "Testing stopped due to failure in PR #$pr_num. You may need manual intervention."
193 | # You might want to change this to 'continue' if you want to test remaining PRs even if one fails
194 | break
195 | fi
196 | done
197 | if $all_passed; then
198 | print_success "All tested PRs passed!"
199 | else
200 | print_error "One or more PRs failed tests."
201 | # Restore stashed changes before exiting
202 | git stash pop > /dev/null
203 | exit 1 # Exit with error if any PR failed
204 | fi
205 | break
206 | ;;
207 | "Quit")
208 | print_status "Exiting."
209 | break
210 | ;;
211 | *)
212 | # Check if the choice is a valid PR option
213 | if [[ "$REPLY" =~ ^[0-9]+$ ]] && [ "$REPLY" -le "$num_prs" ]; then
214 | # Extract PR number from the selected option (e.g., "#123: Title")
215 | pr_string=${pr_options[$(($REPLY-1))]}
216 | pr_number=$(echo "$pr_string" | sed -n 's/^#\([0-9]*\):.*/\1/p')
217 | print_status "Selected: Test PR #$pr_number"
218 | if run_single_test $pr_number; then
219 | print_success "Testing for PR #$pr_number completed successfully."
220 | else
221 | print_error "Testing for PR #$pr_number failed."
222 | # Restore stashed changes before exiting
223 | git stash pop > /dev/null
224 | exit 1 # Exit with error if the selected PR failed
225 | fi
226 | break
227 | else
228 | print_error "Invalid option. Please try again."
229 | fi
230 | ;;
231 | esac
232 | done
233 |
234 | # Restore stashed changes
235 | print_status "Restoring local changes..."
236 | git stash pop > /dev/null
237 |
238 | # Cleanup will run automatically via trap EXIT
239 | print_status "Script finished."
240 | exit 0 # Explicitly exit with success if we reached here
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": false,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "src/index.css",
9 | "baseColor": "slate",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "src/components",
15 | "utils": "src/lib/utils"
16 | }
17 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Artifact Viewer
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["src/*"]
6 | }
7 | },
8 | "include": ["src/**/*"]
9 | }
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "artifact-viewer",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc -b && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@babel/runtime": "^7.27.0",
14 | "@hookform/resolvers": "^3.9.0",
15 | "@radix-ui/react-accordion": "^1.2.0",
16 | "@radix-ui/react-alert-dialog": "^1.1.1",
17 | "@radix-ui/react-aspect-ratio": "^1.1.0",
18 | "@radix-ui/react-avatar": "^1.1.0",
19 | "@radix-ui/react-checkbox": "^1.1.1",
20 | "@radix-ui/react-collapsible": "^1.1.0",
21 | "@radix-ui/react-context-menu": "^2.2.1",
22 | "@radix-ui/react-dialog": "^1.1.1",
23 | "@radix-ui/react-dropdown-menu": "^2.1.1",
24 | "@radix-ui/react-hover-card": "^1.1.1",
25 | "@radix-ui/react-label": "^2.1.0",
26 | "@radix-ui/react-menubar": "^1.1.1",
27 | "@radix-ui/react-navigation-menu": "^1.2.0",
28 | "@radix-ui/react-popover": "^1.1.1",
29 | "@radix-ui/react-progress": "^1.1.0",
30 | "@radix-ui/react-radio-group": "^1.2.0",
31 | "@radix-ui/react-scroll-area": "^1.1.0",
32 | "@radix-ui/react-select": "^2.1.1",
33 | "@radix-ui/react-separator": "^1.1.0",
34 | "@radix-ui/react-slider": "^1.2.0",
35 | "@radix-ui/react-slot": "^1.1.0",
36 | "@radix-ui/react-switch": "^1.1.0",
37 | "@radix-ui/react-tabs": "^1.1.0",
38 | "@radix-ui/react-toast": "^1.2.1",
39 | "@radix-ui/react-toggle": "^1.1.0",
40 | "@radix-ui/react-toggle-group": "^1.1.0",
41 | "@radix-ui/react-tooltip": "^1.1.2",
42 | "class-variance-authority": "^0.7.0",
43 | "clsx": "^2.1.1",
44 | "cmdk": "^1.0.0",
45 | "date-fns": "^3.6.0",
46 | "embla-carousel-react": "^8.1.6",
47 | "input-otp": "^1.2.4",
48 | "lucide-react": "^0.408.0",
49 | "next-themes": "^0.3.0",
50 | "react": "^18.3.1",
51 | "react-day-picker": "^8.10.1",
52 | "react-dom": "^18.3.1",
53 | "react-hook-form": "^7.52.1",
54 | "react-resizable-panels": "^2.0.20",
55 | "react-router-dom": "^7.1.2",
56 | "recharts": "^2.15.0",
57 | "sonner": "^1.5.0",
58 | "tailwind-merge": "^2.4.0",
59 | "tailwindcss-animate": "^1.0.7",
60 | "vaul": "^0.9.1",
61 | "zod": "^3.23.8"
62 | },
63 | "devDependencies": {
64 | "@types/node": "^20.14.10",
65 | "@types/react": "^18.3.3",
66 | "@types/react-dom": "^18.3.0",
67 | "@typescript-eslint/eslint-plugin": "^7.13.1",
68 | "@typescript-eslint/parser": "^7.13.1",
69 | "@vitejs/plugin-react": "^4.3.1",
70 | "autoprefixer": "^10.4.19",
71 | "eslint": "^8.57.0",
72 | "eslint-plugin-react-hooks": "^4.6.2",
73 | "eslint-plugin-react-refresh": "^0.4.7",
74 | "glob": "^10.4.5",
75 | "postcss": "^8.4.39",
76 | "rimraf": "^5.0.5",
77 | "tailwindcss": "^3.4.4",
78 | "typescript": "~5.5.0",
79 | "vite": "^5.4.17",
80 | "vite-plugin-pages": "^0.32.4"
81 | },
82 | "overrides": {
83 | "glob": "^10.4.5",
84 | "rimraf": "^5.0.5",
85 | "@humanwhocodes/config-array": "npm:@eslint/config-array@latest",
86 | "@humanwhocodes/object-schema": "npm:@eslint/object-schema@latest",
87 | "inflight": "^2.0.1"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
1 | iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAGXRFWHRTb2Z0d2FyZQBNYWNpbnRvc2ggSEQgdjEuOTAg4Baq2gAAAD9JREFUKFNjZGBgYGBgUggUGBoa/N8BgwERnLx9uZkBlJDAyMjKAkYEHETVzkKACGcEAMON0IxtZG6XAAAAAElFTkSuQmCC
2 |
--------------------------------------------------------------------------------
/src/artifacts/index.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { AlertCircle, Mail, Lock, Github, Facebook, Twitter } from 'lucide-react';
3 | import { Alert, AlertDescription } from '@/components/ui/alert';
4 | import { Button } from '@/components/ui/button';
5 | import { Input } from '@/components/ui/input';
6 | import { Label } from '@/components/ui/label';
7 | import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
8 | import { Link } from "react-router-dom";
9 |
10 | const LoginForm = () => {
11 | const [email, setEmail] = useState('');
12 | const [password, setPassword] = useState('');
13 | const [error, setError] = useState('');
14 |
15 | const handleLogin = (e: React.FormEvent) => {
16 | e.preventDefault();
17 | if (!email || !password) {
18 | setError('Please fill in all fields');
19 | } else {
20 | setError('');
21 | console.log('Login attempted:', { email, password });
22 | // Here you would typically handle the login logic
23 | alert(`Login attempted: ${email}, ${password}`);
24 | }
25 | };
26 |
27 | const handleSocialLogin = (platform: string) => {
28 | console.log(`${platform} login attempted`);
29 | // Here you would typically handle the social login logic
30 | alert(`${platform} login attempted`);
31 | };
32 |
33 | return (
34 |