├── .gitignore
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── config.yml
└── CONTRIBUTING.md
├── themes
├── Dracula
│ ├── main.css
│ ├── index.js
│ └── tailwind.config.js
├── Github
│ ├── main.css
│ ├── index.js
│ └── tailwind.config.js
├── default
│ ├── main.css
│ ├── index.js
│ └── tailwind.config.js
└── Hacktoberfest2020
│ ├── main.css
│ ├── index.js
│ └── tailwind.config.js
├── .editorconfig
├── LICENSE.md
├── rollup.config.js
├── package.json
├── README.md
├── src
├── DevTools.js
├── Loader.js
└── Viewer.js
├── CHANGELOG.md
├── example.html
├── index.html
└── dist
├── Dracula.js
└── Hacktoberfest2020.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [kevinbatdorf]
--------------------------------------------------------------------------------
/themes/Dracula/main.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss/base";
2 | @import "tailwindcss/components";
3 | @import "tailwindcss/utilities";
4 |
--------------------------------------------------------------------------------
/themes/Github/main.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss/base";
2 | @import "tailwindcss/components";
3 | @import "tailwindcss/utilities";
4 |
--------------------------------------------------------------------------------
/themes/default/main.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss/base";
2 | @import "tailwindcss/components";
3 | @import "tailwindcss/utilities";
4 |
--------------------------------------------------------------------------------
/themes/Hacktoberfest2020/main.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss/base";
2 | @import "tailwindcss/components";
3 | @import "tailwindcss/utilities";
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/themes/Dracula/index.js:
--------------------------------------------------------------------------------
1 | import DevTools from '../../src/DevTools'
2 | import Loader from '../../src/Loader.js'
3 | import Viewer from '../../src/Viewer.js'
4 | import theme from './main.css'
5 |
6 | window.alpineDevToolsThemeDracula = theme
7 | const alpine = window.deferLoadingAlpine || ((alpine) => alpine())
8 | window.deferLoadingAlpine = callback => {
9 | alpine(callback)
10 | DevTools.start(Loader, Viewer, theme)
11 | }
12 |
--------------------------------------------------------------------------------
/themes/Github/index.js:
--------------------------------------------------------------------------------
1 | import DevTools from '../../src/DevTools'
2 | import Loader from '../../src/Loader.js'
3 | import Viewer from '../../src/Viewer.js'
4 | import theme from './main.css'
5 |
6 | window.alpineDevToolsThemeGitHub = theme
7 | const alpine = window.deferLoadingAlpine || ((alpine) => alpine())
8 | window.deferLoadingAlpine = callback => {
9 | alpine(callback)
10 | DevTools.start(Loader, Viewer, theme)
11 | }
12 |
--------------------------------------------------------------------------------
/themes/default/index.js:
--------------------------------------------------------------------------------
1 | import DevTools from '../../src/DevTools'
2 | import Loader from '../../src/Loader.js'
3 | import Viewer from '../../src/Viewer.js'
4 | import theme from './main.css'
5 |
6 | window.alpineDevToolsThemeDefault = theme
7 | const alpine = window.deferLoadingAlpine || ((alpine) => alpine())
8 | window.deferLoadingAlpine = callback => {
9 | alpine(callback)
10 | DevTools.start(Loader, Viewer, theme)
11 | }
12 |
--------------------------------------------------------------------------------
/themes/Hacktoberfest2020/index.js:
--------------------------------------------------------------------------------
1 | import DevTools from '../../src/DevTools'
2 | import Loader from '../../src/Loader.js'
3 | import Viewer from '../../src/Viewer.js'
4 | import theme from './main.css'
5 |
6 | window.alpineDevToolsThemeHacktoberfest2020 = theme
7 | const alpine = window.deferLoadingAlpine || ((alpine) => alpine())
8 | window.deferLoadingAlpine = callback => {
9 | alpine(callback)
10 | DevTools.start(Loader, Viewer, theme)
11 | }
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Bug Report
4 | url: https://github.com/KevinBatdorf/alpine-inline-devtools/issues/new
5 | about: Submit a GitHub issue
6 | # - name: Feature Request
7 | # url: https://github.com/KevinBatdorf/alpine-inline-devtools/discussions/new
8 | # about: Request a feature to be added to the platform
9 | # - name: Ask a Question
10 | # url: https://github.com/KevinBatdorf/alpine-inline-devtools/discussions/new
11 | # about: Ask questions and discuss with other community members
12 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for your interest in contributing to this project! Please take a moment to review this document **before submitting a pull request**.
4 |
5 | ## Pull requests
6 |
7 | **Please ask first before starting work on any significant new features.**
8 |
9 | It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create [an issue](https://github.com/KevinBatdorf/alpine-inline-devtools/issues) to first discuss any significant new features. This includes bug fixes, features, themes, etc.
10 |
11 | ## Theming
12 |
13 | Currently, adding a theme involves duplicating an existing theme and updating the Tailwind config file inside the theme folder. This will later be expanded to override classnames entirely for more custom theming.
14 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright © 2020Kevin Batdorf and contributors
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.
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from '@rollup/plugin-babel'
2 | import commonjs from '@rollup/plugin-commonjs'
3 | import filesize from 'rollup-plugin-filesize'
4 | import fs from 'fs'
5 | import json from '@rollup/plugin-json'
6 | import postcss from "rollup-plugin-postcss";
7 | import resolve from '@rollup/plugin-node-resolve'
8 |
9 | const createConfig = (themeName) => ({
10 | input: `themes/${themeName}/index.js`,
11 | output: [
12 | {
13 | file: `dist/${themeName}.js`,
14 | format: 'umd',
15 | name: `${themeName}`,
16 | }
17 | ],
18 | external: false,
19 | treeshake: {
20 | propertyReadSideEffects: false,
21 | },
22 | plugins: [
23 | babel({
24 | babelHelpers: 'bundled',
25 | exclude: 'node_modules/**',
26 | }),
27 | resolve({
28 | mainFields: ['module', 'jsnext', 'main'],
29 | browser: true,
30 | extensions: ['.mjs', '.js', '.jsx', '.json', '.node'],
31 | preferBuiltins: false,
32 | }),
33 | commonjs({
34 | include: /\/node_modules\//,
35 | }),
36 | postcss({
37 | extensions: [".css"],
38 | plugins: [
39 | require("tailwindcss")(`themes/${themeName}/tailwind.config.js`),
40 | require("autoprefixer"),
41 | ]
42 | }),
43 | json(),
44 | filesize(),
45 | ]
46 | })
47 |
48 | export default process.argv.includes('dev')
49 | ? ['Dracula'].map(createConfig)
50 | : fs.readdirSync('themes').map(createConfig)
51 |
--------------------------------------------------------------------------------
/themes/Hacktoberfest2020/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: {
3 | enabled: !process.argv.includes('dev'),
4 | content: [
5 | 'src/Viewer.js',
6 | 'src/Loader.js',
7 | ],
8 | },
9 | theme: {
10 | fontFamily: {
11 | mono: ['Fira Code', 'monospace'],
12 | },
13 | extend: {
14 | colors: {
15 | 'background': '#072540',
16 | 'container-border': 'transparent',
17 | 'component-divider': '#183d5d',
18 | 'component-title': '#ffffff',
19 | 'status-text': '#93c2db',
20 | 'status-text-hover': '#ff8ae2',
21 | 'property-name-color': '#ff8ae2',
22 | 'property-seperator-color': '#0069ff',
23 | 'typeof-color': '#93c2db',
24 | 'typeof-bg': 'transparent',
25 | 'value-color': '#ffffff',
26 | 'string-editor-color': '#ffffff',
27 | 'string-editor-bg': '#183d5d',
28 | 'icon-color': '#93c2db',
29 | },
30 | }
31 | },
32 | variants: {
33 | opacity: ['responsive', 'hover', 'focus', 'active', 'group-hover'],
34 | },
35 | plugins: [],
36 | future: {
37 | removeDeprecatedGapUtilities: true,
38 | },
39 | experimental: {
40 | extendedSpacingScale: true,
41 | extendedFontSizeScale: true,
42 | },
43 | }
44 |
--------------------------------------------------------------------------------
/themes/default/tailwind.config.js:
--------------------------------------------------------------------------------
1 | console.log(process.argv.includes('dev'))
2 | module.exports = {
3 | purge: {
4 | enabled: !process.argv.includes('dev'),
5 | content: [
6 | 'src/Viewer.js',
7 | 'src/Loader.js',
8 | ],
9 | },
10 | theme: {
11 | fontFamily: {
12 | mono: ['Fira Code', 'monospace'],
13 | },
14 | extend: {
15 | colors: {
16 | 'background': '#1a202c',
17 | 'container-border': 'transparent',
18 | 'component-divider': '#2d3748',
19 | 'component-title': '#d8dee9',
20 | 'status-text': '#8ac0cf',
21 | 'status-text-hover': '#4299e1',
22 | 'property-name-color': '#4aea8b',
23 | 'property-seperator-color': '#ffffff',
24 | 'typeof-color': '#8ac0cf',
25 | 'typeof-bg': '#2d3748',
26 | 'value-color': '#d8dee9',
27 | 'string-editor-color': '#000',
28 | 'string-editor-bg': '#edf2f7',
29 | 'icon-color': '#edf2f7',
30 | },
31 | }
32 | },
33 | variants: {
34 | opacity: ['responsive', 'hover', 'focus', 'active', 'group-hover'],
35 | },
36 | plugins: [],
37 | future: {
38 | removeDeprecatedGapUtilities: true,
39 | },
40 | experimental: {
41 | extendedSpacingScale: true,
42 | extendedFontSizeScale: true,
43 | },
44 | }
45 |
--------------------------------------------------------------------------------
/themes/Dracula/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: {
3 | enabled: !process.argv.includes('dev'),
4 | content: [
5 | 'src/Viewer.js',
6 | 'src/Loader.js',
7 | ],
8 | },
9 | theme: {
10 | fontFamily: {
11 | mono: ['Fira Code', 'monospace'],
12 | },
13 | extend: {
14 | colors: {
15 | // Color palette: https://draculatheme.com/contribute
16 | 'background': '#282a36',
17 | 'container-border': 'transparent',
18 | 'component-divider': '#44475a',
19 | 'component-title': '#ff79c6',
20 | 'status-text': '#6272a4',
21 | 'status-text-hover': '#8be9fd',
22 | 'property-name-color': '#50fa7b',
23 | 'property-seperator-color': '#8be9fd',
24 | 'typeof-color': '#bd93f9',
25 | 'typeof-bg': 'transparent',
26 | 'value-color': '#f8f8f2',
27 | 'string-editor-color': '#f8f8f2',
28 | 'string-editor-bg': '#44475a',
29 | 'icon-color': '#f1fa8c',
30 | },
31 | }
32 | },
33 | variants: {
34 | opacity: ['responsive', 'hover', 'focus', 'active', 'group-hover'],
35 | },
36 | plugins: [],
37 | future: {
38 | removeDeprecatedGapUtilities: true,
39 | },
40 | experimental: {
41 | extendedSpacingScale: true,
42 | extendedFontSizeScale: true,
43 | },
44 | }
45 |
--------------------------------------------------------------------------------
/themes/Github/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: {
3 | enabled: !process.argv.includes('dev'),
4 | content: [
5 | 'src/Viewer.js',
6 | 'src/Loader.js',
7 | ],
8 | },
9 | theme: {
10 | fontFamily: {
11 | mono: ['Fira Code', 'monospace'],
12 | },
13 | extend: {
14 | colors: {
15 | // Color palette: https://primer.style/css/support/color-system
16 | 'background': '#ffffff',
17 | 'container-border': '#f0f0f0',
18 | 'component-divider': '#d1d5da',
19 | 'component-title': '#24292e',
20 | 'status-text': '#6a737d',
21 | 'status-text-hover': '#ea4aaa',
22 | 'property-name-color': '#0366d6',
23 | 'property-seperator-color': '#0069ff',
24 | 'typeof-color': '#2ea44f',
25 | 'typeof-bg': 'transparent',
26 | 'value-color': '#24292e',
27 | 'string-editor-color': '#24292e',
28 | 'string-editor-bg': '#d1d5da',
29 | 'icon-color': '#6a737d',
30 | },
31 | }
32 | },
33 | variants: {
34 | opacity: ['responsive', 'hover', 'focus', 'active', 'group-hover'],
35 | },
36 | plugins: [],
37 | future: {
38 | removeDeprecatedGapUtilities: true,
39 | },
40 | experimental: {
41 | extendedSpacingScale: true,
42 | extendedFontSizeScale: true,
43 | },
44 | }
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@kevinbatdorf/alpine-inline-devtools",
3 | "version": "0.1.0",
4 | "description": "An easy way to monitor your state while developing with Alpine",
5 | "main": "dist/default.js",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build": "rollup -c",
11 | "watch": "concurrently \"rollup -c -w dev\" \"browser-sync --no-open ./ -f index.html example.html dist\"",
12 | "test": "jest",
13 | "test-watch": "jest --watch"
14 | },
15 | "husky": {
16 | "hooks": {
17 | "pre-commit": "npm run build"
18 | }
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/kevinbatdorf/alpine-inline-devtools.git"
23 | },
24 | "keywords": [
25 | "alpinejs"
26 | ],
27 | "author": "Kevin Batdorf",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/kevinbatdorf/alpine-inline-devtools/issues"
31 | },
32 | "homepage": "https://github.com/kevinbatdorf/alpine-inline-devtools#readme",
33 | "dependencies": {},
34 | "peerDependencies": {
35 | "alpinejs": "^2.7"
36 | },
37 | "devDependencies": {
38 | "@babel/preset-env": "^7.12.1",
39 | "@rollup/plugin-babel": "^5.2.1",
40 | "@rollup/plugin-commonjs": "^15.1.0",
41 | "@rollup/plugin-json": "^4.1.0",
42 | "@rollup/plugin-node-resolve": "^9.0.0",
43 | "@testing-library/dom": "^7.26.6",
44 | "@testing-library/jest-dom": "^5.11.6",
45 | "alpinejs": "^2.7.3",
46 | "autoprefixer": "9.8.6",
47 | "browser-sync": "^2.26.13",
48 | "concurrently": "^5.3.0",
49 | "husky": "^4.3.0",
50 | "jest": "^26.6.3",
51 | "jsdom-simulant": "^1.1.2",
52 | "rollup": "^2.33.2",
53 | "rollup-plugin-filesize": "^9.0.2",
54 | "rollup-plugin-postcss": "^3.1.8",
55 | "tailwindcss": "^1.9.6"
56 | },
57 | "files": [
58 | "dist/*",
59 | "package.json",
60 | "LICENSE.md",
61 | "README.md"
62 | ]
63 | }
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Alpine Inline DevTools
2 | Monitor and update your component state while developing with Alpine JS
3 |
4 | [Live Demo](https://kevinbatdorf.github.io/alpine-inline-devtools/)
5 |
6 | ## Installation
7 |
8 | Include the following `
14 | @endif
15 | ```
16 |
17 | ## Coming soon
18 | - Currently from the Dev Tools, you can update strings, booleans, and arrays, but not numbers and objects.
19 |
20 | ## Themes
21 | Choose from a variety of themes. ([Demo](https://kevinbatdorf.github.io/alpine-inline-devtools/))
22 |
23 | My current favorite is Dracula:
24 | ```html
25 |
26 | ```
27 |
28 | ## Tips
29 | - Add `x-devtools-ignore` to instruct the DevTools to ignore specific components.
30 | - Add `x-title` to set the title (will default to the `aria-label`, `x-id` then `id` otherwise).
31 | - Add your own button with an `id` of `alpine-devtools-button` to prevent the iframe from loading (will load a popup when pressed)
32 | - Add your own iframe with an `id` of `alpine-devtools-iframe` to position it where you like (see [demo](https://kevinbatdorf.github.io/alpine-inline-devtools/))
33 | - Click the status bar on the iframe to collapse it. It will remember this on page reload.
34 |
35 | ## Wrapped up as a browser extension
36 | If there's enough interest I will look into packaging this up as a browser extension so you can run it on any page whether in development or not
37 |
38 | ## Contributing
39 | If you're interested in contributing to this project, please read our [contributing docs](https://github.com/KevinBatdorf/alpine-inline-devtools/blob/master/.github/CONTRIBUTING.md) **before submitting a pull request**.
40 |
41 | ## License
42 |
43 | Copyright (c) 2020 Kevin Batdorf
44 |
45 | Licensed under the MIT license, see [LICENSE.md](LICENSE.md) for details.
46 |
--------------------------------------------------------------------------------
/src/DevTools.js:
--------------------------------------------------------------------------------
1 | const DevTools = {
2 | start(Loader, Viewer, theme) {
3 | window.alpineDevTools = {
4 | version: '0.11.1',
5 | Viewer: Viewer,
6 | Loader: Loader,
7 | theme: theme,
8 | }
9 | window.addEventListener('DOMContentLoaded', () => {
10 | this.injectDevToolsHandler()
11 |
12 | // A button is on the page already. use that instead
13 | const button = document.getElementById('alpine-devtools-button')
14 | if (button) {
15 | button.addEventListener('click', () => {
16 | window.dispatchEvent(new CustomEvent('open-alpine-devtools-popup', {
17 | bubbles: true,
18 | }))
19 | })
20 | }
21 | if (sessionStorage.getItem('alpine-devtools') !== 'Popup') {
22 | setTimeout(() => {
23 | window.dispatchEvent(new CustomEvent('open-alpine-devtools', {
24 | bubbles: true,
25 | }))
26 | }, 0)
27 | }
28 | })
29 | },
30 | injectDevToolsHandler() {
31 | const alpineDevToolsComponent = document.createElement('div')
32 | alpineDevToolsComponent.id = 'alpine-devtools'
33 | alpineDevToolsComponent.setAttribute('x-data', 'window.alpineDevTools.Loader(window.alpineDevTools.Viewer, window.alpineDevTools.theme)')
34 | alpineDevToolsComponent.setAttribute('x-devtools-ignore', '')
35 | alpineDevToolsComponent.setAttribute('x-on:open-alpine-devtools.window', 'openIframe()')
36 | alpineDevToolsComponent.setAttribute('x-on:open-alpine-devtools-popup.window', 'openPopup()')
37 | alpineDevToolsComponent.setAttribute('x-on:focus-alpine-devtools.window', 'focusDevTools()')
38 | alpineDevToolsComponent.setAttribute('x-on:alpine-devtools-switch-theme.window', 'setTheme($event.detail)')
39 | alpineDevToolsComponent.setAttribute('x-init', '$nextTick(() => { start() })')
40 | alpineDevToolsComponent.style.cssText = "display:none!important"
41 | document.body.appendChild(alpineDevToolsComponent)
42 | }
43 | }
44 |
45 | export default DevTools
46 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
9 | ## [0.12.0] - 2020-10-07
10 | ### Fixed
11 | - Shortened the iframe collapsed height to avoid overflow
12 | - Removed double scrollbars in Windows.
13 | - Made strings scrollable instead of a long paragraph.
14 | ### Added
15 | - You can now edit strings to that are on arrays
16 | - You can now append strings to arrays
17 | - You can now delete items from arrays
18 | - You can now collapse arrays (it doesn't persist but I might make that an option later on)
19 |
20 | ## [0.11.1] - 2020-10-07
21 | ### Fixed
22 | - Fix min height issue for the inner container
23 | - Fix loading the viewer twice
24 |
25 | ## [0.11.0] - 2020-10-07
26 | ### Added
27 | - Added true inline DevTools via an iframe (default configuration)
28 | - Added a button so you can open the iframe in a tab
29 | - Added a border class for themes
30 | - If you add your own iframe or button to the page, it will override the default loading
31 | - The iframe can be collapsable, even between sessions (click the status bar)
32 | ### Changed
33 | - Simplified the landing page to be more interactive and removed most the text.
34 |
35 | ## [0.10.1] - 2020-10-04
36 | ### Fixed
37 | - Fixed typo `x-ignore` to `x-devtools-ignore`
38 |
39 | ## [0.10.0] - 2020-10-04
40 | ### Fixed
41 | - Bumped up gutter button padding 1px
42 | ### Added
43 | - Added an `x-devtools-ignore` attribute to hide components from DevTools
44 |
45 | ## [0.9.2] - 2020-10-03
46 | ### Fixed
47 | - Updated build files with proper version numbers (TODO: automate that!)
48 |
49 | ## [0.9.1] - 2020-10-03
50 | ### Fixed
51 | - Update build file name case in git (ie. `Github.js` -> `GitHub.js`)
52 |
53 | ## [0.9.0] - 2020-10-03
54 | ### Added
55 | - Added live demo page
56 | - Added version number that you can access from a component
57 | - Added a way to live swap a theme (probably only useful for the live demo)
58 | - Added a way to focus the DevTools (also probably only useful for the demo)
59 | ### Changed
60 | - Updated the default theme spelling to Default to match others
61 | - Hide functions (maybe a regression as I thought this was already happening)
62 | ### Fixed
63 | - Updated the color of the text when no data was found
64 | - Fixed bug where strings would infinitely nest quotes
65 |
66 | ## [0.8.1] - 2020-10-03
67 | ### Fixed
68 | - Waits for `DOMContentLoaded` before injecting the button
69 |
70 | ## [0.8.0] - 2020-10-03
71 | ### Added
72 | - Added a theme based on Github (https://primer.style/css/support/color-system)
73 | - Added a theme based on Hacktoberfest 2020 (http://web.archive.org/web/20200924003932/https://embed-ssl.wistia.com/deliveries/49bd387c40e2c5aada92abdf973bc46d.webp)
74 | - Added a theme based on Dracula (https://draculatheme.com/contribute)
75 | ### Changed
76 | - Updated font to Fira Code (Will eventually make font customizable)
77 | ### Fixed
78 | - Fixed scoping issue where the scope label was updated twice
79 |
80 | ## [0.7.0] - 2020-09-28
81 | ### Added
82 | - Improved how the script gets injected into the popup
83 | - Includes Tailwind config with purging of the CSS instead of loading the CDN
84 | - Added a theming setup based on a few Tailwind config items. Will add in more themes soon
85 | - Improved development of this project with reloading popup instead of previously having to open/close the popup
86 | - A default status message shows how many components are being watched
87 | - Added internal way to track the nested scope (i.e. determine whether a string is inside an array)
88 | ### Changed
89 | - Extracted functions into separate files for better loading
90 | - Made the status icons hopefully less distracting
91 | ### Removed
92 | - Removes the string label and puts the text in quotes (For now. Might consider adding this back)
93 | ### Fixed
94 | - Fixed bug where you could attempt to edit strings in objects
95 |
96 | ## [0.6.0] - 2020-09-08
97 | ### Added
98 | - Discovers new components added after page load
99 | - Adds refresh attribute to elements to trigger external mutation watcher
100 | - You can now edit strings from the dev tools
101 | - Adds a status bar for important info (ex during string update)
102 | ### Removed
103 | - No longer registers component events on the window
104 |
--------------------------------------------------------------------------------
/example.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
24 |
25 |
26 |
27 |
28 |
29 |
35 |
41 |
42 |
String simple
43 |
44 |
45 |
46 |
String HTML
47 |
48 |
49 |
50 |
String Paragraph
51 |
52 |
53 |
54 |
63 |
76 |
77 |
Friends
78 |
79 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/Loader.js:
--------------------------------------------------------------------------------
1 | const Loader = function (Viewer, theme) {
2 | return {
3 | alpines: [],
4 | open: false,
5 | observer: null,
6 | windowRef: null,
7 | viewerScript: Viewer,
8 | viewer: null,
9 | theme: theme,
10 | iframe: null,
11 | type: 'Iframe',
12 | sessionId : Date.now() + Math.floor(Math.random() * 1000000),
13 | start() {
14 | this.alpines = document.querySelectorAll('[x-data]:not([x-devtools-ignore])')
15 | if (!this.alpines) return
16 | this.registerAlpines(this.alpines)
17 |
18 | // If the window is already open, refresh it
19 | if (sessionStorage.getItem('alpine-devtools')) {
20 | this.type = sessionStorage.getItem('alpine-devtools')
21 | // If viewer is already there, only update
22 | this.viewer || this.load()
23 | this.updateAlpines()
24 | }
25 | },
26 | focusDevTools() {
27 | if (!this.windowRef) {
28 | this[`open${this.type}`]()
29 | }
30 | this.windowRef.focus()
31 | },
32 | registerAlpines(alpines) {
33 | this.observer = new MutationObserver(mutations => {
34 | this.updateAlpines()
35 | })
36 | alpines.forEach(alpine => {
37 | // This will trigger a mutation when internal data changes but no visual mutation is observed
38 | alpine.setAttribute('x-bind:data-last-refresh', 'Date.now()')
39 | this.observer.observe(alpine, {
40 | attributes: true,
41 | childList: true,
42 | characterData: true,
43 | subtree: true,
44 | })
45 | })
46 | },
47 | updateAlpines() {
48 | this.checkIfNewAlpinesWereAddedAndRegisterThem()
49 | if (this.windowRef) {
50 | this.windowRef.alpines = this.alpines
51 | const viewer = this.windowRef.document.querySelector('#alpine-devtools-viewer')
52 | if (!viewer) return
53 | if (viewer.__x.$data.editing.length) return
54 | typeof viewer.__x !== 'undefined' && viewer.__x.updateElements(viewer)
55 | }
56 | },
57 | checkIfNewAlpinesWereAddedAndRegisterThem() {
58 | const fresh = [...document.querySelectorAll('[x-data]:not([x-devtools-ignore])')]
59 | const newAlpines = fresh.filter(alpine => {
60 | return ![...this.alpines].includes(alpine)
61 | })
62 | if (newAlpines) {
63 | this.alpines = document.querySelectorAll('[x-data]:not([x-devtools-ignore])')
64 | this.registerAlpines(newAlpines)
65 | }
66 | },
67 | openIframe() {
68 | this.iframe = document.getElementById('alpine-devtools-iframe')
69 | if (!this.iframe) {
70 | const state = sessionStorage.getItem('alpine-devtools-iframe-state')
71 | this.iframe = document.createElement('iframe')
72 | this.iframe.id = 'alpine-devtools-iframe'
73 | this.iframe.width = 450
74 | this.iframe.height = 650
75 | this.iframe.setAttribute('x-data', state ? state : '{height: 650, margin: "0.75rem"}')
76 | this.iframe.setAttribute('x-devtools-ignore', '')
77 | this.iframe.setAttribute('x-on:collapse-devtools.window', 'height = height === 650 ? 25 : 650;margin = margin === "0.75rem" ? "0" : "0.75rem";sessionStorage.setItem("alpine-devtools-iframe-state", `{height: ${height}, margin: "${margin}"}`)')
78 | this.iframe.setAttribute('x-bind:style', '`position:fixed;box-shadow:0 25px 50px -12px rgba(0,0,0,.25)!important;bottom:0!important;right:0!important;margin:${margin}!important;background-color:#252f3f!important;height: ${height}px!important`')
79 | document.body.appendChild(this.iframe)
80 | }
81 | this.windowRef = this.iframe.contentWindow
82 | this.type = 'Iframe'
83 | this.load()
84 | },
85 | openPopup() {
86 | if (this.iframe) {
87 | this.iframe.parentNode.removeChild(this.iframe)
88 | this.iframe = null
89 | }
90 | this.windowRef = window.open('', 'alpine-devtools', 'width=450, height=650, left=100, top=100')
91 | this.type = 'Popup'
92 | this.load()
93 | },
94 | load() {
95 | if (!this.windowRef) {
96 | sessionStorage.removeItem('alpine-devtools')
97 | this[`open${this.type}`]()
98 | }
99 |
100 | // Add the reference to the session so we don't need to reopen the popup everytime
101 | sessionStorage.setItem('alpine-devtools', this.type)
102 |
103 | // Starting color. Consider some loading animation
104 | this.windowRef.document.body.style.backgroundColor = '#1a202c'
105 | this.windowRef.document.body.style.height = '100%'
106 | this.windowRef.document.body.innerHTML = ''
107 | this.windowRef.document.title = 'Alpine DevTools'
108 | this.windowRef.alpines = this.alpines
109 |
110 | // TODO: take in AlpineJS version option
111 | if (!this.windowRef.document.getElementById('alpine-devtools-script')) {
112 | const alpineScript = this.windowRef.document.createElement('script')
113 | alpineScript.id = 'alpine-devtools-script'
114 | const version = window.Alpine.version || '2.x.x'
115 | alpineScript.src = `https://cdn.jsdelivr.net/gh/alpinejs/alpine@v${version}/dist/alpine.min.js`
116 | this.windowRef.document.head.appendChild(alpineScript)
117 | }
118 |
119 | // TODO: This should eventually be settable somehow
120 | if (!this.windowRef.document.getElementById('alpine-devtools-font')) {
121 | const font = this.windowRef.document.createElement('link')
122 | font.id = 'alpine-devtools-font'
123 | font.rel = 'stylesheet'
124 | font.href = "https://fonts.googleapis.com/css2?family=Fira+Code&display=swap"
125 | this.windowRef.document.head.appendChild(font)
126 | }
127 |
128 | this.setTheme(this.theme)
129 |
130 | // Add the dev tools component function and remove any previous
131 | const oldScript = this.windowRef.document.getElementById('devtools-script')
132 | oldScript && oldScript.remove()
133 | const devtoolsScript = this.windowRef.document.createElement('script')
134 | devtoolsScript.id = 'devtools-script'
135 | devtoolsScript.textContent = `window.Viewer = ${this.viewerScript}`
136 | this.windowRef.document.head.appendChild(devtoolsScript)
137 |
138 | this.loadView()
139 | },
140 | setTheme(theme) {
141 | if (!this.windowRef) {
142 | this[`open${this.type}`]()
143 | }
144 | this.theme = theme ? theme : this.theme
145 | // Add the theme and remove any previous (Can possibly support theme swapping)
146 | const oldTheme = this.windowRef.document.getElementById('alpine-devtools-theme')
147 | oldTheme && oldTheme.remove()
148 | const devtoolsTheme = this.windowRef.document.createElement('style')
149 | devtoolsTheme.id = 'alpine-devtools-theme'
150 | devtoolsTheme.textContent = this.theme
151 | this.windowRef.document.head.appendChild(devtoolsTheme)
152 | },
153 | loadView() {
154 | this.viewer = this.windowRef.document.createElement('div')
155 | this.viewer.id = 'alpine-devtools-viewer'
156 | this.viewer.setAttribute('x-data', `window.Viewer('${this.type}')`)
157 | this.viewer.setAttribute('x-init', 'setup()')
158 | this.viewer.setAttribute('x-on:open-alpine-devtools-popup.window', `
159 | window.parent.dispatchEvent(new CustomEvent('open-alpine-devtools-popup', {
160 | bubbles: true,
161 | event: 'Popup'
162 | }))`)
163 | this.viewer.setAttribute('x-init', 'setup()')
164 |
165 | this.open = true
166 | window.alpineDevTools.open = true
167 | setTimeout(() => {
168 | this.windowRef.document.body.appendChild(this.viewer)
169 | }, 500)
170 |
171 | this.windowRef.addEventListener('beforeunload', () => {
172 | sessionStorage.removeItem('alpine-devtools')
173 | this.type === 'popup' && this.windowRef.close()
174 | this.windowRef = null
175 | this.open = false
176 | window.alpineDevTools.open = false
177 | })
178 | },
179 | }
180 | }
181 |
182 | export default Loader
183 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
29 |
59 |
60 |
61 |
62 |
63 |
64 |
97 |
98 |
99 |
100 |
101 |
102 |
115 |
116 |
117 |
118 | What is it?
119 |
120 |
Alpine Inline DevTools is an obverser tool that's useful for developing with Alpine.js.
121 |
By default, the DevTools will automatically load on your page at the bottom right corner ().
122 |
130 |
To load in a popup window, press the small square on the bottom right See the full documentation on GitHub.
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
Alpine Inline DevTools
142 |
148 |
149 |
150 |
151 |
Alpine Inline DevTools comes with some pre-built themes. Press a theme button to preview and copy the script location to your clipboard.
152 |
201 |
210 | Theme Copied!
211 |
212 |
213 |
237 |
238 |
239 |
240 |
241 |
242 |
260 |
261 |
--------------------------------------------------------------------------------
/src/Viewer.js:
--------------------------------------------------------------------------------
1 | const Viewer = function(type) {
2 | return {
3 | editing: '',
4 | type: type,
5 | collapsedArrays: [], // TODO: persist this??
6 | setup() {
7 | this.$el.innerHTML = ``
43 | },
44 | computeTitle(alpine) {
45 | return alpine.getAttribute('x-title')
46 | || alpine.getAttribute('aria-label')
47 | || alpine.getAttribute('x-id')
48 | || alpine.id
49 | || this.convertFunctionName(alpine.getAttribute('x-data'))
50 | || `<${alpine.tagName.toLowerCase()}>`
51 | },
52 | getAlpineData(alpine) {
53 | if (!alpine.__x) return []
54 | return Object.entries(alpine.__x.getUnobservedData())
55 | },
56 | getType(value) {
57 | // Leave as object for now until we need it
58 | // if (value === null) {
59 | // return 'null'
60 | // }
61 | if (Array.isArray(value)) {
62 | return 'array'
63 | }
64 | if (typeof value === 'function') {
65 | return 'function'
66 | }
67 | return typeof value
68 | },
69 | updateAlpine(type, alpineIndex, key, value, scope, context = '') {
70 | let scopes = scope.split('.') // Just focus on the last one
71 | switch (scopes[scopes.length - 1]) {
72 | case 'boolean':
73 | return this.objectSetDeep(window.alpines[alpineIndex].__x.$data, context, (value !== 'true'))
74 | case 'string':
75 | return this.objectSetDeep(window.alpines[alpineIndex].__x.$data, context, value)
76 | }
77 | },
78 | removeItemFromArray(type, alpineIndex, key, value, scope, context = '') {
79 | return this.objectSpliceDeep(window.alpines[alpineIndex].__x.$data, context)
80 | },
81 | getItem(key, value, alpineIndex = null, scope = '', context = '') {
82 | const id = Date.now() + Math.floor(Math.random() * 1000000)
83 | const type = this.getType(value)
84 | scope = scope ? `${scope}.${type}` : type
85 | context = context ? `${context}.${key}` : key
86 | return `
87 |
88 | ${this.getGutterAction(id, type, alpineIndex, key, value, scope, context)}
89 |
90 |
91 | :
92 | ${this.getProperyTypeMessage(id, type, alpineIndex, key, value, scope, context)}
93 |
94 | ${this.getGutterExtraRight(id, type, alpineIndex, key, value, scope, context)}
95 |
96 |
99 |
106 | ${this.getValue(id, type, alpineIndex, key, value, scope, context)}
107 |
108 | ${this.getEditField(id, type, alpineIndex, key, value, scope, context)}
109 |
110 | `
111 | },
112 | getItemRowStyling(id, type, alpineIndex, key, value, scope, context = '') {
113 | switch (type) {
114 | case 'string':
115 | return `relative py-1 pl-1.5 flex items-start`
116 | case 'array':
117 | return `relative py-1 pl-1.5 inline-block`
118 | }
119 | return `relative py-1 pl-1.5 inline-block`
120 | },
121 | getEditField(id, type, alpineIndex, key, value, scope, context = '') {
122 | if (scope.endsWith('array.string') || scope === 'string') {
123 | return `
139 | ${this.escapeHTML(value)}
140 | `
141 | }
142 | return ''
143 | },
144 | getGutterAction(id, type, alpineIndex, key, value, scope, context = '') {
145 | const wrap = (content) => `${content}`
146 | const wrapTight = (content) => `${content}`
147 | switch (type) {
148 | case 'boolean':
149 | return wrap(`
150 | `)
156 | case 'string':
157 | if ('string' !== scope && !scope.endsWith('array.string')) return ''
158 | const deleteButton = `
159 | `
168 | const editButton = `
169 | `
178 | return scope.endsWith('array.string') ? wrapTight(deleteButton + editButton) : wrap(editButton)
179 | case 'array':
180 | return wrap(`
181 | `)
198 | default:
199 | return ''
200 | }
201 | },
202 | getGutterExtraRight(id, type, alpineIndex, key, value, scope, context = '') {
203 | switch (type) {
204 | case 'array':
205 | return `
206 |
212 |
223 | `
224 | case 'string':
225 | if (!scope.endsWith('array.string')) return ''
226 | return ''
227 | }
228 | return ''
229 | },
230 | getProperyTypeMessage(id, type, alpineIndex, key, value, scope) {
231 | let wrap = (content) => `${content}`
232 | switch (type) {
233 | case 'string':
234 | return ''
235 | case 'array':
236 | if (this.collapsedArrays.includes(`${alpineIndex}:${key}`)) {
237 | return wrap(`array[${value.length}]`)
238 | }
239 | return wrap`array`
240 | }
241 | return wrap(type)
242 | },
243 | openEditorAndSelectText(id, type, alpineIndex, key, value, scope, context) {
244 | // If the editor is already open and they press the edit icon again, commit the edit
245 | if (this.editing === `${alpineIndex}-${key}`) {
246 | this.updateAlpine('string', alpineIndex, key, this.$refs[`editor-${alpineIndex}-${key}`].textContent.trim(), scope, context)
247 | this.editing = ''
248 | return
249 | }
250 | this.editing = `${alpineIndex}-${key}`
251 | this.$nextTick(() => {
252 | this.editing && this.$refs[`editor-${alpineIndex}-${key}`].focus()
253 | this.editing && document.execCommand('selectAll', false, null)
254 | })
255 | },
256 | getValue(id, type, alpineIndex, key, value, scope, context = '') {
257 | switch (type) {
258 | case 'function':
259 | return ''
260 | case 'boolean':
261 | return value
262 | case 'number':
263 | return value
264 | case 'string':
265 | if (!['string', 'array.string'].includes(scope)){
266 | return `"${this.escapeHTML(value)}"`
267 | }
268 | return `
271 | "${this.escapeHTML(value)}"
272 | `
273 | case 'array':
274 | if (!value) return value
275 | return Object.entries(value).map(([...item]) => {
276 | return `${this.getItem(item[0], item[1], alpineIndex, scope, context)}
`
277 | }).join('')
278 | case 'object':
279 | if (!value) return value
280 | return Object.entries(value).map(([...item]) => {
281 | return `${this.getItem(item[0], item[1], alpineIndex, scope, context)}
`
282 | }).join('')
283 | }
284 | return value
285 | },
286 | getStatusMessage() {
287 | if (this.editing.length) {
288 | return 'Press Enter or click away to finish. Press Esc to cancel.'
289 | }
290 | return `
291 | Watching ${window.alpines.length} components...
292 |
303 | `
304 | },
305 | convertFunctionName(functionName) {
306 | if (functionName.indexOf('{') === 0) return
307 | let name = functionName.replace(/\(([^\)]+)\)/, '').replace('()', '').replace(/([A-Z])/g, " $1")
308 | return name ? name.charAt(0).toUpperCase() + name.slice(1) : ''
309 | },
310 | escapeHTML(html) {
311 | let div = document.createElement('div')
312 | div.innerText = html
313 | return div.innerHTML
314 | },
315 | // Borrowed from https://stackoverflow.com/a/54733755/1437789
316 | objectSetDeep(object, path, value) {
317 | path = path.toString().match(/[^.[\]]+/g) || []
318 | // Iterate all of them except the last one
319 | path.slice(0, -1).reduce((a, currentKey, index) => {
320 | // If the key does not exist or its value is not an object, create/override the key
321 | if (Object(a[currentKey]) !== a[currentKey]) {
322 | // Is the next key a potential array-index?
323 | a[currentKey] = Math.abs(path[index + 1]) >> 0 === +path[index + 1]
324 | ? [] // Yes: assign a new array object
325 | : {} // No: assign a new plain object
326 | }
327 | return a[currentKey]
328 | }, object)[path[path.length - 1]] = value // Finally assign the value to the last key
329 | return object
330 | },
331 | objectSpliceDeep(object, path) {
332 | path = path.toString().match(/[^.[\]]+/g) || []
333 | path.slice(0, -1).reduce((a, currentKey, index) => {
334 | return a[currentKey]
335 | }, object).splice(path[path.length - 1], 1)
336 | return object
337 | },
338 | }
339 | }
340 |
341 | export default Viewer
342 |
--------------------------------------------------------------------------------
/dist/Dracula.js:
--------------------------------------------------------------------------------
1 | (function (factory) {
2 | typeof define === 'function' && define.amd ? define(factory) :
3 | factory();
4 | }((function () { 'use strict';
5 |
6 | const DevTools = {
7 | start(Loader, Viewer, theme) {
8 | window.alpineDevTools = {
9 | version: '0.11.1',
10 | Viewer: Viewer,
11 | Loader: Loader,
12 | theme: theme
13 | };
14 | window.addEventListener('DOMContentLoaded', () => {
15 | this.injectDevToolsHandler(); // A button is on the page already. use that instead
16 |
17 | const button = document.getElementById('alpine-devtools-button');
18 |
19 | if (button) {
20 | button.addEventListener('click', () => {
21 | window.dispatchEvent(new CustomEvent('open-alpine-devtools-popup', {
22 | bubbles: true
23 | }));
24 | });
25 | }
26 |
27 | if (sessionStorage.getItem('alpine-devtools') !== 'Popup') {
28 | setTimeout(() => {
29 | window.dispatchEvent(new CustomEvent('open-alpine-devtools', {
30 | bubbles: true
31 | }));
32 | }, 0);
33 | }
34 | });
35 | },
36 |
37 | injectDevToolsHandler() {
38 | const alpineDevToolsComponent = document.createElement('div');
39 | alpineDevToolsComponent.id = 'alpine-devtools';
40 | alpineDevToolsComponent.setAttribute('x-data', 'window.alpineDevTools.Loader(window.alpineDevTools.Viewer, window.alpineDevTools.theme)');
41 | alpineDevToolsComponent.setAttribute('x-devtools-ignore', '');
42 | alpineDevToolsComponent.setAttribute('x-on:open-alpine-devtools.window', 'openIframe()');
43 | alpineDevToolsComponent.setAttribute('x-on:open-alpine-devtools-popup.window', 'openPopup()');
44 | alpineDevToolsComponent.setAttribute('x-on:focus-alpine-devtools.window', 'focusDevTools()');
45 | alpineDevToolsComponent.setAttribute('x-on:alpine-devtools-switch-theme.window', 'setTheme($event.detail)');
46 | alpineDevToolsComponent.setAttribute('x-init', '$nextTick(() => { start() })');
47 | alpineDevToolsComponent.style.cssText = "display:none!important";
48 | document.body.appendChild(alpineDevToolsComponent);
49 | }
50 |
51 | };
52 |
53 | const Loader = function (Viewer, theme) {
54 | return {
55 | alpines: [],
56 | open: false,
57 | observer: null,
58 | windowRef: null,
59 | viewerScript: Viewer,
60 | viewer: null,
61 | theme: theme,
62 | iframe: null,
63 | type: 'Iframe',
64 | sessionId: Date.now() + Math.floor(Math.random() * 1000000),
65 |
66 | start() {
67 | this.alpines = document.querySelectorAll('[x-data]:not([x-devtools-ignore])');
68 | if (!this.alpines) return;
69 | this.registerAlpines(this.alpines); // If the window is already open, refresh it
70 |
71 | if (sessionStorage.getItem('alpine-devtools')) {
72 | this.type = sessionStorage.getItem('alpine-devtools'); // If viewer is already there, only update
73 |
74 | this.viewer || this.load();
75 | this.updateAlpines();
76 | }
77 | },
78 |
79 | focusDevTools() {
80 | if (!this.windowRef) {
81 | this[`open${this.type}`]();
82 | }
83 |
84 | this.windowRef.focus();
85 | },
86 |
87 | registerAlpines(alpines) {
88 | this.observer = new MutationObserver(mutations => {
89 | this.updateAlpines();
90 | });
91 | alpines.forEach(alpine => {
92 | // This will trigger a mutation when internal data changes but no visual mutation is observed
93 | alpine.setAttribute('x-bind:data-last-refresh', 'Date.now()');
94 | this.observer.observe(alpine, {
95 | attributes: true,
96 | childList: true,
97 | characterData: true,
98 | subtree: true
99 | });
100 | });
101 | },
102 |
103 | updateAlpines() {
104 | this.checkIfNewAlpinesWereAddedAndRegisterThem();
105 |
106 | if (this.windowRef) {
107 | this.windowRef.alpines = this.alpines;
108 | const viewer = this.windowRef.document.querySelector('#alpine-devtools-viewer');
109 | if (!viewer) return;
110 | if (viewer.__x.$data.editing.length) return;
111 | typeof viewer.__x !== 'undefined' && viewer.__x.updateElements(viewer);
112 | }
113 | },
114 |
115 | checkIfNewAlpinesWereAddedAndRegisterThem() {
116 | const fresh = [...document.querySelectorAll('[x-data]:not([x-devtools-ignore])')];
117 | const newAlpines = fresh.filter(alpine => {
118 | return ![...this.alpines].includes(alpine);
119 | });
120 |
121 | if (newAlpines) {
122 | this.alpines = document.querySelectorAll('[x-data]:not([x-devtools-ignore])');
123 | this.registerAlpines(newAlpines);
124 | }
125 | },
126 |
127 | openIframe() {
128 | this.iframe = document.getElementById('alpine-devtools-iframe');
129 |
130 | if (!this.iframe) {
131 | const state = sessionStorage.getItem('alpine-devtools-iframe-state');
132 | this.iframe = document.createElement('iframe');
133 | this.iframe.id = 'alpine-devtools-iframe';
134 | this.iframe.width = 450;
135 | this.iframe.height = 650;
136 | this.iframe.setAttribute('x-data', state ? state : '{height: 650, margin: "0.75rem"}');
137 | this.iframe.setAttribute('x-devtools-ignore', '');
138 | this.iframe.setAttribute('x-on:collapse-devtools.window', 'height = height === 650 ? 25 : 650;margin = margin === "0.75rem" ? "0" : "0.75rem";sessionStorage.setItem("alpine-devtools-iframe-state", `{height: ${height}, margin: "${margin}"}`)');
139 | this.iframe.setAttribute('x-bind:style', '`position:fixed;box-shadow:0 25px 50px -12px rgba(0,0,0,.25)!important;bottom:0!important;right:0!important;margin:${margin}!important;background-color:#252f3f!important;height: ${height}px!important`');
140 | document.body.appendChild(this.iframe);
141 | }
142 |
143 | this.windowRef = this.iframe.contentWindow;
144 | this.type = 'Iframe';
145 | this.load();
146 | },
147 |
148 | openPopup() {
149 | if (this.iframe) {
150 | this.iframe.parentNode.removeChild(this.iframe);
151 | this.iframe = null;
152 | }
153 |
154 | this.windowRef = window.open('', 'alpine-devtools', 'width=450, height=650, left=100, top=100');
155 | this.type = 'Popup';
156 | this.load();
157 | },
158 |
159 | load() {
160 | if (!this.windowRef) {
161 | sessionStorage.removeItem('alpine-devtools');
162 | this[`open${this.type}`]();
163 | } // Add the reference to the session so we don't need to reopen the popup everytime
164 |
165 |
166 | sessionStorage.setItem('alpine-devtools', this.type); // Starting color. Consider some loading animation
167 |
168 | this.windowRef.document.body.style.backgroundColor = '#1a202c';
169 | this.windowRef.document.body.style.height = '100%';
170 | this.windowRef.document.body.innerHTML = '';
171 | this.windowRef.document.title = 'Alpine DevTools';
172 | this.windowRef.alpines = this.alpines; // TODO: take in AlpineJS version option
173 |
174 | if (!this.windowRef.document.getElementById('alpine-devtools-script')) {
175 | const alpineScript = this.windowRef.document.createElement('script');
176 | alpineScript.id = 'alpine-devtools-script';
177 | const version = window.Alpine.version || '2.x.x';
178 | alpineScript.src = `https://cdn.jsdelivr.net/gh/alpinejs/alpine@v${version}/dist/alpine.min.js`;
179 | this.windowRef.document.head.appendChild(alpineScript);
180 | } // TODO: This should eventually be settable somehow
181 |
182 |
183 | if (!this.windowRef.document.getElementById('alpine-devtools-font')) {
184 | const font = this.windowRef.document.createElement('link');
185 | font.id = 'alpine-devtools-font';
186 | font.rel = 'stylesheet';
187 | font.href = "https://fonts.googleapis.com/css2?family=Fira+Code&display=swap";
188 | this.windowRef.document.head.appendChild(font);
189 | }
190 |
191 | this.setTheme(this.theme); // Add the dev tools component function and remove any previous
192 |
193 | const oldScript = this.windowRef.document.getElementById('devtools-script');
194 | oldScript && oldScript.remove();
195 | const devtoolsScript = this.windowRef.document.createElement('script');
196 | devtoolsScript.id = 'devtools-script';
197 | devtoolsScript.textContent = `window.Viewer = ${this.viewerScript}`;
198 | this.windowRef.document.head.appendChild(devtoolsScript);
199 | this.loadView();
200 | },
201 |
202 | setTheme(theme) {
203 | if (!this.windowRef) {
204 | this[`open${this.type}`]();
205 | }
206 |
207 | this.theme = theme ? theme : this.theme; // Add the theme and remove any previous (Can possibly support theme swapping)
208 |
209 | const oldTheme = this.windowRef.document.getElementById('alpine-devtools-theme');
210 | oldTheme && oldTheme.remove();
211 | const devtoolsTheme = this.windowRef.document.createElement('style');
212 | devtoolsTheme.id = 'alpine-devtools-theme';
213 | devtoolsTheme.textContent = this.theme;
214 | this.windowRef.document.head.appendChild(devtoolsTheme);
215 | },
216 |
217 | loadView() {
218 | this.viewer = this.windowRef.document.createElement('div');
219 | this.viewer.id = 'alpine-devtools-viewer';
220 | this.viewer.setAttribute('x-data', `window.Viewer('${this.type}')`);
221 | this.viewer.setAttribute('x-init', 'setup()');
222 | this.viewer.setAttribute('x-on:open-alpine-devtools-popup.window', `
223 | window.parent.dispatchEvent(new CustomEvent('open-alpine-devtools-popup', {
224 | bubbles: true,
225 | event: 'Popup'
226 | }))`);
227 | this.viewer.setAttribute('x-init', 'setup()');
228 | this.open = true;
229 | window.alpineDevTools.open = true;
230 | setTimeout(() => {
231 | this.windowRef.document.body.appendChild(this.viewer);
232 | }, 500);
233 | this.windowRef.addEventListener('beforeunload', () => {
234 | sessionStorage.removeItem('alpine-devtools');
235 | this.type === 'popup' && this.windowRef.close();
236 | this.windowRef = null;
237 | this.open = false;
238 | window.alpineDevTools.open = false;
239 | });
240 | }
241 |
242 | };
243 | };
244 |
245 | const Viewer = function (type) {
246 | return {
247 | editing: '',
248 | type: type,
249 | collapsedArrays: [],
250 |
251 | // TODO: persist this??
252 | setup() {
253 | this.$el.innerHTML = `
257 |
261 |
281 |
287 |
288 |
`;
289 | },
290 |
291 | computeTitle(alpine) {
292 | return alpine.getAttribute('x-title') || alpine.getAttribute('aria-label') || alpine.getAttribute('x-id') || alpine.id || this.convertFunctionName(alpine.getAttribute('x-data')) || `<${alpine.tagName.toLowerCase()}>`;
293 | },
294 |
295 | getAlpineData(alpine) {
296 | if (!alpine.__x) return [];
297 | return Object.entries(alpine.__x.getUnobservedData());
298 | },
299 |
300 | getType(value) {
301 | // Leave as object for now until we need it
302 | // if (value === null) {
303 | // return 'null'
304 | // }
305 | if (Array.isArray(value)) {
306 | return 'array';
307 | }
308 |
309 | if (typeof value === 'function') {
310 | return 'function';
311 | }
312 |
313 | return typeof value;
314 | },
315 |
316 | updateAlpine(type, alpineIndex, key, value, scope, context = '') {
317 | let scopes = scope.split('.'); // Just focus on the last one
318 |
319 | switch (scopes[scopes.length - 1]) {
320 | case 'boolean':
321 | return this.objectSetDeep(window.alpines[alpineIndex].__x.$data, context, value !== 'true');
322 |
323 | case 'string':
324 | return this.objectSetDeep(window.alpines[alpineIndex].__x.$data, context, value);
325 | }
326 | },
327 |
328 | removeItemFromArray(type, alpineIndex, key, value, scope, context = '') {
329 | return this.objectSpliceDeep(window.alpines[alpineIndex].__x.$data, context);
330 | },
331 |
332 | getItem(key, value, alpineIndex = null, scope = '', context = '') {
333 | const id = Date.now() + Math.floor(Math.random() * 1000000);
334 | const type = this.getType(value);
335 | scope = scope ? `${scope}.${type}` : type;
336 | context = context ? `${context}.${key}` : key;
337 | return `
338 |
339 | ${this.getGutterAction(id, type, alpineIndex, key, value, scope, context)}
340 |
341 |
342 | :
343 | ${this.getProperyTypeMessage(id, type, alpineIndex, key, value, scope, context)}
344 |
345 | ${this.getGutterExtraRight(id, type, alpineIndex, key, value, scope, context)}
346 |
347 |
350 |
357 | ${this.getValue(id, type, alpineIndex, key, value, scope, context)}
358 |
359 | ${this.getEditField(id, type, alpineIndex, key, value, scope, context)}
360 |
361 | `;
362 | },
363 |
364 | getItemRowStyling(id, type, alpineIndex, key, value, scope, context = '') {
365 | switch (type) {
366 | case 'string':
367 | return `relative py-1 pl-1.5 flex items-start`;
368 |
369 | case 'array':
370 | return `relative py-1 pl-1.5 inline-block`;
371 | }
372 |
373 | return `relative py-1 pl-1.5 inline-block`;
374 | },
375 |
376 | getEditField(id, type, alpineIndex, key, value, scope, context = '') {
377 | if (scope.endsWith('array.string') || scope === 'string') {
378 | return `
394 | ${this.escapeHTML(value)}
395 | `;
396 | }
397 |
398 | return '';
399 | },
400 |
401 | getGutterAction(id, type, alpineIndex, key, value, scope, context = '') {
402 | const wrap = content => `${content}`;
403 |
404 | const wrapTight = content => `${content}`;
405 |
406 | switch (type) {
407 | case 'boolean':
408 | return wrap(`
409 | `);
415 |
416 | case 'string':
417 | if ('string' !== scope && !scope.endsWith('array.string')) return '';
418 | const deleteButton = `
419 | `;
428 | const editButton = `
429 | `;
438 | return scope.endsWith('array.string') ? wrapTight(deleteButton + editButton) : wrap(editButton);
439 |
440 | case 'array':
441 | return wrap(`
442 | `);
459 |
460 | default:
461 | return '';
462 | }
463 | },
464 |
465 | getGutterExtraRight(id, type, alpineIndex, key, value, scope, context = '') {
466 | switch (type) {
467 | case 'array':
468 | return `
469 |
475 |
486 | `;
487 |
488 | case 'string':
489 | if (!scope.endsWith('array.string')) return '';
490 | return '';
491 | }
492 |
493 | return '';
494 | },
495 |
496 | getProperyTypeMessage(id, type, alpineIndex, key, value, scope) {
497 | let wrap = content => `${content}`;
498 |
499 | switch (type) {
500 | case 'string':
501 | return '';
502 |
503 | case 'array':
504 | if (this.collapsedArrays.includes(`${alpineIndex}:${key}`)) {
505 | return wrap(`array[${value.length}]`);
506 | }
507 |
508 | return wrap`array`;
509 | }
510 |
511 | return wrap(type);
512 | },
513 |
514 | openEditorAndSelectText(id, type, alpineIndex, key, value, scope, context) {
515 | // If the editor is already open and they press the edit icon again, commit the edit
516 | if (this.editing === `${alpineIndex}-${key}`) {
517 | this.updateAlpine('string', alpineIndex, key, this.$refs[`editor-${alpineIndex}-${key}`].textContent.trim(), scope, context);
518 | this.editing = '';
519 | return;
520 | }
521 |
522 | this.editing = `${alpineIndex}-${key}`;
523 | this.$nextTick(() => {
524 | this.editing && this.$refs[`editor-${alpineIndex}-${key}`].focus();
525 | this.editing && document.execCommand('selectAll', false, null);
526 | });
527 | },
528 |
529 | getValue(id, type, alpineIndex, key, value, scope, context = '') {
530 | switch (type) {
531 | case 'function':
532 | return '';
533 |
534 | case 'boolean':
535 | return value;
536 |
537 | case 'number':
538 | return value;
539 |
540 | case 'string':
541 | if (!['string', 'array.string'].includes(scope)) {
542 | return `"${this.escapeHTML(value)}"`;
543 | }
544 |
545 | return `
548 | "${this.escapeHTML(value)}"
549 | `;
550 |
551 | case 'array':
552 | if (!value) return value;
553 | return Object.entries(value).map(([...item]) => {
554 | return `${this.getItem(item[0], item[1], alpineIndex, scope, context)}
`;
555 | }).join('');
556 |
557 | case 'object':
558 | if (!value) return value;
559 | return Object.entries(value).map(([...item]) => {
560 | return `${this.getItem(item[0], item[1], alpineIndex, scope, context)}
`;
561 | }).join('');
562 | }
563 |
564 | return value;
565 | },
566 |
567 | getStatusMessage() {
568 | if (this.editing.length) {
569 | return 'Press Enter or click away to finish. Press Esc to cancel.';
570 | }
571 |
572 | return `
573 | Watching ${window.alpines.length} components...
574 |
585 | `;
586 | },
587 |
588 | convertFunctionName(functionName) {
589 | if (functionName.indexOf('{') === 0) return;
590 | let name = functionName.replace(/\(([^\)]+)\)/, '').replace('()', '').replace(/([A-Z])/g, " $1");
591 | return name ? name.charAt(0).toUpperCase() + name.slice(1) : '';
592 | },
593 |
594 | escapeHTML(html) {
595 | let div = document.createElement('div');
596 | div.innerText = html;
597 | return div.innerHTML;
598 | },
599 |
600 | // Borrowed from https://stackoverflow.com/a/54733755/1437789
601 | objectSetDeep(object, path, value) {
602 | path = path.toString().match(/[^.[\]]+/g) || []; // Iterate all of them except the last one
603 |
604 | path.slice(0, -1).reduce((a, currentKey, index) => {
605 | // If the key does not exist or its value is not an object, create/override the key
606 | if (Object(a[currentKey]) !== a[currentKey]) {
607 | // Is the next key a potential array-index?
608 | a[currentKey] = Math.abs(path[index + 1]) >> 0 === +path[index + 1] ? [] // Yes: assign a new array object
609 | : {}; // No: assign a new plain object
610 | }
611 |
612 | return a[currentKey];
613 | }, object)[path[path.length - 1]] = value; // Finally assign the value to the last key
614 |
615 | return object;
616 | },
617 |
618 | objectSpliceDeep(object, path) {
619 | path = path.toString().match(/[^.[\]]+/g) || [];
620 | path.slice(0, -1).reduce((a, currentKey, index) => {
621 | return a[currentKey];
622 | }, object).splice(path[path.length - 1], 1);
623 | return object;
624 | }
625 |
626 | };
627 | };
628 |
629 | function styleInject(css, ref) {
630 | if ( ref === void 0 ) ref = {};
631 | var insertAt = ref.insertAt;
632 |
633 | if (!css || typeof document === 'undefined') { return; }
634 |
635 | var head = document.head || document.getElementsByTagName('head')[0];
636 | var style = document.createElement('style');
637 | style.type = 'text/css';
638 |
639 | if (insertAt === 'top') {
640 | if (head.firstChild) {
641 | head.insertBefore(style, head.firstChild);
642 | } else {
643 | head.appendChild(style);
644 | }
645 | } else {
646 | head.appendChild(style);
647 | }
648 |
649 | if (style.styleSheet) {
650 | style.styleSheet.cssText = css;
651 | } else {
652 | style.appendChild(document.createTextNode(css));
653 | }
654 | }
655 |
656 | var css_248z = "/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n\n/**\n * Manually forked from SUIT CSS Base: https://github.com/suitcss/base\n * A thin layer on top of normalize.css that provides a starting point more\n * suitable for web applications.\n */\n\n/**\n * Removes the default spacing and border for appropriate elements.\n */\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nbutton {\n background-color: transparent;\n background-image: none;\n}\n\n/**\n * Work around a Firefox/IE bug where the transparent `button` background\n * results in a loss of the default `button` focus styles.\n */\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/**\n * Tailwind custom reset styles\n */\n\n/**\n * 1. Use the user's configured `sans` font-family (with Tailwind's default\n * sans-serif font stack as a fallback) as a sane default.\n * 2. Use Tailwind's default \"normal\" line-height so the user isn't forced\n * to override it to ensure consistency even when using the default theme.\n */\n\nhtml {\n font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 1 */\n line-height: 1.5; /* 2 */\n}\n\n/**\n * 1. Prevent padding and border from affecting element width.\n *\n * We used to set this in the html element and inherit from\n * the parent element for everything else. This caused issues\n * in shadow-dom-enhanced elements like where the content\n * is wrapped by a div with box-sizing set to `content-box`.\n *\n * https://github.com/mozdevs/cssremedy/issues/4\n *\n *\n * 2. Allow adding a border to an element by just adding a border-width.\n *\n * By default, the way the browser specifies that an element should have no\n * border is by setting it's border-style to `none` in the user-agent\n * stylesheet.\n *\n * In order to easily add borders to elements by just setting the `border-width`\n * property, we change the default border-style for all elements to `solid`, and\n * use border-width to hide them instead. This way our `border` utilities only\n * need to set the `border-width` property instead of the entire `border`\n * shorthand, making our border utilities much more straightforward to compose.\n *\n * https://github.com/tailwindcss/tailwindcss/pull/116\n */\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e2e8f0; /* 2 */\n}\n\n/*\n * Ensure horizontal rules are visible by default\n */\n\nhr {\n border-top-width: 1px;\n}\n\n/**\n * Undo the `border-style: none` reset that Normalize applies to images so that\n * our `border-{width}` utilities have the expected effect.\n *\n * The Normalize reset is unnecessary for us since we default the border-width\n * to 0 on all elements.\n *\n * https://github.com/tailwindcss/tailwindcss/issues/362\n */\n\nimg {\n border-style: solid;\n}\n\ntextarea {\n resize: vertical;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #a0aec0;\n}\n\ninput:-ms-input-placeholder, textarea:-ms-input-placeholder {\n color: #a0aec0;\n}\n\ninput::-ms-input-placeholder, textarea::-ms-input-placeholder {\n color: #a0aec0;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n color: #a0aec0;\n}\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\ntable {\n border-collapse: collapse;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/**\n * Reset links to optimize for opt-in styling instead of\n * opt-out.\n */\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/**\n * Reset form element properties that are easy to forget to\n * style explicitly so you don't inadvertently introduce\n * styles that deviate from your design system. These styles\n * supplement a partial reset that is already applied by\n * normalize.css.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n padding: 0;\n line-height: inherit;\n color: inherit;\n}\n\n/**\n * Use the configured 'mono' font family for elements that\n * are expected to be rendered with a monospace font, falling\n * back to the system monospace stack if there is no configured\n * 'mono' font family.\n */\n\npre,\ncode,\nkbd,\nsamp {\n font-family: Fira Code, monospace;\n}\n\n/**\n * Make replaced elements `display: block` by default as that's\n * the behavior you want almost all of the time. Inspired by\n * CSS Remedy, with `svg` added as well.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block;\n vertical-align: middle;\n}\n\n/**\n * Constrain images and videos to the parent width and preserve\n * their instrinsic aspect ratio.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n.container {\n width: 100%;\n}\n\n@media (min-width: 640px) {\n .container {\n max-width: 640px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 768px;\n }\n}\n\n@media (min-width: 1024px) {\n .container {\n max-width: 1024px;\n }\n}\n\n@media (min-width: 1280px) {\n .container {\n max-width: 1280px;\n }\n}\n\n.space-x-2 > :not(template) ~ :not(template) {\n --space-x-reverse: 0;\n margin-right: calc(0.5rem * var(--space-x-reverse));\n margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));\n}\n\n.divide-y-2 > :not(template) ~ :not(template) {\n --divide-y-reverse: 0;\n border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));\n border-bottom-width: calc(2px * var(--divide-y-reverse));\n}\n\n.divide-component-divider > :not(template) ~ :not(template) {\n --divide-opacity: 1;\n border-color: #44475a;\n border-color: rgba(68, 71, 90, var(--divide-opacity));\n}\n\n.bg-background {\n --bg-opacity: 1;\n background-color: #282a36;\n background-color: rgba(40, 42, 54, var(--bg-opacity));\n}\n\n.bg-typeof-bg {\n background-color: transparent;\n}\n\n.bg-string-editor-bg {\n --bg-opacity: 1;\n background-color: #44475a;\n background-color: rgba(68, 71, 90, var(--bg-opacity));\n}\n\n.border-container-border {\n border-color: transparent;\n}\n\n.border-component-divider {\n --border-opacity: 1;\n border-color: #44475a;\n border-color: rgba(68, 71, 90, var(--border-opacity));\n}\n\n.border {\n border-width: 1px;\n}\n\n.border-t {\n border-top-width: 1px;\n}\n\n.block {\n display: block;\n}\n\n.inline-block {\n display: inline-block;\n}\n\n.inline {\n display: inline;\n}\n\n.flex {\n display: flex;\n}\n\n.inline-flex {\n display: inline-flex;\n}\n\n.table {\n display: table;\n}\n\n.hidden {\n display: none;\n}\n\n.flex-col {\n flex-direction: column;\n}\n\n.items-start {\n align-items: flex-start;\n}\n\n.items-center {\n align-items: center;\n}\n\n.justify-end {\n justify-content: flex-end;\n}\n\n.justify-between {\n justify-content: space-between;\n}\n\n.font-mono {\n font-family: Fira Code, monospace;\n}\n\n.font-extrabold {\n font-weight: 800;\n}\n\n.text-xs {\n font-size: 0.75rem;\n}\n\n.text-sm {\n font-size: 0.875rem;\n}\n\n.leading-none {\n line-height: 1;\n}\n\n.leading-relaxed {\n line-height: 1.625;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n\n.ml-1 {\n margin-left: 0.25rem;\n}\n\n.mb-5 {\n margin-bottom: 1.25rem;\n}\n\n.mt-px {\n margin-top: 1px;\n}\n\n.-ml-3 {\n margin-left: -0.75rem;\n}\n\n.-mt-5 {\n margin-top: -1.25rem;\n}\n\n.-ml-7 {\n margin-left: -1.75rem;\n}\n\n.-mt-px {\n margin-top: -1px;\n}\n\n.-ml-3\\.5 {\n margin-left: -0.875rem;\n}\n\n.min-h-full {\n min-height: 100%;\n}\n\n.opacity-0 {\n opacity: 0;\n}\n\n.group:hover .group-hover\\:opacity-100 {\n opacity: 1;\n}\n\n.focus\\:outline-none:focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n\n.overflow-y-hidden {\n overflow-y: hidden;\n}\n\n.p-1 {\n padding: 0.25rem;\n}\n\n.p-2 {\n padding: 0.5rem;\n}\n\n.p-1\\.5 {\n padding: 0.375rem;\n}\n\n.py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\n\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.py-5 {\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n}\n\n.pl-1 {\n padding-left: 0.25rem;\n}\n\n.pl-2 {\n padding-left: 0.5rem;\n}\n\n.pl-4 {\n padding-left: 1rem;\n}\n\n.pl-1\\.5 {\n padding-left: 0.375rem;\n}\n\n.fixed {\n position: fixed;\n}\n\n.absolute {\n position: absolute;\n}\n\n.relative {\n position: relative;\n}\n\n.right-0 {\n right: 0;\n}\n\n.bottom-0 {\n bottom: 0;\n}\n\n.left-0 {\n left: 0;\n}\n\n.top-1 {\n top: 0.25rem;\n}\n\n.stroke-current {\n stroke: currentColor;\n}\n\n.text-component-title {\n --text-opacity: 1;\n color: #ff79c6;\n color: rgba(255, 121, 198, var(--text-opacity));\n}\n\n.text-status-text {\n --text-opacity: 1;\n color: #6272a4;\n color: rgba(98, 114, 164, var(--text-opacity));\n}\n\n.text-property-name-color {\n --text-opacity: 1;\n color: #50fa7b;\n color: rgba(80, 250, 123, var(--text-opacity));\n}\n\n.text-property-seperator-color {\n --text-opacity: 1;\n color: #8be9fd;\n color: rgba(139, 233, 253, var(--text-opacity));\n}\n\n.text-typeof-color {\n --text-opacity: 1;\n color: #bd93f9;\n color: rgba(189, 147, 249, var(--text-opacity));\n}\n\n.text-value-color {\n --text-opacity: 1;\n color: #f8f8f2;\n color: rgba(248, 248, 242, var(--text-opacity));\n}\n\n.text-string-editor-color {\n --text-opacity: 1;\n color: #f8f8f2;\n color: rgba(248, 248, 242, var(--text-opacity));\n}\n\n.text-icon-color {\n --text-opacity: 1;\n color: #f1fa8c;\n color: rgba(241, 250, 140, var(--text-opacity));\n}\n\n.hover\\:text-status-text-hover:hover {\n --text-opacity: 1;\n color: #8be9fd;\n color: rgba(139, 233, 253, var(--text-opacity));\n}\n\n.whitespace-no-wrap {\n white-space: nowrap;\n}\n\n.w-4 {\n width: 1rem;\n}\n\n.w-full {\n width: 100%;\n}\n\n.z-10 {\n z-index: 10;\n}\n\n.z-30 {\n z-index: 30;\n}\n\n.z-50 {\n z-index: 50;\n}\n\n.transform {\n --transform-translate-x: 0;\n --transform-translate-y: 0;\n --transform-rotate: 0;\n --transform-skew-x: 0;\n --transform-skew-y: 0;\n --transform-scale-x: 1;\n --transform-scale-y: 1;\n transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));\n}\n\n.-rotate-90 {\n --transform-rotate: -90deg;\n}\n\n.transition {\n transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;\n}\n\n.duration-200 {\n transition-duration: 200ms;\n}\n\n@-webkit-keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@-webkit-keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@-webkit-keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@-webkit-keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n@keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n@media (min-width: 640px) {\n .sm\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .sm\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .sm\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .sm\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .sm\\:container {\n max-width: 1280px;\n }\n }\n}\n\n@media (min-width: 768px) {\n .md\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .md\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .md\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .md\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .md\\:container {\n max-width: 1280px;\n }\n }\n}\n\n@media (min-width: 1024px) {\n .lg\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .lg\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .lg\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .lg\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .lg\\:container {\n max-width: 1280px;\n }\n }\n}\n\n@media (min-width: 1280px) {\n .xl\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .xl\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .xl\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .xl\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .xl\\:container {\n max-width: 1280px;\n }\n }\n}\n";
657 | styleInject(css_248z);
658 |
659 | window.alpineDevToolsThemeDracula = css_248z;
660 |
661 | const alpine = window.deferLoadingAlpine || (alpine => alpine());
662 |
663 | window.deferLoadingAlpine = callback => {
664 | alpine(callback);
665 | DevTools.start(Loader, Viewer, css_248z);
666 | };
667 |
668 | })));
669 |
--------------------------------------------------------------------------------
/dist/Hacktoberfest2020.js:
--------------------------------------------------------------------------------
1 | (function (factory) {
2 | typeof define === 'function' && define.amd ? define(factory) :
3 | factory();
4 | }((function () { 'use strict';
5 |
6 | const DevTools = {
7 | start(Loader, Viewer, theme) {
8 | window.alpineDevTools = {
9 | version: '0.11.1',
10 | Viewer: Viewer,
11 | Loader: Loader,
12 | theme: theme
13 | };
14 | window.addEventListener('DOMContentLoaded', () => {
15 | this.injectDevToolsHandler(); // A button is on the page already. use that instead
16 |
17 | const button = document.getElementById('alpine-devtools-button');
18 |
19 | if (button) {
20 | button.addEventListener('click', () => {
21 | window.dispatchEvent(new CustomEvent('open-alpine-devtools-popup', {
22 | bubbles: true
23 | }));
24 | });
25 | }
26 |
27 | if (sessionStorage.getItem('alpine-devtools') !== 'Popup') {
28 | setTimeout(() => {
29 | window.dispatchEvent(new CustomEvent('open-alpine-devtools', {
30 | bubbles: true
31 | }));
32 | }, 0);
33 | }
34 | });
35 | },
36 |
37 | injectDevToolsHandler() {
38 | const alpineDevToolsComponent = document.createElement('div');
39 | alpineDevToolsComponent.id = 'alpine-devtools';
40 | alpineDevToolsComponent.setAttribute('x-data', 'window.alpineDevTools.Loader(window.alpineDevTools.Viewer, window.alpineDevTools.theme)');
41 | alpineDevToolsComponent.setAttribute('x-devtools-ignore', '');
42 | alpineDevToolsComponent.setAttribute('x-on:open-alpine-devtools.window', 'openIframe()');
43 | alpineDevToolsComponent.setAttribute('x-on:open-alpine-devtools-popup.window', 'openPopup()');
44 | alpineDevToolsComponent.setAttribute('x-on:focus-alpine-devtools.window', 'focusDevTools()');
45 | alpineDevToolsComponent.setAttribute('x-on:alpine-devtools-switch-theme.window', 'setTheme($event.detail)');
46 | alpineDevToolsComponent.setAttribute('x-init', '$nextTick(() => { start() })');
47 | alpineDevToolsComponent.style.cssText = "display:none!important";
48 | document.body.appendChild(alpineDevToolsComponent);
49 | }
50 |
51 | };
52 |
53 | const Loader = function (Viewer, theme) {
54 | return {
55 | alpines: [],
56 | open: false,
57 | observer: null,
58 | windowRef: null,
59 | viewerScript: Viewer,
60 | viewer: null,
61 | theme: theme,
62 | iframe: null,
63 | type: 'Iframe',
64 | sessionId: Date.now() + Math.floor(Math.random() * 1000000),
65 |
66 | start() {
67 | this.alpines = document.querySelectorAll('[x-data]:not([x-devtools-ignore])');
68 | if (!this.alpines) return;
69 | this.registerAlpines(this.alpines); // If the window is already open, refresh it
70 |
71 | if (sessionStorage.getItem('alpine-devtools')) {
72 | this.type = sessionStorage.getItem('alpine-devtools'); // If viewer is already there, only update
73 |
74 | this.viewer || this.load();
75 | this.updateAlpines();
76 | }
77 | },
78 |
79 | focusDevTools() {
80 | if (!this.windowRef) {
81 | this[`open${this.type}`]();
82 | }
83 |
84 | this.windowRef.focus();
85 | },
86 |
87 | registerAlpines(alpines) {
88 | this.observer = new MutationObserver(mutations => {
89 | this.updateAlpines();
90 | });
91 | alpines.forEach(alpine => {
92 | // This will trigger a mutation when internal data changes but no visual mutation is observed
93 | alpine.setAttribute('x-bind:data-last-refresh', 'Date.now()');
94 | this.observer.observe(alpine, {
95 | attributes: true,
96 | childList: true,
97 | characterData: true,
98 | subtree: true
99 | });
100 | });
101 | },
102 |
103 | updateAlpines() {
104 | this.checkIfNewAlpinesWereAddedAndRegisterThem();
105 |
106 | if (this.windowRef) {
107 | this.windowRef.alpines = this.alpines;
108 | const viewer = this.windowRef.document.querySelector('#alpine-devtools-viewer');
109 | if (!viewer) return;
110 | if (viewer.__x.$data.editing.length) return;
111 | typeof viewer.__x !== 'undefined' && viewer.__x.updateElements(viewer);
112 | }
113 | },
114 |
115 | checkIfNewAlpinesWereAddedAndRegisterThem() {
116 | const fresh = [...document.querySelectorAll('[x-data]:not([x-devtools-ignore])')];
117 | const newAlpines = fresh.filter(alpine => {
118 | return ![...this.alpines].includes(alpine);
119 | });
120 |
121 | if (newAlpines) {
122 | this.alpines = document.querySelectorAll('[x-data]:not([x-devtools-ignore])');
123 | this.registerAlpines(newAlpines);
124 | }
125 | },
126 |
127 | openIframe() {
128 | this.iframe = document.getElementById('alpine-devtools-iframe');
129 |
130 | if (!this.iframe) {
131 | const state = sessionStorage.getItem('alpine-devtools-iframe-state');
132 | this.iframe = document.createElement('iframe');
133 | this.iframe.id = 'alpine-devtools-iframe';
134 | this.iframe.width = 450;
135 | this.iframe.height = 650;
136 | this.iframe.setAttribute('x-data', state ? state : '{height: 650, margin: "0.75rem"}');
137 | this.iframe.setAttribute('x-devtools-ignore', '');
138 | this.iframe.setAttribute('x-on:collapse-devtools.window', 'height = height === 650 ? 25 : 650;margin = margin === "0.75rem" ? "0" : "0.75rem";sessionStorage.setItem("alpine-devtools-iframe-state", `{height: ${height}, margin: "${margin}"}`)');
139 | this.iframe.setAttribute('x-bind:style', '`position:fixed;box-shadow:0 25px 50px -12px rgba(0,0,0,.25)!important;bottom:0!important;right:0!important;margin:${margin}!important;background-color:#252f3f!important;height: ${height}px!important`');
140 | document.body.appendChild(this.iframe);
141 | }
142 |
143 | this.windowRef = this.iframe.contentWindow;
144 | this.type = 'Iframe';
145 | this.load();
146 | },
147 |
148 | openPopup() {
149 | if (this.iframe) {
150 | this.iframe.parentNode.removeChild(this.iframe);
151 | this.iframe = null;
152 | }
153 |
154 | this.windowRef = window.open('', 'alpine-devtools', 'width=450, height=650, left=100, top=100');
155 | this.type = 'Popup';
156 | this.load();
157 | },
158 |
159 | load() {
160 | if (!this.windowRef) {
161 | sessionStorage.removeItem('alpine-devtools');
162 | this[`open${this.type}`]();
163 | } // Add the reference to the session so we don't need to reopen the popup everytime
164 |
165 |
166 | sessionStorage.setItem('alpine-devtools', this.type); // Starting color. Consider some loading animation
167 |
168 | this.windowRef.document.body.style.backgroundColor = '#1a202c';
169 | this.windowRef.document.body.style.height = '100%';
170 | this.windowRef.document.body.innerHTML = '';
171 | this.windowRef.document.title = 'Alpine DevTools';
172 | this.windowRef.alpines = this.alpines; // TODO: take in AlpineJS version option
173 |
174 | if (!this.windowRef.document.getElementById('alpine-devtools-script')) {
175 | const alpineScript = this.windowRef.document.createElement('script');
176 | alpineScript.id = 'alpine-devtools-script';
177 | const version = window.Alpine.version || '2.x.x';
178 | alpineScript.src = `https://cdn.jsdelivr.net/gh/alpinejs/alpine@v${version}/dist/alpine.min.js`;
179 | this.windowRef.document.head.appendChild(alpineScript);
180 | } // TODO: This should eventually be settable somehow
181 |
182 |
183 | if (!this.windowRef.document.getElementById('alpine-devtools-font')) {
184 | const font = this.windowRef.document.createElement('link');
185 | font.id = 'alpine-devtools-font';
186 | font.rel = 'stylesheet';
187 | font.href = "https://fonts.googleapis.com/css2?family=Fira+Code&display=swap";
188 | this.windowRef.document.head.appendChild(font);
189 | }
190 |
191 | this.setTheme(this.theme); // Add the dev tools component function and remove any previous
192 |
193 | const oldScript = this.windowRef.document.getElementById('devtools-script');
194 | oldScript && oldScript.remove();
195 | const devtoolsScript = this.windowRef.document.createElement('script');
196 | devtoolsScript.id = 'devtools-script';
197 | devtoolsScript.textContent = `window.Viewer = ${this.viewerScript}`;
198 | this.windowRef.document.head.appendChild(devtoolsScript);
199 | this.loadView();
200 | },
201 |
202 | setTheme(theme) {
203 | if (!this.windowRef) {
204 | this[`open${this.type}`]();
205 | }
206 |
207 | this.theme = theme ? theme : this.theme; // Add the theme and remove any previous (Can possibly support theme swapping)
208 |
209 | const oldTheme = this.windowRef.document.getElementById('alpine-devtools-theme');
210 | oldTheme && oldTheme.remove();
211 | const devtoolsTheme = this.windowRef.document.createElement('style');
212 | devtoolsTheme.id = 'alpine-devtools-theme';
213 | devtoolsTheme.textContent = this.theme;
214 | this.windowRef.document.head.appendChild(devtoolsTheme);
215 | },
216 |
217 | loadView() {
218 | this.viewer = this.windowRef.document.createElement('div');
219 | this.viewer.id = 'alpine-devtools-viewer';
220 | this.viewer.setAttribute('x-data', `window.Viewer('${this.type}')`);
221 | this.viewer.setAttribute('x-init', 'setup()');
222 | this.viewer.setAttribute('x-on:open-alpine-devtools-popup.window', `
223 | window.parent.dispatchEvent(new CustomEvent('open-alpine-devtools-popup', {
224 | bubbles: true,
225 | event: 'Popup'
226 | }))`);
227 | this.viewer.setAttribute('x-init', 'setup()');
228 | this.open = true;
229 | window.alpineDevTools.open = true;
230 | setTimeout(() => {
231 | this.windowRef.document.body.appendChild(this.viewer);
232 | }, 500);
233 | this.windowRef.addEventListener('beforeunload', () => {
234 | sessionStorage.removeItem('alpine-devtools');
235 | this.type === 'popup' && this.windowRef.close();
236 | this.windowRef = null;
237 | this.open = false;
238 | window.alpineDevTools.open = false;
239 | });
240 | }
241 |
242 | };
243 | };
244 |
245 | const Viewer = function (type) {
246 | return {
247 | editing: '',
248 | type: type,
249 | collapsedArrays: [],
250 |
251 | // TODO: persist this??
252 | setup() {
253 | this.$el.innerHTML = `
257 |
261 |
281 |
287 |
288 |
`;
289 | },
290 |
291 | computeTitle(alpine) {
292 | return alpine.getAttribute('x-title') || alpine.getAttribute('aria-label') || alpine.getAttribute('x-id') || alpine.id || this.convertFunctionName(alpine.getAttribute('x-data')) || `<${alpine.tagName.toLowerCase()}>`;
293 | },
294 |
295 | getAlpineData(alpine) {
296 | if (!alpine.__x) return [];
297 | return Object.entries(alpine.__x.getUnobservedData());
298 | },
299 |
300 | getType(value) {
301 | // Leave as object for now until we need it
302 | // if (value === null) {
303 | // return 'null'
304 | // }
305 | if (Array.isArray(value)) {
306 | return 'array';
307 | }
308 |
309 | if (typeof value === 'function') {
310 | return 'function';
311 | }
312 |
313 | return typeof value;
314 | },
315 |
316 | updateAlpine(type, alpineIndex, key, value, scope, context = '') {
317 | let scopes = scope.split('.'); // Just focus on the last one
318 |
319 | switch (scopes[scopes.length - 1]) {
320 | case 'boolean':
321 | return this.objectSetDeep(window.alpines[alpineIndex].__x.$data, context, value !== 'true');
322 |
323 | case 'string':
324 | return this.objectSetDeep(window.alpines[alpineIndex].__x.$data, context, value);
325 | }
326 | },
327 |
328 | removeItemFromArray(type, alpineIndex, key, value, scope, context = '') {
329 | return this.objectSpliceDeep(window.alpines[alpineIndex].__x.$data, context);
330 | },
331 |
332 | getItem(key, value, alpineIndex = null, scope = '', context = '') {
333 | const id = Date.now() + Math.floor(Math.random() * 1000000);
334 | const type = this.getType(value);
335 | scope = scope ? `${scope}.${type}` : type;
336 | context = context ? `${context}.${key}` : key;
337 | return `
338 |
339 | ${this.getGutterAction(id, type, alpineIndex, key, value, scope, context)}
340 |
341 |
342 | :
343 | ${this.getProperyTypeMessage(id, type, alpineIndex, key, value, scope, context)}
344 |
345 | ${this.getGutterExtraRight(id, type, alpineIndex, key, value, scope, context)}
346 |
347 |
350 |
357 | ${this.getValue(id, type, alpineIndex, key, value, scope, context)}
358 |
359 | ${this.getEditField(id, type, alpineIndex, key, value, scope, context)}
360 |
361 | `;
362 | },
363 |
364 | getItemRowStyling(id, type, alpineIndex, key, value, scope, context = '') {
365 | switch (type) {
366 | case 'string':
367 | return `relative py-1 pl-1.5 flex items-start`;
368 |
369 | case 'array':
370 | return `relative py-1 pl-1.5 inline-block`;
371 | }
372 |
373 | return `relative py-1 pl-1.5 inline-block`;
374 | },
375 |
376 | getEditField(id, type, alpineIndex, key, value, scope, context = '') {
377 | if (scope.endsWith('array.string') || scope === 'string') {
378 | return `
394 | ${this.escapeHTML(value)}
395 | `;
396 | }
397 |
398 | return '';
399 | },
400 |
401 | getGutterAction(id, type, alpineIndex, key, value, scope, context = '') {
402 | const wrap = content => `${content}`;
403 |
404 | const wrapTight = content => `${content}`;
405 |
406 | switch (type) {
407 | case 'boolean':
408 | return wrap(`
409 | `);
415 |
416 | case 'string':
417 | if ('string' !== scope && !scope.endsWith('array.string')) return '';
418 | const deleteButton = `
419 | `;
428 | const editButton = `
429 | `;
438 | return scope.endsWith('array.string') ? wrapTight(deleteButton + editButton) : wrap(editButton);
439 |
440 | case 'array':
441 | return wrap(`
442 | `);
459 |
460 | default:
461 | return '';
462 | }
463 | },
464 |
465 | getGutterExtraRight(id, type, alpineIndex, key, value, scope, context = '') {
466 | switch (type) {
467 | case 'array':
468 | return `
469 |
475 |
486 | `;
487 |
488 | case 'string':
489 | if (!scope.endsWith('array.string')) return '';
490 | return '';
491 | }
492 |
493 | return '';
494 | },
495 |
496 | getProperyTypeMessage(id, type, alpineIndex, key, value, scope) {
497 | let wrap = content => `${content}`;
498 |
499 | switch (type) {
500 | case 'string':
501 | return '';
502 |
503 | case 'array':
504 | if (this.collapsedArrays.includes(`${alpineIndex}:${key}`)) {
505 | return wrap(`array[${value.length}]`);
506 | }
507 |
508 | return wrap`array`;
509 | }
510 |
511 | return wrap(type);
512 | },
513 |
514 | openEditorAndSelectText(id, type, alpineIndex, key, value, scope, context) {
515 | // If the editor is already open and they press the edit icon again, commit the edit
516 | if (this.editing === `${alpineIndex}-${key}`) {
517 | this.updateAlpine('string', alpineIndex, key, this.$refs[`editor-${alpineIndex}-${key}`].textContent.trim(), scope, context);
518 | this.editing = '';
519 | return;
520 | }
521 |
522 | this.editing = `${alpineIndex}-${key}`;
523 | this.$nextTick(() => {
524 | this.editing && this.$refs[`editor-${alpineIndex}-${key}`].focus();
525 | this.editing && document.execCommand('selectAll', false, null);
526 | });
527 | },
528 |
529 | getValue(id, type, alpineIndex, key, value, scope, context = '') {
530 | switch (type) {
531 | case 'function':
532 | return '';
533 |
534 | case 'boolean':
535 | return value;
536 |
537 | case 'number':
538 | return value;
539 |
540 | case 'string':
541 | if (!['string', 'array.string'].includes(scope)) {
542 | return `"${this.escapeHTML(value)}"`;
543 | }
544 |
545 | return `
548 | "${this.escapeHTML(value)}"
549 | `;
550 |
551 | case 'array':
552 | if (!value) return value;
553 | return Object.entries(value).map(([...item]) => {
554 | return `${this.getItem(item[0], item[1], alpineIndex, scope, context)}
`;
555 | }).join('');
556 |
557 | case 'object':
558 | if (!value) return value;
559 | return Object.entries(value).map(([...item]) => {
560 | return `${this.getItem(item[0], item[1], alpineIndex, scope, context)}
`;
561 | }).join('');
562 | }
563 |
564 | return value;
565 | },
566 |
567 | getStatusMessage() {
568 | if (this.editing.length) {
569 | return 'Press Enter or click away to finish. Press Esc to cancel.';
570 | }
571 |
572 | return `
573 | Watching ${window.alpines.length} components...
574 |
585 | `;
586 | },
587 |
588 | convertFunctionName(functionName) {
589 | if (functionName.indexOf('{') === 0) return;
590 | let name = functionName.replace(/\(([^\)]+)\)/, '').replace('()', '').replace(/([A-Z])/g, " $1");
591 | return name ? name.charAt(0).toUpperCase() + name.slice(1) : '';
592 | },
593 |
594 | escapeHTML(html) {
595 | let div = document.createElement('div');
596 | div.innerText = html;
597 | return div.innerHTML;
598 | },
599 |
600 | // Borrowed from https://stackoverflow.com/a/54733755/1437789
601 | objectSetDeep(object, path, value) {
602 | path = path.toString().match(/[^.[\]]+/g) || []; // Iterate all of them except the last one
603 |
604 | path.slice(0, -1).reduce((a, currentKey, index) => {
605 | // If the key does not exist or its value is not an object, create/override the key
606 | if (Object(a[currentKey]) !== a[currentKey]) {
607 | // Is the next key a potential array-index?
608 | a[currentKey] = Math.abs(path[index + 1]) >> 0 === +path[index + 1] ? [] // Yes: assign a new array object
609 | : {}; // No: assign a new plain object
610 | }
611 |
612 | return a[currentKey];
613 | }, object)[path[path.length - 1]] = value; // Finally assign the value to the last key
614 |
615 | return object;
616 | },
617 |
618 | objectSpliceDeep(object, path) {
619 | path = path.toString().match(/[^.[\]]+/g) || [];
620 | path.slice(0, -1).reduce((a, currentKey, index) => {
621 | return a[currentKey];
622 | }, object).splice(path[path.length - 1], 1);
623 | return object;
624 | }
625 |
626 | };
627 | };
628 |
629 | function styleInject(css, ref) {
630 | if ( ref === void 0 ) ref = {};
631 | var insertAt = ref.insertAt;
632 |
633 | if (!css || typeof document === 'undefined') { return; }
634 |
635 | var head = document.head || document.getElementsByTagName('head')[0];
636 | var style = document.createElement('style');
637 | style.type = 'text/css';
638 |
639 | if (insertAt === 'top') {
640 | if (head.firstChild) {
641 | head.insertBefore(style, head.firstChild);
642 | } else {
643 | head.appendChild(style);
644 | }
645 | } else {
646 | head.appendChild(style);
647 | }
648 |
649 | if (style.styleSheet) {
650 | style.styleSheet.cssText = css;
651 | } else {
652 | style.appendChild(document.createTextNode(css));
653 | }
654 | }
655 |
656 | var css_248z = "/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n\n/**\n * Manually forked from SUIT CSS Base: https://github.com/suitcss/base\n * A thin layer on top of normalize.css that provides a starting point more\n * suitable for web applications.\n */\n\n/**\n * Removes the default spacing and border for appropriate elements.\n */\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nbutton {\n background-color: transparent;\n background-image: none;\n}\n\n/**\n * Work around a Firefox/IE bug where the transparent `button` background\n * results in a loss of the default `button` focus styles.\n */\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nol,\nul {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/**\n * Tailwind custom reset styles\n */\n\n/**\n * 1. Use the user's configured `sans` font-family (with Tailwind's default\n * sans-serif font stack as a fallback) as a sane default.\n * 2. Use Tailwind's default \"normal\" line-height so the user isn't forced\n * to override it to ensure consistency even when using the default theme.\n */\n\nhtml {\n font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 1 */\n line-height: 1.5; /* 2 */\n}\n\n/**\n * 1. Prevent padding and border from affecting element width.\n *\n * We used to set this in the html element and inherit from\n * the parent element for everything else. This caused issues\n * in shadow-dom-enhanced elements like where the content\n * is wrapped by a div with box-sizing set to `content-box`.\n *\n * https://github.com/mozdevs/cssremedy/issues/4\n *\n *\n * 2. Allow adding a border to an element by just adding a border-width.\n *\n * By default, the way the browser specifies that an element should have no\n * border is by setting it's border-style to `none` in the user-agent\n * stylesheet.\n *\n * In order to easily add borders to elements by just setting the `border-width`\n * property, we change the default border-style for all elements to `solid`, and\n * use border-width to hide them instead. This way our `border` utilities only\n * need to set the `border-width` property instead of the entire `border`\n * shorthand, making our border utilities much more straightforward to compose.\n *\n * https://github.com/tailwindcss/tailwindcss/pull/116\n */\n\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e2e8f0; /* 2 */\n}\n\n/*\n * Ensure horizontal rules are visible by default\n */\n\nhr {\n border-top-width: 1px;\n}\n\n/**\n * Undo the `border-style: none` reset that Normalize applies to images so that\n * our `border-{width}` utilities have the expected effect.\n *\n * The Normalize reset is unnecessary for us since we default the border-width\n * to 0 on all elements.\n *\n * https://github.com/tailwindcss/tailwindcss/issues/362\n */\n\nimg {\n border-style: solid;\n}\n\ntextarea {\n resize: vertical;\n}\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n color: #a0aec0;\n}\n\ninput:-ms-input-placeholder, textarea:-ms-input-placeholder {\n color: #a0aec0;\n}\n\ninput::-ms-input-placeholder, textarea::-ms-input-placeholder {\n color: #a0aec0;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n color: #a0aec0;\n}\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\ntable {\n border-collapse: collapse;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/**\n * Reset links to optimize for opt-in styling instead of\n * opt-out.\n */\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/**\n * Reset form element properties that are easy to forget to\n * style explicitly so you don't inadvertently introduce\n * styles that deviate from your design system. These styles\n * supplement a partial reset that is already applied by\n * normalize.css.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n padding: 0;\n line-height: inherit;\n color: inherit;\n}\n\n/**\n * Use the configured 'mono' font family for elements that\n * are expected to be rendered with a monospace font, falling\n * back to the system monospace stack if there is no configured\n * 'mono' font family.\n */\n\npre,\ncode,\nkbd,\nsamp {\n font-family: Fira Code, monospace;\n}\n\n/**\n * Make replaced elements `display: block` by default as that's\n * the behavior you want almost all of the time. Inspired by\n * CSS Remedy, with `svg` added as well.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block;\n vertical-align: middle;\n}\n\n/**\n * Constrain images and videos to the parent width and preserve\n * their instrinsic aspect ratio.\n *\n * https://github.com/mozdevs/cssremedy/issues/14\n */\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n.container {\n width: 100%;\n}\n\n@media (min-width: 640px) {\n .container {\n max-width: 640px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 768px;\n }\n}\n\n@media (min-width: 1024px) {\n .container {\n max-width: 1024px;\n }\n}\n\n@media (min-width: 1280px) {\n .container {\n max-width: 1280px;\n }\n}\n\n.space-x-2 > :not(template) ~ :not(template) {\n --space-x-reverse: 0;\n margin-right: calc(0.5rem * var(--space-x-reverse));\n margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));\n}\n\n.divide-y-2 > :not(template) ~ :not(template) {\n --divide-y-reverse: 0;\n border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));\n border-bottom-width: calc(2px * var(--divide-y-reverse));\n}\n\n.divide-component-divider > :not(template) ~ :not(template) {\n --divide-opacity: 1;\n border-color: #183d5d;\n border-color: rgba(24, 61, 93, var(--divide-opacity));\n}\n\n.bg-background {\n --bg-opacity: 1;\n background-color: #072540;\n background-color: rgba(7, 37, 64, var(--bg-opacity));\n}\n\n.bg-typeof-bg {\n background-color: transparent;\n}\n\n.bg-string-editor-bg {\n --bg-opacity: 1;\n background-color: #183d5d;\n background-color: rgba(24, 61, 93, var(--bg-opacity));\n}\n\n.border-container-border {\n border-color: transparent;\n}\n\n.border-component-divider {\n --border-opacity: 1;\n border-color: #183d5d;\n border-color: rgba(24, 61, 93, var(--border-opacity));\n}\n\n.border {\n border-width: 1px;\n}\n\n.border-t {\n border-top-width: 1px;\n}\n\n.block {\n display: block;\n}\n\n.inline-block {\n display: inline-block;\n}\n\n.inline {\n display: inline;\n}\n\n.flex {\n display: flex;\n}\n\n.inline-flex {\n display: inline-flex;\n}\n\n.table {\n display: table;\n}\n\n.hidden {\n display: none;\n}\n\n.flex-col {\n flex-direction: column;\n}\n\n.items-start {\n align-items: flex-start;\n}\n\n.items-center {\n align-items: center;\n}\n\n.justify-end {\n justify-content: flex-end;\n}\n\n.justify-between {\n justify-content: space-between;\n}\n\n.font-mono {\n font-family: Fira Code, monospace;\n}\n\n.font-extrabold {\n font-weight: 800;\n}\n\n.text-xs {\n font-size: 0.75rem;\n}\n\n.text-sm {\n font-size: 0.875rem;\n}\n\n.leading-none {\n line-height: 1;\n}\n\n.leading-relaxed {\n line-height: 1.625;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n\n.ml-1 {\n margin-left: 0.25rem;\n}\n\n.mb-5 {\n margin-bottom: 1.25rem;\n}\n\n.mt-px {\n margin-top: 1px;\n}\n\n.-ml-3 {\n margin-left: -0.75rem;\n}\n\n.-mt-5 {\n margin-top: -1.25rem;\n}\n\n.-ml-7 {\n margin-left: -1.75rem;\n}\n\n.-mt-px {\n margin-top: -1px;\n}\n\n.-ml-3\\.5 {\n margin-left: -0.875rem;\n}\n\n.min-h-full {\n min-height: 100%;\n}\n\n.opacity-0 {\n opacity: 0;\n}\n\n.group:hover .group-hover\\:opacity-100 {\n opacity: 1;\n}\n\n.focus\\:outline-none:focus {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n\n.overflow-y-hidden {\n overflow-y: hidden;\n}\n\n.p-1 {\n padding: 0.25rem;\n}\n\n.p-2 {\n padding: 0.5rem;\n}\n\n.p-1\\.5 {\n padding: 0.375rem;\n}\n\n.py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\n\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.py-5 {\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n}\n\n.pl-1 {\n padding-left: 0.25rem;\n}\n\n.pl-2 {\n padding-left: 0.5rem;\n}\n\n.pl-4 {\n padding-left: 1rem;\n}\n\n.pl-1\\.5 {\n padding-left: 0.375rem;\n}\n\n.fixed {\n position: fixed;\n}\n\n.absolute {\n position: absolute;\n}\n\n.relative {\n position: relative;\n}\n\n.right-0 {\n right: 0;\n}\n\n.bottom-0 {\n bottom: 0;\n}\n\n.left-0 {\n left: 0;\n}\n\n.top-1 {\n top: 0.25rem;\n}\n\n.stroke-current {\n stroke: currentColor;\n}\n\n.text-component-title {\n --text-opacity: 1;\n color: #ffffff;\n color: rgba(255, 255, 255, var(--text-opacity));\n}\n\n.text-status-text {\n --text-opacity: 1;\n color: #93c2db;\n color: rgba(147, 194, 219, var(--text-opacity));\n}\n\n.text-property-name-color {\n --text-opacity: 1;\n color: #ff8ae2;\n color: rgba(255, 138, 226, var(--text-opacity));\n}\n\n.text-property-seperator-color {\n --text-opacity: 1;\n color: #0069ff;\n color: rgba(0, 105, 255, var(--text-opacity));\n}\n\n.text-typeof-color {\n --text-opacity: 1;\n color: #93c2db;\n color: rgba(147, 194, 219, var(--text-opacity));\n}\n\n.text-value-color {\n --text-opacity: 1;\n color: #ffffff;\n color: rgba(255, 255, 255, var(--text-opacity));\n}\n\n.text-string-editor-color {\n --text-opacity: 1;\n color: #ffffff;\n color: rgba(255, 255, 255, var(--text-opacity));\n}\n\n.text-icon-color {\n --text-opacity: 1;\n color: #93c2db;\n color: rgba(147, 194, 219, var(--text-opacity));\n}\n\n.hover\\:text-status-text-hover:hover {\n --text-opacity: 1;\n color: #ff8ae2;\n color: rgba(255, 138, 226, var(--text-opacity));\n}\n\n.whitespace-no-wrap {\n white-space: nowrap;\n}\n\n.w-4 {\n width: 1rem;\n}\n\n.w-full {\n width: 100%;\n}\n\n.z-10 {\n z-index: 10;\n}\n\n.z-30 {\n z-index: 30;\n}\n\n.z-50 {\n z-index: 50;\n}\n\n.transform {\n --transform-translate-x: 0;\n --transform-translate-y: 0;\n --transform-rotate: 0;\n --transform-skew-x: 0;\n --transform-skew-y: 0;\n --transform-scale-x: 1;\n --transform-scale-y: 1;\n transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));\n}\n\n.-rotate-90 {\n --transform-rotate: -90deg;\n}\n\n.transition {\n transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;\n}\n\n.duration-200 {\n transition-duration: 200ms;\n}\n\n@-webkit-keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@-webkit-keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@keyframes ping {\n 75%, 100% {\n transform: scale(2);\n opacity: 0;\n }\n}\n\n@-webkit-keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n\n@-webkit-keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n@keyframes bounce {\n 0%, 100% {\n transform: translateY(-25%);\n -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);\n animation-timing-function: cubic-bezier(0.8,0,1,1);\n }\n\n 50% {\n transform: none;\n -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);\n animation-timing-function: cubic-bezier(0,0,0.2,1);\n }\n}\n\n@media (min-width: 640px) {\n .sm\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .sm\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .sm\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .sm\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .sm\\:container {\n max-width: 1280px;\n }\n }\n}\n\n@media (min-width: 768px) {\n .md\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .md\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .md\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .md\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .md\\:container {\n max-width: 1280px;\n }\n }\n}\n\n@media (min-width: 1024px) {\n .lg\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .lg\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .lg\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .lg\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .lg\\:container {\n max-width: 1280px;\n }\n }\n}\n\n@media (min-width: 1280px) {\n .xl\\:container {\n width: 100%;\n }\n\n @media (min-width: 640px) {\n .xl\\:container {\n max-width: 640px;\n }\n }\n\n @media (min-width: 768px) {\n .xl\\:container {\n max-width: 768px;\n }\n }\n\n @media (min-width: 1024px) {\n .xl\\:container {\n max-width: 1024px;\n }\n }\n\n @media (min-width: 1280px) {\n .xl\\:container {\n max-width: 1280px;\n }\n }\n}\n";
657 | styleInject(css_248z);
658 |
659 | window.alpineDevToolsThemeHacktoberfest2020 = css_248z;
660 |
661 | const alpine = window.deferLoadingAlpine || (alpine => alpine());
662 |
663 | window.deferLoadingAlpine = callback => {
664 | alpine(callback);
665 | DevTools.start(Loader, Viewer, css_248z);
666 | };
667 |
668 | })));
669 |
--------------------------------------------------------------------------------