├── .github
└── workflows
│ ├── compliance.yml
│ └── development.yml
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── assets
├── astro-expand-collapse.gif
├── astro-inspection.gif
└── astro-search.gif
├── jest.config.js
├── package-lock.json
├── package.json
├── src
├── .eslintrc.json
├── __tests__
│ ├── createTree.test.js
│ ├── parseData.test.ts
│ └── parseProps.test.js
├── app
│ ├── App.tsx
│ ├── algorithms
│ │ ├── createTree.tsx
│ │ ├── parseData.ts
│ │ └── parseProps.ts
│ ├── components
│ │ ├── ComponentView.tsx
│ │ ├── ElementView.tsx
│ │ ├── Header.tsx
│ │ └── SearchBar.tsx
│ ├── containers
│ │ ├── Panel.tsx
│ │ └── SidePane.tsx
│ ├── index.tsx
│ ├── styles
│ │ ├── _app.scss
│ │ ├── _header.scss
│ │ ├── _panel.scss
│ │ ├── _searchBar.scss
│ │ ├── _sidePane.scss
│ │ ├── _variables.scss
│ │ └── styles.scss
│ └── types
│ │ ├── css.d.ts
│ │ └── types.ts
└── extension
│ ├── assets
│ ├── astrospect-logo-16.png
│ ├── astrospect-logo-48.png
│ └── astrospect-logo.png
│ ├── devtools.html
│ ├── devtools.js
│ ├── manifest.json
│ └── panel.html
├── tsconfig.json
├── webpack.config.js
└── website
├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .prettierrc
├── LICENSE
├── astro.config.mjs
├── package.json
├── public
├── accessible-components.webp
├── astrospect-feature.png
├── astrospect-logo.png
├── demo.webp
├── evan.png
├── fonts
│ ├── OpenSans-Bold.woff
│ ├── OpenSans-Bold.woff2
│ ├── OpenSans-ExtraBold.woff
│ ├── OpenSans-ExtraBold.woff2
│ ├── OpenSans-Italic.woff
│ ├── OpenSans-Italic.woff2
│ ├── OpenSans-Regular.woff
│ └── OpenSans-Regular.woff2
├── jackson.png
├── john.png
├── nick.png
├── social-preview-image.png
└── wcag-compliant.webp
├── src
├── assets
│ └── scss
│ │ ├── base
│ │ ├── _breakpoint.scss
│ │ ├── _button.scss
│ │ ├── _color.scss
│ │ ├── _container.scss
│ │ ├── _font.scss
│ │ ├── _list.scss
│ │ ├── _outline.scss
│ │ ├── _reset.scss
│ │ ├── _root.scss
│ │ └── _space-content.scss
│ │ └── globals.scss
├── components
│ ├── AboutCard.astro
│ ├── CallToAction.astro
│ ├── ContentMedia.astro
│ ├── Feature.astro
│ ├── Footer.astro
│ ├── Header.astro
│ ├── Hero.astro
│ ├── Navigation.astro
│ ├── ResponsiveToggle.astro
│ └── SiteMeta.astro
├── env.d.ts
├── layouts
│ ├── DefaultLayout.astro
│ └── MarkdownLayout.astro
└── pages
│ ├── 404.astro
│ ├── about
│ └── [...page].astro
│ ├── index.astro
│ ├── markdown-page.md
│ └── mdx-page.mdx
├── tailwind.config.js
└── tsconfig.json
/.github/workflows/compliance.yml:
--------------------------------------------------------------------------------
1 | name: Compliance
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | types:
8 | - opened
9 | - reopened
10 | - synchronize
11 |
12 | jobs:
13 | # check that the PR isn't being attempted from a feature branch to the main branch
14 | branch-compliance-check:
15 | name: Branch compliance check
16 | runs-on: ubuntu-latest
17 | steps:
18 | - name: check out repo
19 | uses: actions/checkout@v2
20 |
21 | - name: check branch name
22 | run: |
23 | if [[ "$GITHUB_HEAD_REF" == "dev" ]]; then
24 | echo "Merging from dev branch is allowed"
25 | exit 0
26 | else
27 | echo "Merging from branch other than dev is not allowed"
28 | exit 1
29 | fi
30 |
--------------------------------------------------------------------------------
/.github/workflows/development.yml:
--------------------------------------------------------------------------------
1 | name: Development
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - opened
7 | - synchronize
8 | - reopened
9 | push:
10 |
11 | jobs:
12 | # tests the webpack bundle to make sure that it can be compiled without any errors
13 | build:
14 | name: Build webpack bundle
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: check out repo
18 | uses: actions/checkout@v2
19 |
20 | - name: Install dependencies
21 | run: npm install
22 |
23 | - name: Build webpack bundle
24 | run: npm run build
25 |
26 | # run the testing suite
27 | test:
28 | name: Test application
29 | runs-on: ubuntu-latest
30 | steps:
31 | - name: check out repo
32 | uses: actions/checkout@v2
33 |
34 | - name: 'set up node'
35 | uses: actions/setup-node@v2.1.5
36 | with:
37 | node-version: 16
38 |
39 | - name: 'install npm@latest'
40 | run: npm i -g npm@latest
41 |
42 | - name: 'install dependencies'
43 | uses: bahmutov/npm-install@v1
44 |
45 | - name: 'run tests'
46 | run: npm run test
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Bundles generate from Webpack
2 | src/extension/bundles/
3 |
4 | #banish DS store
5 | .DS_Store
6 |
7 | # Logs
8 | logs
9 | *.log
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | lerna-debug.log*
14 |
15 | # Diagnostic reports (https://nodejs.org/api/report.html)
16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
17 |
18 | # Runtime data
19 | pids
20 | *.pid
21 | *.seed
22 | *.pid.lock
23 |
24 | # Directory for instrumented libs generated by jscoverage/JSCover
25 | lib-cov
26 |
27 | # Coverage directory used by tools like istanbul
28 | coverage
29 | *.lcov
30 |
31 | # nyc test coverage
32 | .nyc_output
33 |
34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
35 | .grunt
36 |
37 | # Bower dependency directory (https://bower.io/)
38 | bower_components
39 |
40 | # node-waf configuration
41 | .lock-wscript
42 |
43 | # Compiled binary addons (https://nodejs.org/api/addons.html)
44 | build/Release
45 |
46 | # Dependency directories
47 | node_modules/
48 | jspm_packages/
49 |
50 | # TypeScript v1 declaration files
51 | typings/
52 |
53 | # TypeScript cache
54 | *.tsbuildinfo
55 |
56 | # Optional npm cache directory
57 | .npm
58 |
59 | # Optional eslint cache
60 | .eslintcache
61 |
62 | # Microbundle cache
63 | .rpt2_cache/
64 | .rts2_cache_cjs/
65 | .rts2_cache_es/
66 | .rts2_cache_umd/
67 |
68 | # Optional REPL history
69 | .node_repl_history
70 |
71 | # Output of 'npm pack'
72 | *.tgz
73 |
74 | # Yarn Integrity file
75 | .yarn-integrity
76 |
77 | # dotenv environment variables file
78 | .env
79 | .env.test
80 |
81 | # parcel-bundler cache (https://parceljs.org/)
82 | .cache
83 |
84 | # Next.js build output
85 | .next
86 |
87 | # Nuxt.js build / generate output
88 | .nuxt
89 | dist
90 |
91 | # Gatsby files
92 | .cache/
93 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
94 | # https://nextjs.org/blog/next-9-1#public-directory-support
95 | # public
96 |
97 | # vuepress build output
98 | .vuepress/dist
99 |
100 | # Serverless directories
101 | .serverless/
102 |
103 | # FuseBox cache
104 | .fusebox/
105 |
106 | # DynamoDB Local files
107 | .dynamodb/
108 |
109 | # TernJS port file
110 | .tern-port
111 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 OSLabs Beta
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.md:
--------------------------------------------------------------------------------
1 | # AstroSpect
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
22 |
23 |
24 | [![Contributors][contributors-shield]][contributors-url]
25 | [![Forks][forks-shield]][forks-url]
26 | [![Stargazers][stars-shield]][stars-url]
27 | [![MIT License][license-shield]][license-url]
28 | [![Issues][issues-shield]][issues-url]
29 | [![LinkedIn][linkedin-shield]][linkedin-url]
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Table of Contents
38 |
39 |
40 | Summary
41 |
44 |
45 |
46 | Getting Started
47 |
52 |
53 | About
54 |
57 | Roadmap
58 | Contributions
59 | Acknowledgments
60 |
64 |
65 |
66 |
67 |
68 |
69 | ## Summary
70 |
71 | AstroSpect (Astro Inspection) is a Chrome Developer Tool Extension that allows developers to inspect and debug Astro websites more efficiently. With AstroSpect, developers can display a tree diagram of all elements on a page, including static HTML files and hydrated components known as Astro Islands, on a panel. The "All Elements" tab shows all elements, while the "Islands Only" tab displays only Astro Islands. Clicking on an Astro Island reveals information about the component, such as its type, client directive, and props, in a side-pane. AstroSpect features expand and collapse options to open and close tree nodes and a search function for quicker navigation and debugging.
72 |
73 | ### - Built With
74 |
75 |
76 |
77 | [![Astro][astro-shield]][astro-url]
78 | [![React][react-shield]][react-url]
79 | [![Sass][sass-shield]][sass-url]
80 | [![TypeScript][typescript-shield]][typescript-url]
81 | [![Webpack][webpack-shield]][webpack-url]
82 |
83 |
84 |
85 |
86 |
87 | ## Getting Started
88 |
89 | AstroSpect is available for download as a Google Chrome Extension. You can also clone or fork this repo and add it as your own extension manually.
90 |
91 | ### - Prerequisites
92 |
93 | - Chrome Browser
94 | - Astro 2.0
95 | - VS Code
96 |
97 | ### - Installation
98 |
99 | Option 1: Download as a chrome extension
100 |
101 | Option 2: Manually Download
102 |
103 |
104 | Fork or Clone this repo
105 | In the terminal: npm run build
106 | Navigate to chrome://extensions/
107 | Click Load unpacked button
108 | Upload the extension folder
109 |
110 |
111 | ### - Usage
112 |
113 | 1. Start or open an Astro project.
114 | 2. Inspect the Astro webpage
115 | 3. Open the AstroSpect Tab
116 | 4. "ALL ELEMENTS" displays every element on the page
117 | 5. "ISLANDS ONLY" displays hydrated components with client directives
118 |
119 |
120 |
Inspect
121 |
122 |
124 |
125 |
Expand & Collapse
126 |
127 |
129 |
130 |
Search
131 |
132 |
134 |
135 |
136 |
137 |
138 |
139 | ## About
140 |
141 | ### - Astro
142 |
143 | Astro is the all-in-one web framework designed for speed. Pull your content from anywhere and deploy everywhere, all powered by your favorite UI components and libraries. AstroSpect allow developers to inspect the Island Architecture of Astro websites in conjunction with other frameworks (React, Preact, Svelte, Vue, Solid, Lit and more). [Check out Astro to build your next website](https://astro.build/).
144 |
145 |
146 |
147 | ## Roadmap
148 |
149 | - [x] Inspect Astro Websites with AstroSpect Chrome Extension
150 | - [x] Display all HTML elements in "All Elements" tab
151 | - [x] Display Hydrated Components in "Islands Only" tab
152 | - [x] Display Component's Type, Client Directive, and Props in side pane
153 | - [x] Expand and Collapse feature opens and closes all tree nodes
154 | - [x] Search feature highlights the inputted text
155 | - [x] Tracking buttons that directs user to every highlighted word
156 | - [ ] Auto-reload when navigating to a different page
157 | - [ ] Display Framework associated with each island on side pane
158 | - [ ] Access nanostores of all components
159 | - [ ] Display state of components in side pane
160 | - [ ] Highlight over islands when clicked in the panel
161 | - [ ] Highlight over islands when clicked in the panel
162 |
163 | Check out the [open issues](https://github.com/oslabs-beta/AstroSpect/issues) for a full list of proposed features (and known issues).
164 |
165 |
166 |
167 | ## Contributions
168 |
169 | All contributions are hightly welcomed and appreciated here at AstroSpect. We are open to suggestions so please submit an issue and the AstroSpect team will get back to you as soon as possible. If you would like to add a new feature, please follow the steps below.
170 |
171 | #### STEP 1 — Fork and Clone the repository
172 |
173 | ```
174 | git clone https://github.com/oslabs-beta/AstroSpect.git
175 | ```
176 |
177 | #### STEP 2 — Create a Feature Branch
178 |
179 | ```
180 | git checkout -b [name]/[feature-name]
181 | ```
182 |
183 | #### STEP 3 — Install Dependencies
184 |
185 | ```
186 | npm install
187 | ```
188 |
189 | #### STEP 4 — Bundle the Code
190 |
191 | ```
192 | npm run build
193 | ```
194 |
195 | #### STEP 5 — Upload Extension Folder to Chrome
196 |
197 | 1. Navigate to chrome://extensions/ using chrome
198 | 2. Click Load unpacked button
199 | 3. Upload extension folder
200 | 4. Test Extension by inspecting an Astro webpage
201 |
202 | #### STEP 6 — Add and Commit
203 |
204 | ```
205 | git add [file-name]
206 | git commit -m "describe your new feature"
207 | ```
208 |
209 | #### STEP 7 — Submit a Pull Request
210 |
211 | Merge your feature branch into dev
212 |
213 | #### STEP 8 — Create an Issue
214 |
215 | Click the Issues tab and create a new issue
216 |
217 |
218 |
219 | ## Acknowledgments
220 |
221 | ### - Community
222 |
223 | - [OpenSource Labs](https://opensourcelabs.io/)
224 | - [Astro Discord](https://discord.com/invite/grF4GTXXYm)
225 | - [AstroSpect Users](https://www.astrospect.dev/)
226 |
227 | Distributed under the MIT License. See `LICENSE` for more information.
228 |
229 | ### - Authors
230 |
231 |
232 |
233 |
234 | 🧑🏻🚀
235 | Evan Jones
236 |
237 |
238 |
239 |
240 |
241 | 🧑🏼🚀
242 | Nicholas Park
243 |
244 |
245 |
246 |
247 | 👨🏻🚀
248 | John Roman
249 |
250 |
251 |
252 |
253 | 👩🏼🚀
254 | Jackson Ta
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 | (back to top )
263 |
264 | [github-shield]: https://img.shields.io/badge/-Github-808080?logo=github
265 | [contributors-shield]: https://img.shields.io/github/contributors/oslabs-beta/AstroSpect?color=navy&label=Contributors
266 | [contributors-url]: https://github.com/oslabs-beta/AstroSpect/graphs/contributors
267 | [forks-shield]: https://img.shields.io/github/forks/oslabs-beta/AstroSpect?color=gold&label=Forks
268 | [forks-url]: https://github.com/oslabs-beta/AstroSpect/forks
269 | [stars-shield]: https://img.shields.io/github/stars/oslabs-beta/AstroSpect?color=%234B0082&label=Stars
270 | [stars-url]: https://github.com/oslabs-beta/AstroSpect/stargazers
271 | [issues-shield]: https://img.shields.io/github/issues/oslabs-beta/AstroSpect?color=%23483D8B&label=Issues
272 | [issues-url]: https://github.com/oslabs-beta/AstroSpect/issues
273 | [license-shield]: https://img.shields.io/github/license/oslabs-beta/AstroSpect?color=%09%23FF8C00&label=License
274 | [license-url]: https://github.com/oslabs-beta/AstroSpect/blob/master/LICENSE
275 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-0072b1?logo=linkedin
276 | [linkedin-url]: https://www.linkedin.com/company/astrospect
277 | [astro-shield]: https://img.shields.io/badge/-ASTRO-4c00b0?logo=astro
278 | [astro-url]: https://astro.build/
279 | [react-shield]: https://img.shields.io/badge/-REACT-333333?logo=react
280 | [react-url]: https://reactjs.org/
281 | [sass-shield]: https://img.shields.io/badge/-SASS-FFC0CB?logo=sass
282 | [sass-url]: https://sass-lang.com/
283 | [typescript-shield]: https://img.shields.io/badge/-TYPESCRIPT-e6e6e6?logo=typescript
284 | [typescript-url]: https://www.typescriptlang.org
285 | [webpack-url]: https://webpack.js.org/
286 | [webpack-shield]: https://img.shields.io/badge/-WEBPACK-1e3f66?logo=webpack
287 |
--------------------------------------------------------------------------------
/assets/astro-expand-collapse.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/assets/astro-expand-collapse.gif
--------------------------------------------------------------------------------
/assets/astro-inspection.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/assets/astro-inspection.gif
--------------------------------------------------------------------------------
/assets/astro-search.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/assets/astro-search.gif
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest').JestConfigWithTsJest} */
2 | module.exports = {
3 | preset: 'ts-jest',
4 | testEnvironment: 'node',
5 | };
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "astro-dev-tools",
3 | "version": "1.0.0",
4 | "description": "Astro Dev Tools OSP",
5 | "main": "index.js",
6 | "directories": {
7 | "doc": "docs"
8 | },
9 | "scripts": {
10 | "test": "jest --verbose",
11 | "start": "webpack-dev-server --open",
12 | "build": "webpack",
13 | "lint": "eslint . --ext .tsx"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/oslabs-beta/astro-dev-tools.git"
18 | },
19 | "author": "Evan Jones, Nicholas Park, John Roman, Jackson Ta",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/oslabs-beta/astro-dev-tools/issues"
23 | },
24 | "homepage": "https://github.com/oslabs-beta/astro-dev-tools#readme",
25 | "dependencies": {
26 | "@emotion/react": "^11.10.6",
27 | "@emotion/styled": "^11.10.6",
28 | "@mui/icons-material": "^5.11.9",
29 | "@mui/lab": "^5.0.0-alpha.120",
30 | "@mui/material": "^5.11.10",
31 | "@types/react": "^18.0.28",
32 | "@types/react-dom": "^18.0.11",
33 | "file-loader": "^6.2.0",
34 | "react": "^18.2.0",
35 | "react-dom": "^18.2.0",
36 | "react-router-dom": "^6.9.0",
37 | "ts-loader": "^9.4.2",
38 | "url-loader": "^4.1.1",
39 | "vite": "^4.1.4"
40 | },
41 | "devDependencies": {
42 | "@babel/core": "^7.21.0",
43 | "@babel/preset-env": "^7.20.2",
44 | "@babel/preset-react": "^7.18.6",
45 | "@babel/preset-typescript": "^7.21.4",
46 | "@jest/globals": "^29.5.0",
47 | "@testing-library/react": "^14.0.0",
48 | "@types/jest": "^29.5.0",
49 | "@typescript-eslint/eslint-plugin": "^5.54.1",
50 | "@typescript-eslint/parser": "^5.58.0",
51 | "babel-loader": "^9.1.2",
52 | "babel-preset-react-app": "^10.0.1",
53 | "css-loader": "^6.7.3",
54 | "eslint": "^8.35.0",
55 | "eslint-config-prettier": "^8.8.0",
56 | "eslint-config-standard-with-typescript": "^34.0.0",
57 | "eslint-plugin-import": "^2.27.5",
58 | "eslint-plugin-n": "^15.6.1",
59 | "eslint-plugin-promise": "^6.1.1",
60 | "eslint-plugin-react": "^7.32.2",
61 | "html-webpack-plugin": "^5.5.0",
62 | "jest": "^29.5.0",
63 | "jsdom": "^21.1.1",
64 | "jsdom-global": "^3.0.2",
65 | "prettier": "^2.8.7",
66 | "sass": "^1.58.3",
67 | "sass-loader": "^13.2.0",
68 | "style-loader": "^3.3.1",
69 | "ts-jest": "^29.1.0",
70 | "typescript": "^4.9.5",
71 | "webpack-cli": "^5.0.1",
72 | "webpack-dev-server": "^4.11.1"
73 | },
74 | "quokka": {
75 | "plugins": [
76 | "jsdom-quokka-plugin"
77 | ],
78 | "babel": {
79 | "presets": [
80 | "react-app"
81 | ]
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": ["eslint:recommended"],
7 | "overrides": [],
8 | "parser": "@typescript-eslint/parser",
9 | "parserOptions": {
10 | "ecmaVersion": "latest",
11 | "sourceType": "module",
12 | "project": "./tsconfig.json"
13 | },
14 | "plugins": ["react", "import", "@typescript-eslint"],
15 | "ignorePatterns": ["__tests__", "extension"],
16 | "rules": {
17 | "semi": ["off", "always"],
18 | "@typescript-eslint/semi": "off",
19 | "no-unexpected-multiline": "error"
20 | },
21 | "globals": {
22 | "JSX": "readonly",
23 | "React": "readonly"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/__tests__/createTree.test.js:
--------------------------------------------------------------------------------
1 | // tests createTree algorithm
2 | const jsdom = require('jsdom');
3 | const { JSDOM } = jsdom;
4 | // import { TreeItem } from '@mui/lab';
5 | // import createTree from '../app/algorithms/createTree';
6 |
7 | xdescribe('createTree', () => {
8 | beforeEach(() => {
9 | // Reset any side effects from previous tests.
10 | // Assumes you have reset functions implemented for addId and addIslandData.
11 | addId.reset();
12 | addIslandData.reset();
13 | });
14 |
15 | test('creates a leaf tree item for a node without children', () => {
16 | const node = document.createElement('div');
17 | const result = createTree(node, '0');
18 | const { getByText } = render(result);
19 |
20 | expect(getByText('div')).toBeInTheDocument();
21 | });
22 |
23 | it('should have three elements in islands array', () => {
24 | // create a DOM environment and load a test document
25 | const dom = new JSDOM(`
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Sass
-- CSS pre-processor
34 | Svelte
-- trendy frontend library
35 |
36 |
37 | `);
38 | const document = dom.window.document;
39 |
40 | expect(islands).toHaveLength(3);
41 | expect(islands[0].framework).not.toBe(null);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/src/__tests__/parseData.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect } from '@jest/globals';
2 | // import jsdom from 'jsdom';
3 | const jsdom = require('jsdom');
4 | const { JSDOM } = jsdom;
5 | require('jsdom-global')();
6 | global.DOMParser = window.DOMParser;
7 |
8 | describe('parse data tests', () => {
9 | it('should return an object', () => {
10 | // create fake DOM tree string that would be returned from Chrome API
11 | const html: string = new JSDOM(`
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Sass
-- CSS pre-processor
20 | Svelte
-- trendy frontend library
21 |
22 |
23 | `);
24 |
25 | const parser = new window.DOMParser();
26 | const stringToDoc: {} = parser.parseFromString(html, 'text/html');
27 |
28 | expect(stringToDoc && typeof stringToDoc === 'object').toBe(true);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/__tests__/parseProps.test.js:
--------------------------------------------------------------------------------
1 | // tests parseProps algorithm
2 | const parseProps = require('../app/algorithms/parseProps').default;
3 |
4 | describe('parseProps ', () => {
5 | it('returns object for one prop', () => {
6 | const unparsedProps = '{"hello": "world"}';
7 |
8 | const parsedProps = parseProps(unparsedProps);
9 |
10 | expect(parsedProps).toEqual({ hello: 'world' });
11 | });
12 |
13 | it('returns object for multiple, properly formatted, non-nested props', () => {
14 | // create a DOM environment and load a test document
15 |
16 | const unparsedProps = '{"hello": "world", "number": 2}';
17 |
18 | const parsedProps = parseProps(unparsedProps);
19 |
20 | expect(parsedProps).toEqual({ hello: 'world', number: 2 });
21 | });
22 |
23 | it('returns object for multiple, properly formatted, nested props', () => {
24 | // create a DOM environment and load a test document
25 | const unparsedProps = `{"labels": {"useLight": "Use light theme","useDark": "Use dark theme"},"isInsideHeader": true,"class": "astro-2W66RQV5"}`;
26 |
27 | const parsedProps = parseProps(unparsedProps);
28 |
29 | const targetObj = {
30 | labels: {
31 | useLight: 'Use light theme',
32 | useDark: 'Use dark theme',
33 | },
34 | isInsideHeader: true,
35 | class: 'astro-2W66RQV5',
36 | };
37 |
38 | expect(parsedProps).toEqual(targetObj);
39 | });
40 |
41 | it('returns object for multiple, properly formatted, nested props', () => {
42 | // create a DOM environment and load a test document
43 |
44 | const unparsedProps = `{"labels":[0,{"useLight":[0,"Use light theme"],"useDark":[0,"Use dark theme"]}],"isInsideHeader":[0,true],"class":[0,"astro-2W66RQV5"]}`;
45 |
46 | const parsedProps = parseProps(unparsedProps);
47 |
48 | const targetObj = {
49 | labels: {
50 | useLight: 'Use light theme',
51 | useDark: 'Use dark theme',
52 | },
53 | isInsideHeader: true,
54 | class: 'astro-2W66RQV5',
55 | };
56 |
57 | expect(parsedProps).toEqual(targetObj);
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/src/app/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // @ts-ignore
3 | import Panel from './containers/Panel';
4 | // @ts-ignore
5 | import SidePane from './containers/SidePane';
6 | import { useState, useEffect } from 'react';
7 | import parseData from './algorithms/parseData';
8 | import Header from './components/Header';
9 | import {
10 | CurrentComp,
11 | IslandData,
12 | AddIslandData,
13 | HandleClick,
14 | AddId,
15 | } from './types/types';
16 |
17 | const App: React.FC = (): JSX.Element => {
18 | //body data raw document nodes from target html
19 | const [bodyData, setBodyData] = useState(null);
20 | //prop info displayed in sidepane for the node that is clicked
21 | const [currentComp, setCurrentComp] = useState(null);
22 | //An object with prop data that is referenced when setting current comp
23 | const [islandData, setIslandData] = useState({});
24 | const [idSet, setIdSet] = useState>(new Set());
25 | const [idArray, setIdArray] = useState([]);
26 |
27 | // set the currentComp when a node is selected so we can display the Astro Island information if the node is an Island
28 | const handleClick: HandleClick = (event, id) => {
29 | if (islandData[id]) {
30 | setCurrentComp(islandData[id]);
31 | } else setCurrentComp(null);
32 | };
33 |
34 | // function to add astro island nodes to state when parsing dom
35 | const addIslandData: AddIslandData = (astroIsland, id) => {
36 | setIslandData((prevIslandData) => ({
37 | ...prevIslandData,
38 | [id]: astroIsland,
39 | }));
40 | };
41 |
42 | // adds id of each node in tree to an array of all ids
43 | const addId: AddId = (id) => {
44 | if (!idSet.has(id)) {
45 | setIdSet(new Set(idSet.add(id)));
46 | const idArray: string[] = Array.from(idSet);
47 | setIdArray([...idArray]);
48 | }
49 | };
50 |
51 | // parse the data from the DOM of the target page so we can pass in the DOM representation when creating the MUI tree
52 | useEffect((): void => {
53 | (async function fetchData(): Promise {
54 | const data: Document = await parseData();
55 | setBodyData(data);
56 | })();
57 | }, []);
58 |
59 | // if bodyData is not fetched, renders Loading screen
60 | return (
61 | <>
62 |
63 |
64 | {!bodyData &&
Loading...
}
65 | {bodyData && (
66 |
73 | )}
74 | {bodyData &&
}
75 |
76 | >
77 | );
78 | };
79 |
80 | export default App;
81 |
--------------------------------------------------------------------------------
/src/app/algorithms/createTree.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TreeItem from '@mui/lab/TreeItem';
3 | import parseProps from './parseProps';
4 | import { AddId, AddIslandData } from '../types/types';
5 |
6 | const createTree = (
7 | node: any,
8 | id: string,
9 | addId: AddId,
10 | addIslandData: AddIslandData
11 | ) => {
12 | //Recursive function to store tree structure in panel (all elements & all islands)
13 | const inner = (
14 | node: any,
15 | id: string,
16 | addId: AddId,
17 | addIslandData: AddIslandData,
18 | fontColor: string = '#F5F5F5'
19 | ) => {
20 | // adds id to idArray, required for expandAll functionality
21 | addId(id);
22 |
23 | // Stores ASTRO-ISLAND data in islandData state (from app)
24 | if (node.nodeName === 'ASTRO-ISLAND') {
25 | // parse props attribute of astro-island element
26 | const parsedProps = parseProps(node.attributes.props.value);
27 | // creates island object, with client directive and props info
28 | const island = {
29 | client: node.attributes.client.value,
30 | props: parsedProps,
31 | };
32 | // saves island object to an object of all island objects with its ID and parsed props
33 | addIslandData(island, id);
34 | // parses component-url attrbute to give astro-island descriptive name
35 | let componentFile = node.attributes['component-url'].value;
36 | let lastIndex: number = NaN;
37 | for (let i = componentFile.length - 1; i > 0; i--) {
38 | if (componentFile[i] === '.') lastIndex = i;
39 | if (componentFile[i] === '/') {
40 | if (lastIndex) componentFile = componentFile.slice(i + 1, lastIndex);
41 | else componentFile = componentFile.slice(i + 1);
42 | break;
43 | }
44 | }
45 | //if the current astro island has no children, save treeitem.
46 | if (!node.children) {
47 | const islandTreeItem = (
48 |
54 | );
55 | allIslands.push(islandTreeItem);
56 | return islandTreeItem;
57 | } else {
58 | // when astro island has children, returns parent TreeItem (orange) & recursively traverses through children
59 | const children = Array.from(node.children);
60 | const islandTreeItem = (
61 |
67 | {children.map((child, index) =>
68 | inner(child, `${id}-${index}`, addId, addIslandData, '#d494ffa6')
69 | )}
70 |
71 | );
72 | allIslands.push(islandTreeItem);
73 | return islandTreeItem;
74 | }
75 | }
76 |
77 | // If node has no children, return node
78 | if (!node.children) {
79 | return (
80 |
86 | );
87 | } else {
88 | const children = Array.from(node.children);
89 | // If node has children, recurse through function with each child node
90 | return (
91 |
97 | {children.map((child, index) =>
98 | inner(child, `${id}-${index}`, addId, addIslandData, fontColor)
99 | )}
100 |
101 | );
102 | }
103 | };
104 |
105 | // array of all islands (declared outside of the inner function because we don't want this array to be wiped with each recursive function call)
106 | const allIslands: JSX.Element[] = [];
107 | // invokes inner, which will return dom tree of all elements.
108 | const allElements: JSX.Element = inner(
109 | node,
110 | id,
111 | addId,
112 | addIslandData
113 | );
114 | //return all elements (to be rendered in ElementsView) & all islands (to be rendered in ComponentView)
115 | return {
116 | allElements,
117 | allIslands,
118 | };
119 | };
120 |
121 | export default createTree;
122 |
--------------------------------------------------------------------------------
/src/app/algorithms/parseData.ts:
--------------------------------------------------------------------------------
1 | declare const chrome: any;
2 |
3 | // parses html of target page in order to construct tree
4 | const parseData = async (): Promise => {
5 | // gets html of target page using Chrome API methods
6 | const html: string = await new Promise((resolve, reject) => {
7 | chrome.devtools.inspectedWindow.eval(
8 | 'document.documentElement.outerHTML',
9 | (result: string, exception: string) => {
10 | if (exception) {
11 | reject(exception);
12 | } else {
13 | resolve(result);
14 | }
15 | }
16 | );
17 | });
18 |
19 | // parses HTML string into document object
20 | const parser = new DOMParser();
21 | const stringToDoc: Document = parser.parseFromString(html, 'text/html');
22 |
23 | // returns document object
24 | return stringToDoc;
25 | };
26 |
27 | export default parseData;
28 |
--------------------------------------------------------------------------------
/src/app/algorithms/parseProps.ts:
--------------------------------------------------------------------------------
1 | // parses props of astro islands for display in side pane
2 | const parseProps = (attribute: string): Record => {
3 | // parses JSON string of props attribute
4 | const parsed: { [k: string]: any } = JSON.parse(attribute);
5 |
6 | // recursively parses nested props to get rid of unnecessary data from astro-island props
7 | // example of how the JSON is structured coming in: { "name": "[0, "Commander Roman"]" }
8 | // output after running JSON object through parseProps: { name: "Commander Roman" }
9 | const spreader = (obj: { [k: string]: any }): void => {
10 | for (const key in obj) {
11 | if (Array.isArray(obj[key])) {
12 | let newVal: any[] = obj[key].slice(1);
13 | obj[key] = newVal[0];
14 | spreader(obj[key]);
15 | }
16 | }
17 | };
18 |
19 | // calls spreader helper function
20 | spreader(parsed);
21 |
22 | // returns object of parsed props
23 | return parsed;
24 | };
25 |
26 | export default parseProps;
27 |
--------------------------------------------------------------------------------
/src/app/components/ComponentView.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TreeView from '@mui/lab/TreeView';
3 | import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
4 | import ChevronRightIcon from '@mui/icons-material/ChevronRight';
5 | import { ComponentViewProps } from '../types/types';
6 |
7 | const ComponentView: React.FC = (props): JSX.Element => {
8 | const { componentData, handleToggle, expanded, handleClick } = props;
9 |
10 | return (
11 | <>
12 | {componentData.length === 0 && (
13 | No Astro Islands found.
14 | )}
15 | {componentData.length > 0 && (
16 | }
19 | defaultExpandIcon={ }
20 | onNodeSelect={handleClick}
21 | onNodeToggle={handleToggle}
22 | expanded={expanded}
23 | sx={{
24 | height: '100vh',
25 | flexGrow: 1,
26 | width: 'auto',
27 | overflowY: 'auto',
28 | }}
29 | >
30 | {componentData}
31 |
32 | )}
33 | >
34 | );
35 | };
36 |
37 | export default ComponentView;
38 |
--------------------------------------------------------------------------------
/src/app/components/ElementView.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TreeView from '@mui/lab/TreeView';
3 | import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
4 | import ChevronRightIcon from '@mui/icons-material/ChevronRight';
5 | import { ElementViewProps } from '../types/types';
6 |
7 | const ElementView: React.FC = (props): JSX.Element => {
8 | const { handleClick, expanded, handleToggle, elementData } = props;
9 |
10 | // returns the completed tree
11 | return (
12 | <>
13 | }
16 | defaultExpandIcon={ }
17 | onNodeSelect={handleClick}
18 | onNodeToggle={handleToggle}
19 | expanded={expanded}
20 | sx={{
21 | height: '100vh',
22 | flexGrow: 1,
23 | width: 'auto',
24 | overflowY: 'auto',
25 | }}
26 | >
27 | {elementData}
28 |
29 | >
30 | );
31 | };
32 |
33 | export default ElementView;
34 |
--------------------------------------------------------------------------------
/src/app/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import AppBar from '@mui/material/AppBar';
3 | import Box from '@mui/material/Box';
4 | import Toolbar from '@mui/material/Toolbar';
5 | import Typography from '@mui/material/Typography';
6 | import GitHubIcon from '@mui/icons-material/GitHub';
7 | import WebIcon from '@mui/icons-material/Web';
8 | import Logo from '../../extension/assets/astrospect-logo.png';
9 |
10 | const Header: React.FC = (): JSX.Element => {
11 | return (
12 | // possibly pass in the system light/dark mode preferences into the props?
13 |
14 |
15 |
19 |
20 |
26 | AstroSpect
27 |
28 |
29 |
30 | {/* media query that gets rid of the 'GitHub' and 'Site' text? */}
31 |
70 |
71 |
72 |
73 | );
74 | };
75 |
76 | export default Header;
77 |
--------------------------------------------------------------------------------
/src/app/components/SearchBar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Button from '@mui/material/Button';
3 | import SearchIcon from '@mui/icons-material/Search';
4 | import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
5 | import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
6 | import { SearchBarProps } from '../types/types';
7 |
8 | // search bar in panel
9 | const SearchBar: React.FC = (
10 | props: SearchBarProps
11 | ): JSX.Element => {
12 | const { handleExpandClick, expanded } = props;
13 | const [found, setFound] = useState([]);
14 | const [current, setCurrent] = useState(0);
15 | const [searchVal, setSearchVal] = useState('');
16 |
17 | // searches through the tree to find text that matches the value of the text input
18 | const search = (): void => {
19 | // sets found and current to initial values of new search
20 | setFound([]);
21 | setCurrent(0);
22 | // assigns the text input value to a variable
23 | let textToSearch: string = (
24 | document.getElementById('text-to-search') as HTMLInputElement
25 | ).value;
26 |
27 | setSearchVal(textToSearch);
28 | // assign the searched text (tree view) to a variable
29 | const searchContents = document.querySelectorAll('.MuiTreeItem-label');
30 | // changing textToSearch to be a string that can be used as literal string in a regular expression without any unintended special meaning
31 | textToSearch = textToSearch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
32 | // object created which contains the escaped search string and flags 'gi' (means search is global and case sensitive)
33 | const pattern: RegExp = new RegExp(`${textToSearch}`, 'gi');
34 | // sets new array to be filled with matched elements
35 | const newArr: HTMLElement[] = [];
36 | // loop through each element in searchContents
37 | searchContents.forEach((item) => {
38 | const htmlItem = item as HTMLElement;
39 | if (htmlItem.textContent && htmlItem.textContent.match(pattern)) {
40 | newArr.push(htmlItem);
41 | }
42 | // get the textContent inside each element
43 | // replace any matches of the pattern with a highlighted version of the match
44 | const content = htmlItem.textContent || '';
45 | const highlightedText: string = content.replace(
46 | pattern,
47 | (match) => `${match} ` // performs the highlighting
48 | );
49 | // this replaces the original text content with the highlighted version in the actual HTML of the page
50 | item.innerHTML = highlightedText;
51 | });
52 | // sets found to the array of matching elements
53 | setFound([...newArr]);
54 | // scrolls automatically to first found element
55 | if (found[current]) found[current].scrollIntoView();
56 | };
57 |
58 | // scroll to next found element
59 | const scrollNext = (): void => {
60 | current < found.length - 1 ? setCurrent(current + 1) : setCurrent(0);
61 | found[current].scrollIntoView();
62 | };
63 |
64 | // scrolls to previous found element
65 | const scrollPrev = (): void => {
66 | current === 0 ? setCurrent(found.length - 1) : setCurrent(current - 1);
67 | found[current].scrollIntoView();
68 | };
69 |
70 | // // searchs when input field is updated
71 | // const handleInputChange = () => {
72 | // search();
73 | // };
74 |
75 | return (
76 |
77 |
78 |
86 |
87 | {searchVal.length > 0 && (
88 |
89 |
90 |
91 |
92 | {found.length === 0 ? 0 : current + 1}/{found.length}
93 |
94 |
95 | )}
96 |
97 |
98 |
99 | {expanded.length === 0 ? 'Expand' : 'Collapse'}
100 |
101 |
102 | );
103 | };
104 |
105 | export default SearchBar;
106 |
--------------------------------------------------------------------------------
/src/app/containers/Panel.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import SearchBar from '../components/SearchBar';
3 | import ElementView from '../components/ElementView';
4 | import ComponentView from '../components/ComponentView';
5 | import createTree from '../algorithms/createTree';
6 | import { HandleExpandClick, PanelProps } from '../types/types';
7 | import type { HandleToggle } from '../types/types';
8 |
9 | const Panel = (props: PanelProps): JSX.Element => {
10 | const { html, handleClick, addIslandData, idArray, addId } = props;
11 | const [expanded, setExpanded] = useState([]);
12 | const [selectedTab, setSelectedTab] = useState(0);
13 | const [elementData, setElementData] = useState([]);
14 | const [componentData, setComponentData] = useState([]);
15 |
16 | // alternates between expanding and collapsing all nodes
17 | const handleExpandClick: HandleExpandClick = () => {
18 | setExpanded((oldExpanded) => (oldExpanded.length === 0 ? idArray : []));
19 | };
20 |
21 | // updates expanded nodes on toggle of individual nodes
22 | const handleToggle: HandleToggle = (event, nodeIds) => {
23 | setExpanded(nodeIds);
24 | };
25 |
26 | // creates a tree of target HTML DOM represenataion upon component render; uses MUI Tree-item components
27 | useEffect(() => {
28 | const { allElements, allIslands } = createTree(
29 | html.body,
30 | '0',
31 | addId,
32 | addIslandData
33 | );
34 |
35 | // set the state of the Panel component with the elements and islands returned from calling createTree
36 | setElementData(allElements.props.children);
37 | setComponentData([...allIslands]);
38 | }, []);
39 |
40 | // returns the panel with toggle buttons and the search bar, which displays either the element view or island view
41 | return (
42 |
43 |
64 |
65 | {selectedTab === 0 && (
66 |
72 | )}
73 | {selectedTab === 1 && (
74 |
80 | )}
81 |
82 |
83 | );
84 | };
85 |
86 | export default Panel;
87 |
--------------------------------------------------------------------------------
/src/app/containers/SidePane.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TreeView from '@mui/lab/TreeView';
3 | import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
4 | import ChevronRightIcon from '@mui/icons-material/ChevronRight';
5 | import TreeItem from '@mui/lab/TreeItem';
6 | import { Typography } from '@mui/material';
7 | import { SidePaneProps } from '../types/types';
8 |
9 | // Displays Side Pane of type, props, and client-directive of element clicked (in Panel)
10 | const SidePane: React.FC = (props): JSX.Element => {
11 | // currentComp contains info on currently clicked element (ASTRO-ISLAND or null)
12 | const { currentComp } = props;
13 | // creates a dropdown tree of currently clicked ASTRO-ISLAND's props
14 | const createPropsDisplay = (obj: Record, id: number) => {
15 | // initializes array of parent props
16 | const topLevel: JSX.Element[] = [];
17 | // loops through props in currentComp obj
18 | for (const propName in obj) {
19 | // sets elem to be pushed to topLevel
20 | let elem: JSX.Element;
21 | // sets id to be assigned to elem
22 | let newId: number = id++;
23 | const propValue = obj[propName];
24 | // if there are children props, creates a tree item with children
25 | if (typeof obj[propName] === 'object') {
26 | // sets elem to tree item with recursive call to create child item
27 | elem = (
28 |
33 | {propName}:
34 |
35 | }
36 | >
37 | {createPropsDisplay(obj[propName], newId + 1)}
38 |
39 | );
40 | } else {
41 | // sets elem as leaf tree item
42 | elem = (
43 |
48 | {propName}:
49 | {String(propValue)}
50 |
51 | }
52 | />
53 | );
54 | }
55 | // resulting elem is then pushed to the array of parent tree items
56 | topLevel.push(elem);
57 | }
58 | // returns array of parent tree items, with nested children (if any)
59 | return topLevel;
60 | };
61 |
62 | // array that stores tree items of calling createPropsDisplay
63 | let propsDisplay: JSX.Element[] = [];
64 |
65 | // if an ASTRO-ISLAND is currently clicked, propsDisplay is updated
66 | if (currentComp) {
67 | // propsDisplay updates to result of calling CPD on that ASTRO-ISLAND's props
68 | propsDisplay = createPropsDisplay(currentComp.props, 99);
69 | }
70 |
71 | return (
72 |
73 | {/* // when element clicked is not an ASTRO-ISLAND */}
74 | {!currentComp && (
75 | <>
76 |
Type:
77 |
Static HTML
78 |
79 | >
80 | )}
81 |
82 | {/* // when element clicked is an ASTRO-ISLAND */}
83 | {currentComp && (
84 | <>
85 |
Type:
86 |
Astro Island
87 |
88 |
Client Directive:
89 |
{currentComp.client}
90 |
91 |
Props:
92 |
}
95 | defaultExpandIcon={
}
96 | sx={{
97 | height: '100vh',
98 | flexGrow: 1,
99 | width: 'auto',
100 | overflowY: 'auto',
101 | fontFamily: 'Roboto mono, monospace',
102 | }}
103 | >
104 | {propsDisplay}
105 |
106 |
107 | >
108 | )}
109 |
110 | );
111 | };
112 |
113 | export default SidePane;
114 |
--------------------------------------------------------------------------------
/src/app/index.tsx:
--------------------------------------------------------------------------------
1 | // render App
2 | import React from 'react';
3 | import { createRoot } from 'react-dom/client';
4 | import App from './App';
5 | import './styles/styles.scss';
6 |
7 | const domNode = document.getElementById('root');
8 | const root = createRoot(domNode);
9 | root.render( );
10 |
--------------------------------------------------------------------------------
/src/app/styles/_app.scss:
--------------------------------------------------------------------------------
1 | html {
2 | background-color: $softBlack;
3 | overflow: hidden;
4 | }
5 |
6 | body {
7 | color: whitesmoke;
8 | font-family: 'Figtree', sans-serif;
9 | margin: 0;
10 | height: 100%;
11 | scrollbar-color: dark;
12 | }
13 |
14 | #root {
15 | display: flex;
16 | flex-direction: column;
17 | overflow: none;
18 | }
19 |
20 | #main-container {
21 | display: flex;
22 | flex-direction: row;
23 | background-color: $softBlack;
24 | overflow: none;
25 | border-top: 1px solid #3d424a;
26 | }
27 |
28 | .container {
29 | padding-top: 10px;
30 | }
31 |
32 | @media (max-width: $panel-breakpoint) {
33 | #main-container {
34 | flex-wrap: wrap;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/app/styles/_header.scss:
--------------------------------------------------------------------------------
1 | .astrospect-wrapper {
2 | display: flex;
3 | gap: 8px;
4 | flex-grow: 1;
5 | align-items: center;
6 |
7 | &:hover {
8 | cursor: default;
9 | }
10 | }
11 |
12 | @media (max-width: $header-breakpoint) {
13 | .astrospect-text {
14 | display: none;
15 | }
16 | }
17 |
18 | .header-element-wrapper {
19 | display: flex;
20 | align-items: center;
21 | gap: 16px;
22 | }
23 |
24 | .header-element {
25 | display: flex;
26 | align-items: center;
27 | gap: 8px;
28 |
29 | .MuiTypography-root {
30 | display: inline-block;
31 | font-size: 1rem;
32 | padding: 6px 0;
33 | background-image: linear-gradient($tealAccent 0 0);
34 | background-position: 0 100%;
35 | background-size: 0% 3px;
36 | background-repeat: no-repeat;
37 | }
38 |
39 | .MuiTypography-root,
40 | a {
41 | transition: color 0.2s, background-size 0.3s, background-position 0s 0.3s;
42 | }
43 |
44 | a {
45 | color: whitesmoke;
46 | }
47 |
48 | &:hover {
49 | .MuiTypography-root,
50 | a {
51 | background-position: 100% 100%;
52 | background-size: 100% 3px;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/app/styles/_panel.scss:
--------------------------------------------------------------------------------
1 | .components-body {
2 | width: 60%;
3 | }
4 |
5 | #no-islands {
6 | margin: 10px;
7 | font-size: 1rem;
8 | }
9 |
10 | #panel-container {
11 | // set height of panel to all available vertical space minus the height of the header
12 | height: calc(100vh - 64px);
13 | background-color: $softBlack;
14 | width: 59%;
15 | overflow-x: none;
16 | overflow-y: auto;
17 | border-right: 1px solid #3d424a;
18 |
19 | #panel-header {
20 | display: flex;
21 | background-color: $softerBlack;
22 | flex-direction: column;
23 | justify-content: flex-start;
24 | align-items: stretch;
25 |
26 | #panel-toggle {
27 | display: flex;
28 | justify-content: center;
29 | border-bottom: 1px solid #3d424a;
30 | padding: 8px;
31 | .buttonToggle {
32 | font-family: 'Figtree', sans-serif;
33 | background-color: $softerBlack;
34 | opacity: 0.5;
35 | color: whitesmoke;
36 | padding: 5px 15px;
37 | border-color: rgb(211, 211, 211);
38 | border: 1px solid grey;
39 | text-transform: uppercase;
40 | box-shadow: 1px 1px grey;
41 | transition: ease background-color 250ms;
42 | &:hover {
43 | background-color: #d494ff63;
44 | }
45 | &:disabled {
46 | cursor: default;
47 | opacity: 0.7;
48 | }
49 | &.active {
50 | opacity: 1;
51 | background-color: #72329d;
52 | }
53 | }
54 | .buttonToggle.button0 {
55 | border-radius: 5px 0px 0px 5px;
56 | }
57 | .buttonToggle.button1 {
58 | border-radius: 0px 5px 5px 0px;
59 | }
60 | }
61 |
62 | #search-bar {
63 | display: flex;
64 | align-items: center;
65 | padding: 2px 10px;
66 | .search-icon {
67 | opacity: 0.5;
68 | scale: 0.8;
69 | }
70 | #text-to-search {
71 | background-color: $softerBlack;
72 | width: 100%;
73 | border: none;
74 | margin: 5px;
75 | padding-left: 10px;
76 | }
77 | }
78 | }
79 | .MuiTreeItem-label {
80 | font-family: 'Figtree', sans-serif;
81 | }
82 | .MuiTreeItem-content {
83 | :hover {
84 | background-color: #d494ff21;
85 | }
86 | &.Mui-selected {
87 | background-color: #8c4ab821;
88 | }
89 | }
90 |
91 | .element,
92 | .component {
93 | .MuiTreeView-root {
94 | // set the tree display height to the full available space minus the search bar and header
95 | height: calc(100vh - 160px);
96 |
97 | // to avoid vertical scrolling due to list items taking up too much space
98 | li {
99 | width: 95%;
100 | }
101 | }
102 | }
103 | }
104 |
105 | @media (max-width: $panel-breakpoint) {
106 | #panel-container {
107 | width: 100%;
108 | height: calc(50vh - 49px);
109 | overflow: hidden;
110 |
111 | .element,
112 | .component {
113 | .MuiTreeView-root {
114 | height: calc(50vh - 160px);
115 | }
116 | }
117 | }
118 | }
119 |
120 | ::placeholder {
121 | /* Chrome, Firefox, Opera, Safari 10.1+ */
122 | color: #f8f8ff;
123 | opacity: 0.5; /* Firefox */
124 | }
125 |
--------------------------------------------------------------------------------
/src/app/styles/_searchBar.scss:
--------------------------------------------------------------------------------
1 | .search-bar-text {
2 | font-size: 14px;
3 | color: whitesmoke;
4 | &:focus {
5 | outline: none;
6 | }
7 | }
8 |
9 | .prev-next {
10 | display: flex;
11 | align-items: center;
12 |
13 | .MuiSvgIcon-root {
14 | &:active {
15 | color: $tealAccent;
16 | }
17 |
18 | &:hover {
19 | cursor: pointer;
20 | }
21 | }
22 | }
23 |
24 | .separator {
25 | height: 20px;
26 | width: 1px;
27 | flex: 0 0 1px;
28 | margin: 0 0.25rem;
29 | background-color: #3d424a;
30 | }
31 |
32 | #search-bar {
33 | .expand-collapse {
34 | min-width: auto;
35 | color: lightgrey;
36 | }
37 | }
38 | .MuiButton-root.expand-collapse:hover {
39 | -webkit-text-decoration: none;
40 | text-decoration: none;
41 | background-color: #6befe087;
42 | }
43 |
--------------------------------------------------------------------------------
/src/app/styles/_sidePane.scss:
--------------------------------------------------------------------------------
1 | #sidepane-container {
2 | // i don't know why this works but it avoids weird scroll behavior; the 105px is a necessary and exact value
3 | height: calc(100vh - 105px);
4 | overflow-y: auto;
5 |
6 | width: 39%;
7 | background-color: $softBlack;
8 | padding: 20px;
9 | color: whitesmoke;
10 | margin: 0;
11 | font-size: 1rem;
12 |
13 | .MuiTreeView-root {
14 | height: auto;
15 |
16 | // to avoid horizontal scrolling due to list items taking up too much space
17 | li {
18 | width: 33vw;
19 |
20 | ul li {
21 | width: 30vw;
22 | }
23 | }
24 | }
25 | }
26 |
27 | hr {
28 | margin: 30px -20px 30px;
29 | border: 0;
30 | border-top: 1px solid #3d424a;
31 | }
32 |
33 | h3 {
34 | color: $tealAccent;
35 | // text-shadow: 0 0 0.5px gray;
36 | }
37 |
38 | @media (max-width: $panel-breakpoint) {
39 | #sidepane-container {
40 | width: 100%;
41 | height: calc(50vh - 49px);
42 | border-top: 3px solid #3d424a;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/app/styles/_variables.scss:
--------------------------------------------------------------------------------
1 | // Blues
2 | $lightestBlue: #f6fbff;
3 | $lightBlue: #8ab1cf;
4 | $darkBlue: #152642;
5 | $darkestBlue: #0f172a;
6 |
7 | // Purples
8 | $lightestPurple: #d5bcef;
9 | $lightPurple: #5965a4;
10 | $darkPurple: #372a48;
11 | $darkestPurple: #1a1429;
12 |
13 | // Oranges
14 | $primaryOrange: #ff7300;
15 | $lightOrange: lighten($primaryOrange, 40%);
16 | $lightestOrange: lighten($primaryOrange, 49.8%);
17 |
18 | // media query breakpoint values
19 | $panel-breakpoint: 599px;
20 | $header-breakpoint: 420px;
21 |
22 | $softerBlack: #202427;
23 | $softBlack: #161618;
24 | $tealAccent: #6befe0;
25 | $pinkAccent: #d494ff;
26 | $darkerPinkAccent: #d494ffa6;
27 |
--------------------------------------------------------------------------------
/src/app/styles/styles.scss:
--------------------------------------------------------------------------------
1 | // @import url('https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap');
2 | @import url('https://fonts.googleapis.com/css2?family=Figtree&display=swap');
3 |
4 | // import from other scss files
5 | @import 'variables';
6 | @import 'app';
7 | @import 'header';
8 | @import 'panel';
9 | @import 'sidePane';
10 | @import 'searchBar';
11 |
--------------------------------------------------------------------------------
/src/app/types/css.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/src/app/types/types.ts:
--------------------------------------------------------------------------------
1 | // exports reusable types
2 |
3 | export type CurrentComp = {
4 | client: string;
5 | props: Record;
6 | };
7 |
8 | export type IslandData = {
9 | [k: string]: CurrentComp;
10 | };
11 |
12 | export type ComponentViewProps = {
13 | componentData: JSX.Element[];
14 | handleToggle: HandleToggle;
15 | expanded: string[];
16 | handleClick: HandleClick;
17 | };
18 |
19 | export type ElementViewProps = {
20 | handleClick: HandleClick;
21 | expanded: string[];
22 | handleToggle: HandleToggle;
23 | elementData: JSX.Element[];
24 | };
25 |
26 | export type SearchBarProps = {
27 | handleExpandClick: HandleExpandClick;
28 | expanded: string[];
29 | };
30 |
31 | export type PanelProps = {
32 | html: Document;
33 | handleClick: HandleClick;
34 | addIslandData: AddIslandData;
35 | addId: AddId;
36 | idArray: string[];
37 | };
38 |
39 | export type SidePaneProps = {
40 | currentComp: CurrentComp | null;
41 | };
42 |
43 | export type HandleExpandClick = () => void;
44 |
45 | /* eslint-disable */
46 |
47 | export type AddId = (id: string) => void;
48 |
49 | export type HandleToggle = (
50 | event: React.SyntheticEvent,
51 | nodeIds: string[]
52 | ) => void;
53 |
54 | export type HandleClick = (
55 | event: React.SyntheticEvent,
56 | id: string
57 | ) => void;
58 |
59 | export type AddIslandData = (astroIsland: CurrentComp, id: string) => void;
60 |
61 | /* eslint-enable */
62 |
--------------------------------------------------------------------------------
/src/extension/assets/astrospect-logo-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/src/extension/assets/astrospect-logo-16.png
--------------------------------------------------------------------------------
/src/extension/assets/astrospect-logo-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/src/extension/assets/astrospect-logo-48.png
--------------------------------------------------------------------------------
/src/extension/assets/astrospect-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/src/extension/assets/astrospect-logo.png
--------------------------------------------------------------------------------
/src/extension/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/extension/devtools.js:
--------------------------------------------------------------------------------
1 | // Creates Chrome DevTools panel
2 | chrome.devtools.panels.create('AstroSpect', null, 'panel.html', (panel) => {});
3 |
--------------------------------------------------------------------------------
/src/extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "AstroSpect",
4 | "version": "1.0.0",
5 | "description": "AstroSpect is a Chrome DevTools extension for Astro developers.",
6 | "icons": {
7 | "16": "assets/astrospect-logo-16.png",
8 | "48": "assets/astrospect-logo-48.png",
9 | "128": "assets/astrospect-logo.png"
10 | },
11 | "devtools_page": "devtools.html"
12 | }
13 |
--------------------------------------------------------------------------------
/src/extension/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": true,
4 | "noEmitOnError": true,
5 | "module": "CommonJS",
6 | "watch": true,
7 | "jsx": "react",
8 | "esModuleInterop": true
9 | },
10 | "include": ["src/**/*"],
11 | "exclude": ["node_modules"]
12 | }
13 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 |
4 | module.exports = {
5 | entry: {
6 | // entry point of our app
7 | app: './src/app/index.tsx',
8 | },
9 | output: {
10 | path: path.resolve(__dirname, 'src/extension/bundles'),
11 | // publicPath: '/',
12 | filename: '[name].bundle.js',
13 | },
14 | devtool: 'cheap-module-source-map',
15 | mode: 'development',
16 | devServer: {
17 | host: 'localhost',
18 | port: 8080,
19 | // match the output path
20 | static: {
21 | directory: path.join(__dirname, './dist'),
22 | },
23 | // enable HMR on the devServer
24 | hot: true,
25 | // fallback to root for other urls
26 | historyApiFallback: true,
27 |
28 | headers: { 'Access-Control-Allow-Origin': '*' },
29 | /**
30 | * proxy is required in order to make api calls to
31 | * express server while using hot-reload webpack server
32 | * routes api fetch requests from localhost:8080/api/* (webpack dev server)
33 | * to localhost:3000/api/* (where our Express server is running)
34 | */
35 | proxy: {
36 | '/api/**': {
37 | target: 'http://localhost:3000/',
38 | secure: false,
39 | },
40 | },
41 | },
42 | module: {
43 | rules: [
44 | {
45 | test: /.(js|jsx)$/,
46 | exclude: /node_modules/,
47 | use: {
48 | loader: 'babel-loader',
49 | options: {
50 | presets: ['@babel/preset-env', '@babel/preset-react'],
51 | },
52 | },
53 | },
54 | {
55 | test: /.(ts|tsx)$/,
56 | exclude: /node_modules/,
57 | use: {
58 | loader: 'ts-loader',
59 | },
60 | },
61 | {
62 | test: /.(css|scss)$/,
63 | exclude: [/node_modules/, /client\/stylesheets\/modules/],
64 | use: ['style-loader', 'css-loader', 'sass-loader'],
65 | },
66 | {
67 | test: /.(css|scss)$/,
68 | include: [/client\/stylesheets\/modules/],
69 | use: [
70 | 'style-loader',
71 | {
72 | loader: 'css-loader',
73 | options: {
74 | modules: true,
75 | localIdentName: '[name]__[local]___[hash:base64:5]',
76 | },
77 | },
78 | 'sass-loader',
79 | ],
80 | },
81 | {
82 | test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
83 | loader: 'url-loader',
84 | },
85 | ],
86 | },
87 | plugins: [
88 | new HtmlWebpackPlugin({
89 | template: './src/extension/panel.html',
90 | }),
91 | ],
92 | resolve: {
93 | // Enable importing JS / JSX files without specifying their extension
94 | extensions: ['.js', '.jsx', '.ts', '.tsx'],
95 | },
96 | };
97 |
--------------------------------------------------------------------------------
/website/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | node: true,
4 | es2022: true,
5 | browser: true,
6 | },
7 | extends: ['eslint:recommended', 'plugin:astro/recommended', 'plugin:astro/jsx-a11y-strict'],
8 | parserOptions: {
9 | ecmaVersion: 'latest',
10 | sourceType: 'module',
11 | },
12 | rules: {},
13 | overrides: [
14 | {
15 | files: ['*.js'],
16 | rules: {
17 | 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
18 | },
19 | },
20 | {
21 | files: ['*.astro'],
22 | parser: 'astro-eslint-parser',
23 | parserOptions: {
24 | parser: '@typescript-eslint/parser',
25 | extraFileExtensions: ['.astro'],
26 | },
27 | rules: {
28 | 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
29 | },
30 | },
31 | {
32 | files: ['*.ts'],
33 | parser: '@typescript-eslint/parser',
34 | extends: ['plugin:@typescript-eslint/recommended'],
35 | rules: {
36 | '@typescript-eslint/no-unused-vars': [
37 | 'error',
38 | { argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_' },
39 | ],
40 | '@typescript-eslint/no-non-null-assertion': 'off',
41 | },
42 | },
43 | {
44 | files: ['**/*.astro/*.js', '*.astro/*.js'],
45 | parser: '@typescript-eslint/parser',
46 | },
47 | ],
48 | }
49 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist
3 |
4 | # dependencies
5 | node_modules/
6 | .snowpack/
7 |
8 | # logs
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | # environment variables
14 | .env
15 | .env.production
16 |
17 | # macOS-specific files
18 | .DS_Store
19 |
20 | # vscode settings and dictionaries
21 | .vscode
22 |
23 | # webstorm settings and dictionaries
24 | .idea
25 |
26 | # package lock
27 | package-lock.json
28 |
--------------------------------------------------------------------------------
/website/.npmrc:
--------------------------------------------------------------------------------
1 | # Expose Astro dependencies for `pnpm` users
2 | shamefully-hoist = true
--------------------------------------------------------------------------------
/website/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "semi": false,
4 | "singleQuote": true,
5 | "printWidth": 120,
6 | "plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
7 | "pluginSearchDirs": false
8 | }
9 |
--------------------------------------------------------------------------------
/website/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Mark Teekman
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 |
--------------------------------------------------------------------------------
/website/astro.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'astro/config';
2 | import compress from 'astro-compress';
3 | import mdx from '@astrojs/mdx';
4 | import tailwind from '@astrojs/tailwind';
5 |
6 | // https://astro.build/config
7 | export default defineConfig({
8 | integrations: [compress(), mdx(), tailwind()]
9 | });
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eminent-eclipse",
3 | "description": "An Accessible Starter Theme for Astro including several accessiblity features and tools to help you build faster.",
4 | "version": "2.0.0",
5 | "author": "Mark Teekman",
6 | "homepage": "https://accessible-astro.dev",
7 | "scripts": {
8 | "dev": "astro dev",
9 | "start": "astro dev",
10 | "build": "astro build",
11 | "preview": "astro preview"
12 | },
13 | "devDependencies": {
14 | "@astrojs/mdx": "^0.15.2",
15 | "@astrojs/partytown": "^1.0.3",
16 | "@astrojs/tailwind": "^3.0.0",
17 | "@typescript-eslint/eslint-plugin": "^5.50.0",
18 | "@typescript-eslint/parser": "^5.50.0",
19 | "accessible-astro-components": "^1.6.4",
20 | "astro": "^2.0.1",
21 | "astro-compress": "1.1.28",
22 | "astro-icon": "^0.7.3",
23 | "eslint": "^8.33.0",
24 | "eslint-plugin-astro": "^0.23.0",
25 | "eslint-plugin-jsx-a11y": "^6.7.1",
26 | "prettier": "^2.8.3",
27 | "prettier-plugin-astro": "^0.8.0",
28 | "prettier-plugin-tailwindcss": "^0.2.2",
29 | "sass": "^1.49.9",
30 | "svgo": "^2.8.0",
31 | "tailwindcss": "^3.2.7"
32 | },
33 | "dependencies": {
34 | "@types/react": "^18.0.28",
35 | "@types/react-dom": "^18.0.11",
36 | "react": "^18.2.0",
37 | "react-dom": "^18.2.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/website/public/accessible-components.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/accessible-components.webp
--------------------------------------------------------------------------------
/website/public/astrospect-feature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/astrospect-feature.png
--------------------------------------------------------------------------------
/website/public/astrospect-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/astrospect-logo.png
--------------------------------------------------------------------------------
/website/public/demo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/demo.webp
--------------------------------------------------------------------------------
/website/public/evan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/evan.png
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-Bold.woff
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-Bold.woff2
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-ExtraBold.woff
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-ExtraBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-ExtraBold.woff2
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-Italic.woff
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-Italic.woff2
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-Regular.woff
--------------------------------------------------------------------------------
/website/public/fonts/OpenSans-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/fonts/OpenSans-Regular.woff2
--------------------------------------------------------------------------------
/website/public/jackson.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/jackson.png
--------------------------------------------------------------------------------
/website/public/john.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/john.png
--------------------------------------------------------------------------------
/website/public/nick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/nick.png
--------------------------------------------------------------------------------
/website/public/social-preview-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/social-preview-image.png
--------------------------------------------------------------------------------
/website/public/wcag-compliant.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/AstroSpect/8e62314368b99e39a2d0fd87e77652a3d14e2a49/website/public/wcag-compliant.webp
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_breakpoint.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Breakpoint
3 | // | -------------------------------------------------------------
4 |
5 | $breakpoints: (
6 | "default": 0,
7 | "small": 24em,
8 | "medium": 48em,
9 | "large": 75em
10 | ) !default;
11 |
12 | @mixin breakpoint($breakpoint) {
13 | @if map-has-key($breakpoints, $breakpoint) {
14 | @media (min-width: map-get($breakpoints, $breakpoint)) {
15 | @content;
16 | }
17 | } @else if (type_of($breakpoint) == number) {
18 | @media (min-width: $breakpoint+"px") {
19 | @content;
20 | }
21 | }
22 | @else {
23 | @error "Not a correct value, check _base-breakpoints for availible values.";
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_button.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Button
3 | // | -------------------------------------------------------------
4 |
5 | .button {
6 | display: inline-block;
7 | padding: 0.75rem 1rem;
8 | font-weight: bold;
9 | text-decoration: none;
10 | text-align: center;
11 | color: var(--neutral-900);
12 | background-color: var(--primary-100);
13 | border: 3px solid var(--primary-100);
14 | border-radius: 3px;
15 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
16 |
17 | &:hover,
18 | &:focus {
19 | text-decoration: underline;
20 | background-color: var(--primary-200);
21 | border-color: var(--primary-200);
22 | }
23 |
24 | &:visited {
25 | color: var(--neutral-900);
26 | }
27 |
28 | &.color-secondary {
29 | background-color: var(--secondary-100);
30 | border-color: (var(--secondary-100));
31 |
32 | &:hover,
33 | &:focus {
34 | background-color: var(--secondary-400);
35 | border-color: var(--secondary-400);
36 | }
37 | }
38 |
39 | &.color-neutral {
40 | background-color: var(--neutral-500);
41 | border-color: (var(--neutral-500));
42 |
43 | &:hover,
44 | &:focus {
45 | background-color: var(--neutral-400);
46 | border-color: var(--neutral-400);
47 | }
48 | }
49 |
50 | &.color-info {
51 | background-color: var(--info-300);
52 | border-color: (var(--info-300));
53 |
54 | &:hover,
55 | &:focus {
56 | background-color: var(--info-200);
57 | border-color: var(--info-200);
58 | }
59 | }
60 |
61 | &.color-success {
62 | background-color: var(--success-400);
63 | border-color: (var(--success-400));
64 |
65 | &:hover,
66 | &:focus {
67 | background-color: var(--success-300);
68 | border-color: var(--success-300);
69 | }
70 | }
71 |
72 | &.color-warning {
73 | background-color: var(--warning-400);
74 | border-color: (var(--warning-400));
75 |
76 | &:hover,
77 | &:focus {
78 | background-color: var(--warning-300);
79 | border-color: var(--warning-300);
80 | }
81 | }
82 |
83 | &.color-error {
84 | background-color: var(--error-300);
85 | border-color: (var(--error-300));
86 |
87 | &:hover,
88 | &:focus {
89 | background-color: var(--error-200);
90 | border-color: var(--error-200);
91 | }
92 | }
93 |
94 | &.size-tiny {
95 | padding: 0.125rem 0.25rem;
96 | font-size: 0.75rem;
97 | line-height: 1.125rem;
98 | }
99 |
100 | &.size-small {
101 | padding: 0.25rem 0.5rem;
102 | font-size: 0.875rem;
103 | line-height: 1.3125rem;
104 | }
105 |
106 | &.size-large {
107 | padding: 0.75rem 1rem;
108 | font-size: 1.125rem;
109 | line-height: 1.6875rem;
110 | }
111 |
112 | &.size-huge {
113 | padding: 1rem 2rem;
114 | font-size: 1.25rem;
115 | line-height: 1.875rem;
116 | }
117 |
118 | &.behavior-full {
119 | display: block;
120 | width: 100%;
121 | }
122 |
123 | &.type-secondary {
124 | background-color: transparent;
125 |
126 | &:hover,
127 | &:focus {
128 | background-color: transparent;
129 | }
130 | }
131 |
132 | &.has-icon {
133 | display: flex;
134 | align-items: center;
135 | gap: 0.5rem;
136 |
137 | [astro-icon] {
138 | width: 30px;
139 | }
140 | }
141 | }
142 |
143 | .darkmode .button.type-secondary {
144 | color: var(--neutral-100);
145 | }
146 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_color.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Color
3 | // | -------------------------------------------------------------
4 |
5 | $colors: (
6 | primary: (
7 | 100: hsl(276, 100%, 79%),
8 | 200: hsl(276, 79%, 69%),
9 | 300: hsl(276, 53%, 49%),
10 | 400: hsl(276, 64%, 48%),
11 | 500: hsl(276, 96%, 20%),
12 | ),
13 | secondary: (
14 | 100: hsl(173, 81%, 68%),
15 | 200: hsl(173, 80%, 63%),
16 | 300: hsl(173, 72%, 57%),
17 | 400: hsl(173, 75%, 47%),
18 | 500: hsl(173, 90%, 30%),
19 | ),
20 | neutral: (
21 | 100: hsl(0 0% 100%),
22 | 200: hsl(200 23% 97%),
23 | 300: hsl(200 12% 95%),
24 | 400: hsl(205 12% 88%),
25 | 500: hsl(209 13% 83%),
26 | 600: hsl(208 6% 55%),
27 | 700: hsl(210 8% 31%),
28 | 800: hsl(212 9% 22%),
29 | 900: hsl(210 10% 14%),
30 | ),
31 | dark: (
32 | 100: hsl(240, 4%, 9%),
33 | ),
34 | );
35 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_container.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Container
3 | // | -------------------------------------------------------------
4 |
5 | @use "breakpoint" as *;
6 |
7 | .container {
8 | margin: 0 auto;
9 | padding: 0 calc(2rem / 2);
10 | max-width: 100%;
11 |
12 | @include breakpoint(medium) {
13 | padding: 0 2rem;
14 | }
15 |
16 | @include breakpoint(large) {
17 | padding: 0 calc(2rem / 2);
18 | max-width: 1200px;
19 | }
20 |
21 | &.stretch {
22 | max-width: 100%;
23 | }
24 | }
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_font.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Font
3 | // | -------------------------------------------------------------
4 |
5 | @use 'breakpoint' as *;
6 |
7 | @font-face {
8 | font-family: 'Open Sans';
9 | src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), url('/fonts/OpenSans-ExtraBold.woff2') format('woff2'),
10 | url('/fonts/OpenSans-ExtraBold.woff') format('woff');
11 | font-weight: bold;
12 | font-style: normal;
13 | font-display: swap;
14 | }
15 |
16 | @font-face {
17 | font-family: 'Open Sans';
18 | src: local('Open Sans Bold'), local('OpenSans-Bold'), url('/fonts/OpenSans-Bold.woff2') format('woff2'),
19 | url('/fonts/OpenSans-Bold.woff') format('woff');
20 | font-weight: bold;
21 | font-style: normal;
22 | font-display: swap;
23 | }
24 |
25 | @font-face {
26 | font-family: 'Open Sans';
27 | src: local('Open Sans Italic'), local('OpenSans-Italic'), url('/fonts/OpenSans-Italic.woff2') format('woff2'),
28 | url('/fonts/OpenSans-Italic.woff') format('woff');
29 | font-weight: normal;
30 | font-style: italic;
31 | font-display: swap;
32 | }
33 |
34 | @font-face {
35 | font-family: 'Open Sans';
36 | src: local('Open Sans Regular'), local('OpenSans-Regular'), url('/fonts/OpenSans-Regular.woff2') format('woff2'),
37 | url('/fonts/OpenSans-Regular.woff') format('woff');
38 | font-weight: normal;
39 | font-style: normal;
40 | font-display: swap;
41 | }
42 |
43 | body {
44 | font-family: var(--font-family-default);
45 | text-shadow: rgba(0, 0, 0, 0.01) 0 0 1px;
46 | text-rendering: optimizeLegibility;
47 | font-synthesis: none;
48 | font-size: 1rem;
49 | line-height: 1.5rem;
50 | -webkit-text-size-adjust: 100%;
51 | -moz-osx-font-smoothing: grayscale;
52 | -webkit-font-smoothing: antialiased;
53 |
54 | a:not(.button) {
55 | color: var(--action-color);
56 | text-decoration: underline;
57 |
58 | &:visited {
59 | color: var(--action-color);
60 | }
61 |
62 | &:hover,
63 | &:focus {
64 | color: var(--action-color-state);
65 | text-decoration: none;
66 | }
67 | }
68 |
69 | :where(main) a {
70 | word-wrap: break-word;
71 | word-break: break-word;
72 | }
73 |
74 | :where(h1, h2) {
75 | font-family: var(--font-family-special);
76 | }
77 |
78 | h1,
79 | h2 {
80 | font-weight: 800;
81 | }
82 |
83 | h3,
84 | h4,
85 | h5,
86 | h6 {
87 | font-weight: 600;
88 | }
89 |
90 | h1 {
91 | font-size: 2.25rem;
92 | line-height: 3.375rem;
93 |
94 | @include breakpoint(medium) {
95 | font-size: 3rem;
96 | line-height: 3.625rem;
97 | }
98 | }
99 |
100 | h2 {
101 | font-size: 1.875rem;
102 | line-height: 2.8125rem;
103 |
104 | @include breakpoint(medium) {
105 | font-size: 2.25rem;
106 | line-height: 3.375rem;
107 | }
108 | }
109 |
110 | h3 {
111 | font-size: 1.5rem;
112 | line-height: 2.25rem;
113 |
114 | @include breakpoint(medium) {
115 | font-size: 1.875rem;
116 | line-height: 2.8125rem;
117 | }
118 | }
119 |
120 | h4 {
121 | font-size: 1.25rem;
122 | line-height: 1.875rem;
123 |
124 | @include breakpoint(medium) {
125 | font-size: 1.5rem;
126 | line-height: 2.25rem;
127 | }
128 | }
129 |
130 | h5 {
131 | font-size: 1.125rem;
132 | line-height: 1.6875rem;
133 |
134 | @include breakpoint(medium) {
135 | font-size: 1.25rem;
136 | line-height: 1.875rem;
137 | h6 {
138 | font-size: 1rem;
139 | }
140 | }
141 |
142 | line-height: 1.5rem;
143 |
144 | @include breakpoint(medium) {
145 | font-size: 1.125rem;
146 | line-height: 1.6875rem;
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_list.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Lists
3 | // | -------------------------------------------------------------
4 |
5 | @use 'breakpoint' as *;
6 |
7 | ul:not([class]),
8 | ol:not([class]) {
9 | margin-left: 1rem;
10 |
11 | ul,
12 | ol {
13 | padding: 0.5rem 1rem 0;
14 | }
15 |
16 | li {
17 | margin-bottom: 0.5rem;
18 | }
19 | }
20 |
21 | ul:not([class]) {
22 | > li::marker {
23 | display: block;
24 | color: var(--primary-800);
25 | }
26 | }
27 |
28 | ol.incremented {
29 | counter-reset: item;
30 |
31 | ol {
32 | counter-reset: item;
33 | }
34 |
35 | ol,
36 | ul {
37 | margin: 0.75rem 0 0 1rem;
38 | }
39 |
40 | li {
41 | display: block;
42 | margin-bottom: 0.5rem;
43 |
44 | @include breakpoint(medium) {
45 | margin-bottom: 0.75rem;
46 | }
47 |
48 | &::before {
49 | content: counters(item, '.') '. ';
50 | counter-increment: item;
51 | }
52 |
53 | &:last-child {
54 | margin-bottom: 0;
55 | }
56 |
57 | p {
58 | display: inline;
59 | }
60 | }
61 |
62 | ul {
63 | li::before {
64 | content: '';
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_outline.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Outline
3 | // | -------------------------------------------------------------
4 |
5 | @mixin outline {
6 | outline: 2px dotted black;
7 | outline-color: black;
8 | outline-offset: 0;
9 | -webkit-box-shadow: 0 0 0 2px white;
10 | box-shadow: 0 0 0 2px white;
11 | }
12 |
13 | *:focus,
14 | *:focus-visible {
15 | @include outline;
16 | }
17 |
18 | *:focus:not(:focus-visible) {
19 | outline: none;
20 | box-shadow: none;
21 | }
22 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_reset.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | > Reset
3 | // | -------------------------------------------------------------
4 |
5 | html {
6 | box-sizing: border-box;
7 | scroll-behavior: smooth;
8 |
9 | @media (prefers-reduced-motion: reduce) {
10 | scroll-behavior: auto;
11 |
12 | body * {
13 | animation-duration: 0s !important;
14 | animation-delay: 0s !important;
15 | }
16 | }
17 | }
18 |
19 | *,
20 | *::after,
21 | *::before {
22 | box-sizing: inherit;
23 | }
24 |
25 | blockquote,
26 | body,
27 | figure,
28 | h1,
29 | h2,
30 | h3,
31 | h4,
32 | h5,
33 | h6,
34 | hr,
35 | li,
36 | ol,
37 | p,
38 | pre,
39 | ul {
40 | margin: 0;
41 | padding: 0;
42 | }
43 |
44 | ul:where([class]) {
45 | list-style: none;
46 | }
47 |
48 | button,
49 | input,
50 | select,
51 | textarea {
52 | color: inherit;
53 | letter-spacing: inherit;
54 | font: inherit;
55 | }
56 |
57 | input[type="text"],
58 | textarea {
59 | width: 100%;
60 | }
61 |
62 | fieldset {
63 | padding: 0;
64 | border: none;
65 | }
66 |
67 | legend {
68 | margin-bottom: 0.5rem;
69 | max-width: 100%;
70 | }
71 |
72 | button,
73 | input,
74 | textarea {
75 | border: 1px solid gray;
76 | }
77 |
78 | button {
79 | padding: 0.75em 1em;
80 | border-radius: 0;
81 | background-color: transparent;
82 | line-height: 1;
83 | }
84 |
85 | button * {
86 | pointer-events: none;
87 | }
88 |
89 | button:hover {
90 | cursor: pointer;
91 | }
92 |
93 | embed,
94 | iframe,
95 | img,
96 | object,
97 | svg,
98 | video {
99 | display: block;
100 | max-width: 100%;
101 | }
102 |
103 | table {
104 | width: 100%;
105 | table-layout: fixed;
106 | }
107 |
108 | [hidden] {
109 | display: none !important;
110 | }
111 |
112 | noscript {
113 | display: block;
114 | margin-top: 1em;
115 | margin-bottom: 1em;
116 | }
117 |
118 | [tabindex="-1"] {
119 | outline: none !important;
120 | box-shadow: none !important;
121 | }
122 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_root.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Root
3 | // | -------------------------------------------------------------
4 |
5 | @use "color" as *;
6 |
7 | :root {
8 | @each $color, $shades in $colors {
9 | @each $shade, $value in $shades {
10 | --#{$color}-#{$shade}: #{$value};
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/website/src/assets/scss/base/_space-content.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Space Content
3 | // | -------------------------------------------------------------
4 |
5 | @use 'breakpoint' as *;
6 |
7 | .space-content {
8 | > * + *,
9 | > dl > * + * {
10 | margin-top: 1.5rem;
11 | margin-bottom: 0;
12 | }
13 |
14 | > h2 {
15 | margin-top: 3rem;
16 |
17 | @include breakpoint(large) {
18 | margin-top: 4rem;
19 | }
20 | }
21 |
22 | > h3 {
23 | margin-top: 2rem;
24 |
25 | @include breakpoint(large) {
26 | margin-top: 3rem;
27 | }
28 | }
29 |
30 | > h4 {
31 | margin-top: 1.5rem;
32 |
33 | @include breakpoint(large) {
34 | margin-top: 2rem;
35 | }
36 | }
37 |
38 | > h5 {
39 | margin-top: 1rem;
40 |
41 | @include breakpoint(large) {
42 | margin-top: 1.5rem;
43 | }
44 | }
45 |
46 | > h6 {
47 | margin-top: 1rem;
48 |
49 | @include breakpoint(large) {
50 | margin-top: 1.5rem;
51 | }
52 | }
53 |
54 | > *:first-child {
55 | margin-top: 0;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/website/src/assets/scss/globals.scss:
--------------------------------------------------------------------------------
1 | // | -------------------------------------------------------------
2 | // | Globals
3 | // | -------------------------------------------------------------
4 |
5 | @use 'base/reset';
6 | @use 'base/root';
7 | @use 'base/font';
8 | @use 'base/list';
9 | @use 'base/container';
10 | @use 'base/breakpoint';
11 | @use 'base/button';
12 | @use 'base/color';
13 | @use 'base/outline';
14 | @use 'base/space-content';
15 |
--------------------------------------------------------------------------------
/website/src/components/AboutCard.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { Icon } from 'astro-icon'
3 |
4 | const { src, name, linkedin, github } = Astro.props;
5 | ---
6 |
7 |
8 |
9 |
10 |
11 |
12 | {name}
13 |
14 |
15 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/website/src/components/CallToAction.astro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Download the extension from the Chrome Store
4 |
Use AstroSpect
5 |
6 |
7 |
8 |
31 |
--------------------------------------------------------------------------------
/website/src/components/ContentMedia.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { Media } from 'accessible-astro-components'
3 |
4 | const { imgSrc, reverseImg = false } = Astro.props
5 | ---
6 |
7 |
8 |
9 |
10 | {!reverseImg ?
: ''}
11 |
12 |
13 |
14 | {reverseImg ?
: ''}
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/website/src/components/Feature.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { Icon } from 'astro-icon'
3 |
4 | const { icon = 'mdi:rocket', title = 'Title' } = Astro.props
5 | ---
6 |
7 |
8 |
9 |
10 |
{title}
11 |
12 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic, corporis.
13 |
14 |
15 |
16 |
17 |
53 |
--------------------------------------------------------------------------------
/website/src/components/Footer.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import CallToAction from './CallToAction.astro'
3 |
4 | const currentYear = new Date().getFullYear()
5 | ---
6 |
7 |
8 |
9 |
10 |
11 |
12 | © {currentYear} - The Starter Theme for this site was made with ❤️ by Mark Teekman .
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/website/src/components/Header.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Navigation from '../components/Navigation.astro'
3 | import { SkipLinks } from 'accessible-astro-components'
4 | import { Icon } from 'astro-icon'
5 | ---
6 |
7 |
8 |
9 |
10 |
13 |
16 |
19 |
24 |
25 |
26 |
27 |
52 |
--------------------------------------------------------------------------------
/website/src/components/Hero.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import { Icon } from 'astro-icon'
3 |
4 | const { src = '/astrospect-logo.png' } = Astro.props
5 | ---
6 |
7 |
8 |
9 |
10 |
11 |
12 | AstroSpect
13 |
14 |
15 | A tool for Astro developers
16 |
17 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
62 |
--------------------------------------------------------------------------------
/website/src/components/Navigation.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ResponsiveToggle from './ResponsiveToggle.astro'
3 | import { DarkMode } from 'accessible-astro-components'
4 | ---
5 |
6 |
28 |
29 |
204 |
205 |
404 |
--------------------------------------------------------------------------------
/website/src/components/ResponsiveToggle.astro:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
37 |
38 |
63 |
--------------------------------------------------------------------------------
/website/src/components/SiteMeta.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const { title, description, url, image, author } = Astro.props
3 | ---
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | AstroSpect — A developer tool for Astro
21 |
--------------------------------------------------------------------------------
/website/src/env.d.ts:
--------------------------------------------------------------------------------
1 | // /
--------------------------------------------------------------------------------
/website/src/layouts/DefaultLayout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import '../assets/scss/globals.scss'
3 | import SiteMeta from '../components/SiteMeta.astro'
4 | import Header from '../components/Header.astro'
5 | import Footer from '../components/Footer.astro'
6 |
7 | const {
8 | title = 'AstroSpect',
9 | description = 'A tool for Astro developers for inspecting props and hydration directives for any Astro Island.',
10 | url,
11 | image = '/social-preview-image.png',
12 | author = 'Jackson Ta, John Roman, Nicholas Park, and Evan Jones',
13 | } = Astro.props
14 | ---
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/website/src/layouts/MarkdownLayout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import DefaultLayout from './DefaultLayout.astro'
3 |
4 | const { frontmatter } = Astro.props
5 | ---
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/website/src/pages/404.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import DefaultLayout from '../layouts/DefaultLayout.astro'
3 | ---
4 |
5 |
6 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/website/src/pages/about/[...page].astro:
--------------------------------------------------------------------------------
1 | ---
2 | import DefaultLayout from '../../layouts/DefaultLayout.astro'
3 | import AboutCard from '../../components/AboutCard.astro'
4 | import { Card, Pagination } from 'accessible-astro-components'
5 | import { Icon } from 'astro-icon'
6 |
7 | export async function getStaticPaths({ paginate }) {
8 | const response = await fetch('https://jsonplaceholder.typicode.com/posts')
9 | const data = await response.json()
10 |
11 | return paginate(data, { pageSize: 6 })
12 | }
13 |
14 | const { jackson = '/jackson.png', nick = '/nick.png', john = '/john.png', evan = '/evan.png', page } = Astro.props
15 | ---
16 |
17 |
21 |
22 |
23 |
About AstroSpect
24 |
25 | AstroSpect was developed under OSLabs by Jackson Ta, John Roman, Nicholas Park, and Evan Jones. Feel free to reach out at contact@astrospect.dev or shoot any of us a Linkedin message!
26 |
27 |
28 |
29 |
30 |
31 |
Our team
32 |
33 |
39 |
40 |
46 |
47 |
53 |
54 |
60 |
61 |
62 |
63 |
64 |
65 |
81 |
--------------------------------------------------------------------------------
/website/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import DefaultLayout from '../layouts/DefaultLayout.astro'
3 | import Hero from '../components/Hero.astro'
4 | import Feature from '../components/Feature.astro'
5 | import ContentMedia from '../components/ContentMedia.astro'
6 | ---
7 |
8 |
9 |
10 |
11 | Inspect Astro Islands on any page
12 |
13 | Astro shook up the development world with Astro Islands . AstroSpect gives you the tools you need to get information about Astro Islands, including component props and hydration directives. Read our Medium Article to learn more about why AstroSpect is needed.
14 |
15 |
16 |
17 | Get only the information you need
18 |
19 | The "Islands Only" tab allows you to see a list of all the Astro Islands rendered to the target page — and nothing else. Click through on the dropdowns to see the HTML elements that comprise the Island.
20 |
21 |
22 |
23 |
24 |
Features
25 |
26 |
27 | Look at a full representation of the DOM with the Astro Islands highlighted.
28 |
29 |
30 | Look exclusively at the Astro Islands in the target page.
31 |
32 |
33 | Keyword search for any element in the DOM tree or any Astro Island name.
34 |
35 |
36 | Use the "Expand" and "Collapse" buttons to open or close all the dropdowns in either view.
37 |
38 |
39 | Click on any Astro Island dropdown to see all of its props.
40 |
41 |
42 | Look above the props panel to see the hydration directive for the component.
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/website/src/pages/markdown-page.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: ../layouts/MarkdownLayout.astro
3 | title: Markdown Page
4 | ---
5 |
6 | # Markdown Page
7 |
8 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
9 |
10 | ```js
11 | console.log('Hello Accessible World!')
12 | ```
13 |
14 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
15 |
16 | [Get this theme on GitHub](https://github.com/markteekman/accessible-astro-starter)
17 |
--------------------------------------------------------------------------------
/website/src/pages/mdx-page.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | layout: ../layouts/MarkdownLayout.astro
3 | title: MDX Page
4 | ---
5 |
6 | import { Icon } from 'astro-icon'
7 | import { Notification } from 'accessible-astro-components'
8 |
9 | # MDX Page
10 |
11 |
12 |
13 |
14 | Info: This page utilizes Astro's MDX feature which let's you use components in a markdown file and
15 | supports out-of-the-box syntax highlighting with Shiki.
16 |
17 |
18 |
19 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
20 |
21 | ```js
22 | console.log('Hello Accessible World!')
23 | ```
24 |
25 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Vitae veniam repellat deleniti obcaecati facilis non, praesentium aperiam laudantium excepturi assumenda doloremque animi quis aliquam eligendi quia nemo asperiores et eaque, sunt voluptatibus, saepe exercitationem id. Quis sequi maxime fugiat nam reprehenderit nesciunt quaerat obcaecati, ipsa dignissimos voluptatum voluptatem, optio quidem quos repudiandae dolorem voluptatibus fuga officia odio nemo recusandae voluptas.
26 |
27 | [Get this theme on GitHub](https://github.com/markteekman/accessible-astro-starter)
28 |
--------------------------------------------------------------------------------
/website/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | darkMode: ['class', '.darkmode'],
9 | }
10 |
--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-jsx",
4 | "jsxImportSource": "react"
5 | }
6 | }
--------------------------------------------------------------------------------