eyeloupe 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +48 -10
  4. data/app/assets/builds/eyeloupe.css +1 -1
  5. data/app/assets/javascripts/eyeloupe/controllers/eyeloupe/ai_assistant_controller.js +27 -0
  6. data/app/assets/javascripts/eyeloupe/controllers/eyeloupe/refresh_controller.js +3 -1
  7. data/app/assets/stylesheets/application.tailwind.css +8 -0
  8. data/app/assets/stylesheets/eyeloupe/application.css +0 -1
  9. data/app/controllers/eyeloupe/ai_assistant_responses_controller.rb +30 -0
  10. data/app/controllers/eyeloupe/data_controller.rb +2 -0
  11. data/app/controllers/eyeloupe/exceptions_controller.rb +29 -0
  12. data/app/models/eyeloupe/exception.rb +6 -0
  13. data/app/models/eyeloupe/in_request.rb +1 -0
  14. data/app/models/eyeloupe/out_request.rb +1 -0
  15. data/app/views/eyeloupe/exceptions/_frame.html.erb +39 -0
  16. data/app/views/eyeloupe/exceptions/index.html.erb +13 -0
  17. data/app/views/eyeloupe/exceptions/show.html.erb +126 -0
  18. data/app/views/eyeloupe/in_requests/_frame.html.erb +1 -1
  19. data/app/views/eyeloupe/in_requests/index.html.erb +1 -1
  20. data/app/views/eyeloupe/in_requests/show.html.erb +17 -1
  21. data/app/views/eyeloupe/out_requests/_frame.html.erb +1 -1
  22. data/app/views/eyeloupe/out_requests/index.html.erb +1 -1
  23. data/app/views/eyeloupe/out_requests/show.html.erb +17 -1
  24. data/app/views/layouts/eyeloupe/application.html.erb +41 -5
  25. data/config/importmap.rb +2 -1
  26. data/config/routes.rb +2 -0
  27. data/db/migrate/20230604190442_create_eyeloupe_exceptions.rb +21 -0
  28. data/lib/eyeloupe/concerns/rescuable.rb +14 -0
  29. data/lib/eyeloupe/configuration.rb +7 -0
  30. data/lib/eyeloupe/engine.rb +46 -0
  31. data/lib/eyeloupe/processors/exception.rb +71 -0
  32. data/lib/eyeloupe/processors/in_request.rb +15 -4
  33. data/lib/eyeloupe/processors/out_request.rb +10 -20
  34. data/lib/eyeloupe/request_middleware.rb +13 -1
  35. data/lib/eyeloupe/version.rb +1 -1
  36. data/lib/eyeloupe.rb +4 -1
  37. metadata +28 -4
  38. data/lib/eyeloupe/http.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34e1fd5eb04fa196ab6163e97232eca59d4c3d20d31b1a7761de7bc6e0f0b96c
4
- data.tar.gz: 5d472b7ee15e5e699f99bb8f3342296bb32c8be364fa7b6e5f975c21965bed4e
3
+ metadata.gz: 5a2fc3039c5a9cdcbd79a0a5639b74c52c3fc42f0c96fc569f92ce38255e7768
4
+ data.tar.gz: 4de09b66849266fa6e43af59a55a60ab60464bb1310f8dff5a28a8e84904dac9
5
5
  SHA512:
6
- metadata.gz: '096bb0cc90cf6a836e13cc7894f46e6ba597046f0c3427e5a0322b32e4cdc7f89338b330ffdcd2538bf77e99f66804bb9e1880d7c2294f8582a1be448ac68297'
7
- data.tar.gz: e160242a1d2564176498e8ff6319cac337ac8ea5b241cc1a7ab2ab4ea93f1d2b633ccd78a84a069c75357dbc3912e66342cbd7175ac9ddd024395126015729e6
6
+ metadata.gz: 0c02695f8f4f26d09df3fda942691bf6729c9c1774b95acc1e9663e48c0ae25bad3d6991804a94163ff6595c8dbc8e56cb7f6dbd6f0be6eb195a2332446b5d9d
7
+ data.tar.gz: b61c44badf4b0713186338a76d0a246c1f87586d88e8404861931b326aafcfc8945977222e2296fc3d10aee2eb2f369df99cdcc84d4a8c9bb508453f579a6927
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.3.0
2
+
3
+ - Add exceptions: Framework + ActiveJob + Sidekiq Worker exceptions.
4
+ - Add OpenAI support for AI assistant in exceptions.
5
+ - Fix exceptions when using importmap binary by adding net/http override in railties initializer.
6
+
7
+ ## 0.2.0
8
+
9
+ - Fix missing require for `pagy` gem.
10
+
1
11
  ## 0.1.0
2
12
 
3
13
  - Initial release including incoming and outgoing requests.
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Gem Version](https://badge.fury.io/rb/eyeloupe.svg)](https://badge.fury.io/rb/eyeloupe)
2
+
1
3
  [![Contributors][contributors-shield]][contributors-url]
2
4
  [![Forks][forks-shield]][forks-url]
3
5
  [![Stargazers][stars-shield]][stars-url]
@@ -10,10 +12,10 @@
10
12
  <img src="app/assets/images/eyeloupe/logo.png" width=120 alt="Logo" >
11
13
  </a>
12
14
 
13
- <h3 align="center">Eyeloupe</h3>
15
+ <h3 align="center">Eyeloupe (beta)</h3>
14
16
 
15
17
  <p align="center">
16
- The elegant Rails debug assistant.
18
+ The elegant Rails debug assistant. AI powered.
17
19
  <br />
18
20
  <a href="https://github.com/alxlion/eyeloupe/issues">Report Bug</a>
19
21
  ·
@@ -23,7 +25,7 @@
23
25
 
24
26
  [![Eyeloupe screenshot][eyeloupe-screen]](https://github.com/alxlion/eyeloupe)
25
27
 
26
- Eyeloupe is the elegant Rails debug assistant. It helps you to debug your Rails application by providing a simple and elegant interface to view your incoming and outgoing requests, and a lot more.
28
+ Eyeloupe is the elegant Rails debug assistant. It helps you to debug your Rails application by providing a simple and elegant interface to view your incoming/outgoing requests and exceptions, powered by AI.
27
29
 
28
30
  ## Installation
29
31
  Add this line to your application's Gemfile:
@@ -47,23 +49,40 @@ And run the migrations:
47
49
  $ rails db:migrate
48
50
  ```
49
51
 
52
+ To access Eyeloupe dashboard you need to add the following route to your `config/routes.rb` file:
53
+ ```ruby
54
+ mount Eyeloupe::Engine => "/eyeloupe"
55
+ ```
56
+
50
57
  ## Configuration
51
58
 
52
59
  This is an example of the configuration you can add to your `initializers/eyeloupe.rb` file:
53
60
 
54
61
  ```ruby
55
62
  Eyeloupe.configure do |config|
56
- config.excluded_paths = %w[assets favicon.ico dance.riv service-worker.js manifest.json]
57
- config.capture = true
63
+ config.excluded_paths = %w[assets favicon.ico service-worker.js manifest.json]
64
+ config.capture = Rails.env.development?
65
+ config.openai_access_key = "your-openai-access-key"
66
+ config.openai_model = "gpt-4"
58
67
  end
59
68
  ```
60
69
 
61
70
  - `excluded_paths` is an array of paths you want to exclude from Eyeloupe capture. Eyeloupe adds these excluded paths to the default ones: ` %w[mini-profiler eyeloupe active_storage]`
62
71
  - `capture` is a boolean to enable/disable Eyeloupe capture. By default, it's set to `true`.
72
+ - `openai_access_key` is the access key to use the OpenAI API. You can get one [here](https://platform.openai.com/).
73
+ - `openai_model` is the model to use for the OpenAI API. You can find the list of available models [here](https://platform.openai.com/docs/models).
74
+
75
+ ### Exception handling
76
+
77
+ To be able to handle exceptions, be sure to disable the default Rails exception handling in your environment config file (e.g. `config/environments/development.rb`):
78
+
79
+ ```ruby
80
+ config.consider_all_requests_local = false
81
+ ```
63
82
 
64
83
  ## Usage
65
84
 
66
- Eyeloupe is exclusively developed for Rails framework.
85
+ Eyeloupe is exclusively developed for the Rails framework.
67
86
 
68
87
  You can use it in your development environment to debug your application but it's not recommended to use it in production.
69
88
 
@@ -75,6 +94,21 @@ By activating auto-fresh, every _3 seconds_ the page will be refreshed to show y
75
94
 
76
95
  You can delete all the data stored by Eyeloupe by clicking on the trash button.
77
96
 
97
+ ### AI Assistant
98
+
99
+ When you define an OpenAI access key in the configuration, you could see a new section in the exception details page. This section is powered by the OpenAI API and it's able to give you a solution to solve your exception.
100
+ It sends the entire content of the file containing the exception to have the best answer to your problem.
101
+
102
+ ![Eyeloupe ai_assistant](/doc/img/ai-assistant.gif)
103
+
104
+ ## Upgrade
105
+
106
+ When your upgrade Eyeloupe to the latest version, be sure to run the following commands:
107
+
108
+ ```bash
109
+ $ rails eyeloupe:install:migrations
110
+ $ rails db:migrate
111
+ ```
78
112
 
79
113
  ## Q/A
80
114
 
@@ -84,15 +118,17 @@ Eyeloupe is not a performance-oriented tool, the request time is the same you ca
84
118
 
85
119
  ### Is this the Laravel Telescope for Rails ?
86
120
 
87
- Yes, Eyeloupe is inspired by Laravel Telescope. Lot of people coming from Laravel are missing Telescope or looking for something similar, so Eyeloupe is here to fill this gap.
121
+ Yes, Eyeloupe is inspired by Laravel Telescope. A lot of people coming from Laravel are missing Telescope or looking for something similar, so Eyeloupe is here to fill this gap.
88
122
 
89
123
  ## Roadmap
90
124
 
91
- - [ ] Exceptions - Track all the exceptions thrown by your application
125
+ - [x] Exceptions - Track all the exceptions thrown by your application
126
+ - [x] AI assistant - Use OpenAI API to help you to solve your exceptions
92
127
  - [ ] Custom links to the menu - To access all of your debug tool in one place (Sidekiq web, Mailhog, etc.)
128
+ - [ ] Refactoring / clean code - To make the code more readable and maintainable
93
129
 
94
130
  ## Contributing
95
- Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
131
+ Contributions are what makes the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
96
132
 
97
133
  If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
98
134
  Don't forget to give the project a star! Thanks again!
@@ -126,4 +162,6 @@ Project Link: [https://github.com/alxlion/eyeloupe](https://github.com/alxlion/e
126
162
  [issues-url]: https://github.com/alxlion/eyeloupe/issues
127
163
  [license-shield]: https://img.shields.io/github/license/alxlion/eyeloupe.svg?style=for-the-badge
128
164
  [license-url]: https://github.com/alxlion/eyeloupe/blob/master/MIT-LICENSE.txt
129
- [eyeloupe-screen]: /doc/img/screen.png
165
+ [eyeloupe-screen]: /doc/img/screen.png
166
+ [gem-version]: https://badge.fury.io/rb/eyeloupe.svg
167
+ [gem-url]: https://rubygems.org/gems/eyeloupe
@@ -1 +1 @@
1
- /*! tailwindcss v3.1.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-family:Fira Sans,sans-serif,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{--tw-shadow:0 0 #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid #0000;outline-offset:2px}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}select{color-adjust:exact;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact}[multiple]{color-adjust:unset;background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset}[type=checkbox],[type=radio]{color-adjust:exact;--tw-shadow:0 0 #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;width:1rem}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid #0000;outline-offset:2px}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:#0000}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=checkbox]:indeterminate,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:#0000}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:#0000}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px auto -webkit-focus-ring-color}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:-webkit-sticky;position:sticky}.inset-0{bottom:0;left:0;right:0;top:0}.left-full{left:100%}.top-0{top:0}.z-50{z-index:50}.z-40{z-index:40}.-m-2\.5{margin:-.625rem}.-m-2{margin:-.5rem}.-mx-4{margin-left:-1rem;margin-right:-1rem}.-my-2{margin-bottom:-.5rem;margin-top:-.5rem}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.mx-auto{margin-left:auto;margin-right:auto}.mt-4{margin-top:1rem}.mt-2{margin-top:.5rem}.mt-8{margin-top:2rem}.mt-6{margin-top:1.5rem}.mt-1{margin-top:.25rem}.mr-16{margin-right:4rem}.ml-2{margin-left:.5rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.flow-root{display:flow-root}.hidden{display:none}.h-6{height:1.5rem}.h-16{height:4rem}.h-7{height:1.75rem}.h-12{height:3rem}.h-5{height:1.25rem}.w-full{width:100%}.w-16{width:4rem}.w-6{width:1.5rem}.w-auto{width:auto}.w-5{width:1.25rem}.min-w-full{min-width:100%}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.flex-col{flex-direction:column}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-y-5{row-gap:1.25rem}.gap-y-7{row-gap:1.75rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 213 219/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-t{border-top-width:1px}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity))}.bg-gray-900\/80{background-color:#111827cc}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-1{padding:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-10{padding-bottom:2.5rem;padding-top:2.5rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pl-3{padding-left:.75rem}.pr-4{padding-right:1rem}.pt-5{padding-top:1.25rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-2xl{font-size:1.5rem;line-height:2rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-6{line-height:1.5rem}.leading-7{line-height:1.75rem}.tracking-wide{letter-spacing:.025em}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-green-600\/20{--tw-ring-color:#16a34a33}.ring-blue-700\/10{--tw-ring-color:#1d4ed81a}.ring-yellow-600\/20{--tw-ring-color:#ca8a0433}.ring-red-600\/10{--tw-ring-color:#dc26261a}.ring-gray-500\/10{--tw-ring-color:#6b72801a}.ring-gray-500{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity))}.ring-purple-700\/10{--tw-ring-color:#7e22ce1a}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-500{transition-duration:.5s}.pagination{display:inline-flex;position:relative;z-index:0}.pagination>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(-1px*(1 - var(--tw-space-x-reverse)));margin-right:calc(-1px*var(--tw-space-x-reverse))}.pagination{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);border-radius:.375rem;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.pagination .prev a{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-bottom-left-radius:.375rem;border-color:rgb(209 213 219/var(--tw-border-opacity));border-top-left-radius:.375rem;border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.5rem;position:relative}.pagination .prev a:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .next a{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-bottom-right-radius:.375rem;border-color:rgb(209 213 219/var(--tw-border-opacity));border-top-right-radius:.375rem;border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.5rem;position:relative}.pagination .next a:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .current{--tw-text-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity));border-top-width:2px;color:rgb(239 68 68/var(--tw-text-opacity));padding-left:1rem;padding-right:1rem;padding-top:1rem}.pagination .current,.pagination a{--tw-border-opacity:1;align-items:center;display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem}.pagination a{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-color:rgb(209 213 219/var(--tw-border-opacity));border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));padding:.5rem 1rem;position:relative}.pagination a:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .disabled{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-color:rgb(209 213 219/var(--tw-border-opacity));border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;opacity:.4;padding:.5rem 1rem;position:relative}.pagination .disabled:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .gap{background-color:rgb(255 255 255/var(--tw-bg-opacity));border-color:rgb(209 213 219/var(--tw-border-opacity));color:rgb(55 65 81/var(--tw-text-opacity))}.pagination .active,.pagination .gap{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;border-width:1px;display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.5rem 1rem;position:relative}.pagination .active{background-color:rgb(254 242 242/var(--tw-bg-opacity));border-color:rgb(239 68 68/var(--tw-border-opacity));color:rgb(239 68 68/var(--tw-text-opacity));z-index:10}.pagination .next_page,.pagination .previous_page{display:none}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(239 68 68/var(--tw-ring-opacity))}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mt-0{margin-top:0}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:flex-auto{flex:1 1 auto}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:items-center{align-items:center}.sm\:gap-4{gap:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-0{padding-right:0}.sm\:pl-0,.sm\:px-0{padding-left:0}.sm\:pr-0{padding-right:0}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:1024px){.lg\:fixed{position:fixed}.lg\:inset-y-0{bottom:0;top:0}.lg\:z-50{z-index:50}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-72{width:18rem}.lg\:flex-col{flex-direction:column}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:pl-72{padding-left:18rem}}
1
+ /*! tailwindcss v3.1.3 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-family:Fira Sans,sans-serif,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],select,textarea{--tw-shadow:0 0 #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,select:focus,textarea:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid #0000;outline-offset:2px}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em}select{color-adjust:exact;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact}[multiple]{color-adjust:unset;background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset}[type=checkbox],[type=radio]{color-adjust:exact;--tw-shadow:0 0 #0000;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;width:1rem}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);outline:2px solid #0000;outline-offset:2px}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:#0000}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E")}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=checkbox]:indeterminate,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:#0000}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:#0000}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px auto -webkit-focus-ring-color}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:-webkit-sticky;position:sticky}.inset-0{bottom:0;left:0;right:0;top:0}.left-full{left:100%}.top-0{top:0}.z-50{z-index:50}.z-40{z-index:40}.col-span-1{grid-column:span 1/span 1}.col-span-11{grid-column:span 11/span 11}.-m-2\.5{margin:-.625rem}.-m-2{margin:-.5rem}.-mx-4{margin-left:-1rem;margin-right:-1rem}.-my-2{margin-bottom:-.5rem;margin-top:-.5rem}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.mx-auto{margin-left:auto;margin-right:auto}.mt-4{margin-top:1rem}.mt-2{margin-top:.5rem}.mt-8{margin-top:2rem}.mt-6{margin-top:1.5rem}.mt-1{margin-top:.25rem}.mr-16{margin-right:4rem}.ml-2{margin-left:.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.h-6{height:1.5rem}.h-16{height:4rem}.h-7{height:1.75rem}.h-12{height:3rem}.h-5{height:1.25rem}.w-full{width:100%}.w-16{width:4rem}.w-6{width:1.5rem}.w-auto{width:auto}.w-5{width:1.25rem}.min-w-full{min-width:100%}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.flex-col{flex-direction:column}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.gap-y-5{row-gap:1.25rem}.gap-y-7{row-gap:1.75rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-gray-300>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(209 213 219/var(--tw-divide-opacity))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-t{border-top-width:1px}.border-b{border-bottom-width:1px}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity))}.bg-gray-900\/80{background-color:#111827cc}.bg-opacity-30{--tw-bg-opacity:0.3}.p-2{padding:.5rem}.\!p-0{padding:0!important}.p-2\.5{padding:.625rem}.p-1{padding:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.px-4{padding-left:1rem;padding-right:1rem}.py-5{padding-bottom:1.25rem;padding-top:1.25rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-0{padding-left:0;padding-right:0}.py-1{padding-bottom:.25rem;padding-top:.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-10{padding-bottom:2.5rem;padding-top:2.5rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pl-3{padding-left:.75rem}.pr-4{padding-right:1rem}.pt-2{padding-top:.5rem}.pb-4{padding-bottom:1rem}.pb-3{padding-bottom:.75rem}.pt-5{padding-top:1.25rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-right{text-align:right}.align-middle{vertical-align:middle}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-2xl{font-size:1.5rem;line-height:2rem}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-6{line-height:1.5rem}.leading-7{line-height:1.75rem}.tracking-wide{letter-spacing:.025em}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity))}.underline{-webkit-text-decoration-line:underline;text-decoration-line:underline}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-green-600\/20{--tw-ring-color:#16a34a33}.ring-blue-700\/10{--tw-ring-color:#1d4ed81a}.ring-yellow-600\/20{--tw-ring-color:#ca8a0433}.ring-red-600\/10{--tw-ring-color:#dc26261a}.ring-gray-500\/10{--tw-ring-color:#6b72801a}.ring-gray-500{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity))}.ring-purple-700\/10{--tw-ring-color:#7e22ce1a}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-500{transition-duration:.5s}.pagination{display:inline-flex;position:relative;z-index:0}.pagination>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(-1px*(1 - var(--tw-space-x-reverse)));margin-right:calc(-1px*var(--tw-space-x-reverse))}.pagination{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);border-radius:.375rem;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.pagination .prev a{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-bottom-left-radius:.375rem;border-color:rgb(209 213 219/var(--tw-border-opacity));border-top-left-radius:.375rem;border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.5rem;position:relative}.pagination .prev a:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .next a{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-bottom-right-radius:.375rem;border-color:rgb(209 213 219/var(--tw-border-opacity));border-top-right-radius:.375rem;border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.5rem;position:relative}.pagination .next a:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .current{--tw-text-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity));border-top-width:2px;color:rgb(239 68 68/var(--tw-text-opacity));padding-left:1rem;padding-right:1rem;padding-top:1rem}.pagination .current,.pagination a{--tw-border-opacity:1;align-items:center;display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem}.pagination a{--tw-bg-opacity:1;--tw-text-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-color:rgb(209 213 219/var(--tw-border-opacity));border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));padding:.5rem 1rem;position:relative}.pagination a:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .disabled{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;background-color:rgb(255 255 255/var(--tw-bg-opacity));border-color:rgb(209 213 219/var(--tw-border-opacity));border-width:1px;color:rgb(107 114 128/var(--tw-text-opacity));display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;opacity:.4;padding:.5rem 1rem;position:relative}.pagination .disabled:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.pagination .gap{background-color:rgb(255 255 255/var(--tw-bg-opacity));border-color:rgb(209 213 219/var(--tw-border-opacity));color:rgb(55 65 81/var(--tw-text-opacity))}.pagination .active,.pagination .gap{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1;align-items:center;border-width:1px;display:inline-flex;font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.5rem 1rem;position:relative}.pagination .active{background-color:rgb(254 242 242/var(--tw-bg-opacity));border-color:rgb(239 68 68/var(--tw-border-opacity));color:rgb(239 68 68/var(--tw-text-opacity));z-index:10}.pagination .next_page,.pagination .previous_page{display:none}#result pre{background-color:rgb(0 0 0/var(--tw-bg-opacity));color:rgb(255 255 255/var(--tw-text-opacity));margin-bottom:.5rem;margin-top:.5rem;padding:.75rem}#result p code,#result pre{--tw-bg-opacity:1;--tw-text-opacity:1;border-radius:.375rem}#result p code{background-color:rgb(229 231 235/var(--tw-bg-opacity));color:rgb(75 85 99/var(--tw-text-opacity));font-size:.875rem;line-height:1.25rem;padding:.125rem .5rem}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-red-400:hover{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(239 68 68/var(--tw-ring-opacity))}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:-mx-6{margin-left:-1.5rem;margin-right:-1.5rem}.sm\:mt-0{margin-top:0}.sm\:flex{display:flex}.sm\:grid{display:grid}.sm\:flex-auto{flex:1 1 auto}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:items-center{align-items:center}.sm\:gap-4{gap:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:px-0{padding-right:0}.sm\:pl-0,.sm\:px-0{padding-left:0}.sm\:pr-0{padding-right:0}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:1024px){.lg\:fixed{position:fixed}.lg\:inset-y-0{bottom:0;top:0}.lg\:z-50{z-index:50}.lg\:-mx-8{margin-left:-2rem;margin-right:-2rem}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-72{width:18rem}.lg\:flex-col{flex-direction:column}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:pl-72{padding-left:18rem}}
@@ -0,0 +1,27 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import showdown from "showdown"
3
+ export default class extends Controller {
4
+ static targets = ["result", "loader", "btn"]
5
+ static values = { url: String }
6
+
7
+ assist() {
8
+ this.btnTarget.classList.add("hidden")
9
+ this.resultTarget.innerHTML = ""
10
+ this.loaderTarget.classList.remove("hidden")
11
+
12
+ fetch(this.urlValue)
13
+ .then(response => response.json())
14
+ .then(json => {
15
+ let result = json.choices[0].message.content
16
+ let converter = new showdown.Converter()
17
+ this.resultTarget.innerHTML = converter.makeHtml(result)
18
+
19
+ })
20
+ .catch(error => {
21
+ this.resultTarget.innerHTML = error
22
+ })
23
+ .finally(() => {
24
+ this.loaderTarget.classList.add("hidden")
25
+ })
26
+ }
27
+ }
@@ -43,7 +43,9 @@ export default class extends Controller {
43
43
  }
44
44
 
45
45
  _fetch() {
46
- this.frameTarget.reload()
46
+ if (this.hasFrameTarget) {
47
+ this.frameTarget.reload()
48
+ }
47
49
  }
48
50
 
49
51
  get enabled() {
@@ -39,4 +39,12 @@
39
39
 
40
40
  .pagination .previous_page, .pagination .next_page {
41
41
  @apply hidden;
42
+ }
43
+
44
+ #result pre {
45
+ @apply bg-black text-white p-3 rounded-md my-2;
46
+ }
47
+
48
+ #result p code {
49
+ @apply bg-gray-200 text-gray-600 px-2 py-0.5 text-sm rounded-md;
42
50
  }
@@ -13,4 +13,3 @@
13
13
  *= require_tree .
14
14
  *= require_self
15
15
  */
16
-
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eyeloupe
4
+ class AiAssistantResponsesController < ApplicationController
5
+
6
+ before_action :set_exception, only: %i[ show ]
7
+
8
+ def show
9
+ client = OpenAI::Client.new
10
+
11
+ code = File.read(@exception.file)
12
+
13
+ @response = client.chat(
14
+ parameters: {
15
+ model: Eyeloupe::configuration.openai_model,
16
+ messages: [{"role": "system", "content": "You are a Ruby on Rails software developer, you develop software programs and applications using programming languages like Ruby and Ruby on Rails and development tools."},
17
+ {"role": "user", "content": "I have a problem with my Ruby on Rails application. I am getting an error message that says: #{@exception.kind} #{@exception.message}. Here is my code, the error is in line #{@exception.line}: #{code}. Answer as concise as possible. Show me resulting code. The response should in Markdown format."}],
18
+ temperature: 0.7
19
+ })
20
+
21
+ render json: @response
22
+ end
23
+
24
+ private
25
+
26
+ def set_exception
27
+ @exception = Exception.find(params[:id])
28
+ end
29
+ end
30
+ end
@@ -7,7 +7,9 @@ module Eyeloupe
7
7
  # Delete all data in the database
8
8
  # DELETE /data
9
9
  def destroy
10
+ Exception.destroy_all
10
11
  InRequest.destroy_all
12
+ OutRequest.destroy_all
11
13
  redirect_to root_path, status: 303
12
14
  end
13
15
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eyeloupe
4
+ class ExceptionsController < ApplicationController
5
+
6
+ before_action :set_exception, only: %i[ show ]
7
+
8
+ # GET /out_requests
9
+ def index
10
+ @pagy, @exceptions = pagy(Exception.all.order(created_at: :desc), items: 50)
11
+
12
+ render partial: 'frame' if params[:frame].present?
13
+ end
14
+
15
+ # GET /out_requests/1
16
+ def show
17
+ start = @exception.line - 5
18
+ start = 0 if start < 0
19
+ @line_numbers = [*start..@exception.line+6]
20
+ end
21
+
22
+ private
23
+ # Use callbacks to share common setup or constraints between actions.
24
+ def set_exception
25
+ @exception = Exception.find(params[:id])
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ module Eyeloupe
2
+ class Exception < ApplicationRecord
3
+ has_one :in_request, class_name: "Eyeloupe::InRequest"
4
+ has_one :out_request, class_name: "Eyeloupe::OutRequest"
5
+ end
6
+ end
@@ -1,4 +1,5 @@
1
1
  module Eyeloupe
2
2
  class InRequest < ApplicationRecord
3
+ has_one :exception, class_name: "Eyeloupe::Exception"
3
4
  end
4
5
  end
@@ -1,4 +1,5 @@
1
1
  module Eyeloupe
2
2
  class OutRequest < ApplicationRecord
3
+ has_one :exception, class_name: "Eyeloupe::Exception"
3
4
  end
4
5
  end
@@ -0,0 +1,39 @@
1
+ <%= turbo_frame_tag "frame" do %>
2
+ <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
3
+ <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
4
+
5
+ <table class="min-w-full divide-y divide-gray-300">
6
+ <thead>
7
+ <tr>
8
+ <th scope="col" class="py-3 pl-4 pr-3 text-left text-sm font-medium uppercase tracking-wide text-gray-500 sm:pl-0">Kind</th>
9
+ <th scope="col" class="px-3 py-3 text-left text-sm font-medium uppercase tracking-wide text-gray-500">Count</th>
10
+ <th scope="col" class="px-3 py-3 text-left text-sm font-medium uppercase tracking-wide text-gray-500">Occured</th>
11
+ <th scope="col" class="relative py-3 pl-3 pr-4 sm:pr-0">
12
+ <span class="sr-only">Details</span>
13
+ </th>
14
+ </tr>
15
+ </thead>
16
+ <tbody class="divide-y divide-gray-200">
17
+ <% @exceptions.each do |ex| %>
18
+ <tr>
19
+ <td class="whitespace-nowrap py-4 pl-4 pr-3 text-base font-medium text-gray-900 sm:pl-0">
20
+ <%= ex.kind %>
21
+ <p class="text-sm font-normal text-gray-500"><%= ex.message.truncate(100) %></p>
22
+ </td>
23
+ <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500"><%= ex.count %></td>
24
+ <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500"><%= distance_of_time_in_words(ex.updated_at, DateTime.now) %></td>
25
+ <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-base font-medium sm:pr-0">
26
+ <%= link_to "Details", exception_path(ex), class: "text-gray-600 hover:text-gray-900", data: {"turbo_frame": "_top"} %>
27
+ </td>
28
+ </tr>
29
+ <% end %>
30
+ </tbody>
31
+ </table>
32
+ </div>
33
+ <aside class="mt-4 px-4 py-3 flex items-center justify-center sm:px-6" aria-label="Pagination">
34
+ <div class="flex-1 flex justify-center">
35
+ <%== pagy_nav(@pagy) %>
36
+ </div>
37
+ </aside>
38
+ </div>
39
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <div class="px-4 sm:px-6 lg:px-8 bg-white rounded-md shadow-md py-5">
2
+ <div class="sm:flex sm:items-center">
3
+ <div class="sm:flex-auto">
4
+ <h1 class="text-xl font-semibold leading-6 text-gray-900">Exceptions</h1>
5
+ <p class="mt-2 text-sm text-gray-700">All exceptions that have been raised in the application.</p>
6
+ </div>
7
+ <div>
8
+ </div>
9
+ </div>
10
+ <div class="mt-8 flow-root">
11
+ <%= turbo_frame_tag "frame", src: exceptions_path(frame: true), data: {"eyeloupe--refresh-target": "frame"} do %><% end %>
12
+ </div>
13
+ </div>
@@ -0,0 +1,126 @@
1
+ <div class="px-4 sm:px-6 lg:px-8 bg-white rounded-md shadow-md py-5">
2
+ <div class="px-4 sm:px-0">
3
+ <h3 class="text-xl font-semibold leading-7 text-gray-900">Exception Details</h3>
4
+ </div>
5
+ <div class="mt-6 border-t border-gray-100">
6
+ <dl class="divide-y divide-gray-100">
7
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
8
+ <dt class="text-base font-medium leading-6 text-gray-900">Time</dt>
9
+ <dd class="mt-1 text-base leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
10
+ <%= @exception.created_at.to_formatted_s(:long) %> (<%= distance_of_time_in_words(@exception.updated_at, DateTime.now) %>)
11
+ </dd>
12
+ </div>
13
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
14
+ <dt class="text-base font-medium leading-6 text-gray-900">Hostname</dt>
15
+ <dd class="mt-1 text-base leading-6 text-gray-700 sm:col-span-2 sm:mt-0"><%= @exception.hostname %></dd>
16
+ </div>
17
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
18
+ <dt class="text-base font-medium leading-6 text-gray-900">Kind</dt>
19
+ <dd class="mt-1 text-base leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
20
+ <%= @exception.kind %>
21
+ </dd>
22
+ </div>
23
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
24
+ <dt class="text-base font-medium leading-6 text-gray-900">Count</dt>
25
+ <dd class="mt-1 text-base leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
26
+ <%= @exception.count %>
27
+ </dd>
28
+ </div>
29
+ <% if @exception.in_request_id.present? %>
30
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
31
+ <dt class="text-base font-medium leading-6 text-gray-900">Request</dt>
32
+ <dd class="mt-1 text-base leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
33
+ <%= link_to "View request", in_request_path(@exception.in_request_id), class: "underline" %>
34
+ </dd>
35
+ </div>
36
+ <% end %>
37
+ <% if @exception.out_request_id.present? %>
38
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
39
+ <dt class="text-base font-medium leading-6 text-gray-900">Request</dt>
40
+ <dd class="mt-1 text-base leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
41
+ <%= link_to "View request", out_request_path(@exception.out_request_id), class: "underline" %>
42
+ </dd>
43
+ </div>
44
+ <% end %>
45
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
46
+ <dt class="text-base font-medium leading-6 text-gray-900">Message</dt>
47
+ <dd class="mt-1 text-base leading-6 sm:col-span-2 sm:mt-0 rounded-md bg-black text-white overflow-x-auto">
48
+ <pre class="p-2"><%= @exception.message %></pre>
49
+ </dd>
50
+ </div>
51
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
52
+ <dt class="text-base font-medium leading-6 text-gray-900">Backtrace</dt>
53
+ <dd class="mt-1 text-base leading-6 sm:col-span-2 sm:mt-0 rounded-md bg-black text-white overflow-x-auto">
54
+ <pre class="p-2"><%= JSON.parse(@exception.stacktrace).join("\n") %></pre>
55
+ </dd>
56
+ </div>
57
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
58
+ <dt class="text-base font-medium leading-6 text-gray-900">Code preview</dt>
59
+ <dd class="mt-1 text-base leading-6 sm:col-span-2 sm:mt-0 rounded-md bg-black text-white overflow-x-auto px-2 pt-2">
60
+ <p class="pb-4 font-semibold"><%= @exception.file %></p>
61
+ <% JSON.parse(@exception.location).each_with_index do |line, i| %>
62
+ <div class="grid grid-cols-12 gap-x-1 <%= @line_numbers[i] + 1 == @exception.line ? "bg-red-500 bg-opacity-30" : "" %>">
63
+ <span class="text-right col-span-1 font-medium text-base text-gray-300"><%= @line_numbers[i] + 1 %></span>
64
+ <pre class="col-span-11"><code class="!p-0"><%= line %></code></pre>
65
+ </div>
66
+ <% end %>
67
+ </dd>
68
+ </div>
69
+ <% if Eyeloupe::configuration.openai_access_key.present? %>
70
+ <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0" data-controller="eyeloupe--ai-assistant" data-eyeloupe--ai-assistant-url-value="<%= ai_assistant_response_url(@exception) %>">
71
+ <dt class="text-base font-medium leading-6 text-gray-900">AI Assistant</dt>
72
+ <dd class="mt-1 text-base leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
73
+ <button data-action="eyeloupe--ai-assistant#assist" data-eyeloupe--ai-assistant-target="btn" class="rounded-md px-2 py-1 text-white bg-red-500 text-xl hover:shadow-md hover:bg-red-400 transition flex gap-x-2">
74
+ <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-robot" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
75
+ <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
76
+ <path d="M7 7h10a2 2 0 0 1 2 2v1l1 1v3l-1 1v3a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-3l-1 -1v-3l1 -1v-1a2 2 0 0 1 2 -2z"></path>
77
+ <path d="M10 16h4"></path>
78
+ <circle cx="8.5" cy="11.5" r=".5" fill="currentColor"></circle>
79
+ <circle cx="15.5" cy="11.5" r=".5" fill="currentColor"></circle>
80
+ <path d="M9 7l-1 -4"></path>
81
+ <path d="M15 7l1 -4"></path>
82
+ </svg>
83
+ <span>Click to get some help</span>
84
+ </button>
85
+
86
+ <svg data-eyeloupe--ai-assistant-target="loader" width="50" class="hidden" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" fill="#ff3130">
87
+ <circle cx="15" cy="15" r="15">
88
+ <animate attributeName="r" from="15" to="15"
89
+ begin="0s" dur="0.8s"
90
+ values="15;9;15" calcMode="linear"
91
+ repeatCount="indefinite" />
92
+ <animate attributeName="fill-opacity" from="1" to="1"
93
+ begin="0s" dur="0.8s"
94
+ values="1;.5;1" calcMode="linear"
95
+ repeatCount="indefinite" />
96
+ </circle>
97
+ <circle cx="60" cy="15" r="9" fill-opacity="0.3">
98
+ <animate attributeName="r" from="9" to="9"
99
+ begin="0s" dur="0.8s"
100
+ values="9;15;9" calcMode="linear"
101
+ repeatCount="indefinite" />
102
+ <animate attributeName="fill-opacity" from="0.5" to="0.5"
103
+ begin="0s" dur="0.8s"
104
+ values=".5;1;.5" calcMode="linear"
105
+ repeatCount="indefinite" />
106
+ </circle>
107
+ <circle cx="105" cy="15" r="15">
108
+ <animate attributeName="r" from="15" to="15"
109
+ begin="0s" dur="0.8s"
110
+ values="15;9;15" calcMode="linear"
111
+ repeatCount="indefinite" />
112
+ <animate attributeName="fill-opacity" from="1" to="1"
113
+ begin="0s" dur="0.8s"
114
+ values="1;.5;1" calcMode="linear"
115
+ repeatCount="indefinite" />
116
+ </circle>
117
+ </svg>
118
+
119
+ <p data-eyeloupe--ai-assistant-target="result" id="result"></p>
120
+ </dd>
121
+ </div>
122
+ <% end %>
123
+ </dl>
124
+ </div>
125
+
126
+ </div>
@@ -21,7 +21,7 @@
21
21
  <td class="whitespace-nowrap py-4 pl-4 pr-3 text-base font-medium text-gray-900 sm:pl-0">
22
22
  <%= render "eyeloupe/shared/verb", verb: request.verb %>
23
23
  </td>
24
- <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500"><%= request.path.truncate(50) %></td>
24
+ <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500"><%= request.path.truncate(100) %></td>
25
25
  <td class="whitespace-nowrap px-3 py-4 text-base text-gray-500">
26
26
  <%= render "eyeloupe/shared/status_code", code: request.status %>
27
27
  </td>
@@ -1,4 +1,4 @@
1
- <div data-controller="eyeloupe--search">
1
+ <div data-controller="eyeloupe--search" class="px-4 sm:px-6 lg:px-8 bg-white rounded-md shadow-md py-5">
2
2
  <div class="sm:flex sm:items-center">
3
3
  <div class="sm:flex-auto">
4
4
  <h1 class="text-xl font-semibold leading-6 text-gray-900">Requests</h1>