rails_pulse 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +638 -0
- data/Rakefile +207 -0
- data/app/assets/images/rails_pulse/dashboard.png +0 -0
- data/app/assets/images/rails_pulse/menu.svg +1 -0
- data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
- data/app/assets/images/rails_pulse/request.png +0 -0
- data/app/assets/images/rails_pulse/routes.png +0 -0
- data/app/assets/stylesheets/rails_pulse/application.css +102 -0
- data/app/assets/stylesheets/rails_pulse/components/alert.css +24 -0
- data/app/assets/stylesheets/rails_pulse/components/badge.css +58 -0
- data/app/assets/stylesheets/rails_pulse/components/base.css +79 -0
- data/app/assets/stylesheets/rails_pulse/components/breadcrumb.css +31 -0
- data/app/assets/stylesheets/rails_pulse/components/button.css +99 -0
- data/app/assets/stylesheets/rails_pulse/components/card.css +19 -0
- data/app/assets/stylesheets/rails_pulse/components/chart.css +18 -0
- data/app/assets/stylesheets/rails_pulse/components/csp_safe_positioning.css +86 -0
- data/app/assets/stylesheets/rails_pulse/components/descriptive_list.css +9 -0
- data/app/assets/stylesheets/rails_pulse/components/dialog.css +56 -0
- data/app/assets/stylesheets/rails_pulse/components/flash.css +47 -0
- data/app/assets/stylesheets/rails_pulse/components/input.css +80 -0
- data/app/assets/stylesheets/rails_pulse/components/layouts.css +63 -0
- data/app/assets/stylesheets/rails_pulse/components/menu.css +43 -0
- data/app/assets/stylesheets/rails_pulse/components/popover.css +36 -0
- data/app/assets/stylesheets/rails_pulse/components/prose.css +144 -0
- data/app/assets/stylesheets/rails_pulse/components/row.css +24 -0
- data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +79 -0
- data/app/assets/stylesheets/rails_pulse/components/skeleton.css +5 -0
- data/app/assets/stylesheets/rails_pulse/components/table.css +37 -0
- data/app/assets/stylesheets/rails_pulse/components/utilities.css +36 -0
- data/app/controllers/concerns/chart_table_concern.rb +82 -0
- data/app/controllers/concerns/response_range_concern.rb +24 -0
- data/app/controllers/concerns/time_range_concern.rb +67 -0
- data/app/controllers/concerns/zoom_range_concern.rb +40 -0
- data/app/controllers/rails_pulse/application_controller.rb +67 -0
- data/app/controllers/rails_pulse/assets_controller.rb +33 -0
- data/app/controllers/rails_pulse/caches_controller.rb +115 -0
- data/app/controllers/rails_pulse/csp_test_controller.rb +57 -0
- data/app/controllers/rails_pulse/dashboard_controller.rb +6 -0
- data/app/controllers/rails_pulse/operations_controller.rb +219 -0
- data/app/controllers/rails_pulse/queries_controller.rb +121 -0
- data/app/controllers/rails_pulse/requests_controller.rb +69 -0
- data/app/controllers/rails_pulse/routes_controller.rb +99 -0
- data/app/helpers/rails_pulse/application_helper.rb +111 -0
- data/app/helpers/rails_pulse/breadcrumbs_helper.rb +62 -0
- data/app/helpers/rails_pulse/cached_component_helper.rb +73 -0
- data/app/helpers/rails_pulse/chart_formatters.rb +43 -0
- data/app/helpers/rails_pulse/chart_helper.rb +140 -0
- data/app/helpers/rails_pulse/formatting_helper.rb +29 -0
- data/app/helpers/rails_pulse/status_helper.rb +279 -0
- data/app/helpers/rails_pulse/table_helper.rb +54 -0
- data/app/javascript/rails_pulse/application.js +119 -0
- data/app/javascript/rails_pulse/controllers/color_scheme_controller.js +20 -0
- data/app/javascript/rails_pulse/controllers/context_menu_controller.js +16 -0
- data/app/javascript/rails_pulse/controllers/dialog_controller.js +21 -0
- data/app/javascript/rails_pulse/controllers/expandable_row_controller.js +67 -0
- data/app/javascript/rails_pulse/controllers/form_controller.js +39 -0
- data/app/javascript/rails_pulse/controllers/icon_controller.js +170 -0
- data/app/javascript/rails_pulse/controllers/index_controller.js +230 -0
- data/app/javascript/rails_pulse/controllers/menu_controller.js +60 -0
- data/app/javascript/rails_pulse/controllers/pagination_controller.js +69 -0
- data/app/javascript/rails_pulse/controllers/popover_controller.js +91 -0
- data/app/javascript/rails_pulse/controllers/timezone_controller.js +106 -0
- data/app/javascript/rails_pulse/theme.js +416 -0
- data/app/jobs/rails_pulse/application_job.rb +4 -0
- data/app/jobs/rails_pulse/cleanup_job.rb +21 -0
- data/app/mailers/rails_pulse/application_mailer.rb +6 -0
- data/app/models/rails_pulse/application_record.rb +7 -0
- data/app/models/rails_pulse/component_cache_key.rb +33 -0
- data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +27 -0
- data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +37 -0
- data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +59 -0
- data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +45 -0
- data/app/models/rails_pulse/operation.rb +87 -0
- data/app/models/rails_pulse/queries/cards/average_query_times.rb +52 -0
- data/app/models/rails_pulse/queries/cards/execution_rate.rb +57 -0
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +71 -0
- data/app/models/rails_pulse/queries/charts/average_query_times.rb +112 -0
- data/app/models/rails_pulse/query.rb +58 -0
- data/app/models/rails_pulse/request.rb +64 -0
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +99 -0
- data/app/models/rails_pulse/requests/charts/operations_chart.rb +35 -0
- data/app/models/rails_pulse/route.rb +77 -0
- data/app/models/rails_pulse/routes/cards/average_response_times.rb +54 -0
- data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +73 -0
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +73 -0
- data/app/models/rails_pulse/routes/cards/request_count_totals.rb +59 -0
- data/app/models/rails_pulse/routes/charts/average_response_times.rb +115 -0
- data/app/models/rails_pulse/routes/tables/index.rb +63 -0
- data/app/services/rails_pulse/sql_query_normalizer.rb +124 -0
- data/app/views/layouts/rails_pulse/_menu_items.html.erb +19 -0
- data/app/views/layouts/rails_pulse/_sidebar_menu.html.erb +44 -0
- data/app/views/layouts/rails_pulse/application.html.erb +72 -0
- data/app/views/rails_pulse/caches/show.html.erb +9 -0
- data/app/views/rails_pulse/components/_breadcrumbs.html.erb +12 -0
- data/app/views/rails_pulse/components/_code_panel.html.erb +12 -0
- data/app/views/rails_pulse/components/_metric_card.html.erb +55 -0
- data/app/views/rails_pulse/components/_metric_row.html.erb +9 -0
- data/app/views/rails_pulse/components/_operation_details_popover.html.erb +241 -0
- data/app/views/rails_pulse/components/_panel.html.erb +56 -0
- data/app/views/rails_pulse/components/_sparkline_stats.html.erb +15 -0
- data/app/views/rails_pulse/components/_table.html.erb +50 -0
- data/app/views/rails_pulse/components/_table_head.html.erb +20 -0
- data/app/views/rails_pulse/components/_table_pagination.html.erb +45 -0
- data/app/views/rails_pulse/components/_time_period.html.erb +16 -0
- data/app/views/rails_pulse/csp_test/show.html.erb +207 -0
- data/app/views/rails_pulse/dashboard/charts/_bar_chart.html.erb +1 -0
- data/app/views/rails_pulse/dashboard/index.html.erb +64 -0
- data/app/views/rails_pulse/dashboard/tables/_routes_table.html.erb +32 -0
- data/app/views/rails_pulse/dashboard/tables/_standard_table.html.erb +1 -0
- data/app/views/rails_pulse/operations/_operation_analysis_application.html.erb +43 -0
- data/app/views/rails_pulse/operations/_operation_analysis_database.html.erb +12 -0
- data/app/views/rails_pulse/operations/_operation_analysis_generic.html.erb +15 -0
- data/app/views/rails_pulse/operations/_operation_analysis_other.html.erb +69 -0
- data/app/views/rails_pulse/operations/_operation_analysis_view.html.erb +39 -0
- data/app/views/rails_pulse/operations/show.html.erb +79 -0
- data/app/views/rails_pulse/queries/_show_table.html.erb +19 -0
- data/app/views/rails_pulse/queries/_table.html.erb +31 -0
- data/app/views/rails_pulse/queries/index.html.erb +64 -0
- data/app/views/rails_pulse/queries/show.html.erb +86 -0
- data/app/views/rails_pulse/requests/_operations.html.erb +85 -0
- data/app/views/rails_pulse/requests/_table.html.erb +31 -0
- data/app/views/rails_pulse/requests/index.html.erb +64 -0
- data/app/views/rails_pulse/requests/show.html.erb +44 -0
- data/app/views/rails_pulse/routes/_table.html.erb +29 -0
- data/app/views/rails_pulse/routes/index.html.erb +65 -0
- data/app/views/rails_pulse/routes/show.html.erb +67 -0
- data/app/views/rails_pulse/skeletons/_chart.html.erb +3 -0
- data/app/views/rails_pulse/skeletons/_metric_card.html.erb +20 -0
- data/app/views/rails_pulse/skeletons/_panel.html.erb +19 -0
- data/app/views/rails_pulse/skeletons/_table.html.erb +8 -0
- data/config/importmap.rb +12 -0
- data/config/initializers/rails_charts_csp_patch.rb +83 -0
- data/config/initializers/rails_pulse.rb +198 -0
- data/config/routes.rb +16 -0
- data/db/migrate/20250227235904_create_routes.rb +12 -0
- data/db/migrate/20250227235915_create_requests.rb +19 -0
- data/db/migrate/20250228000000_create_queries.rb +14 -0
- data/db/migrate/20250228000056_create_operations.rb +24 -0
- data/lib/generators/rails_pulse/install_generator.rb +17 -0
- data/lib/generators/rails_pulse/templates/rails_pulse.rb +198 -0
- data/lib/rails_pulse/cleanup_service.rb +212 -0
- data/lib/rails_pulse/configuration.rb +176 -0
- data/lib/rails_pulse/engine.rb +88 -0
- data/lib/rails_pulse/middleware/asset_server.rb +84 -0
- data/lib/rails_pulse/middleware/request_collector.rb +120 -0
- data/lib/rails_pulse/migration.rb +29 -0
- data/lib/rails_pulse/subscribers/operation_subscriber.rb +280 -0
- data/lib/rails_pulse/version.rb +3 -0
- data/lib/rails_pulse.rb +38 -0
- data/lib/tasks/rails_pulse_tasks.rake +138 -0
- data/public/rails-pulse-assets/csp-test.js +110 -0
- data/public/rails-pulse-assets/rails-pulse-icons.js +89 -0
- data/public/rails-pulse-assets/rails-pulse-icons.js.map +13 -0
- data/public/rails-pulse-assets/rails-pulse.css +1 -0
- data/public/rails-pulse-assets/rails-pulse.css.map +1 -0
- data/public/rails-pulse-assets/rails-pulse.js +183 -0
- data/public/rails-pulse-assets/rails-pulse.js.map +7 -0
- metadata +339 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
/* CSP-Safe Positioning Utilities for Rails Pulse */
|
2
|
+
/* Supports dynamic positioning using CSS custom properties */
|
3
|
+
|
4
|
+
/* Rails Pulse CSS loaded indicator for CSP testing */
|
5
|
+
:root {
|
6
|
+
--rails-pulse-loaded: true;
|
7
|
+
}
|
8
|
+
|
9
|
+
/* Popover positioning using CSS custom properties */
|
10
|
+
.positioned {
|
11
|
+
--popover-x: 0px;
|
12
|
+
--popover-y: 0px;
|
13
|
+
--context-menu-x: 0px;
|
14
|
+
--context-menu-y: 0px;
|
15
|
+
}
|
16
|
+
|
17
|
+
/* Popover positioning (used by popover_controller.js) */
|
18
|
+
[popover].positioned {
|
19
|
+
position: fixed;
|
20
|
+
inset-inline-start: var(--popover-x, 0px) !important;
|
21
|
+
inset-block-start: var(--popover-y, 0px) !important;
|
22
|
+
}
|
23
|
+
|
24
|
+
/* Context menu positioning (used by context_menu_controller.js) */
|
25
|
+
[popover].positioned {
|
26
|
+
inset-inline-start: var(--context-menu-x, var(--popover-x, 0px)) !important;
|
27
|
+
inset-block-start: var(--context-menu-y, var(--popover-y, 0px)) !important;
|
28
|
+
}
|
29
|
+
|
30
|
+
/* Icon loading states for icon_controller.js */
|
31
|
+
[data-controller*="rails-pulse--icon"] {
|
32
|
+
display: inline-block;
|
33
|
+
line-height: 0;
|
34
|
+
}
|
35
|
+
|
36
|
+
[data-controller*="rails-pulse--icon"].loading {
|
37
|
+
opacity: 0.6;
|
38
|
+
}
|
39
|
+
|
40
|
+
[data-controller*="rails-pulse--icon"].error {
|
41
|
+
opacity: 0.4;
|
42
|
+
filter: grayscale(1);
|
43
|
+
}
|
44
|
+
|
45
|
+
[data-controller*="rails-pulse--icon"].loaded {
|
46
|
+
opacity: 1;
|
47
|
+
}
|
48
|
+
|
49
|
+
/* CSP-safe icon rendering */
|
50
|
+
[data-controller*="rails-pulse--icon"] svg {
|
51
|
+
display: block;
|
52
|
+
width: inherit;
|
53
|
+
height: inherit;
|
54
|
+
}
|
55
|
+
|
56
|
+
/* Accessibility improvements */
|
57
|
+
[data-controller*="rails-pulse--icon"][aria-label] {
|
58
|
+
position: relative;
|
59
|
+
}
|
60
|
+
|
61
|
+
/* Focus indicators for interactive icons */
|
62
|
+
[data-controller*="rails-pulse--icon"]:focus-visible {
|
63
|
+
outline: 2px solid currentColor;
|
64
|
+
outline-offset: 2px;
|
65
|
+
border-radius: 2px;
|
66
|
+
}
|
67
|
+
|
68
|
+
/* CSP Test Page Utilities */
|
69
|
+
.csp-test-grid-single {
|
70
|
+
--columns: 1;
|
71
|
+
}
|
72
|
+
|
73
|
+
.csp-test-context-area {
|
74
|
+
padding: 2rem;
|
75
|
+
border: 2px dashed var(--color-border);
|
76
|
+
text-align: center;
|
77
|
+
}
|
78
|
+
|
79
|
+
.csp-test-nav-gap {
|
80
|
+
--column-gap: 1rem;
|
81
|
+
}
|
82
|
+
|
83
|
+
/* Sheet sizing for dialog */
|
84
|
+
.csp-test-sheet {
|
85
|
+
--sheet-size: 288px;
|
86
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
.dialog {
|
2
|
+
background-color: var(--color-bg);
|
3
|
+
border-radius: var(--rounded-lg);
|
4
|
+
border-width: var(--border);
|
5
|
+
box-shadow: var(--shadow-lg);
|
6
|
+
color: var(--color-text);
|
7
|
+
inline-size: var(--size-full);
|
8
|
+
margin: auto;
|
9
|
+
max-inline-size: var(--dialog-size, var(--max-i-lg));
|
10
|
+
|
11
|
+
&::backdrop {
|
12
|
+
background-color: rgba(0, 0, 0, .8);
|
13
|
+
}
|
14
|
+
|
15
|
+
/* Final state of exit animation and setup */
|
16
|
+
opacity: 0;
|
17
|
+
transform: var(--scale-95);
|
18
|
+
transition-behavior: allow-discrete;
|
19
|
+
transition-duration: var(--time-200);
|
20
|
+
transition-property: display, overlay, opacity, transform;
|
21
|
+
|
22
|
+
&::backdrop {
|
23
|
+
opacity: 0;
|
24
|
+
transition-behavior: allow-discrete;
|
25
|
+
transition-duration: var(--time-200);
|
26
|
+
transition-property: display, overlay, opacity;
|
27
|
+
}
|
28
|
+
|
29
|
+
/* Final state of entry animation */
|
30
|
+
&[open] { opacity: 1; transform: var(--scale-100); }
|
31
|
+
&[open]::backdrop { opacity: 1; }
|
32
|
+
|
33
|
+
/* Initial state of entry animation */
|
34
|
+
@starting-style {
|
35
|
+
&[open] { opacity: 0; transform: var(--scale-95); }
|
36
|
+
&[open]::backdrop { opacity: 0; }
|
37
|
+
}
|
38
|
+
|
39
|
+
/* Drawer component on mobile */
|
40
|
+
@media (width < 40rem) {
|
41
|
+
border-end-end-radius: 0;
|
42
|
+
border-end-start-radius: 0;
|
43
|
+
margin-block-end: 0;
|
44
|
+
max-inline-size: none;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
.dialog__content {
|
49
|
+
padding: var(--size-6);
|
50
|
+
}
|
51
|
+
|
52
|
+
.dialog__close {
|
53
|
+
inset-block-start: var(--size-3);
|
54
|
+
inset-inline-end: var(--size-3);
|
55
|
+
position: absolute;
|
56
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
.flash {
|
2
|
+
align-items: center;
|
3
|
+
animation: appear-then-fade 4s 300ms both;
|
4
|
+
backdrop-filter: var(--blur-sm) var(--contrast-75);
|
5
|
+
background-color: var(--flash-background, rgb(from var(--color-text) r g b / .65));
|
6
|
+
border-radius: var(--rounded-full);
|
7
|
+
color: var(--flash-color, var(--color-text-reversed));
|
8
|
+
column-gap: var(--size-2);
|
9
|
+
display: flex;
|
10
|
+
font-size: var(--text-fluid-base);
|
11
|
+
justify-content: center;
|
12
|
+
line-height: var(--leading-none);
|
13
|
+
margin-block-start: var(--flash-position, var(--size-4));
|
14
|
+
margin-inline: auto;
|
15
|
+
min-block-size: var(--size-11);
|
16
|
+
padding: var(--size-1) var(--size-4);
|
17
|
+
text-align: center;
|
18
|
+
|
19
|
+
[data-turbo-preview] & {
|
20
|
+
display: none;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
.flash--positive {
|
25
|
+
--flash-background: var(--color-positive);
|
26
|
+
--flash-color: white;
|
27
|
+
}
|
28
|
+
|
29
|
+
.flash--negative {
|
30
|
+
--flash-background: var(--color-negative);
|
31
|
+
--flash-color: white;
|
32
|
+
}
|
33
|
+
|
34
|
+
.flash--extended {
|
35
|
+
animation-name: appear-then-fade-extended;
|
36
|
+
animation-duration: 12s;
|
37
|
+
}
|
38
|
+
|
39
|
+
@keyframes appear-then-fade {
|
40
|
+
0%, 100% { opacity: 0; }
|
41
|
+
5%, 60% { opacity: 1; }
|
42
|
+
}
|
43
|
+
|
44
|
+
@keyframes appear-then-fade-extended {
|
45
|
+
0%, 100% { opacity: 0; }
|
46
|
+
2%, 90% { opacity: 1; }
|
47
|
+
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
.input {
|
2
|
+
appearance: none;
|
3
|
+
background-color: var(--input-background, transparent);
|
4
|
+
block-size: var(--input-block-size, auto);
|
5
|
+
border: 1px solid var(--input-border-color, var(--color-border));
|
6
|
+
border-radius: var(--input-radius, var(--rounded-md));
|
7
|
+
box-shadow: var(--input-box-shadow, var(--shadow-xs));
|
8
|
+
font-size: var(--input-font-size, var(--text-sm));
|
9
|
+
inline-size: var(--input-inline-size, var(--size-full));
|
10
|
+
padding: var(--input-padding, .375rem .75rem);
|
11
|
+
|
12
|
+
&:is(textarea[rows=auto]) {
|
13
|
+
field-sizing: content;
|
14
|
+
max-block-size: calc(.875rem + var(--input-max-rows, 10lh));
|
15
|
+
min-block-size: calc(.875rem + var(--input-rows, 2lh));
|
16
|
+
}
|
17
|
+
|
18
|
+
&:is(select):not([multiple], [size]) {
|
19
|
+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m6 9 6 6 6-6'/%3e%3c/svg%3e");
|
20
|
+
background-position: center right var(--size-2);
|
21
|
+
background-repeat: no-repeat;
|
22
|
+
background-size: var(--size-4) auto;
|
23
|
+
}
|
24
|
+
|
25
|
+
&::file-selector-button {
|
26
|
+
font-weight: var(--font-medium);
|
27
|
+
}
|
28
|
+
|
29
|
+
&:user-invalid {
|
30
|
+
border-color: var(--color-negative);
|
31
|
+
}
|
32
|
+
|
33
|
+
&:user-invalid ~ .invalid-feedback {
|
34
|
+
display: flex;
|
35
|
+
}
|
36
|
+
|
37
|
+
&:disabled {
|
38
|
+
cursor: not-allowed; opacity: var(--opacity-50);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
.input--actor {
|
43
|
+
input {
|
44
|
+
border: 0; inline-size: 100%; outline: 0;
|
45
|
+
}
|
46
|
+
|
47
|
+
img:not([class]) {
|
48
|
+
filter: var(--input-icon-color, var(--color-filter-text));
|
49
|
+
}
|
50
|
+
|
51
|
+
&:focus-within {
|
52
|
+
outline: var(--input-outline-size, 2px) solid var(--color-selected-dark);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
.invalid-feedback {
|
57
|
+
display: none;
|
58
|
+
}
|
59
|
+
|
60
|
+
:is(.checkbox, .radio) {
|
61
|
+
transform: scale(1.2);
|
62
|
+
}
|
63
|
+
|
64
|
+
:is(.checkbox, .radio, .range) {
|
65
|
+
accent-color: var(--color-primary);
|
66
|
+
}
|
67
|
+
|
68
|
+
:is(.input, .checkbox, .radio, .range) {
|
69
|
+
&:focus-visible {
|
70
|
+
outline: var(--input-outline-size, 2px) solid var(--color-selected-dark);
|
71
|
+
}
|
72
|
+
|
73
|
+
&:focus-visible:user-invalid {
|
74
|
+
outline: none;
|
75
|
+
}
|
76
|
+
|
77
|
+
.field_with_errors & {
|
78
|
+
border-color: var(--color-negative); display: contents;
|
79
|
+
}
|
80
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
.sidebar-layout {
|
2
|
+
display: grid;
|
3
|
+
grid-template-areas: "header header" "sidebar main";
|
4
|
+
grid-template-columns: var(--sidebar-width, 0) 1fr;
|
5
|
+
grid-template-rows: auto 1fr;
|
6
|
+
block-size: 100dvh;
|
7
|
+
|
8
|
+
@media (width >= 48rem) {
|
9
|
+
--sidebar-border-width: var(--border);
|
10
|
+
--sidebar-padding: var(--size-2);
|
11
|
+
--sidebar-width: var(--max-i-3xs);
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
.header-layout {
|
16
|
+
display: grid;
|
17
|
+
grid-template-areas: "header" "main";
|
18
|
+
grid-template-rows: auto 1fr;
|
19
|
+
block-size: 100dvh;
|
20
|
+
}
|
21
|
+
|
22
|
+
.centered-layout {
|
23
|
+
display: grid;
|
24
|
+
place-items: center;
|
25
|
+
block-size: 100dvh;
|
26
|
+
}
|
27
|
+
|
28
|
+
.container {
|
29
|
+
inline-size: 100%;
|
30
|
+
margin-inline: auto;
|
31
|
+
max-inline-size: var(--container-width, 80rem);
|
32
|
+
}
|
33
|
+
|
34
|
+
#header {
|
35
|
+
align-items: center;
|
36
|
+
background-color: rgb(from var(--color-border-light) r g b / .5);
|
37
|
+
border-block-end-width: var(--border);
|
38
|
+
block-size: var(--size-16);
|
39
|
+
column-gap: var(--size-4);
|
40
|
+
display: flex;
|
41
|
+
grid-area: header;
|
42
|
+
padding-inline: var(--size-4);
|
43
|
+
}
|
44
|
+
|
45
|
+
#sidebar {
|
46
|
+
background-color: rgb(from var(--color-border-light) r g b / .5);
|
47
|
+
border-inline-end-width: var(--sidebar-border-width, 0);
|
48
|
+
display: flex;
|
49
|
+
flex-direction: column;
|
50
|
+
grid-area: sidebar;
|
51
|
+
overflow-x: hidden;
|
52
|
+
padding: var(--sidebar-padding, 0);
|
53
|
+
row-gap: var(--size-2);
|
54
|
+
}
|
55
|
+
|
56
|
+
#main {
|
57
|
+
display: flex;
|
58
|
+
flex-direction: column;
|
59
|
+
gap: var(--size-4);
|
60
|
+
grid-area: main;
|
61
|
+
overflow: auto;
|
62
|
+
padding: var(--size-4);
|
63
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
.menu {
|
2
|
+
display: flex;
|
3
|
+
flex-direction: column;
|
4
|
+
padding: var(--size-1);
|
5
|
+
row-gap: var(--size-1);
|
6
|
+
}
|
7
|
+
|
8
|
+
.menu__header {
|
9
|
+
font-size: var(--text-sm);
|
10
|
+
font-weight: var(--font-semibold);
|
11
|
+
padding: var(--size-1_5) var(--size-2);
|
12
|
+
}
|
13
|
+
|
14
|
+
.menu__group {
|
15
|
+
display: flex;
|
16
|
+
flex-direction: column;
|
17
|
+
row-gap: 1px;
|
18
|
+
}
|
19
|
+
|
20
|
+
.menu__separator {
|
21
|
+
margin-inline: -0.25rem;
|
22
|
+
}
|
23
|
+
|
24
|
+
.menu__item {
|
25
|
+
--btn-border-color: transparent;
|
26
|
+
--btn-box-shadow: none;
|
27
|
+
--btn-font-weight: var(--font-normal);
|
28
|
+
--btn-hover-color: var(--color-secondary);
|
29
|
+
--btn-justify-content: start;
|
30
|
+
--btn-outline-size: 0;
|
31
|
+
--btn-padding: var(--size-1_5) var(--size-2);
|
32
|
+
--btn-text-align: start;
|
33
|
+
|
34
|
+
&:focus-visible {
|
35
|
+
--btn-background: var(--color-secondary);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
.menu__item-key {
|
40
|
+
color: var(--color-text-subtle);
|
41
|
+
font-size: var(--text-xs);
|
42
|
+
margin-inline-start: auto;
|
43
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
.popover {
|
2
|
+
background-color: var(--color-bg);
|
3
|
+
border-radius: var(--rounded-md);
|
4
|
+
border-width: var(--border);
|
5
|
+
box-shadow: var(--shadow-md);
|
6
|
+
color: var(--color-text);
|
7
|
+
inline-size: var(--popover-size, max-content);
|
8
|
+
|
9
|
+
/* Final state of exit animation and setup */
|
10
|
+
opacity: 0;
|
11
|
+
transform: var(--scale-95);
|
12
|
+
transition-behavior: allow-discrete;
|
13
|
+
transition-duration: var(--time-150);
|
14
|
+
transition-property: display, overlay, opacity, transform;
|
15
|
+
|
16
|
+
/* Final state of entry animation */
|
17
|
+
&:popover-open {
|
18
|
+
opacity: 1; transform: var(--scale-100);
|
19
|
+
}
|
20
|
+
|
21
|
+
/* Initial state of entry animation */
|
22
|
+
@starting-style {
|
23
|
+
&:popover-open {
|
24
|
+
opacity: 0; transform: var(--scale-95);
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
/* Positioning rules for Floating UI */
|
29
|
+
&.positioned {
|
30
|
+
position: fixed !important;
|
31
|
+
left: var(--popover-x, 0) !important;
|
32
|
+
top: var(--popover-y, 0) !important;
|
33
|
+
margin: 0 !important;
|
34
|
+
inset: unset !important;
|
35
|
+
}
|
36
|
+
}
|
@@ -0,0 +1,144 @@
|
|
1
|
+
.prose {
|
2
|
+
font-size: var(--text-fluid-base);
|
3
|
+
max-inline-size: 65ch;
|
4
|
+
|
5
|
+
/* Antialiased fonts */
|
6
|
+
-moz-osx-font-smoothing: grayscale;
|
7
|
+
-webkit-font-smoothing: antialiased;
|
8
|
+
|
9
|
+
:is(h1, h2, h3, h4, h5, h6) {
|
10
|
+
font-weight: var(--font-extrabold);
|
11
|
+
hyphens: auto;
|
12
|
+
letter-spacing: -0.02ch;
|
13
|
+
line-height: 1.1;
|
14
|
+
margin-block: 0.5em;
|
15
|
+
overflow-wrap: break-word;
|
16
|
+
text-wrap: balance;
|
17
|
+
}
|
18
|
+
|
19
|
+
h1 {
|
20
|
+
font-size: 2.4em;
|
21
|
+
}
|
22
|
+
|
23
|
+
h2 {
|
24
|
+
font-size: 1.8em;
|
25
|
+
}
|
26
|
+
|
27
|
+
h3 {
|
28
|
+
font-size: 1.5em;
|
29
|
+
}
|
30
|
+
|
31
|
+
h4 {
|
32
|
+
font-size: 1.2em;
|
33
|
+
}
|
34
|
+
|
35
|
+
h5 {
|
36
|
+
font-size: 1em;
|
37
|
+
}
|
38
|
+
|
39
|
+
h6 {
|
40
|
+
font-size: 0.8em;
|
41
|
+
}
|
42
|
+
|
43
|
+
:is(ul, ol, menu) {
|
44
|
+
list-style: revert;
|
45
|
+
padding-inline-start: revert;
|
46
|
+
}
|
47
|
+
|
48
|
+
:is(p, ul, ol, dl, blockquote, pre, figure, table, hr) {
|
49
|
+
margin-block: 0.65lh;
|
50
|
+
overflow-wrap: break-word;
|
51
|
+
text-wrap: pretty;
|
52
|
+
}
|
53
|
+
|
54
|
+
hr {
|
55
|
+
border-color: var(--color-border-dark);
|
56
|
+
border-style: var(--border-style, solid) none none;
|
57
|
+
margin: 2lh auto;
|
58
|
+
}
|
59
|
+
|
60
|
+
:is(b, strong) {
|
61
|
+
font-weight: var(--font-bold);
|
62
|
+
}
|
63
|
+
|
64
|
+
:is(pre, code) {
|
65
|
+
background-color: var(--color-border-light);
|
66
|
+
border: 1px solid var(--color-border);
|
67
|
+
border-radius: var(--rounded-sm);
|
68
|
+
font-family: var(--font-monospace-code);
|
69
|
+
font-size: 0.85em;
|
70
|
+
}
|
71
|
+
|
72
|
+
code {
|
73
|
+
padding: 0.1em 0.3em;
|
74
|
+
}
|
75
|
+
|
76
|
+
pre {
|
77
|
+
border-radius: 0.5em;
|
78
|
+
overflow-x: auto;
|
79
|
+
padding: 0.5lh 2ch;
|
80
|
+
text-wrap: nowrap;
|
81
|
+
}
|
82
|
+
|
83
|
+
pre code {
|
84
|
+
background-color: transparent;
|
85
|
+
border: 0;
|
86
|
+
font-size: 1em;
|
87
|
+
padding: 0;
|
88
|
+
}
|
89
|
+
|
90
|
+
p {
|
91
|
+
hyphens: auto;
|
92
|
+
letter-spacing: -0.005ch;
|
93
|
+
}
|
94
|
+
|
95
|
+
blockquote {
|
96
|
+
font-style: italic;
|
97
|
+
margin: 0 3ch;
|
98
|
+
}
|
99
|
+
|
100
|
+
blockquote p {
|
101
|
+
hyphens: none;
|
102
|
+
}
|
103
|
+
|
104
|
+
table {
|
105
|
+
border: 1px solid var(--color-border-dark);
|
106
|
+
border-collapse: collapse;
|
107
|
+
margin: 1lh 0;
|
108
|
+
}
|
109
|
+
|
110
|
+
th {
|
111
|
+
font-weight: var(--font-bold);
|
112
|
+
}
|
113
|
+
|
114
|
+
:is(th, td) {
|
115
|
+
border: 1px solid var(--color-border-dark);
|
116
|
+
padding: 0.2lh 1ch;
|
117
|
+
text-align: start;
|
118
|
+
}
|
119
|
+
|
120
|
+
th {
|
121
|
+
border-block-end-width: 3px;
|
122
|
+
}
|
123
|
+
|
124
|
+
del {
|
125
|
+
background-color: rgb(from var(--color-negative) r g b / .1);
|
126
|
+
color: var(--color-negative);
|
127
|
+
}
|
128
|
+
|
129
|
+
ins {
|
130
|
+
background-color: rgb(from var(--color-positive) r g b / .1);
|
131
|
+
color: var(--color-positive);
|
132
|
+
}
|
133
|
+
|
134
|
+
a {
|
135
|
+
color: var(--color-link);
|
136
|
+
text-decoration: underline;
|
137
|
+
text-decoration-skip-ink: auto;
|
138
|
+
}
|
139
|
+
|
140
|
+
mark {
|
141
|
+
color: var(--color-text);
|
142
|
+
background-color: var(--color-highlight);
|
143
|
+
}
|
144
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
.row {
|
2
|
+
display: flex;
|
3
|
+
justify-content: space-between;
|
4
|
+
width: 100%;
|
5
|
+
gap: var(--column-gap, 0.5rem);
|
6
|
+
}
|
7
|
+
|
8
|
+
.row > * {
|
9
|
+
flex: 1;
|
10
|
+
min-width: 0;
|
11
|
+
}
|
12
|
+
|
13
|
+
/* Stack items on smaller screens */
|
14
|
+
@media (max-width: 768px) {
|
15
|
+
.row {
|
16
|
+
flex-direction: column;
|
17
|
+
gap: 0.5rem;
|
18
|
+
}
|
19
|
+
|
20
|
+
.row > * {
|
21
|
+
flex: none;
|
22
|
+
width: 100%;
|
23
|
+
}
|
24
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
.sidebar-menu {
|
2
|
+
display: flex;
|
3
|
+
flex-direction: column;
|
4
|
+
row-gap: var(--size-4);
|
5
|
+
block-size: var(--size-full);
|
6
|
+
}
|
7
|
+
|
8
|
+
.sidebar-menu__button {
|
9
|
+
--btn-background: transparent;
|
10
|
+
--btn-border-color: transparent;
|
11
|
+
--btn-box-shadow: none;
|
12
|
+
--btn-font-weight: var(--font-normal);
|
13
|
+
--btn-hover-color: var(--color-secondary);
|
14
|
+
--btn-justify-content: start;
|
15
|
+
--btn-outline-size: 0;
|
16
|
+
--btn-inline-size: var(--size-full);
|
17
|
+
--btn-padding: var(--size-1) var(--size-2);
|
18
|
+
--btn-text-align: start;
|
19
|
+
|
20
|
+
&:focus-visible {
|
21
|
+
--btn-background: var(--color-secondary);
|
22
|
+
}
|
23
|
+
|
24
|
+
&:is(summary) {
|
25
|
+
&::after {
|
26
|
+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m9 18 6-6-6-6'/%3e%3c/svg%3e");
|
27
|
+
background-size: cover;
|
28
|
+
block-size: var(--size-4);
|
29
|
+
content: "";
|
30
|
+
filter: var(--color-filter-text);
|
31
|
+
inline-size: var(--size-4);
|
32
|
+
margin-inline-start: auto;
|
33
|
+
min-inline-size: var(--size-4);
|
34
|
+
transition: transform var(--time-200);
|
35
|
+
}
|
36
|
+
|
37
|
+
details[open] > &::after {
|
38
|
+
transform: var(--rotate-90);
|
39
|
+
}
|
40
|
+
|
41
|
+
&::-webkit-details-marker {
|
42
|
+
display: none;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
.sidebar-menu__content {
|
48
|
+
display: flex;
|
49
|
+
flex-direction: column;
|
50
|
+
row-gap: var(--size-4);
|
51
|
+
overflow-y: scroll;
|
52
|
+
}
|
53
|
+
|
54
|
+
.sidebar-menu__group {
|
55
|
+
display: flex;
|
56
|
+
flex-direction: column;
|
57
|
+
}
|
58
|
+
|
59
|
+
.sidebar-menu__group-label {
|
60
|
+
color: var(--color-text-subtle);
|
61
|
+
font-size: var(--text-xs);
|
62
|
+
font-weight: var(--font-medium);
|
63
|
+
padding: var(--size-1_5) var(--size-2);
|
64
|
+
}
|
65
|
+
|
66
|
+
.sidebar-menu__items {
|
67
|
+
display: flex;
|
68
|
+
flex-direction: column;
|
69
|
+
row-gap: var(--size-1);
|
70
|
+
}
|
71
|
+
|
72
|
+
.sidebar-menu__sub {
|
73
|
+
border-inline-start-width: var(--border);
|
74
|
+
display: flex;
|
75
|
+
flex-direction: column;
|
76
|
+
margin-inline-start: var(--size-4);
|
77
|
+
padding: var(--size-0_5) var(--size-2);
|
78
|
+
row-gap: var(--size-1);
|
79
|
+
}
|