debugbar 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/debugbar/assets_controller.rb +1 -1
- data/build.sh +7 -0
- data/debugbar.gemspec +6 -1
- data/lib/debugbar/engine.rb +0 -8
- data/lib/debugbar/version.rb +1 -1
- data/lib/debugbar.rb +0 -7
- metadata +2 -54
- data/client/.gitignore +0 -25
- data/client/README.md +0 -0
- data/client/dist/.vite/manifest.json +0 -14
- data/client/dist/assets/debugbar-u5mP-5-z.js +0 -34
- data/client/dist/assets/ruby-logo-kn_8RniZ.svg +0 -946
- data/client/index.html +0 -78
- data/client/package-lock.json +0 -2393
- data/client/package.json +0 -32
- data/client/postcss.config.js +0 -6
- data/client/src/App.vue +0 -17
- data/client/src/AppDemo.vue +0 -29
- data/client/src/AppDev.vue +0 -20
- data/client/src/Debugbar.vue +0 -276
- data/client/src/assets/rails-logo.svg +0 -1
- data/client/src/assets/ruby-logo.svg +0 -946
- data/client/src/components/TabButton.vue +0 -32
- data/client/src/components/panels/CachePanel.vue +0 -41
- data/client/src/components/panels/JobsPanel.vue +0 -52
- data/client/src/components/panels/JsonPanel.vue +0 -15
- data/client/src/components/panels/LogsPanel.vue +0 -43
- data/client/src/components/panels/MessagesPanel.vue +0 -25
- data/client/src/components/panels/ModelsPanel.vue +0 -37
- data/client/src/components/panels/Panel.vue +0 -7
- data/client/src/components/panels/RequestPanel.vue +0 -98
- data/client/src/components/queries/QueriesPanel.vue +0 -17
- data/client/src/components/queries/QueryItem.vue +0 -65
- data/client/src/components/ui/Foldable.vue +0 -39
- data/client/src/components/ui/KeyValueTable.vue +0 -16
- data/client/src/components/ui/Row.vue +0 -14
- data/client/src/components/ui/logo-ruby.vue +0 -547
- data/client/src/demo.ts +0 -17
- data/client/src/dev.ts +0 -20
- data/client/src/main.ts +0 -17
- data/client/src/models/Config.ts +0 -27
- data/client/src/models/Request.ts +0 -183
- data/client/src/stores/RequestsStore.ts +0 -36
- data/client/src/stores/configStore.ts +0 -8
- data/client/src/style.css +0 -23
- data/client/src/types.d.ts +0 -9
- data/client/src/vite-env.d.ts +0 -1
- data/client/tailwind.config.js +0 -16
- data/client/tsconfig.json +0 -29
- data/client/tsconfig.node.json +0 -10
- data/client/vite.config.ts +0 -44
- data/fixtures/requests/1706607114--demo_post_list.json +0 -499
- data/fixtures/requests/1706607120--api_jobs.json +0 -176
- data/fixtures/requests/1706607123--api_jobs.json +0 -119
- data/fixtures/requests/1706607133--demo_slow_page.json +0 -130
- data/fixtures/requests/1706607136--demo_post.json +0 -164
- data/fixtures/requests/1706607136--demo_random_post.json +0 -106
- data/fixtures/requests/1706607141--api_errors.json +0 -73
- data/package-lock.json +0 -50
- data/package.json +0 -5
data/client/package.json
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"name": "client",
|
3
|
-
"private": true,
|
4
|
-
"version": "0.0.0",
|
5
|
-
"type": "module",
|
6
|
-
"scripts": {
|
7
|
-
"dev": "vite",
|
8
|
-
"build": "vue-tsc && vite build",
|
9
|
-
"preview": "vite preview"
|
10
|
-
},
|
11
|
-
"dependencies": {
|
12
|
-
"@heroicons/vue": "^2.1.1",
|
13
|
-
"@highlightjs/vue-plugin": "^2.1.0",
|
14
|
-
"@rails/actioncable": "^6.1.7",
|
15
|
-
"lodash": "^4.17.21",
|
16
|
-
"pinia": "^2.1.7",
|
17
|
-
"sql-formatter": "^15.0.2",
|
18
|
-
"vue": "^3.3.8",
|
19
|
-
"vue-shadow-dom": "^4.2.0"
|
20
|
-
},
|
21
|
-
"devDependencies": {
|
22
|
-
"@types/lodash": "^4.14.202",
|
23
|
-
"@types/node": "^20.10.4",
|
24
|
-
"@vitejs/plugin-vue": "^4.5.0",
|
25
|
-
"autoprefixer": "^10.4.16",
|
26
|
-
"postcss": "^8.4.31",
|
27
|
-
"tailwindcss": "^3.4",
|
28
|
-
"typescript": "^5.2.2",
|
29
|
-
"vite": "^5.0.0",
|
30
|
-
"vue-tsc": "^1.8.22"
|
31
|
-
}
|
32
|
-
}
|
data/client/postcss.config.js
DELETED
data/client/src/App.vue
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import Debugbar from "@/Debugbar.vue"
|
3
|
-
import { ShadowRoot, ShadowStyle } from "vue-shadow-dom"
|
4
|
-
|
5
|
-
import css from "./style.css?inline"
|
6
|
-
</script>
|
7
|
-
|
8
|
-
<template>
|
9
|
-
<div>
|
10
|
-
<shadow-root id="__debugbar-shadow-root">
|
11
|
-
<debugbar></debugbar>
|
12
|
-
<shadow-style>
|
13
|
-
{{ css }}
|
14
|
-
</shadow-style>
|
15
|
-
</shadow-root>
|
16
|
-
</div>
|
17
|
-
</template>
|
data/client/src/AppDemo.vue
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import Debugbar from "@/Debugbar.vue"
|
3
|
-
import { ShadowRoot, ShadowStyle } from "vue-shadow-dom"
|
4
|
-
import { onMounted } from "vue"
|
5
|
-
import { useRequestsStore } from "@/stores/RequestsStore.ts"
|
6
|
-
import { BackendRequestData } from "@/models/Request.ts"
|
7
|
-
|
8
|
-
import css from "./style.css?inline"
|
9
|
-
|
10
|
-
onMounted(() => {
|
11
|
-
console.log(`Using debugbar in DEMO mode`)
|
12
|
-
|
13
|
-
const requests = import.meta.glob("../../fixtures/requests/*.json", { eager: true })
|
14
|
-
const firstId = useRequestsStore().addRequests(Object.values(requests) as BackendRequestData[])[0]
|
15
|
-
|
16
|
-
useRequestsStore().setCurrentRequestById(firstId)
|
17
|
-
})
|
18
|
-
</script>
|
19
|
-
|
20
|
-
<template>
|
21
|
-
<div>
|
22
|
-
<shadow-root id="__debugbar-shadow-root">
|
23
|
-
<debugbar></debugbar>
|
24
|
-
<shadow-style>
|
25
|
-
{{ css }}
|
26
|
-
</shadow-style>
|
27
|
-
</shadow-root>
|
28
|
-
</div>
|
29
|
-
</template>
|
data/client/src/AppDev.vue
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import Debugbar from "@/Debugbar.vue"
|
3
|
-
import { onMounted } from "vue"
|
4
|
-
import { useRequestsStore } from "@/stores/RequestsStore.ts"
|
5
|
-
import { BackendRequestData } from "@/models/Request.ts"
|
6
|
-
|
7
|
-
onMounted(() => {
|
8
|
-
if (import.meta.env.DEV && import.meta.env.VITE_LOAD_FIXTURES) {
|
9
|
-
const requests = import.meta.glob("../../fixtures/requests/*.json", { eager: true })
|
10
|
-
const lastId = useRequestsStore()
|
11
|
-
.addRequests(Object.values(requests) as BackendRequestData[])
|
12
|
-
.pop()
|
13
|
-
useRequestsStore().setCurrentRequestById(lastId)
|
14
|
-
}
|
15
|
-
})
|
16
|
-
</script>
|
17
|
-
|
18
|
-
<template>
|
19
|
-
<debugbar />
|
20
|
-
</template>
|
data/client/src/Debugbar.vue
DELETED
@@ -1,276 +0,0 @@
|
|
1
|
-
<script setup lang="ts">
|
2
|
-
import { createConsumer } from "@rails/actioncable"
|
3
|
-
import { computed, onMounted, reactive, ref } from "vue"
|
4
|
-
import { CodeBracketIcon, XCircleIcon, ArrowDownLeftIcon, TrashIcon } from "@heroicons/vue/16/solid"
|
5
|
-
|
6
|
-
import TabButton from "@/components/TabButton.vue"
|
7
|
-
import ModelsPanel from "@/components/panels/ModelsPanel.vue"
|
8
|
-
import QueriesPanel from "@/components/queries/QueriesPanel.vue"
|
9
|
-
import JobsPanel from "@/components/panels/JobsPanel.vue"
|
10
|
-
import LogsPanel from "@/components/panels/LogsPanel.vue"
|
11
|
-
import MessagesPanel from "@/components/panels/MessagesPanel.vue"
|
12
|
-
|
13
|
-
import { useRequestsStore } from "@/stores/RequestsStore.ts"
|
14
|
-
import { useConfigStore } from "@/stores/configStore.ts"
|
15
|
-
import CachePanel from "@/components/panels/CachePanel.vue"
|
16
|
-
import RequestPanel from "@/components/panels/RequestPanel.vue"
|
17
|
-
import JsonPanel from "@/components/panels/JsonPanel.vue"
|
18
|
-
import LogoRuby from "@/components/ui/logo-ruby.vue"
|
19
|
-
|
20
|
-
let requestsStore = useRequestsStore()
|
21
|
-
let configStore = useConfigStore()
|
22
|
-
|
23
|
-
const header = ref(null)
|
24
|
-
|
25
|
-
const state = reactive({
|
26
|
-
activeTab: "",
|
27
|
-
minimized: false,
|
28
|
-
isResizing: false,
|
29
|
-
height: configStore.config.height,
|
30
|
-
})
|
31
|
-
|
32
|
-
const isActive = computed(() => {
|
33
|
-
return state.activeTab != ""
|
34
|
-
})
|
35
|
-
|
36
|
-
const devMode = computed(() => {
|
37
|
-
return import.meta.env.DEV
|
38
|
-
})
|
39
|
-
|
40
|
-
const routeAlias = computed(() => {
|
41
|
-
return requestsStore.currentRequest?.meta.params.controller + "#" + requestsStore.currentRequest?.meta.params.action
|
42
|
-
})
|
43
|
-
|
44
|
-
let debugbarChannel
|
45
|
-
|
46
|
-
if (configStore.config.mode == "ws") {
|
47
|
-
debugbarChannel = createConsumer(configStore.config.actionCableUrl).subscriptions.create(
|
48
|
-
{ channel: configStore.config.channelName },
|
49
|
-
{
|
50
|
-
connected() {
|
51
|
-
console.log("🟢 Connected to channel")
|
52
|
-
debugbarChannel.send({ ids: [] })
|
53
|
-
},
|
54
|
-
|
55
|
-
disconnected() {
|
56
|
-
console.log("🔴 Disconnected from channel")
|
57
|
-
},
|
58
|
-
received(data) {
|
59
|
-
if (data.length == 0) {
|
60
|
-
return
|
61
|
-
}
|
62
|
-
|
63
|
-
const ids = requestsStore.addRequests(data)
|
64
|
-
|
65
|
-
if (!isActive.value) {
|
66
|
-
requestsStore.setCurrentRequestById(ids[ids.length - 1])
|
67
|
-
}
|
68
|
-
|
69
|
-
setTimeout(() => {
|
70
|
-
debugbarChannel.send({ ids: ids })
|
71
|
-
}, 50)
|
72
|
-
},
|
73
|
-
}
|
74
|
-
)
|
75
|
-
} else {
|
76
|
-
console.log(`Using debugbar in "offline mode", ideal for demoing with fixtures.`)
|
77
|
-
debugbarChannel = {
|
78
|
-
send: (data) => {
|
79
|
-
// No-op
|
80
|
-
console.log("Ignoring `send` call", data)
|
81
|
-
},
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
const clearRequests = () => {
|
86
|
-
console.log("Clearing requests")
|
87
|
-
state.activeTab = ""
|
88
|
-
requestsStore.clearRequests()
|
89
|
-
debugbarChannel.send({ clear: true })
|
90
|
-
}
|
91
|
-
|
92
|
-
// Resizing the debugbar
|
93
|
-
onMounted(() => {
|
94
|
-
window.onresize = function () {
|
95
|
-
if (window.innerHeight < state.height) {
|
96
|
-
state.height = window.innerHeight - header.value.clientHeight
|
97
|
-
}
|
98
|
-
}
|
99
|
-
|
100
|
-
document.onmousemove = function (e) {
|
101
|
-
if (!state.isResizing) {
|
102
|
-
return
|
103
|
-
}
|
104
|
-
|
105
|
-
state.height = window.innerHeight - e.clientY - header.value.clientHeight
|
106
|
-
}
|
107
|
-
|
108
|
-
document.onmouseup = function (_e) {
|
109
|
-
state.isResizing = false
|
110
|
-
}
|
111
|
-
})
|
112
|
-
|
113
|
-
const setActiveTab = (tab) => {
|
114
|
-
if (state.activeTab == tab) {
|
115
|
-
state.activeTab = "" // Close if you click on active tab
|
116
|
-
} else {
|
117
|
-
if (window.innerHeight < state.height) {
|
118
|
-
state.height = window.innerHeight - header.value.clientHeight * 2
|
119
|
-
}
|
120
|
-
state.activeTab = tab
|
121
|
-
}
|
122
|
-
}
|
123
|
-
</script>
|
124
|
-
|
125
|
-
<template>
|
126
|
-
<div
|
127
|
-
v-if="state.minimized"
|
128
|
-
@click="state.minimized = false"
|
129
|
-
class="z-[9999] fixed left-0 bottom-0 bg-transparent cursor-pointer"
|
130
|
-
>
|
131
|
-
<div class="p-1 pt-1.5">
|
132
|
-
<img class="h-5" src="./assets/ruby-logo.svg" alt="Logo" />
|
133
|
-
</div>
|
134
|
-
</div>
|
135
|
-
|
136
|
-
<div v-if="!state.minimized && requestsStore.currentRequest == null" class="z-[9999] fixed left-0 bottom-0 w-full">
|
137
|
-
<div class="h-0.5 bg-red-700 cursor-row-resize" />
|
138
|
-
<div class="flex items-center justify-between bg-stone-100 border-b border-stone-200">
|
139
|
-
<div class="px-5 py-1.5 italic">No request yet</div>
|
140
|
-
<div class="px-3">
|
141
|
-
<button @click="state.minimized = true" title="Hide in the corner">
|
142
|
-
<arrow-down-left-icon class="size-4" />
|
143
|
-
</button>
|
144
|
-
</div>
|
145
|
-
</div>
|
146
|
-
</div>
|
147
|
-
|
148
|
-
<div v-if="!state.minimized && requestsStore.currentRequest" class="z-[9999] fixed left-0 bottom-0 w-full">
|
149
|
-
<div id="draggable-bar" @mousedown="state.isResizing = true" class="h-0.5 bg-red-700 cursor-row-resize" />
|
150
|
-
|
151
|
-
<div
|
152
|
-
id="debubgbar-header"
|
153
|
-
ref="header"
|
154
|
-
class="flex px-1 items-center justify-between bg-stone-100 border-b-2 border-stone-300"
|
155
|
-
>
|
156
|
-
<!-- Left -->
|
157
|
-
<div>
|
158
|
-
<div class="flex">
|
159
|
-
<div class="p-1 pt-1.5">
|
160
|
-
<logo-ruby />
|
161
|
-
</div>
|
162
|
-
|
163
|
-
<tab-button
|
164
|
-
v-for="(v, k) in requestsStore.currentRequest.dataForTabs"
|
165
|
-
key="k"
|
166
|
-
:label="v.label"
|
167
|
-
:count="v?.count"
|
168
|
-
:is-active="k === state.activeTab"
|
169
|
-
:disabled="v.count == 0"
|
170
|
-
@click="setActiveTab(k)"
|
171
|
-
>{{ v.label }}</tab-button
|
172
|
-
>
|
173
|
-
|
174
|
-
<button
|
175
|
-
v-if="devMode"
|
176
|
-
@click="setActiveTab('debug')"
|
177
|
-
class="px-3 py-1.5 text-stone-600"
|
178
|
-
:class="{ 'bg-stone-300': state.activeTab == 'debug' }"
|
179
|
-
>
|
180
|
-
<CodeBracketIcon class="size-4" />
|
181
|
-
</button>
|
182
|
-
</div>
|
183
|
-
</div>
|
184
|
-
|
185
|
-
<!-- Right -->
|
186
|
-
<div class="flex items-center space-x-2.5 pr-1">
|
187
|
-
<div @click="setActiveTab('request')" class="flex space-x-2 cursor-pointer">
|
188
|
-
<span class="text-sm text-stone-600 font-medium tracking-wide">
|
189
|
-
{{ routeAlias }}
|
190
|
-
</span>
|
191
|
-
|
192
|
-
<span
|
193
|
-
class="text-sm font-bold"
|
194
|
-
v-if="requestsStore.currentRequest.meta.duration < 1000"
|
195
|
-
:class="{
|
196
|
-
'text-orange-600': requestsStore.currentRequest.meta.duration >= 800,
|
197
|
-
}"
|
198
|
-
>{{ requestsStore.currentRequest.meta.duration.toFixed(1) }}ms</span
|
199
|
-
>
|
200
|
-
<span
|
201
|
-
class="text-sm font-bold text-red-600 bg-red-100 px-1 rounded"
|
202
|
-
v-if="requestsStore.currentRequest.meta.duration >= 1000"
|
203
|
-
>{{ (requestsStore.currentRequest.meta.duration / 1000).toFixed(2) }}s</span
|
204
|
-
>
|
205
|
-
|
206
|
-
<span
|
207
|
-
class="px-1 py-0.5 rounded text-xs"
|
208
|
-
:class="{
|
209
|
-
'bg-green-600 text-white': requestsStore.currentRequest.meta.status < 300,
|
210
|
-
'bg-amber-600 text-white':
|
211
|
-
requestsStore.currentRequest.meta.status >= 400 && requestsStore.currentRequest.meta.status < 500,
|
212
|
-
'bg-red-600 text-white': requestsStore.currentRequest.meta.status >= 500,
|
213
|
-
}"
|
214
|
-
>{{ requestsStore.currentRequest.meta.status }}</span
|
215
|
-
>
|
216
|
-
</div>
|
217
|
-
|
218
|
-
<select
|
219
|
-
class="px-2 py-1.5 bg-white border border-stone-200 rounded w-[330px] text-sm"
|
220
|
-
name="current_request_id"
|
221
|
-
@change="
|
222
|
-
(event) => {
|
223
|
-
const target = event.target as HTMLSelectElement
|
224
|
-
requestsStore.setCurrentRequestById(target.value)
|
225
|
-
}
|
226
|
-
"
|
227
|
-
>
|
228
|
-
<option
|
229
|
-
v-for="r in requestsStore.requests"
|
230
|
-
:selected="requestsStore.currentRequest.id == r.id"
|
231
|
-
v-text="r.pathWithVerb"
|
232
|
-
:value="r.id"
|
233
|
-
/>
|
234
|
-
</select>
|
235
|
-
|
236
|
-
<button @click="clearRequests" title="Clear all requests (frontend and backend)">
|
237
|
-
<trash-icon class="size-4" />
|
238
|
-
</button>
|
239
|
-
|
240
|
-
<div class="flex items-center pl-2 space-x-2">
|
241
|
-
<button @click="state.minimized = true" title="Hide in the corner">
|
242
|
-
<arrow-down-left-icon class="size-4" />
|
243
|
-
</button>
|
244
|
-
<button :disabled="!isActive" @click="state.activeTab = ''" title="Close">
|
245
|
-
<x-circle-icon class="size-4" />
|
246
|
-
</button>
|
247
|
-
</div>
|
248
|
-
</div>
|
249
|
-
</div>
|
250
|
-
|
251
|
-
<div
|
252
|
-
ref="body"
|
253
|
-
id="debugbar-body"
|
254
|
-
class="bg-white overflow-scroll"
|
255
|
-
v-if="state.activeTab != ''"
|
256
|
-
:style="`height: ${state.height}px`"
|
257
|
-
>
|
258
|
-
<request-panel v-if="state.activeTab == 'request'" :request="requestsStore.currentRequest" />
|
259
|
-
<messages-panel v-if="state.activeTab == 'messages'" :messages="requestsStore.currentRequest?.messages" />
|
260
|
-
<models-panel
|
261
|
-
v-if="state.activeTab == 'models'"
|
262
|
-
:models="requestsStore.currentRequest?.models"
|
263
|
-
:count="requestsStore.currentRequest?.modelsCount"
|
264
|
-
/>
|
265
|
-
<queries-panel v-if="state.activeTab == 'queries'" :current-request="requestsStore.currentRequest" />
|
266
|
-
<jobs-panel v-if="state.activeTab == 'jobs'" :jobs="requestsStore.currentRequest?.jobs" />
|
267
|
-
<cache-panel v-if="state.activeTab == 'cache'" :cache="requestsStore.currentRequest?.cache" />
|
268
|
-
<logs-panel v-if="state.activeTab == 'logs'" :logs="requestsStore.currentRequest?.logs" />
|
269
|
-
<json-panel
|
270
|
-
v-if="devMode && state.activeTab == 'debug'"
|
271
|
-
:current-request="requestsStore.currentRequest"
|
272
|
-
class="px-3 py-2"
|
273
|
-
/>
|
274
|
-
</div>
|
275
|
-
</div>
|
276
|
-
</template>
|
@@ -1 +0,0 @@
|
|
1
|
-
<svg height="32" viewBox="0 0 90 32" width="90" xmlns="http://www.w3.org/2000/svg"><path d="m418.082357 25.9995403v4.1135034h-7.300339v1.89854h3.684072c1.972509 0 4.072534 1.4664311 4.197997 3.9665124l.005913.2373977v1.5821167c-.087824 3.007959-2.543121 4.1390018-4.071539 4.2011773l-.132371.0027328h-7.390745v-4.0909018l7.481152-.0226016v-1.9889467l-1.190107.0007441-.346911.0008254-.084566.0003251-.127643.0007097-.044785.0003793-.055764.0007949-.016378.0008259c.000518.0004173.013246.0008384.034343.0012518l.052212.000813c.030547.0003979.066903.0007803.105225.0011355l.078131.0006709-.155385-.0004701c-.31438-.001557-.85249-.0041098-1.729029-.0080055-1.775258 0-4.081832-1.3389153-4.219994-3.9549201l-.006518-.24899v-1.423905c0-2.6982402 2.278213-4.182853 4.065464-4.2678491l.161048-.003866zm-18.691579 0v11.8658752h6.170255v4.1361051h-10.735792v-16.0019803zm-6.441475 0v16.0019803h-4.588139v-16.0019803zm-10.803597 0c1.057758 0 4.04923.7305141 4.198142 3.951222l.005768.2526881v11.7980702h-4.271715v-2.8252084h-4.136105v2.8252084h-4.407325v-11.7980702c0-1.3184306 1.004082-4.0468495 3.946899-4.197411l.257011-.0064991zm-24.147177-.0027581 8.580186.0005749c.179372.0196801 4.753355.5702841 4.753355 5.5438436s-3.775694 5.3947112-3.92376 5.4093147l-.004472.0004216 5.00569 5.0505836h-6.374959l-3.726209-3.8608906v3.8608906h-4.309831zm22.418634-2.6971669.033418.0329283s-.384228.27122-.791058.610245c-12.837747-9.4927002-20.680526-5.0175701-23.144107-3.8196818-11.187826 6.2428065-7.954768 21.5678895-7.888988 21.8737669l.001006.0046469h-17.855317s.67805-6.6900935 5.4244-14.600677c4.74635-7.9105834 12.837747-13.9000252 19.414832-14.4876686 12.681632-1.2703535 24.110975 9.7062594 24.805814 10.3864403zm-31.111679 14.1815719 2.44098.881465c.113008.8852319.273103 1.7233771.441046 2.4882761l.101394.4499406-2.7122-.9718717c-.113009-.67805-.226017-1.6499217-.27122-2.84781zm31.506724-7.6619652h-1.514312c-1.128029 0-1.333125.5900716-1.370415.8046431l-.007251.056292-.000906.0152319-.00013 3.9153864h4.136105l-.000316-3.916479c-.004939-.0795522-.08331-.8750744-1.242775-.8750744zm-50.492125.339025 2.599192.94927c-.316423.731729-.719369 1.6711108-1.011998 2.4093289l-.118085.3028712-2.599192-.94927c.226017-.610245.700652-1.7403284 1.130083-2.7122001zm35.445121-.1434449h-3.456844v3.6588673h3.434397s.98767-.3815997.98767-1.8406572-.965223-1.8182101-.965223-1.8182101zm-15.442645-.7606218 1.62732 1.2882951c-.180814.705172-.318232 1.410344-.412255 2.115516l-.06238.528879-1.830735-1.4465067c.180813-.81366.384228-1.6499217.67805-2.4861834zm4.000495-6.3058651 1.017075 1.5369134c-.39779.4158707-.766649.8317413-1.095006 1.2707561l-.238493.3339623-1.08488-1.6273201c.40683-.5198383.881465-1.0396767 1.401304-1.5143117zm-16.182794-3.3450467 1.604719 1.4013034c-.40683.4237812-.800947.8729894-1.172815 1.3285542l-.364099.4569775-1.740328-1.4917101c.519838-.5650416 1.08488-1.1300833 1.672523-1.695125zm22.398252-.0904067.497237 1.4917101c-.524359.162732-1.048717.3688592-1.573076.6068095l-.393269.1842488-.519838-1.559515c.565041-.2486184 1.22049-.4972367 1.988946-.7232534zm5.28879-.54244c.578603.0361627 1.171671.1012555 1.779204.2068505l.458361.0869712-.090406 1.4013034c-.596684-.1265694-1.193368-.2097435-1.790052-.2495224l-.447513-.0216976zm-18.555968-6.2380601 1.017075 1.559515c-.440733.2203663-.868752.4661594-1.303128.7278443l-.437201.2666291-1.039676-1.5821167c.610245-.3616267 1.197888-.67805 1.76293-.9718717zm18.601172-.8588633c1.344799.3842283 1.923513.6474959 2.155025.7707625l.037336.0202958-.090406 1.5143117c-.482169-.1958811-.964338-.381717-1.453204-.5575078l-.739158-.2561522zm-8.633837-1.3334984.452033 1.3787017h-.226016c-.491587 0-.983173.0127134-1.474759.0476754l-.491587.0427313-.429431-1.3334984c.745855-.0904067 1.469108-.13561 2.16976-.13561z" transform="translate(-329 -10)"/></svg>
|