├── .gitignore
├── LICENSE
├── README.md
├── colada-extension
├── .eslintrc.js
├── README.md
├── app-frontend
│ └── src
│ │ ├── App.vue
│ │ ├── assets
│ │ └── style.scss
│ │ ├── components
│ │ ├── ChartNode.vue
│ │ ├── ChartsContainer.vue
│ │ ├── CurrentNode.vue
│ │ ├── CurrentStore.vue
│ │ ├── Navbar.vue
│ │ ├── StoreInfo.vue
│ │ ├── TimelineNode.vue
│ │ └── VertTimeline.vue
│ │ ├── main.js
│ │ ├── router
│ │ └── index.js
│ │ └── views
│ │ ├── ChartView.vue
│ │ ├── MenuView.vue
│ │ └── TimelineView.vue
├── devtools-panel.js
├── index.html
├── package-lock.json
├── package.json
├── public
│ ├── assets
│ │ ├── chrome-dev-tools.png
│ │ ├── icon-128.png
│ │ ├── icon-16.png
│ │ └── icon-48.png
│ ├── content-script.js
│ ├── devtools-background.html
│ ├── devtools-background.js
│ ├── manifest.json
│ └── popup.html
└── vite.config.js
├── colada-plugin
├── .eslintrc.js
├── .npmignore
├── README.md
├── __tests__
│ ├── handleStoreChange.test.js
│ └── stateHandler.test.js
├── package-lock.json
├── package.json
├── rollup.config.mjs
├── src
│ ├── ColadaDevToolsPlugin
│ │ ├── index.ts
│ │ ├── inspector.ts
│ │ ├── stateHandler.ts
│ │ └── timeline.ts
│ ├── PiniaColadaPlugin
│ │ └── index.ts
│ ├── index.ts
│ └── types.ts
├── tsconfig.json
└── vite.config.js
├── demo-project
├── .eslintrc.js
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
│ └── favicon.ico
├── reload.sh
├── src
│ ├── App.vue
│ ├── assets
│ │ ├── base.css
│ │ ├── logo.svg
│ │ └── main.css
│ ├── components
│ │ ├── Counter.vue
│ │ ├── DoubleStore.vue
│ │ ├── Header.vue
│ │ ├── Main.vue
│ │ ├── WelcomeItem.vue
│ │ └── __tests__
│ │ │ └── HelloWorld.spec.js
│ ├── main.js
│ └── stores
│ │ ├── counter.js
│ │ └── store.js
└── vite.config.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 | dist.*
85 |
86 | # Gatsby files
87 | .cache/
88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
89 | # https://nextjs.org/blog/next-9-1#public-directory-support
90 | # public
91 |
92 | # vuepress build output
93 | .vuepress/dist
94 |
95 | # Serverless directories
96 | .serverless/
97 |
98 | # FuseBox cache
99 | .fusebox/
100 |
101 | # DynamoDB Local files
102 | .dynamodb/
103 |
104 | # TernJS port file
105 | .tern-port
106 |
107 | #.DS_Store
108 | .DS_Store
109 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OSLabs Beta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | # What is [Colada](https://colada.dev/)?
20 |
21 | ## *Time-travel debugging for [Pinia🍍](https://pinia.vuejs.org/ "Pinia homepage and documentation"), Vue's official state management library*
22 |
23 |
24 | ## Colada offers a suite of tools for Vue developers working with the [Pinia state management library](https://pinia.vuejs.org/):
25 | 1. [Chrome DevTool Extension](https://chrome.google.com/webstore/detail/colada-devtools/icdbaobbeemmhlmjolbkedcneadkfpdl)
26 | 2. [NPM package](https://www.npmjs.com/package/colada-plugin) that serves as a [plugin](https://devtools.vuejs.org/plugin/plugins-guide.html) for the [Vue.js DevTools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en) Chrome Extension
27 | 3. [Pinia🍍](https://pinia.vuejs.org/) plugin to directly access and mutate your app's store
28 |
29 |
30 |
31 | ## Core Features
32 |
33 | - ✅ Minimal installation and automatic detection of Vue app in [Vue.js DevTools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en)
34 | - 🔄 Direct integration into [Vue.js DevTools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en), so developers can use Colada without leaving their existing devtool configuration
35 | - 🕰️ Time travel debugging
36 | - 🔎 Inspector panel for viewing Pinia state within your Vue app
37 | - 💻 A [Chrome DevTool Extension](https://chrome.google.com/webstore/detail/colada-devtools/icdbaobbeemmhlmjolbkedcneadkfpdl) providing enhanced features, including:
38 | - 🕰️ Time travel debugging
39 | - 🔎 Inspector panel for viewing Pinia state within your Vue app
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | # Getting Started
50 |
51 | ## Installation: **Vue DevTools Plugin**
52 | 0. Ensure the [Vue.js DevTools Chrome Extension](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en) is installed
53 | 1. Install the [Colada npm package](https://www.npmjs.com/package/colada-plugin) in your app's root directory
54 | ```bash
55 | npm install colada-plugin --save-dev
56 | ```
57 |
58 | 2. Add Colada to your Vue app
59 | ```js
60 | // main.js
61 |
62 | import { createApp } from 'vue';
63 | import { createPinia } from 'pinia';
64 | // import Colada Plugin
65 | import Colada, { PiniaColadaPlugin } from 'colada-plugin';
66 | import App from './App.vue';
67 |
68 | const app = createApp(App);
69 | const pinia = createPinia();
70 |
71 | app.use(pinia);
72 | pinia.use(PiniaColadaPlugin);
73 | app.use(Colada);
74 |
75 | app.mount('#app');
76 | ```
77 |
78 |
79 | ## Installation: **Chrome DevTools Extension**
80 |
81 | ### *NOTE: Ensure the [Vue.js DevTools Chrome Extension](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en) is installed before installing the Colada DevTool Chrome Extension*
82 |
83 |
84 | ### There are two ways to install the Colada Chrome Extension:
85 |
86 |
87 | ### 1. **Install from the Chrome Web Store**
88 | 1. Navigate to [Colada on the Chrome Web Store](https://chrome.google.com/webstore/detail/colada-devtools/icdbaobbeemmhlmjolbkedcneadkfpdl), and click "Add to Chrome"
89 |
90 |
91 |
92 | ### 2. **Install from source**
93 |
94 | 1. Clone this repository
95 | 2. Run the following commands
96 | ```
97 | cd colada-extension
98 | npm install
99 | npm run build
100 | ```
101 | 3. This will create a new `/dist` directory in `/colada-extension`
102 | 4. In Chrome, navigate to [chrome://extensions](chrome://extensions).
103 | 5. In the top right of the Extensions page, there is a toggle for "Developer Mode." Make sure this is toggled **ON**.
104 | 6. On the top left of the page, select "Load Unpacked", and select the `colada/colada-extension/dist` directory.
105 |
106 |
107 |
108 |
109 | # How to Use Colada
110 |
111 | ## Using the Colada **Vue DevTools Plugin**
112 | - Navigate to the Vue.js DevTools
113 |
114 |
115 | ### **Time Travel Debugging**
116 | - Select the "Colada" timeline in the timeline view
117 | - Turn off screenshots
118 |
119 | - Changes in your app's store and state will automatically be tracked on the timeline
120 | - Click on timeline events to travel through time and update your app's state
121 |
122 |
123 | ### **Inspector Panel** - View Your App's Stores and State in Real Time
124 | - Select "Colada" in the component menu drop down
125 | - Click on your Pinia store to view state, actions, and getters updated in real time
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | ## Using the Colada **Chrome DevTool Extension**
135 | - Navigate to Colada DevTools in Chrome
136 |
137 |
138 |
139 | - Changes in your app's store and state will automatically be tracked on the timeline
140 | - Click on a timestamp or use the arrows to travel through time and update your app's state
141 | - View your app's state as you time travel in the inspector panel on the right
142 |
143 |
144 |
145 |
146 |
147 |
148 | # How to Give Colada a Test Run With Our Demo App
149 |
150 | 1. Clone this repository
151 | 2. Navigate to the ```demo-project``` directory
152 | ```
153 | cd demo-project
154 | ```
155 | 3. Install packages and run application
156 | ```bash
157 | npm install
158 | npm run dev
159 | ```
160 | 4. Interact with the app to watch the app's state update in real-time!
161 |
162 |
163 |
164 |
165 | # Contributing and Issues
166 | Interested in conributing to Colada? Reach out to our [core team](https://colada.dev/#contributors)
167 | Feature requests or issues/bugs to report? [Let us know!](https://github.com/oslabs-beta/colada/issues)
168 |
169 |
170 |
171 | # Release Notes
172 | ## Contributors
173 | - Parker Steinberg • [LinkedIn](https://www.linkedin.com/in/parker-steinberg/) • [Github](https://github.com/parkersteinberg)
174 | - Jonathan Chen • [LinkedIn](https://www.linkedin.com/in/jonathan-hp-chen/) • [Github](https://github.com/JonHPC)
175 | - Vaughn Sulit • [LinkedIn](https://www.linkedin.com/in/bvaughnsulit/) • [Github](https://github.com/bvaughnsulit)
176 | - Dan Steinbrook • [LinkedIn](https://www.linkedin.com/in/daniel-steinbrook/) • [Github](https://github.com/dsteinbrook)
177 |
178 |
179 | 0.1.1 | Initial release of Colada, more to come!
180 |
181 |
182 |
183 |
184 | # License
185 | [MIT](http://opensource.org/licenses/MIT)
--------------------------------------------------------------------------------
/colada-extension/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'env': {
3 | 'browser': true,
4 | 'es2021': true
5 | },
6 | 'extends': [
7 | 'eslint:recommended',
8 | 'plugin:vue/vue3-essential'
9 | ],
10 | 'overrides': [
11 | ],
12 | 'parserOptions': {
13 | 'ecmaVersion': 'latest',
14 | 'sourceType': 'module'
15 | },
16 | 'globals' : {
17 | 'chrome' : 'readonly'
18 | },
19 | 'ignorePatterns' : ['dist/'],
20 | 'plugins': [
21 | 'vue'
22 | ],
23 | 'rules': {
24 | 'indent': [
25 | 'warn',
26 | 2
27 | ],
28 | 'linebreak-style': [
29 | 'warn',
30 | 'unix'
31 | ],
32 | 'quotes': [
33 | 'warn',
34 | 'single'
35 | ],
36 | 'semi': [
37 | 'warn',
38 | 'always'
39 | ]
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/colada-extension/README.md:
--------------------------------------------------------------------------------
1 | #colada-extension
2 | ```
3 | cd colada-extension
4 | npm install
5 | ```
6 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
32 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/assets/style.scss:
--------------------------------------------------------------------------------
1 | //VARIABLES
2 | $primary-color:rgb(96, 202, 140);
3 | $background-color: rgb(13,16,21);
4 | $nav-icon:white;
5 | $nav-hover:rgb(48,61,78);
6 | $nav-click:rgb(71, 91, 118);
7 | $border-color:rgb(71, 91, 118);
8 | $store-color:rgb(199, 165, 243);
9 | $pinia-color:rgb(250,217,111);
10 | $red-500: #ef4444;
11 | $slate-400: #94a3b8;
12 | $slate-800:#1e293b;
13 |
14 | //****************** Below is App.vue styling ***************
15 |
16 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400&display=swap');
17 | * {
18 | box-sizing: border-box;
19 | margin: 0;
20 | padding: 0;
21 | }
22 | html{
23 | width:100vw;
24 | height:100vh;
25 | }
26 | body {
27 | // font-family: 'Poppins', sans-serif;
28 | font-family:monospace;
29 | font-size: 100%; //override the chrome bug that sets font size to 75%
30 | display:flex;
31 | flex-direction:column;
32 | justify-content:flex-start;
33 | align-items:center;
34 | background-color:$background-color;
35 | color:rgb(182,193,201);
36 | overflow:hidden;
37 | }
38 | .main{
39 | display:flex;
40 | flex-direction:column;
41 | justify-content: flex-start;
42 | align-items:center;
43 | }
44 | .container {
45 | max-width: 500px;
46 | margin: 30px auto;
47 | overflow: auto;
48 | min-height: 300px;
49 | border: 1px solid steelblue;
50 | padding: 30px;
51 | border-radius: 5px;
52 | }
53 | //****************** Above is App.vue styling ***************
54 |
55 | //****************** Below is Button styling ***************
56 | .btn {
57 | display: inline-block;
58 | background: $background-color;
59 | color: $primary-color;
60 | border: none;
61 | padding: 3px 14px;
62 | margin: 3px;
63 | border-radius: 5px;
64 | cursor: pointer;
65 | text-decoration: none;
66 | font-size: 16px;
67 | font-family: inherit;
68 | transition:0.25s;
69 | border:2px solid $primary-color;
70 | }
71 |
72 | .btn:hover{
73 | background-color:$primary-color;
74 | color: $background-color;
75 | }
76 |
77 | .btn:focus {
78 | outline: none;
79 | }
80 | .btn:active {
81 | transform: scale(0.9);
82 | }
83 | .btn-block {
84 | display: block;
85 | width: 100%;
86 | }
87 |
88 | .clear-btn {
89 | display: inline-block;
90 | background: $background-color;
91 | color: $red-500;
92 | border: none;
93 | padding: 3px 14px;
94 | margin: 3px;
95 | border-radius: 5px;
96 | cursor: pointer;
97 | text-decoration: none;
98 | font-size: 16px;
99 | font-family: inherit;
100 | transition:0.25s;
101 | border:2px solid $red-500;
102 | }
103 |
104 | .clear-btn:hover{
105 | background-color:$red-500;
106 | color: $background-color;
107 | }
108 |
109 | .clear-btn:focus {
110 | outline: none;
111 | }
112 | .clear-btn:active {
113 | transform: scale(0.9);
114 | }
115 | .clear-btn-block {
116 | display: block;
117 | width: 100%;
118 | }
119 |
120 | .timestamp-btn {
121 | display: inline-block;
122 | background: $background-color;
123 | color: $primary-color;
124 | border: none;
125 | padding: 3px 6px;
126 | margin: 3px;
127 | border-radius: 5px;
128 | cursor: pointer;
129 | text-decoration: none;
130 | font-size: 12px;
131 | font-family: inherit;
132 | transition:0.25s;
133 | border:2px solid $primary-color;
134 | }
135 |
136 | .timestamp-btn:hover{
137 | background-color:$primary-color;
138 | color: $background-color;
139 | }
140 |
141 | .timestamp-btn:focus {
142 | outline: none;
143 | }
144 | .timestamp-btn:active {
145 | transform: scale(0.9);
146 | }
147 | .timestamp-btn-block {
148 | display: block;
149 | width: 100%;
150 | }
151 | //****************** Above is Button styling ***************
152 |
153 | //****************** Below is Navbar.vue styling ***************
154 | .navbar{
155 | display:flex;
156 | justify-content:space-between;
157 | align-items:center;
158 | border-bottom: 3px double $border-color;
159 | width:100vw;
160 | padding: 0rem 0.5rem;
161 | }
162 |
163 | .navbar-icons{
164 | display:flex;
165 | justify-content:space-evenly;
166 | align-items:center;
167 | }
168 |
169 | .nav-icon{
170 | width:48px;
171 | height:48px;
172 | color:$nav-icon;
173 | transition:0.2s;
174 | border-radius:8px;
175 | display:flex;
176 | justify-content:center;
177 | align-items:center;
178 | }
179 |
180 | .nav-icon:hover{
181 | background-color:$nav-hover;
182 | }
183 |
184 | .nav-icon:active{
185 | background-color:$nav-click;
186 | }
187 | //****************** Above is Navbar.vue styling ***************
188 |
189 | //****************** Below is VertTimeline.vue styling ***************
190 | .timeline-container{
191 | display:flex;
192 | flex-direction:row;
193 | justify-content:space-between;
194 | align-items:flex-start;
195 | gap:1rem;
196 | width: 100vw;
197 | height: 100vh;
198 | }
199 |
200 | .vertical-left{
201 | display:flex;
202 | justify-content:flex-start;
203 | align-items:flex-start;
204 | width:50%;
205 | height:100%;
206 | padding-top:1rem;
207 | flex-grow:1;
208 | font-family:monospace;
209 | }
210 |
211 | .vert-timeline{
212 | height:90%;
213 | width:100%;
214 | display:flex;
215 | flex-direction:column;
216 | justify-content:flex-start;
217 | align-items:center;
218 | overflow-x: hidden;
219 | overflow-y: auto;
220 |
221 | }
222 |
223 | .timeline {
224 | list-style-type: none;
225 | display: flex;
226 | flex-direction:column;
227 | align-items: flex-start;
228 | justify-content: flex-start;
229 | }
230 |
231 | .timeline-nodes{
232 | display:flex;
233 | width:100%;
234 | flex-direction:column;
235 | align-items:flex-start;
236 | justify-content:space-evenly;
237 | border-left: 2px solid #D6DCE0;
238 | border-top: 1px solid $border-color;
239 | position:relative;
240 | transition: all 200ms ease-in;
241 | z-index:1;
242 | &:before{
243 | content: '';
244 | width: 8px;
245 | height: 8px;
246 | background-color: $slate-400;
247 | border-radius: 25px;
248 | border: 1px solid $slate-400;
249 | position: absolute;
250 | top: 25%;
251 | left: -6px;
252 | transition: all 200ms ease-in;
253 | }
254 | }
255 |
256 | // .timeline-nodes:hover{
257 | // background-color:$nav-hover;
258 | // cursor:pointer;
259 | // }
260 |
261 | // .timeline-nodes:focus, .timeline-nodes:active{
262 | // background-color:$nav-click;
263 | // }
264 |
265 | // .timeline-nodes.clicked{
266 | // background-color:$nav-click;
267 | // }
268 |
269 | .node{
270 | display:flex;
271 | flex-direction:column;
272 | }
273 |
274 | .timeline-node{
275 | display:flex;
276 | justify-content:space-evenly;
277 | height:100%;
278 | }
279 |
280 | .timestamp{
281 | padding: 0px 20px;
282 | display: flex;
283 | flex-direction: column;
284 | justify-content:center;
285 | align-items: center;
286 | font-weight: 100;
287 | }
288 | .status{
289 | padding: 0px 20px;
290 | display: flex;
291 | justify-content: center;
292 | align-items:center;
293 | position: relative;
294 | transition: all 200ms ease-in;
295 | h4{
296 | font-weight:600;
297 | }
298 | }
299 |
300 | .timeline-nodes.complete{
301 | background-color:$slate-800;
302 | border-left: 2px solid $primary-color;
303 | color: $store-color;
304 | &:before{
305 | background-color: $primary-color;
306 | transition: all 200ms ease-in ;
307 | border: 1px solid $primary-color;
308 | }
309 | h4 {
310 | color: $primary-color;
311 | }
312 | }
313 |
314 | //****************** Above is VertTimeline.vue styling ***************
315 |
316 | //****************** Below is Animations ***************
317 |
318 | .slide-right-enter-active {
319 | transition: all 0.25s ease-out;
320 | }
321 |
322 | .slide-right-leave-active {
323 | opacity:0;
324 | }
325 |
326 | .slide-right-enter-from, .slide-right-leave-to {
327 | transform: translateX(-200px);
328 | opacity: 0;
329 | }
330 |
331 | .slide-left-enter-active {
332 | transition: all 0.25s ease-out;
333 | }
334 |
335 | .slide-left-leave-active {
336 | opacity:0;
337 | }
338 |
339 | .slide-left-enter-from, .slide-left-leave-to {
340 | transform: translateX(200px);
341 | opacity: 0;
342 | }
343 |
344 | .fade-enter-active, .fade-leave-active {
345 | transition: opacity 0.5s ease;
346 | }
347 |
348 | .fade-enter-from, .fade-leave-to {
349 | opacity: 0;
350 | }
351 |
352 | //****************** Above is Animations ***************
353 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/ChartNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{element}}
4 |
5 |
6 |
7 |
26 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/ChartsContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
25 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/CurrentNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Current Node
4 |
5 |
6 |
7 |
8 |
9 |
10 |
34 |
35 |
59 |
60 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/CurrentStore.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
32 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Colada DevTools
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/StoreInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Store: {{info.key}}
4 |
State:
5 |
12 |
13 |
Timestamp: {{timestamp}}
14 |
15 |
16 |
17 |
59 |
60 |
100 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/TimelineNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | {{formattedTime}}
10 |
11 |
12 |
13 |
14 |
77 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/components/VertTimeline.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
35 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 | import router from './router';
4 |
5 |
6 | const app = createApp(App);
7 | app.use(router);
8 | app.mount('#app');
9 |
10 | // export async function initDevTools(){
11 | // const app = createApp(App)
12 | // app.mount('#app')
13 | // }
14 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/router/index.js:
--------------------------------------------------------------------------------
1 | import {createRouter, createWebHistory} from 'vue-router';
2 | import TimelineView from '../views/TimelineView.vue';
3 | import ChartView from '../views/ChartView.vue';
4 | import MenuView from '../views/MenuView.vue';
5 |
6 | const routes = [
7 | {
8 | path: '/index.html',
9 | redirect: '/'
10 | },
11 | {
12 | path: '/',
13 | name: 'TimelineView',
14 | component: TimelineView,
15 | meta: {transition: 'slide-right'}
16 | },
17 | {
18 | path: '/chart',
19 | name: 'ChartView',
20 | component: ChartView,
21 | meta: {transition: 'slide-left'}
22 | },
23 | {
24 | path: '/menu',
25 | name: 'MenuView',
26 | component: MenuView,
27 | meta: {transition: 'slide-left'}
28 | }
29 | ];
30 |
31 | const router = createRouter({
32 | history: createWebHistory(),
33 | routes
34 | });
35 |
36 | export default router;
37 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/views/ChartView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Component tree chart goes here
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/views/MenuView.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
42 |
43 |
--------------------------------------------------------------------------------
/colada-extension/app-frontend/src/views/TimelineView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
205 |
206 |
215 |
--------------------------------------------------------------------------------
/colada-extension/devtools-panel.js:
--------------------------------------------------------------------------------
1 | import {createApp} from 'vue';
2 | import App from './app-frontend/src/App.vue';
3 | import router from './app-frontend/src/router';
4 |
5 | const app = createApp(App);
6 | app.use(router);
7 | app.mount('#app');
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/colada-extension/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/colada-extension/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "vite build --watch",
4 | "build": "vite build"
5 | },
6 | "devDependencies": {
7 | "@vitejs/plugin-vue": "^3.1.0",
8 | "@vitejs/plugin-vue-jsx": "^2.0.1",
9 | "cross-env": "^7.0.3",
10 | "eslint": "^8.23.1",
11 | "eslint-plugin-vue": "^9.5.1",
12 | "laravel-mix": "^6.0.49",
13 | "sass": "^1.54.9",
14 | "sass-loader": "^12.6.0",
15 | "vite": "^3.1.3",
16 | "vue": "^3.2.39",
17 | "vue-loader": "^17.0.0",
18 | "vue-router": "^4.1.5",
19 | "vue-template-compiler": "^2.7.10"
20 | },
21 | "dependencies": {
22 | "bootstrap-icons": "^1.9.1",
23 | "dotenv": "^16.0.2",
24 | "swiper": "^8.4.2",
25 | "vue-json-pretty": "^2.2.2"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/colada-extension/public/assets/chrome-dev-tools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/colada/6253de89595e6a19443fc45b6527aa0432e48321/colada-extension/public/assets/chrome-dev-tools.png
--------------------------------------------------------------------------------
/colada-extension/public/assets/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/colada/6253de89595e6a19443fc45b6527aa0432e48321/colada-extension/public/assets/icon-128.png
--------------------------------------------------------------------------------
/colada-extension/public/assets/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/colada/6253de89595e6a19443fc45b6527aa0432e48321/colada-extension/public/assets/icon-16.png
--------------------------------------------------------------------------------
/colada-extension/public/assets/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/colada/6253de89595e6a19443fc45b6527aa0432e48321/colada-extension/public/assets/icon-48.png
--------------------------------------------------------------------------------
/colada-extension/public/content-script.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | function saveMessage(event) {
3 | const parsed = typeof event.data === 'string' ? JSON.parse(event.data) : '';
4 | if (parsed && parsed.source === 'colada') {
5 | const date = Object.keys(parsed.payload)[0];
6 | chrome.storage.local.set({ [date]: parsed.payload }, () => { });
7 | }
8 | }
9 | window.addEventListener('message', saveMessage);
10 | window.addEventListener('load', () => { chrome.storage.local.clear(); });
11 | chrome.runtime.onMessage.addListener((message) => {
12 | if (message.source === 'colada-extension') {
13 | window.postMessage(JSON.stringify(message), window.location.href);
14 | }
15 | });
16 | })();
--------------------------------------------------------------------------------
/colada-extension/public/devtools-background.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/colada-extension/public/devtools-background.js:
--------------------------------------------------------------------------------
1 | // loaded by index.html, which is the the "background" page for the devtools-panel
2 | // this is declared in manifest.json. note that this does not actually represent the UI, that is the devtools "panel")
3 |
4 | chrome.devtools.panels.create('Colada DevTools', '', './index.html');
5 |
6 |
--------------------------------------------------------------------------------
/colada-extension/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Colada DevTools",
3 | "description": "Time-travel debugging for Pinia, Vue's official state management library.",
4 | "version": "0.1.2",
5 | "manifest_version": 3,
6 | "icons": {
7 | "16": "./assets/icon-16.png",
8 | "48": "./assets/icon-48.png",
9 | "128": "./assets/icon-128.png"
10 | },
11 | "permissions": ["storage"],
12 | "action": {
13 | "default_popup": "popup.html"
14 | },
15 | "devtools_page": "devtools-background.html",
16 | "content_scripts": [
17 | {
18 | "matches": [
19 | "*://*/*"
20 | ],
21 | "js": ["./content-script.js"]
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/colada-extension/public/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Colada
5 |
14 |
15 |
16 |
17 |
18 | To use Colada DevTools, open the Chrome Developer Tools, and select Colada DevTools from the main toolbar.
19 |
20 |
21 | For more information, please visit colada.dev
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/colada-extension/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import vue from '@vitejs/plugin-vue';
3 | import vueJsx from '@vitejs/plugin-vue-jsx';
4 |
5 | export default defineConfig({
6 | plugins: [vue(), vueJsx()],
7 | base: '',
8 | build: { minify: false }
9 | });
10 |
--------------------------------------------------------------------------------
/colada-plugin/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'env': {
3 | 'browser': true,
4 | 'es2021': true
5 | },
6 | 'extends': [
7 | 'eslint:recommended',
8 | 'plugin:vue/vue3-essential',
9 | 'plugin:@typescript-eslint/recommended'
10 | ],
11 | 'overrides': [
12 | ],
13 | 'parser': '@typescript-eslint/parser',
14 | 'parserOptions': {
15 | 'ecmaVersion': 'latest',
16 | 'sourceType': 'module'
17 | },
18 | 'plugins': [
19 | 'vue',
20 | '@typescript-eslint'
21 | ],
22 | 'rules': {
23 | 'indent': [
24 | 'warn',
25 | 2
26 | ],
27 | 'linebreak-style': [
28 | 'warn',
29 | 'unix'
30 | ],
31 | 'quotes': [
32 | 'warn',
33 | 'single'
34 | ],
35 | 'semi': [
36 | 'warn',
37 | 'always'
38 | ]
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/colada-plugin/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | rollup.config.mjs
3 | tsconfig.json
4 | .eslintrc.js
--------------------------------------------------------------------------------
/colada-plugin/README.md:
--------------------------------------------------------------------------------
1 | ## How to add Colada to your Vue app...
2 | ```
3 | //main.js
4 | import { createApp } from 'vue';
5 | import { createPinia } from 'pinia';
6 | import Colada, { PiniaColadaPlugin } from 'colada-plugin';
7 | import App from './App.vue';
8 |
9 | const app = createApp(App);
10 | const pinia = createPinia();
11 |
12 | app.use(pinia);
13 | pinia.use(PiniaColadaPlugin);
14 | app.use(Colada);
15 |
16 | app.mount('#app');
17 |
--------------------------------------------------------------------------------
/colada-plugin/__tests__/handleStoreChange.test.js:
--------------------------------------------------------------------------------
1 | import { expect, describe, it } from 'vitest';
2 | import { handleStoreChange } from '../src/ColadaDevToolsPlugin/stateHandler';
3 | import * as _ from 'lodash';
4 |
5 |
6 | describe('handleStoreChange does not mutate inputs', () => {
7 |
8 | it('does not mutate snapshsot argument', () => {
9 | const firstSnapshot =
10 | {
11 | timestamp: 1663784676523,
12 | type: 'Store: counter',
13 | key: 'counter',
14 | value: { 'count': 0 },
15 | state: ['state'],
16 | getters: {},
17 | actions: {},
18 | editable: true
19 | };
20 |
21 | handleStoreChange(firstSnapshot);
22 | expect(firstSnapshot).toEqual({
23 | timestamp: 1663784676523,
24 | type: 'Store: counter',
25 | key: 'counter',
26 | value: { 'count': 0 },
27 | state: ['state'],
28 | getters: {},
29 | actions: {},
30 | editable: true
31 | });
32 |
33 | });
34 |
35 | });
--------------------------------------------------------------------------------
/colada-plugin/__tests__/stateHandler.test.js:
--------------------------------------------------------------------------------
1 | // @vitest-environment jsdom
2 |
3 | import {
4 | setAppState,
5 | } from './src/ColadaDevToolsPlugin/stateHandler';
6 | import {describe, expect, it } from 'vitest';
7 |
8 |
9 |
10 | describe('setAppState tests', () => {
11 | window.store = {
12 | 'counter': {
13 | $state: {}
14 | },
15 | 'store': {
16 | $state: {}
17 | }
18 | };
19 |
20 | const mockSnapshot = {
21 | '1663717295074':
22 | {
23 | counter:
24 | {
25 | timestamp: Date.now(),
26 | type: 'Store: counter',
27 | key: 'counter',
28 | value: { 'count': 0 },
29 | state: ['state'],
30 | getters: {},
31 | actions: {},
32 | editable: true
33 | },
34 | store: {
35 | timestamp: Date.now(),
36 | type: 'Store: store',
37 | key: 'store',
38 | value: {myStr: '', elements: []},
39 | state: ['state'],
40 | getters: {},
41 | actions: {},
42 | editable: true
43 | }
44 | }
45 | };
46 |
47 |
48 | it('updates window.store', () => {
49 | setAppState(mockSnapshot);
50 |
51 | expect(window.store['counter'].$state).toEqual({ count: 0 });
52 | expect(window.store['store'].$state).toEqual({ myStr: '', elements: [] });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/colada-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "colada-plugin",
3 | "version": "0.1.0",
4 | "description": "A plugin to support Pinia",
5 | "author": {
6 | "name": "Jonathan Chen, Parker Steinberg, Vaughn Sulit, Dan Steinbrook"
7 | },
8 | "main": "dist/colada-plugin.cjs.js",
9 | "module": "dist/colada-plugin.esm-bundler.js",
10 | "unpkg": "dist/colada-plugin.global.js",
11 | "jsdelivr": "dist/colada-plugin.global.js",
12 | "types": "dist/index.d.ts",
13 | "exports": {
14 | ".": {
15 | "require": "./dist/colada-plugin.cjs.js",
16 | "browser": "./dist/colada-plugin.esm-browser.js",
17 | "import": "./dist/colada-plugin.esm-bundler.js",
18 | "module": "./dist/colada-plugin.esm-bundler.js"
19 | },
20 | "./package.json": "./package.json"
21 | },
22 | "sideEffects": false,
23 | "scripts": {
24 | "prepublish": "npm run dev",
25 | "dev": "rimraf dist && rollup -c rollup.config.mjs",
26 | "ts": "tsc --watch -d",
27 | "test": "vitest",
28 | "coverage": "vitest run --coverage"
29 | },
30 | "dependencies": {
31 | "@rollup/plugin-commonjs": "^22.0.2",
32 | "@rollup/plugin-node-resolve": "^13.3.0",
33 | "@rollup/plugin-replace": "^4.0.0",
34 | "@types/node": "^18.7.14",
35 | "@vue/devtools-api": "^6.2.1",
36 | "lodash.clonedeep": "^4.5.0",
37 | "lodash.debounce": "^4.0.8",
38 | "lodash.isempty": "^4.4.0",
39 | "pascalcase": "^2.0.0",
40 | "pinia": "^2.0.22",
41 | "rimraf": "^3.0.2",
42 | "rollup": "^2.79.0",
43 | "rollup-plugin-terser": "^7.0.2",
44 | "rollup-plugin-typescript2": "^0.33.0",
45 | "rollup-plugin-vue": "^6.0.0",
46 | "typescript": "^4.8.2",
47 | "vue": "^3.2.38"
48 | },
49 | "devDependencies": {
50 | "@types/lodash": "^4.14.185",
51 | "@types/lodash.clonedeep": "^4.5.7",
52 | "@types/lodash.debounce": "^4.0.7",
53 | "@types/lodash.isempty": "^4.4.7",
54 | "@typescript-eslint/eslint-plugin": "^5.37.0",
55 | "@typescript-eslint/parser": "^5.37.0",
56 | "eslint": "^8.23.1",
57 | "eslint-plugin-vue": "^9.5.1",
58 | "jsdom": "^20.0.0",
59 | "vitest": "^0.23.4"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/colada-plugin/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import vuePlugin from 'rollup-plugin-vue';
2 | import replace from '@rollup/plugin-replace';
3 | import resolve from '@rollup/plugin-node-resolve';
4 | import commonjs from '@rollup/plugin-commonjs';
5 | import pascalcase from 'pascalcase';
6 | import pkg from './package.json' assert {type:'json'};
7 | import {terser} from 'rollup-plugin-terser';
8 | import ts from 'rollup-plugin-typescript2';
9 | import * as path from 'path';
10 | import { fileURLToPath } from 'url';
11 |
12 | const __dirname = path.dirname(fileURLToPath(import.meta.url));
13 |
14 | //const pkg = require('./package.json')
15 | const name = pkg.name;
16 |
17 | function getAuthors (pkg) {
18 | const { contributors, author } = pkg;
19 |
20 | const authors = new Set();
21 | if (contributors && contributors) {
22 | contributors.forEach((contributor) => {
23 | authors.add(contributor.name);
24 | });
25 | }
26 | if (author) authors.add(author.name);
27 |
28 | return Array.from(authors).join(', ');
29 | }
30 |
31 | const banner = `/*!
32 | * ${pkg.name} v${pkg.version}
33 | * (c) ${new Date().getFullYear()} ${getAuthors(pkg)}
34 | * @license MIT
35 | */`;
36 |
37 | // ensure TS checks only once for each build
38 | let hasTSChecked = false;
39 |
40 | const outputConfigs = {
41 | // each file name has the format: `dist/${name}.${format}.js`
42 | // format being a key of this object
43 | 'esm-bundler': {
44 | file: pkg.module,
45 | format: 'es'
46 | },
47 | cjs: {
48 | file: pkg.main,
49 | format: 'cjs'
50 | },
51 | global: {
52 | file: pkg.unpkg,
53 | format: 'iife'
54 | },
55 | esm: {
56 | file: pkg.module.replace('bundler', 'browser'),
57 | format: 'es'
58 | }
59 | };
60 |
61 | const allFormats = Object.keys(outputConfigs);
62 | const packageFormats = allFormats;
63 | const packageConfigs = packageFormats.map((format) =>
64 | createConfig(format, outputConfigs[format])
65 | );
66 |
67 | // only add the production ready if we are bundling the options
68 | packageFormats.forEach((format) => {
69 | if (format === 'cjs') {
70 | packageConfigs.push(createProductionConfig(format));
71 | } else if (format === 'global') {
72 | packageConfigs.push(createMinifiedConfig(format));
73 | }
74 | });
75 |
76 | export default packageConfigs;
77 |
78 | function createConfig (format, output, plugins = []) {
79 | if (!output) {
80 | console.log(`invalid format: "${format}"`);
81 | process.exit(1);
82 | }
83 |
84 | output.sourcemap = !!process.env.SOURCE_MAP;
85 | output.banner = banner;
86 | output.externalLiveBindings = false;
87 | output.globals = { vue: 'Vue', '@vue/composition-api': 'vueCompositionApi' };
88 |
89 | const isProductionBuild = /\.prod\.js$/.test(output.file);
90 | const isGlobalBuild = format === 'global';
91 | const isRawESMBuild = format === 'esm';
92 | const isNodeBuild = format === 'cjs';
93 | const isBundlerESMBuild = /esm-bundler/.test(format);
94 |
95 | if (isGlobalBuild) output.name = pascalcase(pkg.name);
96 |
97 |
98 | // **************
99 | const shouldEmitDeclarations = !hasTSChecked;
100 |
101 | const tsPlugin = ts({
102 | check: !hasTSChecked,
103 | tsconfig: path.resolve(__dirname, 'tsconfig.json'),
104 | cacheRoot: path.resolve(__dirname, 'node_modules/.rts2_cache'),
105 | tsconfigOverride: {
106 | compilerOptions: {
107 | sourceMap: output.sourcemap,
108 | declaration: shouldEmitDeclarations,
109 | declarationMap: shouldEmitDeclarations,
110 | },
111 | exclude: ['__tests__', 'test-dts'],
112 | },
113 | });
114 | // we only need to check TS and generate declarations once for each build.
115 | // it also seems to run into weird issues when checking multiple times
116 | // during a single build.
117 | hasTSChecked = true;
118 |
119 | const external = ['vue', '@vue/composition-api'];
120 | if (!isGlobalBuild) {
121 | external.push('@vue/devtools-api');
122 | }
123 |
124 | const nodePlugins = [resolve(), commonjs()];
125 |
126 | return {
127 | input: 'src/index.ts',
128 | //input: 'src/index.js',
129 | // Global and Browser ESM builds inlines everything so that they can be
130 | // used alone.
131 | external,
132 | plugins: [
133 | vuePlugin(),
134 | tsPlugin,
135 | createReplacePlugin(
136 | isProductionBuild,
137 | isBundlerESMBuild
138 | ),
139 | ...nodePlugins,
140 | ...plugins
141 | ],
142 | output
143 | };
144 | }
145 |
146 | function createReplacePlugin (
147 | isProduction,
148 | isBundlerESMBuild
149 | ) {
150 | const replacements = {
151 | 'process.env.NODE_ENV': isBundlerESMBuild
152 | ? // preserve to be handled by bundlers
153 | 'process.env.NODE_ENV'
154 | : // hard coded dev/prod builds
155 | JSON.stringify(isProduction ? 'production' : 'development'),
156 | __VUE_PROD_DEVTOOLS__: isBundlerESMBuild
157 | ? '__VUE_PROD_DEVTOOLS__'
158 | : 'false'
159 | };
160 | // allow inline overrides like
161 | // __RUNTIME_COMPILE__=true yarn build
162 | Object.keys(replacements).forEach((key) => {
163 | if (key in process.env) {
164 | replacements[key] = process.env[key];
165 | }
166 | });
167 | return replace({
168 | preventAssignment: true,
169 | values: replacements
170 | });
171 | }
172 |
173 | function createProductionConfig (format) {
174 | return createConfig(format, {
175 | file: `dist/${name}.${format}.prod.js`,
176 | format: outputConfigs[format].format
177 | });
178 | }
179 |
180 | function createMinifiedConfig (format) {
181 | //const { terser } = require('rollup-plugin-terser')
182 |
183 | return createConfig(
184 | format,
185 | {
186 | file: `dist/${name}.${format}.prod.js`,
187 | format: outputConfigs[format].format
188 | },
189 | [
190 | terser({
191 | module: /^esm/.test(format),
192 | compress: {
193 | ecma: 2015,
194 | pure_getters: true
195 | }
196 | })
197 | ]
198 | );
199 | }
--------------------------------------------------------------------------------
/colada-plugin/src/ColadaDevToolsPlugin/index.ts:
--------------------------------------------------------------------------------
1 | import { setupDevtoolsPlugin } from '@vue/devtools-api';
2 | import { addPiniaStoreLabels, addPiniaStoreData } from './inspector';
3 | import { handleInspectTimelineEvent } from './timeline';
4 | import {
5 | initializeState,
6 | setAppState,
7 | getSnapshotbyTimestamp
8 | } from './stateHandler';
9 |
10 | // declare type for application window
11 | declare const window: any;
12 |
13 |
14 | //*************************************************************************** */
15 | //************* global variables that are used throughout file ************** */
16 | //*************************************************************************** */
17 | const inspectorId = 'colada-plugin';
18 | const timelineLayerId = 'colada-plugin';
19 |
20 |
21 | export function setupDevtools(app: any) {
22 |
23 |
24 | setupDevtoolsPlugin({
25 | id: inspectorId,
26 | label: 'Colada 🥥',
27 | packageName: 'colada-plugin',
28 | homepage: 'https://colada.dev/',
29 | logo: 'https://user-images.githubusercontent.com/34523493/191631808-4dee4315-2638-4214-9c4f-c074316d969e.png',
30 | app,
31 | enableEarlyProxy: true,
32 | settings: {}
33 | }, api => {
34 | //********************************************************************** */
35 | // ************ EVENT LISTENERS *****************************************
36 | //********************************************************************** */
37 |
38 | window.addEventListener('message', (event: any) => {
39 | // parse data from message
40 | const parsed = typeof event.data === 'string' ? JSON.parse(event.data) : '';
41 |
42 | // if source is colada extension, set app's state to snapshot that correspodns with payload's timestamp
43 | if (parsed.source === 'colada-extension') {
44 | const timestamp = parseInt(parsed.payload);
45 | setAppState(getSnapshotbyTimestamp(timestamp));
46 | }
47 | })
48 |
49 | window.addEventListener('DOMContentLoaded', () => {
50 | setTimeout(initializeState, 1000);
51 | });
52 |
53 | //add event listener to the window for 'addTimeLineEvent'
54 | window.addEventListener('addTimelineEvent', (e: any) => {
55 | const eventToAdd = e.detail;
56 |
57 | // grab timestamp from eventToAdd
58 | const currentTimestamp = parseInt(Object.keys(eventToAdd)[0]);
59 | const currentStores = Object.values(eventToAdd[currentTimestamp]);
60 | // iterate over snapshot associated with that timestamp
61 | currentStores.forEach((store: any) => {
62 | // add timelineEvent for each store
63 | api.addTimelineEvent({
64 | layerId: timelineLayerId,
65 | event: {
66 | time: currentTimestamp,
67 | title: store.key,
68 | data: {
69 | state: store.value
70 | },
71 | groupId: store.key
72 | }
73 | });
74 |
75 | });
76 | // refresh inspector state after adding element to timeline
77 | api.sendInspectorState(inspectorId);
78 | //END OF window.addEventListener
79 | });
80 |
81 | api.on.inspectTimelineEvent(handleInspectTimelineEvent);
82 |
83 |
84 | //********************************************************************** */
85 | // ************ COMPONENT SETTINGS *****************************************
86 | //********************************************************************** */
87 |
88 | //Adds a tag to next to the component in the Inspector -> Components Tree
89 | api.on.visitComponentTree((payload) => {
90 | //console.log('context is', context);
91 | const node = payload.treeNode;
92 | if (payload.componentInstance.type.meow) {
93 | node.tags.push({
94 | label: 'colada',
95 | textColor: 0x000000,
96 | backgroundColor: 0xff984f
97 | });
98 | }
99 | });
100 |
101 |
102 | //********************************************************************** */
103 | // ************ INSPECTOR SETTINGS *****************************************
104 | //********************************************************************** */
105 | //adds the Colada label into the Inspector bar
106 | api.addInspector({
107 | id: inspectorId,
108 | label: 'Colada 🥥',
109 | icon: 'code',
110 | treeFilterPlaceholder: 'Searching...',
111 | });
112 |
113 | api.on.getInspectorTree((payload: any) => {
114 | // if payload's inspector id matches our custom Colada inspectorId, add our store labels to the inspector
115 | if (payload.inspectorId === inspectorId) {
116 | addPiniaStoreLabels(payload);
117 | }
118 | });
119 |
120 |
121 | api.on.getInspectorState((payload: any) => {
122 | // if payload inspectorId matches the Colada inspectorId, add the relevant Pinia store data to the inspector panel
123 | if (payload.inspectorId === inspectorId) {
124 | addPiniaStoreData(payload);
125 | }
126 | });
127 |
128 |
129 | //********************************************************************** */
130 | // ************ TIMELINE SETTINGS *****************************************
131 | //********************************************************************** */
132 | //Register a timeline layer
133 | api.addTimelineLayer({
134 | id: timelineLayerId,
135 | color: 0xff984f,
136 | label: 'Colada 🥥',
137 | skipScreenshots: true, // doesn't work :(
138 | });
139 |
140 | });
141 | }
142 |
--------------------------------------------------------------------------------
/colada-plugin/src/ColadaDevToolsPlugin/inspector.ts:
--------------------------------------------------------------------------------
1 | import { getCurrentStores } from './stateHandler';
2 | import { StateObject } from '../types';
3 |
4 | // add elements (names of stores) to inspector
5 | const addPiniaStoreLabels = (payload: any) => {
6 | // iterate over stores and grab each label
7 | const currentStores = getCurrentStores();
8 | // initialize root node for Colada inspector tree
9 | payload.rootNodes = [
10 | {
11 | id: 'root',
12 | label: '🥥 Root',
13 | children: [],
14 | }
15 | ];
16 |
17 | // iterate over currentStores to add children stores to root
18 | Object.values(currentStores).forEach((store: any) => {
19 | const currentLabel = store.key;
20 | // push current store label to children array in payload.rootNodes
21 | payload.rootNodes[0].children.push({
22 | id: currentLabel,
23 | label: `store: ${currentLabel}`
24 | });
25 | });
26 | };
27 |
28 | // add state to inspector
29 | // API: api.on.getInspectorState
30 | // triggered by: when the devtools needs to load the state for the currently selected node in a custom inspector
31 | // iterate over most recent versions of stores and add all that data to the inspector
32 | // make use of getter function exported from stateHandler.ts that grabs the most recent stores from storeHistory
33 | const addPiniaStoreData = (payload: any) => {
34 | // use getCurrentStore from stateHandler to get most recent versions stores
35 | const currentStores = getCurrentStores();
36 |
37 | // initialize a state array
38 | const stateArr: StateObject[] = [];
39 | // initialize a getters array
40 | const gettersArr: any[] = [];
41 | // initialize a actions array
42 | const actionsArr: any[] = [];
43 |
44 | // iterate over currentStores
45 | Object.values(currentStores).forEach((store: any) => {
46 |
47 | const { key, value, getters, actions } = store;
48 | // add state to stateArry, getters to gettersArray, and actions to actionsArray
49 | const stateObj: StateObject = {
50 | key: key,
51 | value: value,
52 | editable: false
53 | };
54 |
55 | const gettersObj: any = {
56 | key: key,
57 | value: getters,
58 | editable: false
59 | };
60 |
61 | const actionsObj: any = {
62 | key: key,
63 | value: actions,
64 | editable: false
65 | };
66 |
67 | stateArr.push(stateObj);
68 | gettersArr.push(gettersObj);
69 | actionsArr.push(actionsObj);
70 |
71 | });
72 |
73 | // if nodeId is root --> add all the data for all stores
74 | if (payload.nodeId === 'root') {
75 | payload.state = {
76 | 'state': stateArr,
77 | 'getters': gettersArr,
78 | 'actions': actionsArr,
79 | };
80 | } else { // if nodeId is not root
81 | // use filter to get state, getters, and actionsArr to match the current node id being selected (which is the selected store)
82 | const filteredStateArr = stateArr.filter((state) => state.key === payload.nodeId);
83 | const filteredGettersArr = gettersArr.filter((getters) => getters.key === payload.nodeId);
84 | const filteredActionsArr = actionsArr.filter((actions) => {
85 | return actions.key === payload.nodeId;
86 | });
87 |
88 | payload.state = {
89 | 'state': filteredStateArr,
90 | 'getters': filteredGettersArr,
91 | 'actions': filteredActionsArr
92 | };
93 | }
94 |
95 | };
96 |
97 | export {
98 | addPiniaStoreLabels,
99 | addPiniaStoreData
100 | };
--------------------------------------------------------------------------------
/colada-plugin/src/ColadaDevToolsPlugin/stateHandler.ts:
--------------------------------------------------------------------------------
1 | import { piniaStores } from '../PiniaColadaPlugin/index';
2 | import * as _ from 'lodash';
3 | import debounce from 'lodash.debounce';
4 | import cloneDeep from 'lodash.clonedeep';
5 | import isEmpty from 'lodash.isempty';
6 |
7 | // delcare global variables
8 | const storeHistory: any = [];
9 | declare const window: any
10 | let combinedSnapshot: any = {};
11 | const storeLabels: any = [];
12 |
13 | /*
14 | * Add missing stores to combinedSnapshot
15 | * Add property hasBeenUpdated to combinedSnapshot and set to false
16 | * push combinedSnapshot to storeHistory
17 | * emit custom addTimelineEvent event with combinedSnapshot as payload
18 | * send data to extension via window.postMessage with combinedSnapshot as payload
19 | */
20 | const outputCombinedSnapshot = debounce(() => {
21 | // delcare variable missing stores, which will have the labels for the missing stores from snapShot
22 | const missingStores = storeLabels.filter((label: any) => {
23 | return !Object.keys(combinedSnapshot[Object.keys(combinedSnapshot)[0]]).includes(label);
24 | });
25 | // iterate over missing stores, find the corresponding most recent snapshot, and add to combinedSnapshot
26 | missingStores.forEach((store: any) => {
27 | // can replace this with getter function below
28 | // need to make a deep clone, otherwise we will be udpated the mostRecentSnapshot inadvertently
29 | const mostRecentSnapshot = getCurrentStores(true);
30 | const mostRecentSnapshotClone: any = cloneDeep(mostRecentSnapshot);
31 | // get correspong store and have to const
32 | const mostRecentStore = mostRecentSnapshotClone[Object.keys(mostRecentSnapshotClone)[0]][store];
33 | // add hasBeenUpdated = false property to snapshot we're adding
34 | mostRecentStore.hasBeenUpdated = false;
35 | // add to combinedSnapshot
36 | combinedSnapshot[Object.keys(combinedSnapshot)[0]][store] = mostRecentStore;
37 | });
38 |
39 | // pushing combinedSnap to storeHistory, triggering custom event, and posting message to window
40 | storeHistory.push(combinedSnapshot);
41 | //emit a custom event with the proxyObj as a payload
42 | const event: any = new CustomEvent('addTimelineEvent', { detail: combinedSnapshot });
43 | window.dispatchEvent(event);
44 |
45 | //send a messsage to the window for the extension to make use of
46 | const messageObj: any = {
47 | source: 'colada',
48 | payload: combinedSnapshot
49 | };
50 |
51 | window.postMessage(JSON.stringify(messageObj), window.location.href);
52 |
53 | // reset combinedSnapshot to empty object
54 | combinedSnapshot = {};
55 | }, 10);
56 |
57 | const handleStoreChange = (snapshot: any) => {
58 |
59 | const snapshotClone = cloneDeep(snapshot)
60 |
61 | // add hasBeenUpdated property to true on snapshotClone
62 | snapshotClone.hasBeenUpdated = true;
63 |
64 | // add snapshots's label ('key' proprety) to storeLabels if it's not already in there
65 | if (!storeLabels.includes(snapshotClone.key)) {
66 | storeLabels.push(snapshotClone.key);
67 | }
68 |
69 | // if finalSnaphsot has no properites, add initial timestamp property along with associated snapshotClone
70 | if (isEmpty(combinedSnapshot)) {
71 | combinedSnapshot[snapshotClone.timestamp] = {
72 | [snapshotClone.key]: snapshotClone
73 | };
74 | }
75 | // else, add a new key to combinedSnapshot at existing timestamp property
76 | else {
77 | combinedSnapshot[Object.keys(combinedSnapshot)[0]][snapshotClone.key] = snapshotClone;
78 | }
79 |
80 | // invoke debounced outputCombinedSnapshot
81 | outputCombinedSnapshot();
82 | };
83 |
84 |
85 | // import the subscribe method and implement associated functionality
86 | const initializeState = () => {
87 | piniaStores.subscribe(handleStoreChange, true);
88 | };
89 |
90 | const resubscribe = () => {
91 | piniaStores.subscribe(handleStoreChange, false);
92 | };
93 |
94 | const unsubscribe = () => {
95 | piniaStores.unsubscribe();
96 | };
97 |
98 |
99 | // NOTE: currently 0(n) ... consider refactoring to use binary search
100 | const getSnapshotbyTimestamp = (timestamp: number) => {
101 | for (const e of storeHistory) {
102 | if (parseInt(Object.keys(e)[0]) === timestamp) return e;
103 | }
104 | };
105 |
106 | const setAppState = (snapshot: any) => {
107 | unsubscribe();
108 | const stores: any = Object.values(snapshot)[0];
109 | for (const key in stores) {
110 | window.store[key].$state = stores[key].value;
111 | }
112 | resubscribe();
113 | };
114 |
115 | /*
116 | @param {boolean} [includeTimestamps=false] - To retrieve data with timestamps. Defaults to false.
117 | */
118 | const getCurrentStores = (includeTimestamps = false) => {
119 | if (includeTimestamps) {
120 | return storeHistory[storeHistory.length - 1];
121 | }
122 | else {
123 | return Object.values(storeHistory[storeHistory.length - 1])[0];
124 | }
125 | };
126 |
127 | export {
128 | initializeState,
129 | getCurrentStores,
130 | getSnapshotbyTimestamp,
131 | setAppState,
132 | resubscribe,
133 | unsubscribe,
134 | handleStoreChange
135 | };
136 |
--------------------------------------------------------------------------------
/colada-plugin/src/ColadaDevToolsPlugin/timeline.ts:
--------------------------------------------------------------------------------
1 | import {
2 | getSnapshotbyTimestamp,
3 | setAppState
4 | } from './stateHandler';
5 |
6 | const handleInspectTimelineEvent = (payload: any): void => {
7 | if (payload.layerId === 'colada-plugin') {
8 | const selectedEventTimestamp: number = payload.event.time;
9 | setAppState(getSnapshotbyTimestamp(selectedEventTimestamp));
10 | return;
11 | }
12 | };
13 |
14 | export { handleInspectTimelineEvent };
15 |
--------------------------------------------------------------------------------
/colada-plugin/src/PiniaColadaPlugin/index.ts:
--------------------------------------------------------------------------------
1 | import { ProxyObject } from '../types';
2 |
3 | declare const window: any;
4 |
5 | const unsubscribeMethods: Array<() => void> = [];
6 | const stores: Array = [];
7 | const piniaProxies: ProxyObject[] = [];
8 |
9 | // declare object where methods will be stored and exported
10 | const piniaStores: any = {};
11 |
12 | // add empty store object to window which will get filled with Pinia stores
13 | window.store = {};
14 |
15 | piniaStores.unsubscribe = () => {
16 | unsubscribeMethods.forEach(e => e());
17 | };
18 |
19 | piniaStores.getPiniaStores = (): any => {
20 | return piniaProxies;
21 | };
22 |
23 | piniaStores.subscribe = (callback: any, setInitialState = false) => {
24 | stores.forEach(store => {
25 | const snapshot: ProxyObject = {
26 | timestamp: Date.now(),
27 | type: `Store: ${store.$id}`,
28 | key: `${store.$id}`,
29 | value: store.$state,
30 | state: store._hmrPayload.state,
31 | getters: store._hmrPayload.getters,
32 | actions: store._hmrPayload.actions,
33 | editable: true
34 | };
35 | const unsubscribeMethod = store.$subscribe(() => {
36 | snapshot.timestamp = Date.now();
37 | // we can also access mutation and state here
38 | callback(snapshot);
39 | });
40 | unsubscribeMethods.push(unsubscribeMethod);
41 | if (setInitialState) callback(snapshot);
42 | });
43 | };
44 |
45 |
46 | const PiniaColadaPlugin = (context: any) => {
47 | const store: any = context.store;
48 | stores.push(store);
49 | window.store[store.$id] = store;
50 | };
51 |
52 | export {
53 | piniaStores,
54 | PiniaColadaPlugin
55 | };
56 |
--------------------------------------------------------------------------------
/colada-plugin/src/index.ts:
--------------------------------------------------------------------------------
1 | import { setupDevtools } from './ColadaDevToolsPlugin/index';
2 | import { PiniaColadaPlugin } from './PiniaColadaPlugin/index';
3 |
4 | export default {
5 | install(app: any) {
6 | setupDevtools(app);
7 | }
8 | };
9 |
10 | export { PiniaColadaPlugin };
11 |
12 |
13 |
--------------------------------------------------------------------------------
/colada-plugin/src/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Types to define for the ProxyObject (and to be used elsewhere):
3 | - value (the state data)
4 | - state (an array of labels for the state within a store)
5 | - getters (an object where keys are labels for the getters within the store and the values are their function definitions)
6 | - actions (object where keys are action labels and values are associated function definitions)
7 | */
8 |
9 | // Other types to define:
10 | // - type for piniaStore
11 |
12 | export type ProxyObject = {
13 | timestamp?: any,
14 | type: string,
15 | key: string,
16 | value: any,
17 | state: any,
18 | getters: any,
19 | actions: any,
20 | editable: boolean
21 | }
22 |
23 | export type StateObject = {
24 | store_id?: string,
25 | key: string,
26 | value: any,
27 | editable: boolean
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/colada-plugin/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "src/**/*.ts"
4 | ],
5 | "compilerOptions": {
6 | "outDir": "dist",
7 | "rootDir": "src",
8 | "sourceMap": false,
9 |
10 | "target": "ES6",
11 | "module": "ESNext",
12 | "moduleResolution": "node",
13 | "allowJs": true,
14 | "skipLibCheck": true,
15 |
16 | "noUnusedLocals": true,
17 | "strictNullChecks": true,
18 | "noImplicitAny": true,
19 | "noImplicitThis": true,
20 | "noImplicitReturns": false,
21 | "strict": true,
22 | "isolatedModules": true,
23 |
24 | "experimentalDecorators": false,
25 | "resolveJsonModule": true,
26 | "esModuleInterop": true,
27 | "removeComments": false,
28 | "lib": [
29 | "es6",
30 | "dom"
31 | ],
32 | "types": [
33 | "node"
34 | ]
35 | }
36 | }
--------------------------------------------------------------------------------
/colada-plugin/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config";
2 |
3 | export default defineConfig({
4 | test: {
5 | globals: true,
6 | environment: 'jsdom'
7 | },
8 | })
--------------------------------------------------------------------------------
/demo-project/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'env': {
3 | 'browser': true,
4 | 'es2021': true
5 | },
6 | 'extends': [
7 | 'eslint:recommended',
8 | 'plugin:vue/vue3-essential',
9 | 'plugin:@typescript-eslint/recommended'
10 | ],
11 | 'overrides': [
12 | ],
13 | 'parser': '@typescript-eslint/parser',
14 | 'parserOptions': {
15 | 'ecmaVersion': 'latest',
16 | 'sourceType': 'module'
17 | },
18 | 'plugins': [
19 | 'vue',
20 | '@typescript-eslint'
21 | ],
22 | 'rules': {
23 | 'indent': [
24 | 'warn',
25 | 2
26 | ],
27 | 'linebreak-style': [
28 | 'warn',
29 | 'unix'
30 | ],
31 | 'quotes': [
32 | 'warn',
33 | 'single'
34 | ],
35 | 'semi': [
36 | 'warn',
37 | 'always'
38 | ]
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/demo-project/README.md:
--------------------------------------------------------------------------------
1 | # vue-project
2 |
3 | This template should help get you started developing with Vue 3 in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
8 |
9 | ## Customize configuration
10 |
11 | See [Vite Configuration Reference](https://vitejs.dev/config/).
12 |
13 | ## Project Setup
14 |
15 | ```sh
16 | npm install
17 | ```
18 |
19 | ### Compile and Hot-Reload for Development
20 |
21 | ```sh
22 | npm run dev
23 | ```
24 |
25 | ### Compile and Minify for Production
26 |
27 | ```sh
28 | npm run build
29 | ```
30 |
31 | ### Run Unit Tests with [Vitest](https://vitest.dev/)
32 |
33 | ```sh
34 | npm run test:unit
35 | ```
36 |
37 | ### Lint with [ESLint](https://eslint.org/)
38 |
39 | ```sh
40 | npm run lint
41 | ```
42 |
--------------------------------------------------------------------------------
/demo-project/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo-project/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-project",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vite build",
7 | "preview": "vite preview --port 4173",
8 | "test:unit": "vitest --environment jsdom",
9 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
10 | },
11 | "dependencies": {
12 | "colada-plugin": "file:../colada-plugin",
13 | "pinia": "^2.0.17",
14 | "vue": "^3.2.37"
15 | },
16 | "devDependencies": {
17 | "@rushstack/eslint-patch": "^1.1.4",
18 | "@typescript-eslint/eslint-plugin": "^5.37.0",
19 | "@typescript-eslint/parser": "^5.37.0",
20 | "@vitejs/plugin-vue": "^3.0.1",
21 | "@vitejs/plugin-vue-jsx": "^2.0.0",
22 | "@vue/eslint-config-prettier": "^7.0.0",
23 | "@vue/test-utils": "^2.0.2",
24 | "eslint": "^8.21.0",
25 | "eslint-plugin-vue": "^9.5.1",
26 | "jsdom": "^20.0.0",
27 | "prettier": "^2.7.1",
28 | "vite": "^3.0.4",
29 | "vitest": "^0.21.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/demo-project/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/colada/6253de89595e6a19443fc45b6527aa0432e48321/demo-project/public/favicon.ico
--------------------------------------------------------------------------------
/demo-project/reload.sh:
--------------------------------------------------------------------------------
1 | cd ../colada-plugin
2 | npm run dev
3 | cd ../demo-project
4 | npm install ../colada-plugin
5 | npm run dev
--------------------------------------------------------------------------------
/demo-project/src/App.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
Count: {{counter.count}}
20 |
21 |
22 |
23 |
24 |
You wrote: {{ store.myStr }}
25 | there are {{ store.totalPeople }} people.
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/demo-project/src/assets/base.css:
--------------------------------------------------------------------------------
1 | /* color palette from */
2 | :root {
3 | --vt-c-white: #ffffff;
4 | --vt-c-white-soft: #f8f8f8;
5 | --vt-c-white-mute: #f2f2f2;
6 |
7 | --vt-c-black: #181818;
8 | --vt-c-black-soft: #222222;
9 | --vt-c-black-mute: #282828;
10 |
11 | --vt-c-indigo: #2c3e50;
12 |
13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
17 |
18 | --vt-c-text-light-1: var(--vt-c-indigo);
19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
20 | --vt-c-text-dark-1: var(--vt-c-white);
21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
22 | }
23 |
24 | /* semantic color variables for this project */
25 | :root {
26 | --color-background: var(--vt-c-white);
27 | --color-background-soft: var(--vt-c-white-soft);
28 | --color-background-mute: var(--vt-c-white-mute);
29 |
30 | --color-border: var(--vt-c-divider-light-2);
31 | --color-border-hover: var(--vt-c-divider-light-1);
32 |
33 | --color-heading: var(--vt-c-text-light-1);
34 | --color-text: var(--vt-c-text-light-1);
35 |
36 | --section-gap: 160px;
37 | }
38 |
39 | @media (prefers-color-scheme: dark) {
40 | :root {
41 | --color-background: var(--vt-c-black);
42 | --color-background-soft: var(--vt-c-black-soft);
43 | --color-background-mute: var(--vt-c-black-mute);
44 |
45 | --color-border: var(--vt-c-divider-dark-2);
46 | --color-border-hover: var(--vt-c-divider-dark-1);
47 |
48 | --color-heading: var(--vt-c-text-dark-1);
49 | --color-text: var(--vt-c-text-dark-2);
50 | }
51 | }
52 |
53 | *,
54 | *::before,
55 | *::after {
56 | box-sizing: border-box;
57 | margin: 0;
58 | position: relative;
59 | font-weight: normal;
60 | }
61 |
62 | body {
63 | min-height: 100vh;
64 | color: var(--color-text);
65 | background: var(--color-background);
66 | transition: color 0.5s, background-color 0.5s;
67 | line-height: 1.6;
68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
70 | font-size: 15px;
71 | text-rendering: optimizeLegibility;
72 | -webkit-font-smoothing: antialiased;
73 | -moz-osx-font-smoothing: grayscale;
74 | }
75 |
76 |
77 |
--------------------------------------------------------------------------------
/demo-project/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo-project/src/assets/main.css:
--------------------------------------------------------------------------------
1 | @import "./base.css";
2 |
3 | #app {
4 | max-width: 1280px;
5 | margin: 0 auto;
6 | padding: 2rem;
7 |
8 | font-weight: normal;
9 | }
10 |
11 | .wrapper{
12 | display:flex;
13 | flex-direction:column;
14 | justify-content:flex-start;
15 | align-items:center;
16 | gap:0.5rem;
17 | }
18 |
19 | .content-container{
20 | display:flex;
21 | justify-content:space-around;
22 | align-items:flex-start;
23 | gap:0.5rem;
24 | width:90%;
25 | }
26 |
27 | .btn-container{
28 | display:flex;
29 | flex-direction:column;
30 | justify-content:space-evenly;
31 | align-items:center;
32 | width:100%;
33 | }
34 |
35 | .text-container{
36 | width:100%;
37 | display:flex;
38 | flex-direction:column;
39 | justify-content:flex-start;
40 | align-items:flex-start;
41 | }
42 |
43 | a,
44 | .green {
45 | text-decoration: none;
46 | color: hsla(160, 100%, 37%, 1);
47 | transition: 0.4s;
48 | }
49 |
50 | .yellow{
51 | text-decoration:none;
52 | color:rgb(226, 206, 52);
53 | transition: 0.4s;
54 | }
55 |
56 | .btn{
57 | background-color:rgb(130, 99, 151);
58 | color:white;
59 | border:none;
60 | border-radius:0.5rem;
61 | padding:0.5rem 1rem;
62 | margin:0.5rem;
63 | }
64 |
65 | .btn:hover{
66 | background-color:rgb(151, 120, 172);
67 | cursor:pointer;
68 | }
69 |
70 | .btn:active{
71 | background-color:rgb(96, 72, 111);
72 | }
73 |
74 |
75 |
76 |
77 |
78 | @media (hover: hover) {
79 | a:hover {
80 | background-color: hsla(160, 100%, 37%, 0.2);
81 | }
82 | }
83 |
84 | /* @media (min-width: 1024px) {
85 | body {
86 | display: flex;
87 | place-items: center;
88 | }
89 |
90 | #app {
91 | display: grid;
92 | grid-template-columns: 1fr 1fr;
93 | padding: 0 2rem;
94 | }
95 | } */
96 |
--------------------------------------------------------------------------------
/demo-project/src/components/Counter.vue:
--------------------------------------------------------------------------------
1 |
2 | Increment
3 |
4 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/demo-project/src/components/DoubleStore.vue:
--------------------------------------------------------------------------------
1 |
2 | Increment & Submit
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demo-project/src/components/Header.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
{{ msg }}
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/demo-project/src/components/Main.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Hello {{ item.name }}
7 |
8 |
--------------------------------------------------------------------------------
/demo-project/src/components/WelcomeItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo-project/src/components/__tests__/HelloWorld.spec.js:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from "vitest";
2 |
3 | import { mount } from "@vue/test-utils";
4 | import HelloWorld from "../HelloWorld.vue";
5 |
6 | describe("HelloWorld", () => {
7 | it("renders properly", () => {
8 | const wrapper = mount(HelloWorld, { props: { msg: "Hello Vitest" } });
9 | expect(wrapper.text()).toContain("Hello Vitest");
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/demo-project/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import { createPinia } from "pinia";
3 | import Colada, {PiniaColadaPlugin} from 'colada-plugin';
4 | import App from "./App.vue";
5 |
6 | import "./assets/main.css";
7 |
8 | const app = createApp(App);
9 | const pinia = createPinia()
10 | app.use(pinia);
11 | pinia.use(PiniaColadaPlugin)
12 | app.use(Colada);
13 | app.mount("#app");
14 |
15 |
--------------------------------------------------------------------------------
/demo-project/src/stores/counter.js:
--------------------------------------------------------------------------------
1 | // stores/counter.js
2 | import { defineStore } from 'pinia'
3 |
4 | export const useCounterStore = defineStore('counter', {
5 | state: () => {
6 | return { count: 0 }
7 | },
8 | getters: {
9 | totalCount: (state) => state.elements.length,
10 | },
11 | actions: {
12 | increment() {
13 | this.count++
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/demo-project/src/stores/store.js:
--------------------------------------------------------------------------------
1 | import { defineStore } from "pinia";
2 |
3 | export const useStore = defineStore({
4 | id: "store",
5 | state: () => ({
6 | myStr: "",
7 | elements: [
8 | ],
9 | }),
10 | getters: {
11 | totalPeople: (state) => state.elements.length,
12 | },
13 | actions: {
14 | addPerson(val) {
15 | this.elements.push({ name: val });
16 | this.myStr = ""
17 | },
18 | },
19 | });
20 |
--------------------------------------------------------------------------------
/demo-project/vite.config.js:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from "node:url";
2 |
3 | import { defineConfig } from "vite";
4 | import vue from "@vitejs/plugin-vue";
5 | import vueJsx from "@vitejs/plugin-vue-jsx";
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [vue(), vueJsx()],
10 | resolve: {
11 | alias: {
12 | "@": fileURLToPath(new URL("./src", import.meta.url)),
13 | },
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "colada",
3 | "lockfileVersion": 2,
4 | "requires": true,
5 | "packages": {}
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------