| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,7 @@ declare global { | |||
| 12 | 12 | const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] | |
| 13 | 13 | const calcExternalLabels: typeof import('./composables/module-graph')['calcExternalLabels'] | |
| 14 | 14 | const codemirrorRef: typeof import('./composables/codemirror')['codemirrorRef'] | |
| 15 | + const columnNumber: typeof import('./composables/params')['columnNumber'] | ||
| 15 | 16 | const computed: typeof import('vue')['computed'] | |
| 16 | 17 | const computedAsync: typeof import('@vueuse/core')['computedAsync'] | |
| 17 | 18 | const computedEager: typeof import('@vueuse/core')['computedEager'] | |
@@ -49,6 +50,7 @@ declare global { | |||
| 49 | 50 | const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] | |
| 50 | 51 | const effectScope: typeof import('vue')['effectScope'] | |
| 51 | 52 | const extendRef: typeof import('@vueuse/core')['extendRef'] | |
| 53 | + const getAttachmentUrl: typeof import('./composables/attachments')['getAttachmentUrl'] | ||
| 52 | 54 | const getCurrentBrowserIframe: typeof import('./composables/api')['getCurrentBrowserIframe'] | |
| 53 | 55 | const getCurrentInstance: typeof import('vue')['getCurrentInstance'] | |
| 54 | 56 | const getCurrentScope: typeof import('vue')['getCurrentScope'] | |
@@ -61,13 +63,16 @@ declare global { | |||
| 61 | 63 | const injectLocal: typeof import('@vueuse/core')['injectLocal'] | |
| 62 | 64 | const isDark: typeof import('./composables/dark')['isDark'] | |
| 63 | 65 | const isDefined: typeof import('@vueuse/core')['isDefined'] | |
| 66 | + const isExternalAttachment: typeof import('./composables/attachments')['isExternalAttachment'] | ||
| 64 | 67 | const isProxy: typeof import('vue')['isProxy'] | |
| 65 | 68 | const isReactive: typeof import('vue')['isReactive'] | |
| 66 | 69 | const isReadonly: typeof import('vue')['isReadonly'] | |
| 67 | 70 | const isRef: typeof import('vue')['isRef'] | |
| 71 | + const isTestFile: typeof import('./composables/error')['isTestFile'] | ||
| 68 | 72 | const lineNumber: typeof import('./composables/params')['lineNumber'] | |
| 69 | 73 | const mainSizes: typeof import('./composables/navigation')['mainSizes'] | |
| 70 | 74 | const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] | |
| 75 | + const mapLeveledTaskStacks: typeof import('./composables/error')['mapLeveledTaskStacks'] | ||
| 71 | 76 | const markRaw: typeof import('vue')['markRaw'] | |
| 72 | 77 | const navigateTo: typeof import('./composables/navigation')['navigateTo'] | |
| 73 | 78 | const nextTick: typeof import('vue')['nextTick'] | |
@@ -94,6 +99,7 @@ declare global { | |||
| 94 | 99 | const onUpdated: typeof import('vue')['onUpdated'] | |
| 95 | 100 | const onWatcherCleanup: typeof import('vue')['onWatcherCleanup'] | |
| 96 | 101 | const openInEditor: typeof import('./composables/error')['openInEditor'] | |
| 102 | + const openScreenshot: typeof import('./composables/screenshot')['openScreenshot'] | ||
| 97 | 103 | const openedTreeItems: typeof import('./composables/navigation')['openedTreeItems'] | |
| 98 | 104 | const panels: typeof import('./composables/navigation')['panels'] | |
| 99 | 105 | const params: typeof import('./composables/params')['params'] | |
@@ -120,19 +126,23 @@ declare global { | |||
| 120 | 126 | const resolveComponent: typeof import('vue')['resolveComponent'] | |
| 121 | 127 | const resolveRef: typeof import('@vueuse/core')['resolveRef'] | |
| 122 | 128 | const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] | |
| 129 | + const sanitizeFilePath: typeof import('./composables/attachments')['sanitizeFilePath'] | ||
| 123 | 130 | const selectedTest: typeof import('./composables/params')['selectedTest'] | |
| 124 | 131 | const setIframeViewport: typeof import('./composables/api')['setIframeViewport'] | |
| 125 | 132 | const shallowReactive: typeof import('vue')['shallowReactive'] | |
| 126 | 133 | const shallowReadonly: typeof import('vue')['shallowReadonly'] | |
| 127 | 134 | const shallowRef: typeof import('vue')['shallowRef'] | |
| 128 | 135 | const shouldOpenInEditor: typeof import('./composables/error')['shouldOpenInEditor'] | |
| 136 | + const showAnnotationSource: typeof import('./composables/codemirror')['showAnnotationSource'] | ||
| 129 | 137 | const showCoverage: typeof import('./composables/navigation')['showCoverage'] | |
| 130 | 138 | const showDashboard: typeof import('./composables/navigation')['showDashboard'] | |
| 131 | 139 | const showLine: typeof import('./composables/codemirror')['showLine'] | |
| 140 | + const showLocationSource: typeof import('./composables/codemirror')['showLocationSource'] | ||
| 132 | 141 | const showNavigationPanel: typeof import('./composables/navigation')['showNavigationPanel'] | |
| 133 | 142 | const showReport: typeof import('./composables/navigation')['showReport'] | |
| 134 | 143 | const showRightPanel: typeof import('./composables/navigation')['showRightPanel'] | |
| 135 | 144 | const showSource: typeof import('./composables/codemirror')['showSource'] | |
| 145 | + const showTaskSource: typeof import('./composables/codemirror')['showTaskSource'] | ||
| 136 | 146 | const syncRef: typeof import('@vueuse/core')['syncRef'] | |
| 137 | 147 | const syncRefs: typeof import('@vueuse/core')['syncRefs'] | |
| 138 | 148 | const templateRef: typeof import('@vueuse/core')['templateRef'] | |
@@ -279,6 +289,7 @@ declare global { | |||
| 279 | 289 | const useSSRWidth: typeof import('@vueuse/core')['useSSRWidth'] | |
| 280 | 290 | const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] | |
| 281 | 291 | const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] | |
| 292 | + const useScreenshot: typeof import('./composables/screenshot')['useScreenshot'] | ||
| 282 | 293 | const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] | |
| 283 | 294 | const useScroll: typeof import('@vueuse/core')['useScroll'] | |
| 284 | 295 | const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,6 +7,7 @@ export {} | |||
| 7 | 7 | /* prettier-ignore */ | |
| 8 | 8 | declare module 'vue' { | |
| 9 | 9 | export interface GlobalComponents { | |
| 10 | + AnnotationAttachmentImage: typeof import('./components/AnnotationAttachmentImage.vue')['default'] | ||
| 10 | 11 | BrowserIframe: typeof import('./components/BrowserIframe.vue')['default'] | |
| 11 | 12 | CodeMirrorContainer: typeof import('./components/CodeMirrorContainer.vue')['default'] | |
| 12 | 13 | ConnectionOverlay: typeof import('./components/ConnectionOverlay.vue')['default'] | |
@@ -38,5 +39,6 @@ declare module 'vue' { | |||
| 38 | 39 | ViewModuleGraph: typeof import('./components/views/ViewModuleGraph.vue')['default'] | |
| 39 | 40 | ViewReport: typeof import('./components/views/ViewReport.vue')['default'] | |
| 40 | 41 | ViewReportError: typeof import('./components/views/ViewReportError.vue')['default'] | |
| 42 | + ViewTestReport: typeof import('./components/views/ViewTestReport.vue')['default'] | ||
| 41 | 43 | } | |
| 42 | 44 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,34 @@ | |||
| 1 | + <script setup lang="ts"> | ||
| 2 | + import type { TestAnnotation } from 'vitest' | ||
| 3 | + import { getAttachmentUrl, isExternalAttachment } from '~/composables/attachments' | ||
| 4 | + | ||
| 5 | + const props = defineProps<{ | ||
| 6 | + annotation: TestAnnotation | ||
| 7 | + }>() | ||
| 8 | + | ||
| 9 | + const href = computed<string>(() => { | ||
| 10 | + const attachment = props.annotation.attachment! | ||
| 11 | + const potentialUrl = attachment.path || attachment.body | ||
| 12 | + if (typeof potentialUrl === 'string' && (potentialUrl.startsWith('http://') || potentialUrl.startsWith('https://'))) { | ||
| 13 | + return potentialUrl | ||
| 14 | + } | ||
| 15 | + else { | ||
| 16 | + return getAttachmentUrl(attachment) | ||
| 17 | + } | ||
| 18 | + }) | ||
| 19 | + </script> | ||
| 20 | + | ||
| 21 | + <template> | ||
| 22 | + <a | ||
| 23 | + v-if="annotation.attachment && annotation.attachment.contentType?.startsWith('image/')" | ||
| 24 | + target="_blank" | ||
| 25 | + class="inline-block mt-2" | ||
| 26 | + :style="{ maxWidth: '600px' }" | ||
| 27 | + :href="href" | ||
| 28 | + :referrerPolicy="isExternalAttachment(annotation.attachment) ? 'no-referrer' : undefined" | ||
| 29 | + > | ||
| 30 | + <img | ||
| 31 | + :src="href" | ||
| 32 | + > | ||
| 33 | + </a> | ||
| 34 | + </template> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,5 @@ | |||
| 1 | 1 | <script setup lang="ts"> | |
| 2 | + import type { RunnerTask, RunnerTestCase } from 'vitest' | ||
| 2 | 3 | import type { ModuleGraph } from '~/composables/module-graph' | |
| 3 | 4 | import type { Params } from '~/composables/params' | |
| 4 | 5 | import { toJSON } from 'flatted' | |
@@ -9,10 +10,11 @@ import { | |||
| 9 | 10 | currentLogs, | |
| 10 | 11 | isReport, | |
| 11 | 12 | } from '~/composables/client' | |
| 13 | + import { explorerTree } from '~/composables/explorer' | ||
| 12 | 14 | import { hasFailedSnapshot } from '~/composables/explorer/collector' | |
| 13 | 15 | import { getModuleGraph } from '~/composables/module-graph' | |
| 14 | 16 | import { viewMode } from '~/composables/params' | |
| 15 | - import { getProjectNameColor } from '~/utils/task' | ||
| 17 | + import { getProjectNameColor, getProjectTextColor } from '~/utils/task' | ||
| 16 | 18 | ||
| 17 | 19 | const graph = ref<ModuleGraph>({ nodes: [], links: [] }) | |
| 18 | 20 | const draft = ref(false) | |
@@ -21,6 +23,12 @@ const loadingModuleGraph = ref(false) | |||
| 21 | 23 | const currentFilepath = ref<string | undefined>(undefined) | |
| 22 | 24 | const hideNodeModules = ref(true) | |
| 23 | 25 | ||
| 26 | + const test = computed(() => { | ||
| 27 | + return selectedTest.value | ||
| 28 | + ? client.state.idMap.get(selectedTest.value) as RunnerTestCase | ||
| 29 | + : undefined | ||
| 30 | + }) | ||
| 31 | + | ||
| 24 | 32 | const graphData = computed(() => { | |
| 25 | 33 | const c = current.value | |
| 26 | 34 | if (!c || !c.filepath) { | |
@@ -137,18 +145,26 @@ debouncedWatch( | |||
| 137 | 145 | ) | |
| 138 | 146 | ||
| 139 | 147 | const projectNameColor = computed(() => { | |
| 140 | - return getProjectNameColor(current.value?.file.projectName) | ||
| 148 | + const projectName = current.value?.file.projectName || '' | ||
| 149 | + return explorerTree.colors.get(projectName) || getProjectNameColor(current.value?.file.projectName) | ||
| 141 | 150 | }) | |
| 142 | 151 | ||
| 143 | - const projectNameTextColor = computed(() => { | ||
| 144 | - switch (projectNameColor.value) { | ||
| 145 | - case 'blue': | ||
| 146 | - case 'green': | ||
| 147 | - case 'magenta': | ||
| 148 | - return 'white' | ||
| 149 | - default: | ||
| 150 | - return 'black' | ||
| 152 | + const projectNameTextColor = computed(() => getProjectTextColor(projectNameColor.value)) | ||
| 153 | + | ||
| 154 | + const testTitle = computed(() => { | ||
| 155 | + const testId = selectedTest.value | ||
| 156 | + if (!testId) { | ||
| 157 | + return current.value?.name | ||
| 158 | + } | ||
| 159 | + const names: string[] = [] | ||
| 160 | + let node: RunnerTask | undefined = client.state.idMap.get(testId) | ||
| 161 | + while (node) { | ||
| 162 | + names.push(node.name) | ||
| 163 | + node = node.suite | ||
| 164 | + ? node.suite | ||
| 165 | + : (node === node.file ? undefined : node.file) | ||
| 151 | 166 | } | |
| 167 | + return names.reverse().join(' > ') | ||
| 152 | 168 | }) | |
| 153 | 169 | </script> | |
| 154 | 170 | ||
@@ -174,7 +190,7 @@ const projectNameTextColor = computed(() => { | |||
| 174 | 190 | {{ current.file.projectName }} | |
| 175 | 191 | </span> | |
| 176 | 192 | <div flex-1 font-light op-50 ws-nowrap truncate text-sm> | |
| 177 | - {{ current?.name }} | ||
| 193 | + {{ testTitle }} | ||
| 178 | 194 | </div> | |
| 179 | 195 | <div class="flex text-lg"> | |
| 180 | 196 | <IconButton | |
@@ -263,7 +279,8 @@ const projectNameTextColor = computed(() => { | |||
| 263 | 279 | :file="current" | |
| 264 | 280 | data-testid="console" | |
| 265 | 281 | /> | |
| 266 | - <ViewReport v-else-if="!viewMode" :file="current" data-testid="report" /> | ||
| 282 | + <ViewReport v-else-if="!viewMode && !test && current" :file="current" data-testid="report" /> | ||
| 283 | + <ViewTestReport v-else-if="!viewMode && test" :test="test" data-testid="report" /> | ||
| 267 | 284 | </div> | |
| 268 | 285 | </div> | |
| 269 | 286 | </template> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,11 +4,12 @@ import type { TaskTreeNodeType } from '~/composables/explorer/types' | |||
| 4 | 4 | import { Tooltip as VueTooltip } from 'floating-vue' | |
| 5 | 5 | import { nextTick } from 'vue' | |
| 6 | 6 | import { client, isReport, runFiles, runTask } from '~/composables/client' | |
| 7 | - import { showSource } from '~/composables/codemirror' | ||
| 7 | + import { showTaskSource } from '~/composables/codemirror' | ||
| 8 | 8 | import { explorerTree } from '~/composables/explorer' | |
| 9 | 9 | import { hasFailedSnapshot } from '~/composables/explorer/collector' | |
| 10 | 10 | import { escapeHtml, highlightRegex } from '~/composables/explorer/state' | |
| 11 | 11 | import { coverageEnabled } from '~/composables/navigation' | |
| 12 | + import { getProjectTextColor } from '~/utils/task' | ||
| 12 | 13 | ||
| 13 | 14 | // TODO: better handling of "opened" - it means to forcefully open the tree item and set in TasksList right now | |
| 14 | 15 | const { | |
@@ -149,26 +150,11 @@ function showDetails() { | |||
| 149 | 150 | onItemClick?.(t) | |
| 150 | 151 | } | |
| 151 | 152 | else { | |
| 152 | - showSource(t) | ||
| 153 | + showTaskSource(t) | ||
| 153 | 154 | } | |
| 154 | 155 | } | |
| 155 | 156 | ||
| 156 | - const projectNameTextColor = computed(() => { | ||
| 157 | - switch (projectNameColor) { | ||
| 158 | - case 'blue': | ||
| 159 | - case 'green': | ||
| 160 | - case 'magenta': | ||
| 161 | - case 'black': | ||
| 162 | - case 'red': | ||
| 163 | - return 'white' | ||
| 164 | - | ||
| 165 | - case 'yellow': | ||
| 166 | - case 'cyan': | ||
| 167 | - case 'white': | ||
| 168 | - default: | ||
| 169 | - return 'black' | ||
| 170 | - } | ||
| 171 | - }) | ||
| 157 | + const projectNameTextColor = computed(() => getProjectTextColor(projectNameColor)) | ||
| 172 | 158 | </script> | |
| 173 | 159 | ||
| 174 | 160 | <template> | |
@@ -223,7 +209,7 @@ const projectNameTextColor = computed(() => { | |||
| 223 | 209 | > | |
| 224 | 210 | <IconButton | |
| 225 | 211 | data-testid="btn-open-details" | |
| 226 | - icon="i-carbon:intrusion-prevention" | ||
| 212 | + :icon="type === 'file' ? 'i-carbon:intrusion-prevention' : 'i-carbon:code-reference'" | ||
| 227 | 213 | @click.prevent.stop="showDetails" | |
| 228 | 214 | /> | |
| 229 | 215 | <template #popper> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,13 +1,14 @@ | |||
| 1 | 1 | <script setup lang="ts"> | |
| 2 | - import type { Task, TestAttachment } from '@vitest/runner' | ||
| 2 | + import type { Task } from '@vitest/runner' | ||
| 3 | 3 | import type CodeMirror from 'codemirror' | |
| 4 | 4 | import type { ErrorWithDiff, File, TestAnnotation, TestError } from 'vitest' | |
| 5 | 5 | import { createTooltip, destroyTooltip } from 'floating-vue' | |
| 6 | + import { getAttachmentUrl, sanitizeFilePath } from '~/composables/attachments' | ||
| 6 | 7 | import { client, isReport } from '~/composables/client' | |
| 7 | 8 | import { finished } from '~/composables/client/state' | |
| 8 | 9 | import { codemirrorRef } from '~/composables/codemirror' | |
| 9 | 10 | import { openInEditor } from '~/composables/error' | |
| 10 | - import { lineNumber } from '~/composables/params' | ||
| 11 | + import { columnNumber, lineNumber } from '~/composables/params' | ||
| 11 | 12 | ||
| 12 | 13 | const props = defineProps<{ | |
| 13 | 14 | file?: File | |
@@ -56,12 +57,12 @@ watch( | |||
| 56 | 57 | { immediate: true }, | |
| 57 | 58 | ) | |
| 58 | 59 | ||
| 59 | - watch(() => [loading.value, saving.value, props.file, lineNumber.value] as const, ([loadingFile, s, _, l]) => { | ||
| 60 | + watch(() => [loading.value, saving.value, props.file, lineNumber.value, columnNumber.value] as const, ([loadingFile, s, _, l, c]) => { | ||
| 60 | 61 | if (!loadingFile && !s) { | |
| 61 | 62 | if (l != null) { | |
| 62 | 63 | nextTick(() => { | |
| 63 | 64 | const cp = currentPosition.value | |
| 64 | - const line = cp ?? { line: l ?? 0, ch: 0 } | ||
| 65 | + const line = cp ?? { line: (l ?? 1) - 1, ch: c ?? 0 } | ||
| 65 | 66 | // restore caret position: the watchDebounced below will use old value | |
| 66 | 67 | if (cp) { | |
| 67 | 68 | currentPosition.value = undefined | |
@@ -155,7 +156,7 @@ function createErrorElement(e: ErrorWithDiff) { | |||
| 155 | 156 | div.className = 'op80 flex gap-x-2 items-center' | |
| 156 | 157 | const pre = document.createElement('pre') | |
| 157 | 158 | pre.className = 'c-red-600 dark:c-red-400' | |
| 158 | - pre.textContent = `${' '.repeat(stack.column)}^ ${e?.nameStr || e.name}: ${ | ||
| 159 | + pre.textContent = `${' '.repeat(stack.column)}^ ${e.name}: ${ | ||
| 159 | 160 | e?.message || '' | |
| 160 | 161 | }` | |
| 161 | 162 | div.appendChild(pre) | |
@@ -184,7 +185,6 @@ function createErrorElement(e: ErrorWithDiff) { | |||
| 184 | 185 | ||
| 185 | 186 | function createAnnotationElement(annotation: TestAnnotation) { | |
| 186 | 187 | if (!annotation.location) { | |
| 187 | - // TODO(v4): print unknown annotations somewhere | ||
| 188 | 188 | return | |
| 189 | 189 | } | |
| 190 | 190 | ||
@@ -222,9 +222,8 @@ function createAnnotationElement(annotation: TestAnnotation) { | |||
| 222 | 222 | if (attachment.contentType?.startsWith('image/')) { | |
| 223 | 223 | const link = document.createElement('a') | |
| 224 | 224 | const img = document.createElement('img') | |
| 225 | - img.classList.add('mt-3', 'inline-block') | ||
| 226 | - img.width = 600 | ||
| 227 | - img.width = 400 | ||
| 225 | + link.classList.add('inline-block', 'mt-3') | ||
| 226 | + link.style.maxWidth = '50vw' | ||
| 228 | 227 | const potentialUrl = attachment.path || attachment.body | |
| 229 | 228 | if (typeof potentialUrl === 'string' && (potentialUrl.startsWith('http://') || potentialUrl.startsWith('https://'))) { | |
| 230 | 229 | img.setAttribute('src', potentialUrl) | |
@@ -241,7 +240,7 @@ function createAnnotationElement(annotation: TestAnnotation) { | |||
| 241 | 240 | else { | |
| 242 | 241 | const download = document.createElement('a') | |
| 243 | 242 | download.href = getAttachmentUrl(attachment) | |
| 244 | - download.download = sanitizeFilePath(annotation.message) | ||
| 243 | + download.download = sanitizeFilePath(annotation.message, attachment.contentType) | ||
| 245 | 244 | download.classList.add('flex', 'w-min', 'gap-2', 'items-center', 'font-sans', 'underline', 'cursor-pointer') | |
| 246 | 245 | const icon = document.createElement('div') | |
| 247 | 246 | icon.classList.add('i-carbon:download', 'block') | |
@@ -254,19 +253,6 @@ function createAnnotationElement(annotation: TestAnnotation) { | |||
| 254 | 253 | widgets.push(codemirrorRef.value!.addLineWidget(line - 1, notice)) | |
| 255 | 254 | } | |
| 256 | 255 | ||
| 257 | - function getAttachmentUrl(attachment: TestAttachment) { | ||
| 258 | - // html reporter always saves files into /data/ folder | ||
| 259 | - if (isReport) { | ||
| 260 | - return `/data/${attachment.path}` | ||
| 261 | - } | ||
| 262 | - const contentType = attachment.contentType ?? 'application/octet-stream' | ||
| 263 | - if (attachment.path) { | ||
| 264 | - return `/__vitest_attachment__?path=${encodeURIComponent(attachment.path)}&contentType=${contentType}&token=${(window as any).VITEST_API_TOKEN}` | ||
| 265 | - } | ||
| 266 | - // attachment.body is always a string outside of the test frame | ||
| 267 | - return `data:${contentType};base64,${attachment.body}` | ||
| 268 | - } | ||
| 269 | - | ||
| 270 | 256 | const { pause, resume } = watch( | |
| 271 | 257 | [codemirrorRef, errors, annotations, finished] as const, | |
| 272 | 258 | ([cmValue, errors, annotations, end]) => { | |
@@ -389,11 +375,6 @@ async function onSave(content: string) { | |||
| 389 | 375 | ||
| 390 | 376 | // we need to remove listeners before unmounting the component: the watcher will not be called | |
| 391 | 377 | onBeforeUnmount(clearListeners) | |
| 392 | - | ||
| 393 | - function sanitizeFilePath(s: string): string { | ||
| 394 | - // eslint-disable-next-line no-control-regex | ||
| 395 | - return s.replace(/[\x00-\x2C\x2E\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/g, '-') | ||
| 396 | - } | ||
| 397 | 378 | </script> | |
| 398 | 379 | ||
| 399 | 380 | <template> | |
| Back | FazBrowse Home | New Git URL |
0 commit comments