refactor: minor ui improvements

This commit is contained in:
liyasthomas
2021-12-15 23:06:35 +05:30
parent 78ed95bcaa
commit daffc3fe0e
31 changed files with 908 additions and 957 deletions

View File

@@ -14,7 +14,7 @@
class="flex flex-col text-secondaryLight p-4 items-center justify-center" class="flex flex-col text-secondaryLight p-4 items-center justify-center"
> >
<i class="opacity-75 pb-2 material-icons">manage_search</i> <i class="opacity-75 pb-2 material-icons">manage_search</i>
<span class="text-center my-2"> <span class="my-2 text-center">
{{ t("state.nothing_found") }} "{{ search }}" {{ t("state.nothing_found") }} "{{ search }}"
</span> </span>
</div> </div>

View File

@@ -17,22 +17,22 @@
class="bg-transparent flex flex-shrink-0 text-base text-secondaryDark p-6" class="bg-transparent flex flex-shrink-0 text-base text-secondaryDark p-6"
/> />
<div <div
class="flex pb-4 px-4 border-b text-tiny border-dividerLight justify-between text-secondaryLight whitespace-nowrap overflow-auto flex-shrink-0 hide-scrollbar" class="border-b border-dividerLight flex flex-shrink-0 text-tiny text-secondaryLight px-4 pb-4 justify-between whitespace-nowrap overflow-auto hide-scrollbar"
> >
<div class="flex items-center"> <div class="flex items-center">
<kbd class="shortcut-key"></kbd> <kbd class="shortcut-key"></kbd>
<kbd class="shortcut-key"></kbd> <kbd class="shortcut-key"></kbd>
<span class="truncate ml-2"> <span class="ml-2 truncate">
{{ t("action.to_navigate") }} {{ t("action.to_navigate") }}
</span> </span>
<kbd class="shortcut-key"></kbd> <kbd class="shortcut-key"></kbd>
<span class="truncate ml-2"> <span class="ml-2 truncate">
{{ t("action.to_select") }} {{ t("action.to_select") }}
</span> </span>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<kbd class="shortcut-key">ESC</kbd> <kbd class="shortcut-key">ESC</kbd>
<span class="truncate ml-2"> <span class="ml-2 truncate">
{{ t("action.to_close") }} {{ t("action.to_close") }}
</span> </span>
</div> </div>

View File

@@ -1,14 +0,0 @@
<template>
<section :id="label.toLowerCase()" class="flex flex-col flex-1 relative">
<slot></slot>
</section>
</template>
<script setup lang="ts">
defineProps({
label: {
type: String,
default: "Section",
},
})
</script>

View File

@@ -1,29 +1,24 @@
<template> <template>
<AppSlideOver :show="show" @close="close()"> <AppSlideOver :show="show" @close="close()">
<template #content> <template #content>
<div <div class="bg-primary flex flex-col top-0 z-10 sticky">
class="bg-primary border-b border-dividerLight flex p-2 top-0 z-10 sticky items-center justify-between" <div
> class="border-b border-dividerLight flex p-2 items-center justify-between"
<h3 class="ml-4 heading">{{ t("app.shortcuts") }}</h3> >
<div class="flex"> <h3 class="ml-4 heading">{{ t("app.shortcuts") }}</h3>
<ButtonSecondary svg="x" @click.native="close()" /> <ButtonSecondary svg="x" @click.native="close()" />
</div> </div>
</div> <div class="border-b border-dividerLight flex flex-col py-4 px-6">
<div class="bg-primary border-b border-dividerLight">
<div class="flex flex-col my-4 mx-6">
<input <input
v-model="filterText" v-model="filterText"
type="search" type="search"
autocomplete="off" autocomplete="off"
class="bg-primaryLight border border-dividerLight rounded flex w-full py-2 px-4 focus-visible:border-divider" class="bg-primaryLight border border-dividerLight rounded flex py-2 px-4 focus-visible:border-divider"
:placeholder="`${t('action.search')}`" :placeholder="`${t('action.search')}`"
/> />
</div> </div>
</div> </div>
<div <div v-if="filterText" class="divide-dividerLight divide-y flex flex-col">
v-if="filterText"
class="divide-dividerLight divide-y flex flex-col flex-1 overflow-auto hide-scrollbar"
>
<div <div
v-for="(map, mapIndex) in searchResults" v-for="(map, mapIndex) in searchResults"
:key="`map-${mapIndex}`" :key="`map-${mapIndex}`"
@@ -43,15 +38,12 @@
class="flex flex-col text-secondaryLight p-4 items-center justify-center" class="flex flex-col text-secondaryLight p-4 items-center justify-center"
> >
<i class="opacity-75 pb-2 material-icons">manage_search</i> <i class="opacity-75 pb-2 material-icons">manage_search</i>
<span class="text-center my-2"> <span class="my-2 text-center">
{{ t("state.nothing_found") }} "{{ filterText }}" {{ t("state.nothing_found") }} "{{ filterText }}"
</span> </span>
</div> </div>
</div> </div>
<div <div v-else class="divide-dividerLight divide-y flex flex-col">
v-else
class="divide-dividerLight divide-y flex flex-col flex-1 overflow-auto hide-scrollbar"
>
<div <div
v-for="(map, mapIndex) in mappings" v-for="(map, mapIndex) in mappings"
:key="`map-${mapIndex}`" :key="`map-${mapIndex}`"

View File

@@ -10,7 +10,7 @@
</div> </div>
</transition> </transition>
<aside <aside
class="bg-primary flex flex-col h-full max-w-full transform transition top-0 ease-in-out right-0 w-96 z-30 duration-300 fixed overflow-auto" class="bg-primary flex flex-col h-full max-w-full transform transition top-0 ease-in-out right-0 w-96 z-30 duration-300 fixed overflow-auto hide-scrollbar"
:class="show ? 'shadow-xl translate-x-0' : 'translate-x-full'" :class="show ? 'shadow-xl translate-x-0' : 'translate-x-full'"
> >
<slot name="content"></slot> <slot name="content"></slot>

View File

@@ -140,7 +140,7 @@
:src="`/images/states/${$colorMode.value}/pack.svg`" :src="`/images/states/${$colorMode.value}/pack.svg`"
loading="lazy" loading="lazy"
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex" class="flex-col object-contain object-center h-16 mb-4 w-16 inline-flex"
:alt="$t('empty.collection')" :alt="`${$t('empty.collection')}`"
/> />
<span class="text-center"> <span class="text-center">
{{ $t("empty.collection") }} {{ $t("empty.collection") }}

View File

@@ -1,8 +1,5 @@
<template> <template>
<AppSection <div :class="{ 'rounded border border-divider': savingMode }">
label="collections"
:class="{ 'rounded border border-divider': savingMode }"
>
<div <div
class="divide-dividerLight divide-y border-b border-dividerLight flex flex-col top-0 z-10 sticky" class="divide-dividerLight divide-y border-b border-dividerLight flex flex-col top-0 z-10 sticky"
:class="{ 'bg-primary': !savingMode }" :class="{ 'bg-primary': !savingMode }"
@@ -13,7 +10,7 @@
type="search" type="search"
autocomplete="off" autocomplete="off"
:placeholder="$t('action.search')" :placeholder="$t('action.search')"
class="bg-transparent flex w-full py-2 px-4" class="bg-transparent flex py-2 px-4"
/> />
<div class="flex flex-1 justify-between"> <div class="flex flex-1 justify-between">
<ButtonSecondary <ButtonSecondary
@@ -84,7 +81,7 @@
class="flex flex-col text-secondaryLight p-4 items-center justify-center" class="flex flex-col text-secondaryLight p-4 items-center justify-center"
> >
<i class="opacity-75 pb-2 material-icons">manage_search</i> <i class="opacity-75 pb-2 material-icons">manage_search</i>
<span class="text-center my-2"> <span class="my-2 text-center">
{{ $t("state.nothing_found") }} "{{ filterText }}" {{ $t("state.nothing_found") }} "{{ filterText }}"
</span> </span>
</div> </div>
@@ -126,7 +123,7 @@
:show="showModalImportExport" :show="showModalImportExport"
@hide-modal="displayModalImportExport(false)" @hide-modal="displayModalImportExport(false)"
/> />
</AppSection> </div>
</template> </template>
<script> <script>

View File

@@ -1,19 +1,16 @@
<template> <template>
<AppSection <div :class="{ 'rounded border border-divider': saveRequest }">
label="collections"
:class="{ 'rounded border border-divider': saveRequest }"
>
<div <div
class="divide-dividerLight divide-y bg-primary border-b border-dividerLight rounded-t flex flex-col z-10 sticky" class="divide-dividerLight divide-y bg-primary border-b border-dividerLight rounded-t flex flex-col z-10 sticky"
:style="saveRequest ? 'top: calc(-1 * var(--font-size-body))' : 'top: 0'" :style="saveRequest ? 'top: calc(-1 * var(--font-size-body))' : 'top: 0'"
> >
<div v-if="!saveRequest" class="search-wrappe"> <div v-if="!saveRequest" class="flex flex-col">
<input <input
v-model="filterText" v-model="filterText"
type="search" type="search"
autocomplete="off" autocomplete="off"
:placeholder="$t('action.search')" :placeholder="$t('action.search')"
class="bg-transparent flex w-full py-2 pr-2 pl-4" class="bg-transparent py-2 pr-2 pl-4"
/> />
</div> </div>
<CollectionsChooseType <CollectionsChooseType
@@ -135,7 +132,7 @@
class="flex flex-col text-secondaryLight p-4 items-center justify-center" class="flex flex-col text-secondaryLight p-4 items-center justify-center"
> >
<i class="opacity-75 pb-2 material-icons">manage_search</i> <i class="opacity-75 pb-2 material-icons">manage_search</i>
<span class="text-center my-2"> <span class="my-2 text-center">
{{ $t("state.nothing_found") }} "{{ filterText }}" {{ $t("state.nothing_found") }} "{{ filterText }}"
</span> </span>
</div> </div>
@@ -181,7 +178,7 @@
@hide-modal="displayModalImportExport(false)" @hide-modal="displayModalImportExport(false)"
@update-team-collections="updateTeamCollections" @update-team-collections="updateTeamCollections"
/> />
</AppSection> </div>
</template> </template>
<script> <script>

View File

@@ -78,7 +78,7 @@
:src="`/images/states/${$colorMode.value}/blockchain.svg`" :src="`/images/states/${$colorMode.value}/blockchain.svg`"
loading="lazy" loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex" class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="$t('empty.environments')" :alt="`${$t('empty.environments')}`"
/> />
<span class="text-center pb-4"> <span class="text-center pb-4">
{{ $t("empty.environments") }} {{ $t("empty.environments") }}

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection :label="`${$t('environment.title')}`"> <div>
<div class="bg-primary rounded-t flex flex-col top-0 z-10 sticky"> <div class="bg-primary rounded-t flex flex-col top-0 z-10 sticky">
<tippy ref="options" interactive trigger="click" theme="popover" arrow> <tippy ref="options" interactive trigger="click" theme="popover" arrow>
<template #trigger> <template #trigger>
@@ -69,19 +69,6 @@
</div> </div>
</div> </div>
</div> </div>
<EnvironmentsAdd
:show="showModalAdd"
@hide-modal="displayModalAdd(false)"
/>
<EnvironmentsEdit
:show="showModalEdit"
:editing-environment-index="editingEnvironmentIndex"
@hide-modal="displayModalEdit(false)"
/>
<EnvironmentsImportExport
:show="showModalImportExport"
@hide-modal="displayModalImportExport(false)"
/>
<div class="flex flex-col"> <div class="flex flex-col">
<EnvironmentsEnvironment <EnvironmentsEnvironment
environment-index="Global" environment-index="Global"
@@ -117,7 +104,20 @@
@click.native="displayModalAdd(true)" @click.native="displayModalAdd(true)"
/> />
</div> </div>
</AppSection> <EnvironmentsAdd
:show="showModalAdd"
@hide-modal="displayModalAdd(false)"
/>
<EnvironmentsEdit
:show="showModalEdit"
:editing-environment-index="editingEnvironmentIndex"
@hide-modal="displayModalEdit(false)"
/>
<EnvironmentsImportExport
:show="showModalImportExport"
@hide-modal="displayModalImportExport(false)"
/>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@@ -7,56 +7,54 @@
:selected="true" :selected="true"
:indicator="gqlQueryString.length > 0" :indicator="gqlQueryString.length > 0"
> >
<AppSection label="query"> <div
<div class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between gqlRunQuery"
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between gqlRunQuery" >
> <label class="font-semibold text-secondaryLight">
<label class="font-semibold text-secondaryLight"> {{ t("request.query") }}
{{ t("request.query") }} </label>
</label> <div class="flex">
<div class="flex"> <ButtonSecondary
<ButtonSecondary :label="`${t('request.run')}`"
:label="`${t('request.run')}`" svg="play"
svg="play" class="rounded-none !text-accent !hover:text-accentDark"
class="rounded-none !text-accent !hover:text-accentDark" @click.native="runQuery()"
@click.native="runQuery()" />
/> <ButtonSecondary
<ButtonSecondary ref="saveRequest"
ref="saveRequest" :label="`${t('request.save')}`"
:label="`${t('request.save')}`" svg="save"
svg="save" class="rounded-none"
class="rounded-none" @click.native="saveRequest"
@click.native="saveRequest" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" to="https://docs.hoppscotch.io/graphql"
to="https://docs.hoppscotch.io/graphql" blank
blank :title="t('app.wiki')"
:title="t('app.wiki')" svg="help-circle"
svg="help-circle" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" :title="t('action.clear_all')"
:title="t('action.clear_all')" svg="trash-2"
svg="trash-2" @click.native="clearGQLQuery()"
@click.native="clearGQLQuery()" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" :title="t('action.prettify')"
:title="t('action.prettify')" :svg="`${prettifyQueryIcon}`"
:svg="`${prettifyQueryIcon}`" @click.native="prettifyQuery"
@click.native="prettifyQuery" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" :title="t('action.copy')"
:title="t('action.copy')" :svg="`${copyQueryIcon}`"
:svg="`${copyQueryIcon}`" @click.native="copyQuery"
@click.native="copyQuery" />
/>
</div>
</div> </div>
<div ref="queryEditor"></div> </div>
</AppSection> <div ref="queryEditor"></div>
</SmartTab> </SmartTab>
<SmartTab <SmartTab
@@ -64,37 +62,42 @@
:label="`${t('tab.variables')}`" :label="`${t('tab.variables')}`"
:indicator="variableString.length > 0" :indicator="variableString.length > 0"
> >
<AppSection label="variables"> <div
<div class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between" >
> <label class="font-semibold text-secondaryLight">
<label class="font-semibold text-secondaryLight"> {{ t("request.variables") }}
{{ t("request.variables") }} </label>
</label> <div class="flex">
<div class="flex"> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" to="https://docs.hoppscotch.io/graphql"
to="https://docs.hoppscotch.io/graphql" blank
blank :title="t('app.wiki')"
:title="t('app.wiki')" svg="help-circle"
svg="help-circle" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" :title="t('action.clear_all')"
:title="t('action.clear_all')" svg="trash-2"
svg="trash-2" @click.native="clearGQLVariables()"
@click.native="clearGQLVariables()" />
/> <ButtonSecondary
<ButtonSecondary ref="prettifyRequest"
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')" :title="t('action.prettify')"
:svg="`${copyVariablesIcon}`" :svg="prettifyVariablesIcon"
@click.native="copyVariables" @click.native="prettifyVariableString"
/> />
</div> <ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')"
:svg="`${copyVariablesIcon}`"
@click.native="copyVariables"
/>
</div> </div>
<div ref="variableEditor"></div> </div>
</AppSection> <div ref="variableEditor"></div>
</SmartTab> </SmartTab>
<SmartTab <SmartTab
@@ -102,57 +105,56 @@
:label="`${t('tab.headers')}`" :label="`${t('tab.headers')}`"
:info="activeGQLHeadersCount === 0 ? null : `${activeGQLHeadersCount}`" :info="activeGQLHeadersCount === 0 ? null : `${activeGQLHeadersCount}`"
> >
<AppSection label="headers"> <div
<div class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between" >
> <label class="font-semibold text-secondaryLight">
<label class="font-semibold text-secondaryLight"> {{ t("tab.headers") }}
{{ t("tab.headers") }} </label>
</label> <div class="flex">
<div class="flex"> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" to="https://docs.hoppscotch.io/graphql"
to="https://docs.hoppscotch.io/graphql" blank
blank :title="t('app.wiki')"
:title="t('app.wiki')" svg="help-circle"
svg="help-circle" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" :title="t('action.clear_all')"
:title="t('action.clear_all')" svg="trash-2"
svg="trash-2" @click.native="clearHeaders()"
@click.native="clearHeaders()" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" :title="t('state.bulk_mode')"
:title="t('state.bulk_mode')" svg="edit"
svg="edit" :class="{ '!text-accent': bulkMode }"
:class="{ '!text-accent': bulkMode }" @click.native="bulkMode = !bulkMode"
@click.native="bulkMode = !bulkMode" />
/> <ButtonSecondary
<ButtonSecondary v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" :title="t('add.new')"
:title="t('add.new')" svg="plus"
svg="plus" :disabled="bulkMode"
:disabled="bulkMode" @click.native="addRequestHeader"
@click.native="addRequestHeader" />
/>
</div>
</div> </div>
<div v-if="bulkMode" ref="bulkEditor"></div> </div>
<div v-else> <div v-if="bulkMode" ref="bulkEditor"></div>
<div <div v-else>
v-for="(header, index) in headers" <div
:key="`header-${String(index)}`" v-for="(header, index) in headers"
class="divide-dividerLight divide-x border-b border-dividerLight flex" :key="`header-${String(index)}`"
> class="divide-dividerLight divide-x border-b border-dividerLight flex"
<SmartAutoComplete >
:placeholder="`${t('count.header', { count: index + 1 })}`" <SmartAutoComplete
:source="commonHeaders" :placeholder="`${t('count.header', { count: index + 1 })}`"
:spellcheck="false" :source="commonHeaders"
:value="header.key" :spellcheck="false"
autofocus :value="header.key"
styles=" autofocus
styles="
bg-transparent bg-transparent
flex flex
flex-1 flex-1
@@ -160,92 +162,90 @@
px-4 px-4
truncate truncate
" "
class="flex-1 !flex" class="flex-1 !flex"
@input=" @input="
updateRequestHeader(index, { updateRequestHeader(index, {
key: $event, key: $event,
value: header.value, value: header.value,
active: header.active, active: header.active,
}) })
"
/>
<input
class="bg-transparent flex flex-1 py-2 px-4"
:placeholder="`${t('count.value', { count: index + 1 })}`"
:name="`value ${String(index)}`"
:value="header.value"
autofocus
@change="
updateRequestHeader(index, {
key: header.key,
value: $event.target.value,
active: header.active,
})
"
/>
<span>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="
header.hasOwnProperty('active')
? header.active
? t('action.turn_off')
: t('action.turn_on')
: t('action.turn_off')
" "
/> :svg="
<input header.hasOwnProperty('active')
class="bg-transparent flex flex-1 py-2 px-4" ? header.active
:placeholder="`${t('count.value', { count: index + 1 })}`" ? 'check-circle'
:name="`value ${String(index)}`" : 'circle'
:value="header.value" : 'check-circle'
autofocus "
@change=" color="green"
@click.native="
updateRequestHeader(index, { updateRequestHeader(index, {
key: header.key, key: header.key,
value: $event.target.value, value: header.value,
active: header.active, active: !header.active,
}) })
" "
/> />
<span> </span>
<ButtonSecondary <span>
v-tippy="{ theme: 'tooltip' }"
:title="
header.hasOwnProperty('active')
? header.active
? t('action.turn_off')
: t('action.turn_on')
: t('action.turn_off')
"
:svg="
header.hasOwnProperty('active')
? header.active
? 'check-circle'
: 'circle'
: 'check-circle'
"
color="green"
@click.native="
updateRequestHeader(index, {
key: header.key,
value: header.value,
active: !header.active,
})
"
/>
</span>
<span>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('action.remove')"
svg="trash"
color="red"
@click.native="removeRequestHeader(index)"
/>
</span>
</div>
<div
v-if="headers.length === 0"
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
>
<img
:src="`/images/states/${$colorMode.value}/add_category.svg`"
loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="`${t('empty.headers')}`"
/>
<span class="text-center pb-4">
{{ t("empty.headers") }}
</span>
<ButtonSecondary <ButtonSecondary
:label="`${t('add.new')}`" v-tippy="{ theme: 'tooltip' }"
filled :title="t('action.remove')"
svg="plus" svg="trash"
class="mb-4" color="red"
@click.native="addRequestHeader" @click.native="removeRequestHeader(index)"
/> />
</div> </span>
</div> </div>
</AppSection> <div
v-if="headers.length === 0"
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
>
<img
:src="`/images/states/${$colorMode.value}/add_category.svg`"
loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="`${t('empty.headers')}`"
/>
<span class="text-center pb-4">
{{ t("empty.headers") }}
</span>
<ButtonSecondary
:label="`${t('add.new')}`"
filled
svg="plus"
class="mb-4"
@click.native="addRequestHeader"
/>
</div>
</div>
</SmartTab> </SmartTab>
</SmartTabs> </SmartTabs>
<CollectionsSaveRequest <CollectionsSaveRequest
mode="graphql" mode="graphql"
:show="showSaveRequestModal" :show="showSaveRequestModal"
@@ -379,8 +379,9 @@ useCodemirror(queryEditor, gqlQueryString, {
}) })
const copyQueryIcon = ref("copy") const copyQueryIcon = ref("copy")
const prettifyQueryIcon = ref("wand")
const copyVariablesIcon = ref("copy") const copyVariablesIcon = ref("copy")
const prettifyQueryIcon = ref("wand")
const prettifyVariablesIcon = ref("wand")
const showSaveRequestModal = ref(false) const showSaveRequestModal = ref(false)
@@ -511,6 +512,19 @@ const copyVariables = () => {
setTimeout(() => (copyVariablesIcon.value = "copy"), 1000) setTimeout(() => (copyVariablesIcon.value = "copy"), 1000)
} }
const prettifyVariableString = () => {
try {
const jsonObj = JSON.parse(variableString.value)
variableString.value = JSON.stringify(jsonObj, null, 2)
prettifyVariablesIcon.value = "check"
} catch (e) {
console.error(e)
prettifyVariablesIcon.value = "info"
toast.error(`${t("error.json_prettify_invalid_body")}`)
}
setTimeout(() => (prettifyVariablesIcon.value = "wand"), 1000)
}
const addRequestHeader = () => { const addRequestHeader = () => {
const empty = { key: "", value: "", active: true } const empty = { key: "", value: "", active: true }
const index = headers.value.length const index = headers.value.length

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection ref="response" label="response"> <div>
<div <div
v-if="responseString === 'loading'" v-if="responseString === 'loading'"
class="flex flex-col p-4 items-center justify-center" class="flex flex-col p-4 items-center justify-center"
@@ -71,7 +71,7 @@
reverse reverse
/> />
</div> </div>
</AppSection> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -26,125 +26,34 @@
icon="book-open" icon="book-open"
:label="`${t('tab.documentation')}`" :label="`${t('tab.documentation')}`"
> >
<AppSection label="docs"> <div
<div v-if="
v-if=" queryFields.length === 0 &&
queryFields.length === 0 && mutationFields.length === 0 &&
mutationFields.length === 0 && subscriptionFields.length === 0 &&
subscriptionFields.length === 0 && graphqlTypes.length === 0
graphqlTypes.length === 0 "
" class="flex flex-col text-secondaryLight p-4 items-center justify-center"
class="flex flex-col text-secondaryLight p-4 items-center justify-center" >
> <img
<img :src="`/images/states/${$colorMode.value}/add_comment.svg`"
:src="`/images/states/${$colorMode.value}/add_comment.svg`" loading="lazy"
loading="lazy" class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex" :alt="`${t('empty.documentation')}`"
:alt="`${t('empty.documentation')}`" />
<span class="text-center mb-4">
{{ t("empty.documentation") }}
</span>
</div>
<div v-else>
<div class="bg-primary flex top-0 z-10 sticky">
<input
v-model="graphqlFieldsFilterText"
type="search"
autocomplete="off"
:placeholder="`${t('action.search')}`"
class="bg-transparent flex w-full p-4 py-2"
/> />
<span class="text-center mb-4">
{{ t("empty.documentation") }}
</span>
</div>
<div v-else>
<div class="bg-primary flex top-0 z-10 sticky">
<input
v-model="graphqlFieldsFilterText"
type="search"
autocomplete="off"
:placeholder="`${t('action.search')}`"
class="bg-transparent flex w-full p-4 py-2"
/>
<div class="flex">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
to="https://docs.hoppscotch.io/quickstart/graphql"
blank
:title="t('app.wiki')"
svg="help-circle"
/>
</div>
</div>
<SmartTabs
ref="gqlTabs"
styles="border-t border-b border-dividerLight bg-primary sticky z-10 top-sidebarPrimaryStickyFold"
>
<div class="gqlTabs">
<SmartTab
v-if="queryFields.length > 0"
:id="'queries'"
:label="`${t('tab.queries')}`"
:selected="true"
class="divide-dividerLight divide-y"
>
<GraphqlField
v-for="(field, index) in filteredQueryFields"
:key="`field-${index}`"
:gql-field="field"
:jump-type-callback="handleJumpToType"
class="p-4"
/>
</SmartTab>
<SmartTab
v-if="mutationFields.length > 0"
:id="'mutations'"
:label="`${t('graphql.mutations')}`"
class="divide-dividerLight divide-y"
>
<GraphqlField
v-for="(field, index) in filteredMutationFields"
:key="`field-${index}`"
:gql-field="field"
:jump-type-callback="handleJumpToType"
class="p-4"
/>
</SmartTab>
<SmartTab
v-if="subscriptionFields.length > 0"
:id="'subscriptions'"
:label="`${t('graphql.subscriptions')}`"
class="divide-dividerLight divide-y"
>
<GraphqlField
v-for="(field, index) in filteredSubscriptionFields"
:key="`field-${index}`"
:gql-field="field"
:jump-type-callback="handleJumpToType"
class="p-4"
/>
</SmartTab>
<SmartTab
v-if="graphqlTypes.length > 0"
:id="'types'"
ref="typesTab"
:label="`${t('tab.types')}`"
class="divide-dividerLight divide-y"
>
<GraphqlType
v-for="(type, index) in filteredGraphqlTypes"
:key="`type-${index}`"
:gql-type="type"
:gql-types="graphqlTypes"
:is-highlighted="isGqlTypeHighlighted(type)"
:highlighted-fields="getGqlTypeHighlightedFields(type)"
:jump-type-callback="handleJumpToType"
/>
</SmartTab>
</div>
</SmartTabs>
</div>
</AppSection>
</SmartTab>
<SmartTab :id="'schema'" icon="box" :label="`${t('tab.schema')}`">
<AppSection ref="schema" label="schema">
<div
v-if="schemaString"
class="bg-primary border-b border-dividerLight flex flex-1 pl-4 top-0 z-10 sticky items-center justify-between"
>
<label class="font-semibold text-secondaryLight">
{{ t("graphql.schema") }}
</label>
<div class="flex"> <div class="flex">
<ButtonSecondary <ButtonSecondary
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
@@ -153,45 +62,132 @@
:title="t('app.wiki')" :title="t('app.wiki')"
svg="help-circle" svg="help-circle"
/> />
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('state.linewrap')"
:class="{ '!text-accent': linewrapEnabled }"
svg="wrap-text"
@click.native.prevent="linewrapEnabled = !linewrapEnabled"
/>
<ButtonSecondary
ref="downloadSchema"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
:svg="downloadSchemaIcon"
@click.native="downloadSchema"
/>
<ButtonSecondary
ref="copySchemaCode"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')"
:svg="copySchemaIcon"
@click.native="copySchema"
/>
</div> </div>
</div> </div>
<div v-if="schemaString" ref="schemaEditor"></div> <SmartTabs
<div ref="gqlTabs"
v-else styles="border-t border-b border-dividerLight bg-primary sticky z-10 top-sidebarPrimaryStickyFold"
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
> >
<img <div class="gqlTabs">
:src="`/images/states/${$colorMode.value}/blockchain.svg`" <SmartTab
loading="lazy" v-if="queryFields.length > 0"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex" :id="'queries'"
:alt="`${t('empty.schema')}`" :label="`${t('tab.queries')}`"
:selected="true"
class="divide-dividerLight divide-y"
>
<GraphqlField
v-for="(field, index) in filteredQueryFields"
:key="`field-${index}`"
:gql-field="field"
:jump-type-callback="handleJumpToType"
class="p-4"
/>
</SmartTab>
<SmartTab
v-if="mutationFields.length > 0"
:id="'mutations'"
:label="`${t('graphql.mutations')}`"
class="divide-dividerLight divide-y"
>
<GraphqlField
v-for="(field, index) in filteredMutationFields"
:key="`field-${index}`"
:gql-field="field"
:jump-type-callback="handleJumpToType"
class="p-4"
/>
</SmartTab>
<SmartTab
v-if="subscriptionFields.length > 0"
:id="'subscriptions'"
:label="`${t('graphql.subscriptions')}`"
class="divide-dividerLight divide-y"
>
<GraphqlField
v-for="(field, index) in filteredSubscriptionFields"
:key="`field-${index}`"
:gql-field="field"
:jump-type-callback="handleJumpToType"
class="p-4"
/>
</SmartTab>
<SmartTab
v-if="graphqlTypes.length > 0"
:id="'types'"
ref="typesTab"
:label="`${t('tab.types')}`"
class="divide-dividerLight divide-y"
>
<GraphqlType
v-for="(type, index) in filteredGraphqlTypes"
:key="`type-${index}`"
:gql-type="type"
:gql-types="graphqlTypes"
:is-highlighted="isGqlTypeHighlighted(type)"
:highlighted-fields="getGqlTypeHighlightedFields(type)"
:jump-type-callback="handleJumpToType"
/>
</SmartTab>
</div>
</SmartTabs>
</div>
</SmartTab>
<SmartTab :id="'schema'" icon="box" :label="`${t('tab.schema')}`">
<div
v-if="schemaString"
class="bg-primary border-b border-dividerLight flex flex-1 pl-4 top-0 z-10 sticky items-center justify-between"
>
<label class="font-semibold text-secondaryLight">
{{ t("graphql.schema") }}
</label>
<div class="flex">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
to="https://docs.hoppscotch.io/quickstart/graphql"
blank
:title="t('app.wiki')"
svg="help-circle"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('state.linewrap')"
:class="{ '!text-accent': linewrapEnabled }"
svg="wrap-text"
@click.native.prevent="linewrapEnabled = !linewrapEnabled"
/>
<ButtonSecondary
ref="downloadSchema"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
:svg="downloadSchemaIcon"
@click.native="downloadSchema"
/>
<ButtonSecondary
ref="copySchemaCode"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')"
:svg="copySchemaIcon"
@click.native="copySchema"
/> />
<span class="text-center mb-4">
{{ t("empty.schema") }}
</span>
</div> </div>
</AppSection> </div>
<div v-if="schemaString" ref="schemaEditor"></div>
<div
v-else
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
>
<img
:src="`/images/states/${$colorMode.value}/blockchain.svg`"
loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="`${t('empty.schema')}`"
/>
<span class="text-center mb-4">
{{ t("empty.schema") }}
</span>
</div>
</SmartTab> </SmartTab>
</SmartTabs> </SmartTabs>
</template> </template>

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection label="history"> <div>
<div class="bg-primary border-b border-dividerLight flex top-0 z-10 sticky"> <div class="bg-primary border-b border-dividerLight flex top-0 z-10 sticky">
<input <input
v-model="filterText" v-model="filterText"
@@ -52,7 +52,7 @@
class="flex flex-col text-secondaryLight p-4 items-center justify-center" class="flex flex-col text-secondaryLight p-4 items-center justify-center"
> >
<i class="opacity-75 pb-2 material-icons">manage_search</i> <i class="opacity-75 pb-2 material-icons">manage_search</i>
<span class="text-center my-2"> <span class="my-2 text-center">
{{ $t("state.nothing_found") }} "{{ filterText }}" {{ $t("state.nothing_found") }} "{{ filterText }}"
</span> </span>
</div> </div>
@@ -64,7 +64,7 @@
:src="`/images/states/${$colorMode.value}/history.svg`" :src="`/images/states/${$colorMode.value}/history.svg`"
loading="lazy" loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex" class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="$t('empty.history')" :alt="`${$t('empty.history')}`"
/> />
<span class="text-center mb-4"> <span class="text-center mb-4">
{{ $t("empty.history") }} {{ $t("empty.history") }}
@@ -76,7 +76,7 @@
@hide-modal="confirmRemove = false" @hide-modal="confirmRemove = false"
@resolve="clearHistory" @resolve="clearHistory"
/> />
</AppSection> </div>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@@ -109,7 +109,7 @@
:src="`/images/states/${$colorMode.value}/login.svg`" :src="`/images/states/${$colorMode.value}/login.svg`"
loading="lazy" loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex" class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="$t('empty.authorization')" :alt="`${$t('empty.authorization')}`"
/> />
<span class="text-center pb-4"> <span class="text-center pb-4">
{{ $t("empty.authorization") }} {{ $t("empty.authorization") }}

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection label="bodyParameters"> <div>
<div <div
class="bg-primary border-b border-dividerLight flex flex-1 top-upperTertiaryStickyFold pl-4 z-10 sticky items-center justify-between" class="bg-primary border-b border-dividerLight flex flex-1 top-upperTertiaryStickyFold pl-4 z-10 sticky items-center justify-between"
> >
@@ -147,7 +147,7 @@
:src="`/images/states/${$colorMode.value}/upload_single_file.svg`" :src="`/images/states/${$colorMode.value}/upload_single_file.svg`"
loading="lazy" loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex" class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="$t('empty.body')" :alt="`${$t('empty.body')}`"
/> />
<span class="text-center pb-4"> <span class="text-center pb-4">
{{ $t("empty.body") }} {{ $t("empty.body") }}
@@ -160,7 +160,7 @@
@click.native="addBodyParam" @click.native="addBodyParam"
/> />
</div> </div>
</AppSection> </div>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection label="headers"> <div>
<div <div
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between" class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
> >
@@ -145,7 +145,7 @@
/> />
</div> </div>
</div> </div>
</AppSection> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection label="parameters"> <div>
<div <div
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between" class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
> >
@@ -138,7 +138,7 @@
/> />
</div> </div>
</div> </div>
</AppSection> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection id="script" :label="`${t('preRequest.script')}`"> <div>
<div <div
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between" class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
> >
@@ -58,7 +58,7 @@
</div> </div>
</div> </div>
</div> </div>
</AppSection> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -117,10 +117,11 @@ const prettifyRequestBody = () => {
const jsonObj = JSON.parse(rawParamsBody.value) const jsonObj = JSON.parse(rawParamsBody.value)
rawParamsBody.value = JSON.stringify(jsonObj, null, 2) rawParamsBody.value = JSON.stringify(jsonObj, null, 2)
prettifyIcon.value = "check" prettifyIcon.value = "check"
setTimeout(() => (prettifyIcon.value = "wand"), 1000)
} catch (e) { } catch (e) {
console.error(e) console.error(e)
prettifyIcon.value = "info"
toast.error(`${t("error.json_prettify_invalid_body")}`) toast.error(`${t("error.json_prettify_invalid_body")}`)
} }
setTimeout(() => (prettifyIcon.value = "wand"), 1000)
} }
</script> </script>

View File

@@ -1,11 +1,11 @@
<template> <template>
<AppSection label="response"> <div>
<HttpResponseMeta :response="response" /> <HttpResponseMeta :response="response" />
<LensesResponseBodyRenderer <LensesResponseBodyRenderer
v-if="!loading && hasResponse" v-if="!loading && hasResponse"
:response="response" :response="response"
/> />
</AppSection> </div>
</template> </template>
<script lang="ts"> <script lang="ts">

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection :label="`${t('test.results')}`"> <div>
<div <div
v-if=" v-if="
testResults && testResults &&
@@ -88,7 +88,7 @@
class="my-4" class="my-4"
/> />
</div> </div>
</AppSection> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection id="script" :label="`${t('test.script')}`"> <div>
<div <div
class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between" class="bg-primary border-b border-dividerLight flex flex-1 top-upperSecondaryStickyFold pl-4 z-10 sticky items-center justify-between"
> >
@@ -58,7 +58,7 @@
</div> </div>
</div> </div>
</div> </div>
</AppSection> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -13,63 +13,57 @@
:size="COLUMN_LAYOUT ? 45 : 50" :size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="request"> <div class="bg-primary flex flex-col space-y-4 p-4 top-0 z-10 sticky">
<div <div class="space-x-2 flex-1 inline-flex">
class="bg-primary flex flex-col space-y-4 p-4 top-0 z-10 sticky" <input
> id="mqtt-url"
<div class="space-x-2 flex-1 inline-flex"> v-model="url"
<input type="url"
id="mqtt-url" autocomplete="off"
v-model="url" spellcheck="false"
type="url" class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
autocomplete="off" :placeholder="$t('mqtt.url')"
spellcheck="false" :disabled="connectionState"
class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark" @keyup.enter="validUrl ? toggleConnection() : null"
:placeholder="$t('mqtt.url')" />
:disabled="connectionState" <ButtonPrimary
@keyup.enter="validUrl ? toggleConnection() : null" id="connect"
/> :disabled="!validUrl"
<ButtonPrimary class="w-32"
id="connect" :label="
:disabled="!validUrl" connectionState
class="w-32" ? $t('action.disconnect')
:label=" : $t('action.connect')
connectionState "
? $t('action.disconnect') :loading="connectingState"
: $t('action.connect') @click.native="toggleConnection"
" />
:loading="connectingState"
@click.native="toggleConnection"
/>
</div>
<div class="flex space-x-4">
<input
id="mqtt-username"
v-model="username"
type="text"
spellcheck="false"
class="input"
:placeholder="$t('authorization.username')"
/>
<input
id="mqtt-password"
v-model="password"
type="password"
spellcheck="false"
class="input"
:placeholder="$t('authorization.password')"
/>
</div>
</div> </div>
</AppSection> <div class="flex space-x-4">
<input
id="mqtt-username"
v-model="username"
type="text"
spellcheck="false"
class="input"
:placeholder="$t('authorization.username')"
/>
<input
id="mqtt-password"
v-model="password"
type="password"
spellcheck="false"
class="input"
:placeholder="$t('authorization.password')"
/>
</div>
</div>
</Pane> </Pane>
<Pane <Pane
:size="COLUMN_LAYOUT ? 65 : 50" :size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="response"> <RealtimeLog :title="$t('mqtt.log')" :log="log" />
<RealtimeLog :title="$t('mqtt.log')" :log="log" />
</AppSection>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</Pane> </Pane>
@@ -79,75 +73,73 @@
min-size="20" min-size="20"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="messages"> <div class="flex flex-col flex-1 p-4 inline-flex">
<div class="flex flex-col flex-1 p-4 inline-flex"> <label for="pub_topic" class="font-semibold text-secondaryLight">
<label for="pub_topic" class="font-semibold text-secondaryLight"> {{ $t("mqtt.topic") }}
{{ $t("mqtt.topic") }} </label>
</label> </div>
</div> <div class="flex px-4">
<div class="flex px-4"> <input
<input id="pub_topic"
id="pub_topic" v-model="pub_topic"
v-model="pub_topic" class="input"
class="input" :placeholder="$t('mqtt.topic_name')"
:placeholder="$t('mqtt.topic_name')" type="text"
type="text" autocomplete="off"
autocomplete="off" spellcheck="false"
spellcheck="false" />
/> </div>
</div> <div class="flex flex-1 p-4 items-center justify-between">
<div class="flex flex-1 p-4 items-center justify-between"> <label for="mqtt-message" class="font-semibold text-secondaryLight">
<label for="mqtt-message" class="font-semibold text-secondaryLight"> {{ $t("mqtt.communication") }}
{{ $t("mqtt.communication") }} </label>
</label> </div>
</div> <div class="flex space-x-2 px-4">
<div class="flex space-x-2 px-4"> <input
<input id="mqtt-message"
id="mqtt-message" v-model="msg"
v-model="msg" class="input"
class="input" type="text"
type="text" autocomplete="off"
autocomplete="off" :placeholder="$t('mqtt.message')"
:placeholder="$t('mqtt.message')" spellcheck="false"
spellcheck="false" />
/> <ButtonPrimary
<ButtonPrimary id="publish"
id="publish" name="get"
name="get" :disabled="!canpublish"
:disabled="!canpublish" :label="$t('mqtt.publish')"
:label="$t('mqtt.publish')" @click.native="publish"
@click.native="publish" />
/> </div>
</div> <div
<div class="border-t border-dividerLight flex flex-col flex-1 mt-4 p-4 inline-flex"
class="border-t border-dividerLight flex flex-col flex-1 mt-4 p-4 inline-flex" >
> <label for="sub_topic" class="font-semibold text-secondaryLight">
<label for="sub_topic" class="font-semibold text-secondaryLight"> {{ $t("mqtt.topic") }}
{{ $t("mqtt.topic") }} </label>
</label> </div>
</div> <div class="flex space-x-2 px-4">
<div class="flex space-x-2 px-4"> <input
<input id="sub_topic"
id="sub_topic" v-model="sub_topic"
v-model="sub_topic" type="text"
type="text" autocomplete="off"
autocomplete="off" :placeholder="$t('mqtt.topic_name')"
:placeholder="$t('mqtt.topic_name')" spellcheck="false"
spellcheck="false" class="input"
class="input" />
/> <ButtonPrimary
<ButtonPrimary id="subscribe"
id="subscribe" name="get"
name="get" :disabled="!cansubscribe"
:disabled="!cansubscribe" :label="
:label=" subscriptionState ? $t('mqtt.unsubscribe') : $t('mqtt.subscribe')
subscriptionState ? $t('mqtt.unsubscribe') : $t('mqtt.subscribe') "
" reverse
reverse @click.native="toggleSubscription"
@click.native="toggleSubscription" />
/> </div>
</div>
</AppSection>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</template> </template>

View File

@@ -13,84 +13,80 @@
:size="COLUMN_LAYOUT ? 45 : 50" :size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="request"> <div class="bg-primary flex p-4 top-0 z-10 sticky">
<div class="bg-primary flex p-4 top-0 z-10 sticky"> <div class="space-x-2 flex-1 inline-flex">
<div class="space-x-2 flex-1 inline-flex"> <div class="flex flex-1">
<div class="flex flex-1"> <label for="client-version">
<label for="client-version"> <tippy
<tippy ref="versionOptions"
ref="versionOptions" interactive
interactive trigger="click"
trigger="click" theme="popover"
theme="popover" arrow
arrow >
> <template #trigger>
<template #trigger> <span class="select-wrapper">
<span class="select-wrapper"> <input
<input id="client-version"
id="client-version" v-tippy="{ theme: 'tooltip' }"
v-tippy="{ theme: 'tooltip' }" title="socket.io-client version"
title="socket.io-client version" class="bg-primaryLight border border-divider rounded-l cursor-pointer flex font-semibold text-secondaryDark py-2 px-4 w-26 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
class="bg-primaryLight border border-divider rounded-l cursor-pointer flex font-semibold text-secondaryDark py-2 px-4 w-26 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark" :value="`Client ${clientVersion}`"
:value="`Client ${clientVersion}`" readonly
readonly :disabled="connectionState"
:disabled="connectionState" />
/> </span>
</span> </template>
</template> <SmartItem
<SmartItem v-for="(_, version) in socketIoClients"
v-for="(_, version) in socketIoClients" :key="`client-${version}`"
:key="`client-${version}`" :label="`Client ${version}`"
:label="`Client ${version}`" @click.native="onSelectVersion(version)"
@click.native="onSelectVersion(version)" />
/> </tippy>
</tippy> </label>
</label> <input
<input id="socketio-url"
id="socketio-url" v-model="url"
v-model="url" type="url"
type="url" autocomplete="off"
autocomplete="off" spellcheck="false"
spellcheck="false" :class="{ error: !urlValid }"
:class="{ error: !urlValid }" class="bg-primaryLight border border-divider flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
class="bg-primaryLight border border-divider flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark" :placeholder="$t('socketio.url')"
:placeholder="$t('socketio.url')" :disabled="connectionState"
:disabled="connectionState" @keyup.enter="urlValid ? toggleConnection() : null"
@keyup.enter="urlValid ? toggleConnection() : null" />
/> <input
<input id="socketio-path"
id="socketio-path" v-model="path"
v-model="path" class="bg-primaryLight border border-divider rounded-r flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
class="bg-primaryLight border border-divider rounded-r flex flex-1 text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark" spellcheck="false"
spellcheck="false" :disabled="connectionState"
:disabled="connectionState" @keyup.enter="urlValid ? toggleConnection() : null"
@keyup.enter="urlValid ? toggleConnection() : null"
/>
</div>
<ButtonPrimary
id="connect"
:disabled="!urlValid"
name="connect"
class="w-32"
:label="
!connectionState
? $t('action.connect')
: $t('action.disconnect')
"
:loading="connectingState"
@click.native="toggleConnection"
/> />
</div> </div>
<ButtonPrimary
id="connect"
:disabled="!urlValid"
name="connect"
class="w-32"
:label="
!connectionState
? $t('action.connect')
: $t('action.disconnect')
"
:loading="connectingState"
@click.native="toggleConnection"
/>
</div> </div>
</AppSection> </div>
</Pane> </Pane>
<Pane <Pane
:size="COLUMN_LAYOUT ? 65 : 50" :size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="response"> <RealtimeLog :title="$t('socketio.log')" :log="log" />
<RealtimeLog :title="$t('socketio.log')" :log="log" />
</AppSection>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</Pane> </Pane>
@@ -100,74 +96,72 @@
min-size="20" min-size="20"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="messages"> <div class="flex flex-col flex-1 p-4 inline-flex">
<div class="flex flex-col flex-1 p-4 inline-flex"> <label for="events" class="font-semibold text-secondaryLight">
<label for="events" class="font-semibold text-secondaryLight"> {{ $t("socketio.events") }}
{{ $t("socketio.events") }} </label>
</label> </div>
</div> <div class="flex px-4">
<div class="flex px-4"> <input
<input id="event_name"
id="event_name" v-model="communication.eventName"
v-model="communication.eventName" class="input"
class="input" name="event_name"
name="event_name" :placeholder="$t('socketio.event_name')"
:placeholder="$t('socketio.event_name')" type="text"
type="text" autocomplete="off"
autocomplete="off" :disabled="!connectionState"
:disabled="!connectionState" />
</div>
<div class="flex flex-1 p-4 items-center justify-between">
<label class="font-semibold text-secondaryLight">
{{ $t("socketio.communication") }}
</label>
<div class="flex">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('add.new')"
svg="plus"
@click.native="addCommunicationInput"
/> />
</div> </div>
<div class="flex flex-1 p-4 items-center justify-between"> </div>
<label class="font-semibold text-secondaryLight"> <div class="flex flex-col space-y-2 px-4 pb-4">
{{ $t("socketio.communication") }} <div
</label> v-for="(input, index) of communication.inputs"
<div class="flex"> :key="`input-${index}`"
>
<div class="flex space-x-2">
<input
v-model="communication.inputs[index]"
class="input"
name="message"
:placeholder="$t('count.message', { count: index + 1 })"
type="text"
autocomplete="off"
:disabled="!connectionState"
@keyup.enter="connectionState ? sendMessage() : null"
/>
<ButtonSecondary <ButtonSecondary
v-if="index + 1 !== communication.inputs.length"
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:title="$t('add.new')" :title="$t('action.remove')"
svg="plus" svg="trash"
@click.native="addCommunicationInput" color="red"
outline
@click.native="removeCommunicationInput({ index })"
/>
<ButtonPrimary
v-if="index + 1 === communication.inputs.length"
id="send"
name="send"
:disabled="!connectionState"
:label="$t('action.send')"
@click.native="sendMessage"
/> />
</div> </div>
</div> </div>
<div class="flex flex-col space-y-2 px-4 pb-4"> </div>
<div
v-for="(input, index) of communication.inputs"
:key="`input-${index}`"
>
<div class="flex space-x-2">
<input
v-model="communication.inputs[index]"
class="input"
name="message"
:placeholder="$t('count.message', { count: index + 1 })"
type="text"
autocomplete="off"
:disabled="!connectionState"
@keyup.enter="connectionState ? sendMessage() : null"
/>
<ButtonSecondary
v-if="index + 1 !== communication.inputs.length"
v-tippy="{ theme: 'tooltip' }"
:title="$t('action.remove')"
svg="trash"
color="red"
outline
@click.native="removeCommunicationInput({ index })"
/>
<ButtonPrimary
v-if="index + 1 === communication.inputs.length"
id="send"
name="send"
:disabled="!connectionState"
:label="$t('action.send')"
@click.native="sendMessage"
/>
</div>
</div>
</div>
</AppSection>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</template> </template>

View File

@@ -45,14 +45,7 @@
</div> </div>
</Pane> </Pane>
<Pane :size="COLUMN_LAYOUT ? 65 : 50" class="hide-scrollbar !overflow-auto"> <Pane :size="COLUMN_LAYOUT ? 65 : 50" class="hide-scrollbar !overflow-auto">
<AppSection label="response"> <RealtimeLog :title="$t('sse.log')" :log="log" />
<ul>
<li>
<RealtimeLog :title="$t('sse.log')" :log="log" />
<div id="result"></div>
</li>
</ul>
</AppSection>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</template> </template>

View File

@@ -13,135 +13,131 @@
:size="COLUMN_LAYOUT ? 45 : 50" :size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="request"> <div class="bg-primary flex p-4 top-0 z-10 sticky">
<div class="bg-primary flex p-4 top-0 z-10 sticky"> <div class="space-x-2 flex-1 inline-flex">
<div class="space-x-2 flex-1 inline-flex">
<input
id="websocket-url"
v-model="url"
class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
type="url"
autocomplete="off"
spellcheck="false"
:class="{ error: !urlValid }"
:placeholder="$t('websocket.url')"
:disabled="connectionState"
@keyup.enter="urlValid ? toggleConnection() : null"
/>
<ButtonPrimary
id="connect"
:disabled="!urlValid"
class="w-32"
name="connect"
:label="
!connectionState
? $t('action.connect')
: $t('action.disconnect')
"
:loading="connectingState"
@click.native="toggleConnection"
/>
</div>
</div>
<div
class="bg-primary border-b border-dividerLight flex flex-1 top-upperPrimaryStickyFold pl-4 z-10 sticky items-center justify-between"
>
<label class="font-semibold text-secondaryLight">
{{ $t("websocket.protocols") }}
</label>
<div class="flex">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('action.clear_all')"
svg="trash-2"
@click.native="clearContent"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('add.new')"
svg="plus"
@click.native="addProtocol"
/>
</div>
</div>
<div
v-for="(protocol, index) of protocols"
:key="`protocol-${index}`"
class="divide-dividerLight divide-x border-b border-dividerLight flex"
>
<input <input
v-model="protocol.value" id="websocket-url"
class="bg-transparent flex flex-1 py-2 px-4" v-model="url"
:placeholder="$t('count.protocol', { count: index + 1 })" class="bg-primaryLight border border-divider rounded text-secondaryDark w-full py-2 px-4 hover:border-dividerDark focus-visible:bg-transparent focus-visible:border-dividerDark"
name="message" type="url"
type="text"
autocomplete="off" autocomplete="off"
@change=" spellcheck="false"
:class="{ error: !urlValid }"
:placeholder="$t('websocket.url')"
:disabled="connectionState"
@keyup.enter="urlValid ? toggleConnection() : null"
/>
<ButtonPrimary
id="connect"
:disabled="!urlValid"
class="w-32"
name="connect"
:label="
!connectionState
? $t('action.connect')
: $t('action.disconnect')
"
:loading="connectingState"
@click.native="toggleConnection"
/>
</div>
</div>
<div
class="bg-primary border-b border-dividerLight flex flex-1 top-upperPrimaryStickyFold pl-4 z-10 sticky items-center justify-between"
>
<label class="font-semibold text-secondaryLight">
{{ $t("websocket.protocols") }}
</label>
<div class="flex">
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('action.clear_all')"
svg="trash-2"
@click.native="clearContent"
/>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('add.new')"
svg="plus"
@click.native="addProtocol"
/>
</div>
</div>
<div
v-for="(protocol, index) of protocols"
:key="`protocol-${index}`"
class="divide-dividerLight divide-x border-b border-dividerLight flex"
>
<input
v-model="protocol.value"
class="bg-transparent flex flex-1 py-2 px-4"
:placeholder="$t('count.protocol', { count: index + 1 })"
name="message"
type="text"
autocomplete="off"
@change="
updateProtocol(index, {
value: $event.target.value,
active: protocol.active,
})
"
/>
<span>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="
protocol.hasOwnProperty('active')
? protocol.active
? $t('action.turn_off')
: $t('action.turn_on')
: $t('action.turn_off')
"
:svg="
protocol.hasOwnProperty('active')
? protocol.active
? 'check-circle'
: 'circle'
: 'check-circle'
"
color="green"
@click.native="
updateProtocol(index, { updateProtocol(index, {
value: $event.target.value, value: protocol.value,
active: protocol.active, active: !protocol.active,
}) })
" "
/> />
<span> </span>
<ButtonSecondary <span>
v-tippy="{ theme: 'tooltip' }" <ButtonSecondary
:title=" v-tippy="{ theme: 'tooltip' }"
protocol.hasOwnProperty('active') :title="$t('action.remove')"
? protocol.active svg="trash"
? $t('action.turn_off') color="red"
: $t('action.turn_on') @click.native="deleteProtocol({ index })"
: $t('action.turn_off')
"
:svg="
protocol.hasOwnProperty('active')
? protocol.active
? 'check-circle'
: 'circle'
: 'check-circle'
"
color="green"
@click.native="
updateProtocol(index, {
value: protocol.value,
active: !protocol.active,
})
"
/>
</span>
<span>
<ButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="$t('action.remove')"
svg="trash"
color="red"
@click.native="deleteProtocol({ index })"
/>
</span>
</div>
<div
v-if="protocols.length === 0"
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
>
<img
:src="`/images/states/${$colorMode.value}/add_category.svg`"
loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="$t('empty.protocols')"
/> />
<span class="text-center mb-4"> </span>
{{ $t("empty.protocols") }} </div>
</span> <div
</div> v-if="protocols.length === 0"
</AppSection> class="flex flex-col text-secondaryLight p-4 items-center justify-center"
>
<img
:src="`/images/states/${$colorMode.value}/add_category.svg`"
loading="lazy"
class="flex-col object-contain object-center h-16 my-4 w-16 inline-flex"
:alt="$t('empty.protocols')"
/>
<span class="text-center mb-4">
{{ $t("empty.protocols") }}
</span>
</div>
</Pane> </Pane>
<Pane <Pane
:size="COLUMN_LAYOUT ? 65 : 50" :size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="response"> <RealtimeLog :title="$t('websocket.log')" :log="log" />
<RealtimeLog :title="$t('websocket.log')" :log="log" />
</AppSection>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</Pane> </Pane>
@@ -151,38 +147,36 @@
min-size="20" min-size="20"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="messages"> <div class="flex flex-col flex-1 p-4 inline-flex">
<div class="flex flex-col flex-1 p-4 inline-flex"> <label
<label for="websocket-message"
for="websocket-message" class="font-semibold text-secondaryLight"
class="font-semibold text-secondaryLight" >
> {{ $t("websocket.communication") }}
{{ $t("websocket.communication") }} </label>
</label> </div>
</div> <div class="flex space-x-2 px-4">
<div class="flex space-x-2 px-4"> <input
<input id="websocket-message"
id="websocket-message" v-model="communication.input"
v-model="communication.input" name="message"
name="message" type="text"
type="text" autocomplete="off"
autocomplete="off" :disabled="!connectionState"
:disabled="!connectionState" :placeholder="$t('websocket.message')"
:placeholder="$t('websocket.message')" class="input"
class="input" @keyup.enter="connectionState ? sendMessage() : null"
@keyup.enter="connectionState ? sendMessage() : null" @keyup.up="connectionState ? walkHistory('up') : null"
@keyup.up="connectionState ? walkHistory('up') : null" @keyup.down="connectionState ? walkHistory('down') : null"
@keyup.down="connectionState ? walkHistory('down') : null" />
/> <ButtonPrimary
<ButtonPrimary id="send"
id="send" name="send"
name="send" :disabled="!connectionState"
:disabled="!connectionState" :label="$t('action.send')"
:label="$t('action.send')" @click.native="sendMessage"
@click.native="sendMessage" />
/> </div>
</div>
</AppSection>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</template> </template>

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection label="teams"> <div>
<div class="space-y-4 p-4"> <div class="space-y-4 p-4">
<ButtonSecondary <ButtonSecondary
:label="`${t('team.create_new')}`" :label="`${t('team.create_new')}`"
@@ -88,7 +88,7 @@
:editing-team-i-d="editingTeamID" :editing-team-i-d="editingTeamID"
@hide-modal="displayModalInvite(false)" @hide-modal="displayModalInvite(false)"
/> />
</AppSection> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@@ -31,6 +31,7 @@ import { lintKeymap } from "@codemirror/lint"
export const baseTheme = EditorView.theme({ export const baseTheme = EditorView.theme({
"&": { "&": {
fontSize: "var(--font-size-body)", fontSize: "var(--font-size-body)",
height: "100%",
}, },
".cm-content": { ".cm-content": {
caretColor: "var(--secondary-light-color)", caretColor: "var(--secondary-light-color)",

View File

@@ -13,111 +13,107 @@
:size="COLUMN_LAYOUT ? 45 : 50" :size="COLUMN_LAYOUT ? 45 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="import"> <div class="flex p-4 items-start justify-between">
<div class="flex p-4 items-start justify-between"> <label>
<label> {{ $t("documentation.generate_message") }}
{{ $t("documentation.generate_message") }} </label>
</label> <span
<span class="bg-accentDark rounded text-accentContrast py-1 px-2 inline-flex"
class="bg-accentDark rounded text-accentContrast py-1 px-2 inline-flex"
>
BETA
</span>
</div>
<div
class="bg-primary border-b border-dividerLight flex top-0 z-10 sticky items-start justify-between"
> >
<label for="collectionUpload"> BETA
<ButtonSecondary </span>
v-tippy="{ theme: 'tooltip' }" </div>
title="JSON" <div
svg="folder" class="bg-primary border-b border-dividerLight flex top-0 z-10 sticky items-start justify-between"
class="!rounded-none" >
:label="$t('import.collections')" <label for="collectionUpload">
@click.native="$refs.collectionUpload.click()"
/>
</label>
<input
ref="collectionUpload"
class="input"
name="collectionUpload"
type="file"
@change="uploadCollection"
/>
<ButtonSecondary <ButtonSecondary
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:title="$t('action.clear')" title="JSON"
svg="trash-2" svg="folder"
@click.native="collectionJSON = '[]'" class="!rounded-none"
:label="$t('import.collections')"
@click.native="$refs.collectionUpload.click()"
/> />
</div> </label>
<textarea-autosize <input
id="import-curl" ref="collectionUpload"
v-model="collectionJSON" class="input"
class="bg-primary font-mono p-4" name="collectionUpload"
autofocus type="file"
rows="8" @change="uploadCollection"
/> />
<div <ButtonSecondary
class="bg-primary border-t border-b border-dividerLight flex p-4 bottom-0 z-10 sticky items-start justify-between" v-tippy="{ theme: 'tooltip' }"
> :title="$t('action.clear')"
<ButtonPrimary svg="trash-2"
:label="$t('documentation.generate')" @click.native="collectionJSON = '[]'"
@click.native="getDoc" />
/> </div>
</div> <textarea-autosize
</AppSection> id="import-curl"
v-model="collectionJSON"
class="bg-primary font-mono p-4"
autofocus
rows="8"
/>
<div
class="bg-primary border-t border-b border-dividerLight flex p-4 bottom-0 z-10 sticky items-start justify-between"
>
<ButtonPrimary
:label="$t('documentation.generate')"
@click.native="getDoc"
/>
</div>
</Pane> </Pane>
<Pane <Pane
:size="COLUMN_LAYOUT ? 65 : 50" :size="COLUMN_LAYOUT ? 65 : 50"
class="hide-scrollbar !overflow-auto" class="hide-scrollbar !overflow-auto"
> >
<AppSection label="documentation"> <div class="flex flex-col">
<div class="flex flex-col"> <div
<div v-if="items.length === 0"
v-if="items.length === 0" class="flex flex-col text-secondaryLight p-4 items-center justify-center"
class="flex flex-col text-secondaryLight p-4 items-center justify-center" >
> <i class="opacity-75 pb-2 material-icons">topic</i>
<i class="opacity-75 pb-2 material-icons">topic</i> <span class="text-center">
<span class="text-center"> {{ $t("helpers.generate_documentation_first") }}
{{ $t("helpers.generate_documentation_first") }} </span>
</span>
</div>
<div
v-else
class="bg-primary border-b border-dividerLight flex flex-1 p-4 top-0 z-10 sticky"
>
<span
v-tippy="{ theme: 'tooltip' }"
:title="
!currentUser
? $t('export.require_github')
: currentUser.provider !== 'github.com'
? $t('export.require_github')
: 'Beta'
"
>
<ButtonPrimary
:disabled="
!currentUser
? true
: currentUser.provider !== 'github.com'
? true
: false
"
:label="$t('export.create_secret_gist')"
@click.native="createDocsGist"
/>
</span>
</div>
<div
v-for="(collection, index) in items"
:key="`collection-${index}`"
>
<DocsCollection :collection="collection" />
</div>
</div> </div>
</AppSection> <div
v-else
class="bg-primary border-b border-dividerLight flex flex-1 p-4 top-0 z-10 sticky"
>
<span
v-tippy="{ theme: 'tooltip' }"
:title="
!currentUser
? $t('export.require_github')
: currentUser.provider !== 'github.com'
? $t('export.require_github')
: 'Beta'
"
>
<ButtonPrimary
:disabled="
!currentUser
? true
: currentUser.provider !== 'github.com'
? true
: false
"
:label="$t('export.create_secret_gist')"
@click.native="createDocsGist"
/>
</span>
</div>
<div
v-for="(collection, index) in items"
:key="`collection-${index}`"
>
<DocsCollection :collection="collection" />
</div>
</div>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
</Pane> </Pane>

View File

@@ -173,9 +173,7 @@
</section> </section>
</SmartTab> </SmartTab>
<SmartTab :id="'teams'" :label="t('team.title')"> <SmartTab :id="'teams'" :label="t('team.title')">
<AppSection label="teams"> <Teams :modal="false" />
<Teams :modal="false" />
</AppSection>
</SmartTab> </SmartTab>
</SmartTabs> </SmartTabs>
</div> </div>