implement search
This commit is contained in:
parent
eb5b6999ce
commit
8761a46767
|
@ -18,4 +18,5 @@ module.exports = [
|
||||||
{id:'fa-user-times', src:'node_modules/font-awesome-svg-png/white/svg/user-times.svg', title: 'Stop Following'},
|
{id:'fa-user-times', src:'node_modules/font-awesome-svg-png/white/svg/user-times.svg', title: 'Stop Following'},
|
||||||
{id:'fa-user-plus', src:'node_modules/font-awesome-svg-png/white/svg/user-plus.svg', title: 'Follow'},
|
{id:'fa-user-plus', src:'node_modules/font-awesome-svg-png/white/svg/user-plus.svg', title: 'Follow'},
|
||||||
{id:'fa-external-link', src:'node_modules/font-awesome-svg-png/white/svg/external-link.svg', title: 'External Link'},
|
{id:'fa-external-link', src:'node_modules/font-awesome-svg-png/white/svg/external-link.svg', title: 'External Link'},
|
||||||
|
{id:'fa-search', src:'node_modules/font-awesome-svg-png/white/svg/search.svg', title: 'Search'},
|
||||||
]
|
]
|
24
routes/_components/LoadingPage.html
Normal file
24
routes/_components/LoadingPage.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<div class="loading-page">
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.loading-page {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import LoadingSpinner from './LoadingSpinner.html'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
LoadingSpinner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -12,6 +12,9 @@
|
||||||
<li>
|
<li>
|
||||||
<NavItem :page name="federated" href="/federated" svg="#fa-globe" label="Federated" />
|
<NavItem :page name="federated" href="/federated" svg="#fa-globe" label="Federated" />
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<NavItem :page name="search" href="/search" svg="#fa-search" label="Search" />
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<NavItem :page name="settings" href="/settings" svg="#fa-gear" label="Settings" />
|
<NavItem :page name="settings" href="/settings" svg="#fa-gear" label="Settings" />
|
||||||
</li>
|
</li>
|
||||||
|
|
49
routes/_components/search/AccountSearchResult.html
Normal file
49
routes/_components/search/AccountSearchResult.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<SearchResult href="/accounts/{{account.id}}">
|
||||||
|
<div class="search-result-account">
|
||||||
|
<Avatar :account size="small" className="search-result-account-avatar"/>
|
||||||
|
<div class="search-result-account-name">
|
||||||
|
{{account.display_name}}
|
||||||
|
</div>
|
||||||
|
<div class="search-result-account-username">
|
||||||
|
{{'@' + account.acct}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SearchResult>
|
||||||
|
<style>
|
||||||
|
.search-result-account {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"avatar name"
|
||||||
|
"avatar username";
|
||||||
|
grid-column-gap: 20px;
|
||||||
|
grid-template-columns: max-content 1fr;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:global(.search-result-account-avatar) {
|
||||||
|
grid-area: avatar;
|
||||||
|
}
|
||||||
|
.search-result-account-name {
|
||||||
|
grid-area: name;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.search-result-account-username {
|
||||||
|
grid-area: username;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: var(--deemphasized-text-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import Avatar from '../status/Avatar.html'
|
||||||
|
import SearchResult from './SearchResult.html'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Avatar,
|
||||||
|
SearchResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
13
routes/_components/search/HashtagSearchResult.html
Normal file
13
routes/_components/search/HashtagSearchResult.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<SearchResult href="/tags/{{hashtag}}">
|
||||||
|
{{'#' + hashtag}}
|
||||||
|
</SearchResult>
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import SearchResult from './SearchResult.html'
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SearchResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
93
routes/_components/search/Search.html
Normal file
93
routes/_components/search/Search.html
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<form class="search-input-form" on:submit="onSubmit(event)">
|
||||||
|
<div class="search-input-wrapper">
|
||||||
|
<input type="search"
|
||||||
|
class="search-input"
|
||||||
|
placeholder="Search"
|
||||||
|
aria-label="Search input"
|
||||||
|
required
|
||||||
|
bind:value="$queryInSearch">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="primary search-button" aria-label="Search">
|
||||||
|
<svg>
|
||||||
|
<use xlink:href="#fa-search" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{{#if loading}}
|
||||||
|
<div class="search-results-container">
|
||||||
|
<LoadingPage />
|
||||||
|
</div>
|
||||||
|
{{elseif $searchResults && $searchResultsForQuery === $queryInSearch}}
|
||||||
|
<div class="search-results-container">
|
||||||
|
<SearchResults />
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<style>
|
||||||
|
.search-input-form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr min-content;
|
||||||
|
grid-gap: 10px;
|
||||||
|
}
|
||||||
|
.search-input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.search-input {
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 10px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.search-button svg {
|
||||||
|
fill: var(--button-primary-text);
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.search-results-container {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.search-button {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import { store } from '../../_store/store'
|
||||||
|
import LoadingPage from '../LoadingPage.html'
|
||||||
|
import { toast } from '../../_utils/toast'
|
||||||
|
import { search } from '../../_utils/mastodon/search'
|
||||||
|
import SearchResults from './SearchResults.html'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
store: () => store,
|
||||||
|
components: {
|
||||||
|
LoadingPage,
|
||||||
|
SearchResults
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async onSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
let instanceName = this.store.get('currentInstance')
|
||||||
|
let accessToken = this.store.get('accessToken')
|
||||||
|
let queryInSearch = this.store.get('queryInSearch')
|
||||||
|
this.set({loading: true})
|
||||||
|
try {
|
||||||
|
let results = await search(instanceName, accessToken, queryInSearch)
|
||||||
|
this.store.set({
|
||||||
|
searchResultsForQuery: queryInSearch,
|
||||||
|
searchResults: results
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
toast.say('Error during search: ' + (e.name || '') + ' ' + (e.message || ''))
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
this.set({loading: false})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
28
routes/_components/search/SearchResult.html
Normal file
28
routes/_components/search/SearchResult.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<li class="search-result">
|
||||||
|
<a href="{{href}}" class="search-result-anchor">
|
||||||
|
<slot></slot>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<style>
|
||||||
|
.search-result {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px solid var(--main-border);
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.search-result:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.search-result-anchor {
|
||||||
|
padding: 20px;
|
||||||
|
flex: 1;
|
||||||
|
background: var(--settings-list-item-bg);
|
||||||
|
color: var(--body-text-color);
|
||||||
|
}
|
||||||
|
.search-result-anchor:hover {
|
||||||
|
background: var(--settings-list-item-bg-hover);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.search-result-anchor:active {
|
||||||
|
background: var(--settings-list-item-bg-active);
|
||||||
|
}
|
||||||
|
</style>
|
34
routes/_components/search/SearchResults.html
Normal file
34
routes/_components/search/SearchResults.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<ul class="search-results">
|
||||||
|
{{#each $searchResults.hashtags as hashtag}}
|
||||||
|
<HashtagSearchResult :hashtag />
|
||||||
|
{{/each}}
|
||||||
|
{{#each $searchResults.accounts as account}}
|
||||||
|
<AccountSearchResult :account />
|
||||||
|
{{/each}}
|
||||||
|
{{#each $searchResults.statuses as status, index}}
|
||||||
|
<StatusSearchResult :status :index length="{{$searchResults.statuses.length}}"/>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
<style>
|
||||||
|
.search-results {
|
||||||
|
list-style: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid var(--main-border);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import { store } from '../../_store/store'
|
||||||
|
import AccountSearchResult from './AccountSearchResult.html'
|
||||||
|
import HashtagSearchResult from './HashtagSearchResult.html'
|
||||||
|
import StatusSearchResult from './StatusSearchResult.html'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
store: () => store,
|
||||||
|
components: {
|
||||||
|
AccountSearchResult,
|
||||||
|
HashtagSearchResult,
|
||||||
|
StatusSearchResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
18
routes/_components/search/StatusSearchResult.html
Normal file
18
routes/_components/search/StatusSearchResult.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<SearchResult href="/statuses/{{status.id}}">
|
||||||
|
<Status :index :length
|
||||||
|
timelineType="search" timelineValue="search"
|
||||||
|
status="{{status}}" />
|
||||||
|
</SearchResult>
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import SearchResult from './SearchResult.html'
|
||||||
|
import Status from '../status/Status.html'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SearchResult,
|
||||||
|
Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,12 +1,12 @@
|
||||||
{{#if error}}
|
{{#if error}}
|
||||||
<svg class="{{className}} avatar size-{{size}}" aria-hidden="true">
|
<svg class="{{className || ''}} avatar size-{{size}}" aria-hidden="true">
|
||||||
<use xlink:href="#fa-user" />
|
<use xlink:href="#fa-user" />
|
||||||
</svg>
|
</svg>
|
||||||
{{elseif $autoplayGifs}}
|
{{elseif $autoplayGifs}}
|
||||||
<img class="{{className}} avatar size-{{size}}" aria-hidden="true" alt=""
|
<img class="{{className || ''}} avatar size-{{size}}" aria-hidden="true" alt=""
|
||||||
src="{{account.avatar}}" on:imgLoadError="set({error: true})" />
|
src="{{account.avatar}}" on:imgLoadError="set({error: true})" />
|
||||||
{{else}}
|
{{else}}
|
||||||
<NonAutoplayImg className="{{className}} avatar size-{{size}}" ariaHidden="true" alt=""
|
<NonAutoplayImg className="{{className || ''}} avatar size-{{size}}" ariaHidden="true" alt=""
|
||||||
src="{{account.avatar}}" staticSrc="{{account.avatar_static}}" on:imgLoadError="set({error: true})" />
|
src="{{account.avatar}}" staticSrc="{{account.avatar_static}}" on:imgLoadError="set({error: true})" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<article class="status-article {{originalStatus.visibility === 'direct' ? 'status-direct' : ''}}"
|
<article class="status-article {{getClasses(originalStatus, timelineType)}}"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-posinset="{{index}}" aria-setsize="{{length}}"
|
aria-posinset="{{index}}" aria-setsize="{{length}}"
|
||||||
on:recalculateHeight>
|
on:recalculateHeight>
|
||||||
|
@ -21,10 +21,8 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.status-article {
|
.status-article {
|
||||||
width: 560px;
|
|
||||||
max-width: calc(100vw - 40px);
|
max-width: calc(100vw - 40px);
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
border-bottom: 1px solid var(--main-border);
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
".............. status-header"
|
".............. status-header"
|
||||||
|
@ -37,6 +35,11 @@
|
||||||
grid-template-columns: 58px 1fr;
|
grid-template-columns: 58px 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-article.status-in-timeline {
|
||||||
|
width: 560px;
|
||||||
|
border-bottom: 1px solid var(--main-border);
|
||||||
|
}
|
||||||
|
|
||||||
.status-article.status-direct {
|
.status-article.status-direct {
|
||||||
background-color: var(--status-direct-background);
|
background-color: var(--status-direct-background);
|
||||||
}
|
}
|
||||||
|
@ -69,6 +72,12 @@
|
||||||
StatusSpoiler
|
StatusSpoiler
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
|
helpers: {
|
||||||
|
getClasses(originalStatus, timelineType) {
|
||||||
|
return (originalStatus.visibility === 'direct' ? 'status-direct' : '') +
|
||||||
|
' ' + (timelineType !== 'search' ? 'status-in-timeline' : '')
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
originalStatus: (status) => status.reblog ? status.reblog : status,
|
originalStatus: (status) => status.reblog ? status.reblog : status,
|
||||||
statusId: (originalStatus) => originalStatus.id,
|
statusId: (originalStatus) => originalStatus.id,
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
<div class="timeline" role="feed" aria-label="{{label}}">
|
<div class="timeline" role="feed" aria-label="{{label}}">
|
||||||
{{#if !$initialized}}
|
{{#if !$initialized}}
|
||||||
<div class="loading-page">
|
<LoadingPage />
|
||||||
<LoadingSpinner />
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if timelineType === 'notifications'}}
|
{{#if timelineType === 'notifications'}}
|
||||||
<VirtualList component="{{NotificationVirtualListItem}}"
|
<VirtualList component="{{NotificationVirtualListItem}}"
|
||||||
|
@ -44,17 +42,6 @@
|
||||||
min-height: 60vh;
|
min-height: 60vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.loading-page {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 150px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 50;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
|
@ -67,7 +54,7 @@
|
||||||
import { timelines } from '../../_static/timelines'
|
import { timelines } from '../../_static/timelines'
|
||||||
import { database } from '../../_utils/database/database'
|
import { database } from '../../_utils/database/database'
|
||||||
import { initializeTimeline, fetchTimelineItemsOnScrollToBottom, setupTimeline } from '../../_actions/timeline'
|
import { initializeTimeline, fetchTimelineItemsOnScrollToBottom, setupTimeline } from '../../_actions/timeline'
|
||||||
import LoadingSpinner from '../LoadingSpinner.html'
|
import LoadingPage from '../LoadingPage.html'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async oncreate() {
|
async oncreate() {
|
||||||
|
@ -118,7 +105,7 @@
|
||||||
components: {
|
components: {
|
||||||
VirtualList,
|
VirtualList,
|
||||||
PseudoVirtualList,
|
PseudoVirtualList,
|
||||||
LoadingSpinner
|
LoadingPage
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initialize() {
|
initialize() {
|
||||||
|
|
|
@ -23,6 +23,7 @@ class PinaforeStore extends LocalStorageStore {
|
||||||
|
|
||||||
const store = new PinaforeStore({
|
const store = new PinaforeStore({
|
||||||
instanceNameInSearch: '',
|
instanceNameInSearch: '',
|
||||||
|
queryInSearch: '',
|
||||||
currentInstance: null,
|
currentInstance: null,
|
||||||
loggedInInstances: {},
|
loggedInInstances: {},
|
||||||
loggedInInstancesInOrder: [],
|
loggedInInstancesInOrder: [],
|
||||||
|
|
12
routes/_utils/mastodon/search.js
Normal file
12
routes/_utils/mastodon/search.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { get, paramsString } from '../ajax'
|
||||||
|
|
||||||
|
export function search(instanceName, accessToken, query) {
|
||||||
|
let url = `https://${instanceName}/api/v1/search?` + paramsString({
|
||||||
|
q: query,
|
||||||
|
resolve: true
|
||||||
|
})
|
||||||
|
return get(url, {
|
||||||
|
'Authorization': `Bearer ${accessToken}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
48
routes/search.html
Normal file
48
routes/search.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<:Head>
|
||||||
|
<title>Pinafore – Search</title>
|
||||||
|
</:Head>
|
||||||
|
|
||||||
|
<Layout page='search'>
|
||||||
|
{{#if $isUserLoggedIn}}
|
||||||
|
<div class="search-page">
|
||||||
|
<Search></Search>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<HiddenFromSSR>
|
||||||
|
<FreeTextLayout>
|
||||||
|
<h1>Search</h1>
|
||||||
|
|
||||||
|
<p>You can search once logged in to an instance.</p>
|
||||||
|
</FreeTextLayout>
|
||||||
|
</HiddenFromSSR>
|
||||||
|
{{/if}}
|
||||||
|
</Layout>
|
||||||
|
<style>
|
||||||
|
.search-page {
|
||||||
|
min-height: 60vh;
|
||||||
|
padding: 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.search-page {
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import Layout from './_components/Layout.html'
|
||||||
|
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||||
|
import { store } from './_store/store.js'
|
||||||
|
import HiddenFromSSR from './_components/HiddenFromSSR'
|
||||||
|
import Search from './_components/search/Search.html'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
store: () => store,
|
||||||
|
components: {
|
||||||
|
Layout,
|
||||||
|
Search,
|
||||||
|
FreeTextLayout,
|
||||||
|
HiddenFromSSR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -129,3 +129,8 @@ button::-moz-focus-inner {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Firefox hacks to remove ugly red border.
|
||||||
|
Unnecessary since it gives a warning if you submit an empty field anyway. */
|
||||||
|
input:required, input:invalid {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
|
@ -11,7 +11,7 @@
|
||||||
<style>
|
<style>
|
||||||
/* auto-generated w/ build-sass.js */
|
/* auto-generated w/ build-sass.js */
|
||||||
body{--button-primary-bg:#6081e6;--button-primary-text:#fff;--button-primary-border:#132c76;--button-primary-bg-active:#456ce2;--button-primary-bg-hover:#6988e7;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#4169e1;--main-bg:#fff;--body-bg:#e8edfb;--body-text-color:#333;--main-border:#dadada;--svg-fill:#4169e1;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#4169e1;--nav-border:#214cce;--nav-a-border:#4169e1;--nav-a-selected-border:#fff;--nav-a-selected-bg:#6d8ce8;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#839deb;--nav-a-bg-hover:#577ae4;--nav-a-border-hover:#4169e1;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#90a8ee;--action-button-fill-color-hover:#a2b6f0;--action-button-fill-color-active:#577ae4;--action-button-fill-color-pressed:#2351dc;--action-button-fill-color-pressed-hover:#3862e0;--action-button-fill-color-pressed-active:#1d44b8;--settings-list-item-bg:#fff;--settings-list-item-text:#4169e1;--settings-list-item-text-hover:#4169e1;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--mask-opaque-bg:rgba(51,51,51,0.8);--loading-bg:#ededed;--deemphasized-text-color:#666;--focus-outline:#c5d1f6;--very-deemphasized-link-color:rgba(65,105,225,0.6);--very-deemphasized-text-color:rgba(102,102,102,0.6);--status-direct-background:#d2dcf8}
|
body{--button-primary-bg:#6081e6;--button-primary-text:#fff;--button-primary-border:#132c76;--button-primary-bg-active:#456ce2;--button-primary-bg-hover:#6988e7;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#4169e1;--main-bg:#fff;--body-bg:#e8edfb;--body-text-color:#333;--main-border:#dadada;--svg-fill:#4169e1;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#4169e1;--nav-border:#214cce;--nav-a-border:#4169e1;--nav-a-selected-border:#fff;--nav-a-selected-bg:#6d8ce8;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#839deb;--nav-a-bg-hover:#577ae4;--nav-a-border-hover:#4169e1;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#90a8ee;--action-button-fill-color-hover:#a2b6f0;--action-button-fill-color-active:#577ae4;--action-button-fill-color-pressed:#2351dc;--action-button-fill-color-pressed-hover:#3862e0;--action-button-fill-color-pressed-active:#1d44b8;--settings-list-item-bg:#fff;--settings-list-item-text:#4169e1;--settings-list-item-text-hover:#4169e1;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--mask-opaque-bg:rgba(51,51,51,0.8);--loading-bg:#ededed;--deemphasized-text-color:#666;--focus-outline:#c5d1f6;--very-deemphasized-link-color:rgba(65,105,225,0.6);--very-deemphasized-text-color:rgba(102,102,102,0.6);--status-direct-background:#d2dcf8}
|
||||||
body{margin:0;font-family:system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue;font-size:14px;line-height:1.3;color:var(--body-text-color);background:var(--body-bg);position:fixed;left:0;right:0;bottom:0;top:0}.container{overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;will-change:transform;position:absolute;top:72px;left:0;right:0;bottom:0}@media (max-width: 767px){.container{top:62px}}main{position:relative;width:602px;max-width:100vw;padding:0;box-sizing:border-box;margin:30px auto 15px;background:var(--main-bg);border:1px solid var(--main-border);border-radius:1px}@media (max-width: 767px){main{margin:5px auto 15px}}h1,h2,h3,h4,h5,h6{margin:0 0 0.5em 0;font-weight:400;line-height:1.2}h1{font-size:2em}a{color:var(--anchor-text);text-decoration:none}a:visited{color:var(--anchor-text)}a:hover{text-decoration:underline}input{border:1px solid var(--input-border);padding:5px}button{font-size:1.2em;background:var(--button-bg);border-radius:2px;padding:10px 15px;border:1px solid var(--button-border);cursor:pointer;color:var(--button-text)}button:hover{background:var(--button-bg-hover)}button:active{background:var(--button-bg-active)}button[disabled]{opacity:0.35;pointer-events:none;cursor:not-allowed}button.primary{border:1px solid var(--button-primary-border);background:var(--button-primary-bg);color:var(--button-primary-text)}button.primary:hover{background:var(--button-primary-bg-hover)}button.primary:active{background:var(--button-primary-bg-active)}p,label,input{font-size:1.3em}ul,li,p{padding:0;margin:0}.hidden{opacity:0}*:focus{outline:2px solid var(--focus-outline)}button::-moz-focus-inner{border:0}
|
body{margin:0;font-family:system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue;font-size:14px;line-height:1.3;color:var(--body-text-color);background:var(--body-bg);position:fixed;left:0;right:0;bottom:0;top:0}.container{overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;will-change:transform;position:absolute;top:72px;left:0;right:0;bottom:0}@media (max-width: 767px){.container{top:62px}}main{position:relative;width:602px;max-width:100vw;padding:0;box-sizing:border-box;margin:30px auto 15px;background:var(--main-bg);border:1px solid var(--main-border);border-radius:1px}@media (max-width: 767px){main{margin:5px auto 15px}}h1,h2,h3,h4,h5,h6{margin:0 0 0.5em 0;font-weight:400;line-height:1.2}h1{font-size:2em}a{color:var(--anchor-text);text-decoration:none}a:visited{color:var(--anchor-text)}a:hover{text-decoration:underline}input{border:1px solid var(--input-border);padding:5px}button{font-size:1.2em;background:var(--button-bg);border-radius:2px;padding:10px 15px;border:1px solid var(--button-border);cursor:pointer;color:var(--button-text)}button:hover{background:var(--button-bg-hover)}button:active{background:var(--button-bg-active)}button[disabled]{opacity:0.35;pointer-events:none;cursor:not-allowed}button.primary{border:1px solid var(--button-primary-border);background:var(--button-primary-bg);color:var(--button-primary-text)}button.primary:hover{background:var(--button-primary-bg-hover)}button.primary:active{background:var(--button-primary-bg-active)}p,label,input{font-size:1.3em}ul,li,p{padding:0;margin:0}.hidden{opacity:0}*:focus{outline:2px solid var(--focus-outline)}button::-moz-focus-inner{border:0}input:required,input:invalid{box-shadow:none}
|
||||||
body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-oaken.offline,body.theme-scarlet.offline,body.theme-seafoam.offline,body.theme-gecko.offline{--button-primary-bg:#ababab;--button-primary-text:#fff;--button-primary-border:#4d4d4d;--button-primary-bg-active:#9c9c9c;--button-primary-bg-hover:#b0b0b0;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#999;--main-bg:#fff;--body-bg:#fafafa;--body-text-color:#333;--main-border:#dadada;--svg-fill:#999;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#999;--nav-border:gray;--nav-a-border:#999;--nav-a-selected-border:#fff;--nav-a-selected-bg:#b3b3b3;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#bfbfbf;--nav-a-bg-hover:#a6a6a6;--nav-a-border-hover:#999;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#c7c7c7;--action-button-fill-color-hover:#d1d1d1;--action-button-fill-color-active:#a6a6a6;--action-button-fill-color-pressed:#878787;--action-button-fill-color-pressed-hover:#949494;--action-button-fill-color-pressed-active:#737373;--settings-list-item-bg:#fff;--settings-list-item-text:#999;--settings-list-item-text-hover:#999;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--mask-opaque-bg:rgba(51,51,51,0.8);--loading-bg:#ededed;--deemphasized-text-color:#666;--focus-outline:#bfbfbf;--very-deemphasized-link-color:rgba(153,153,153,0.6);--very-deemphasized-text-color:rgba(102,102,102,0.6);--status-direct-background:#ededed}
|
body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-oaken.offline,body.theme-scarlet.offline,body.theme-seafoam.offline,body.theme-gecko.offline{--button-primary-bg:#ababab;--button-primary-text:#fff;--button-primary-border:#4d4d4d;--button-primary-bg-active:#9c9c9c;--button-primary-bg-hover:#b0b0b0;--button-bg:#e6e6e6;--button-text:#333;--button-border:#a7a7a7;--button-bg-active:#bfbfbf;--button-bg-hover:#f2f2f2;--input-border:#dadada;--anchor-text:#999;--main-bg:#fff;--body-bg:#fafafa;--body-text-color:#333;--main-border:#dadada;--svg-fill:#999;--form-bg:#f7f7f7;--form-border:#c1c1c1;--nav-bg:#999;--nav-border:gray;--nav-a-border:#999;--nav-a-selected-border:#fff;--nav-a-selected-bg:#b3b3b3;--nav-svg-fill:#fff;--nav-text-color:#fff;--nav-a-selected-border-hover:#fff;--nav-a-selected-bg-hover:#bfbfbf;--nav-a-bg-hover:#a6a6a6;--nav-a-border-hover:#999;--nav-svg-fill-hover:#fff;--nav-text-color-hover:#fff;--action-button-fill-color:#c7c7c7;--action-button-fill-color-hover:#d1d1d1;--action-button-fill-color-active:#a6a6a6;--action-button-fill-color-pressed:#878787;--action-button-fill-color-pressed-hover:#949494;--action-button-fill-color-pressed-active:#737373;--settings-list-item-bg:#fff;--settings-list-item-text:#999;--settings-list-item-text-hover:#999;--settings-list-item-border:#dadada;--settings-list-item-bg-active:#e6e6e6;--settings-list-item-bg-hover:#fafafa;--toast-bg:#333;--toast-border:#fafafa;--toast-text:#fff;--mask-bg:#333;--mask-svg-fill:#fff;--mask-opaque-bg:rgba(51,51,51,0.8);--loading-bg:#ededed;--deemphasized-text-color:#666;--focus-outline:#bfbfbf;--very-deemphasized-link-color:rgba(153,153,153,0.6);--very-deemphasized-text-color:rgba(102,102,102,0.6);--status-direct-background:#ededed}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -83,6 +83,7 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o
|
||||||
<symbol id="fa-user-times" viewBox="0 0 2048 1792"><title>Stop Following</title><path d="M704 896q-159 0-271.5-112.5T320 512t112.5-271.5T704 128t271.5 112.5T1088 512 975.5 783.5 704 896zm1077 320l249 249q9 9 9 23 0 13-9 22l-136 136q-9 9-22 9-14 0-23-9l-249-249-249 249q-9 9-23 9-13 0-22-9l-136-136q-9-9-9-22 0-14 9-23l249-249-249-249q-9-9-9-23 0-13 9-22l136-136q9-9 22-9 14 0 23 9l249 249 249-249q9-9 23-9 13 0 22 9l136 136q9 9 9 22 0 14-9 23zm-498 0l-181 181q-37 37-37 91 0 53 37 90l83 83q-21 3-44 3H267q-121 0-194-69T0 1405q0-53 3.5-103.5t14-109T44 1084t43-97.5 62-81 85.5-53.5T346 832q19 0 39 17 154 122 319 122t319-122q20-17 39-17 28 0 57 6-28 27-41 50t-13 56q0 54 37 91z"></path></symbol>
|
<symbol id="fa-user-times" viewBox="0 0 2048 1792"><title>Stop Following</title><path d="M704 896q-159 0-271.5-112.5T320 512t112.5-271.5T704 128t271.5 112.5T1088 512 975.5 783.5 704 896zm1077 320l249 249q9 9 9 23 0 13-9 22l-136 136q-9 9-22 9-14 0-23-9l-249-249-249 249q-9 9-23 9-13 0-22-9l-136-136q-9-9-9-22 0-14 9-23l249-249-249-249q-9-9-9-23 0-13 9-22l136-136q9-9 22-9 14 0 23 9l249 249 249-249q9-9 23-9 13 0 22 9l136 136q9 9 9 22 0 14-9 23zm-498 0l-181 181q-37 37-37 91 0 53 37 90l83 83q-21 3-44 3H267q-121 0-194-69T0 1405q0-53 3.5-103.5t14-109T44 1084t43-97.5 62-81 85.5-53.5T346 832q19 0 39 17 154 122 319 122t319-122q20-17 39-17 28 0 57 6-28 27-41 50t-13 56q0 54 37 91z"></path></symbol>
|
||||||
<symbol id="fa-user-plus" viewBox="0 0 2048 1792"><title>Follow</title><path d="M704 896q-159 0-271.5-112.5T320 512t112.5-271.5T704 128t271.5 112.5T1088 512 975.5 783.5 704 896zm960 128h352q13 0 22.5 9.5t9.5 22.5v192q0 13-9.5 22.5t-22.5 9.5h-352v352q0 13-9.5 22.5t-22.5 9.5h-192q-13 0-22.5-9.5t-9.5-22.5v-352h-352q-13 0-22.5-9.5t-9.5-22.5v-192q0-13 9.5-22.5t22.5-9.5h352V672q0-13 9.5-22.5t22.5-9.5h192q13 0 22.5 9.5t9.5 22.5v352zm-736 224q0 52 38 90t90 38h256v238q-68 50-171 50H267q-121 0-194-69T0 1405q0-53 3.5-103.5t14-109T44 1084t43-97.5 62-81 85.5-53.5T346 832q19 0 39 17 79 61 154.5 91.5T704 971t164.5-30.5T1023 849q20-17 39-17 132 0 217 96h-223q-52 0-90 38t-38 90v192z"></path></symbol>
|
<symbol id="fa-user-plus" viewBox="0 0 2048 1792"><title>Follow</title><path d="M704 896q-159 0-271.5-112.5T320 512t112.5-271.5T704 128t271.5 112.5T1088 512 975.5 783.5 704 896zm960 128h352q13 0 22.5 9.5t9.5 22.5v192q0 13-9.5 22.5t-22.5 9.5h-352v352q0 13-9.5 22.5t-22.5 9.5h-192q-13 0-22.5-9.5t-9.5-22.5v-352h-352q-13 0-22.5-9.5t-9.5-22.5v-192q0-13 9.5-22.5t22.5-9.5h352V672q0-13 9.5-22.5t22.5-9.5h192q13 0 22.5 9.5t9.5 22.5v352zm-736 224q0 52 38 90t90 38h256v238q-68 50-171 50H267q-121 0-194-69T0 1405q0-53 3.5-103.5t14-109T44 1084t43-97.5 62-81 85.5-53.5T346 832q19 0 39 17 79 61 154.5 91.5T704 971t164.5-30.5T1023 849q20-17 39-17 132 0 217 96h-223q-52 0-90 38t-38 90v192z"></path></symbol>
|
||||||
<symbol id="fa-external-link" viewBox="0 0 1792 1792"><title>External Link</title><path d="M1408 928v320q0 119-84.5 203.5T1120 1536H288q-119 0-203.5-84.5T0 1248V416q0-119 84.5-203.5T288 128h704q14 0 23 9t9 23v64q0 14-9 23t-23 9H288q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113V928q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10L695 983q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z"></path></symbol>
|
<symbol id="fa-external-link" viewBox="0 0 1792 1792"><title>External Link</title><path d="M1408 928v320q0 119-84.5 203.5T1120 1536H288q-119 0-203.5-84.5T0 1248V416q0-119 84.5-203.5T288 128h704q14 0 23 9t9 23v64q0 14-9 23t-23 9H288q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113V928q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10L695 983q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z"></path></symbol>
|
||||||
|
<symbol id="fa-search" viewBox="0 0 1792 1792"><title>Search</title><path d="M1216 832q0-185-131.5-316.5T768 384 451.5 515.5 320 832t131.5 316.5T768 1280t316.5-131.5T1216 832zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225T64 832t55.5-273.5 150-225 225-150T768 128t273.5 55.5 225 150 150 225T1472 832q0 220-124 399l343 343q37 37 37 90z"></path></symbol>
|
||||||
</svg><!-- end insert svg here -->
|
</svg><!-- end insert svg here -->
|
||||||
</svg>
|
</svg>
|
||||||
<!-- The application will be rendered inside this element,
|
<!-- The application will be rendered inside this element,
|
||||||
|
|
Loading…
Reference in a new issue