add service top
This commit is contained in:
@@ -2,5 +2,6 @@
|
|||||||
"title": "ailog",
|
"title": "ailog",
|
||||||
"handle": "syui.ai",
|
"handle": "syui.ai",
|
||||||
"collection": "ai.syui.log.post",
|
"collection": "ai.syui.log.post",
|
||||||
"network": "bsky.social"
|
"network": "bsky.social",
|
||||||
|
"color": "#0066cc"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,24 +23,69 @@ function escapeHtml(str: string): string {
|
|||||||
.replace(/"/g, '"')
|
.replace(/"/g, '"')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderCollections(did: string, handle: string): Promise<string> {
|
async function renderServices(did: string, handle: string): Promise<string> {
|
||||||
const collections = await describeRepo(did)
|
const collections = await describeRepo(did)
|
||||||
|
|
||||||
if (collections.length === 0) {
|
if (collections.length === 0) {
|
||||||
return '<p class="no-data">No collections found</p>'
|
return '<p class="no-data">No collections found</p>'
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = collections.map(col => {
|
// Group by service domain
|
||||||
const service = getServiceInfo(col)
|
const serviceMap = new Map<string, { name: string; favicon: string; count: number }>()
|
||||||
const favicon = service ? `<img src="${service.favicon}" class="collection-favicon" alt="" onerror="this.style.display='none'">` : ''
|
|
||||||
const serviceName = service ? `<span class="collection-service">${service.name}</span>` : ''
|
|
||||||
|
|
||||||
|
for (const col of collections) {
|
||||||
|
const info = getServiceInfo(col)
|
||||||
|
if (info) {
|
||||||
|
const key = info.domain
|
||||||
|
if (!serviceMap.has(key)) {
|
||||||
|
serviceMap.set(key, { name: info.name, favicon: info.favicon, count: 0 })
|
||||||
|
}
|
||||||
|
serviceMap.get(key)!.count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = Array.from(serviceMap.entries()).map(([domain, info]) => {
|
||||||
|
return `
|
||||||
|
<li class="service-list-item">
|
||||||
|
<a href="?mode=browser&handle=${handle}&service=${encodeURIComponent(domain)}" class="service-list-link">
|
||||||
|
<img src="${info.favicon}" class="service-list-favicon" alt="" onerror="this.style.display='none'">
|
||||||
|
<span class="service-list-name">${info.name}</span>
|
||||||
|
<span class="service-list-count">${info.count}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
`
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="services-list">
|
||||||
|
<h3>Services</h3>
|
||||||
|
<ul class="service-list">${items}</ul>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderCollections(did: string, handle: string, serviceDomain: string): Promise<string> {
|
||||||
|
const collections = await describeRepo(did)
|
||||||
|
|
||||||
|
// Filter by service domain
|
||||||
|
const filtered = collections.filter(col => {
|
||||||
|
const info = getServiceInfo(col)
|
||||||
|
return info && info.domain === serviceDomain
|
||||||
|
})
|
||||||
|
|
||||||
|
if (filtered.length === 0) {
|
||||||
|
return '<p class="no-data">No collections found</p>'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get favicon from first collection
|
||||||
|
const firstInfo = getServiceInfo(filtered[0])
|
||||||
|
const favicon = firstInfo ? `<img src="${firstInfo.favicon}" class="collection-header-favicon" alt="" onerror="this.style.display='none'">` : ''
|
||||||
|
|
||||||
|
const items = filtered.map(col => {
|
||||||
return `
|
return `
|
||||||
<li class="collection-item">
|
<li class="collection-item">
|
||||||
<a href="?mode=browser&handle=${handle}&collection=${encodeURIComponent(col)}" class="collection-link">
|
<a href="?mode=browser&handle=${handle}&collection=${encodeURIComponent(col)}" class="collection-link">
|
||||||
${favicon}
|
|
||||||
<span class="collection-nsid">${col}</span>
|
<span class="collection-nsid">${col}</span>
|
||||||
${serviceName}
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
@@ -48,7 +93,7 @@ async function renderCollections(did: string, handle: string): Promise<string> {
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="collections">
|
<div class="collections">
|
||||||
<h3>Collections</h3>
|
<h3 class="collection-header">${favicon}<span>${serviceDomain}</span></h3>
|
||||||
<ul class="collection-list">${items}</ul>
|
<ul class="collection-list">${items}</ul>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -120,6 +165,7 @@ export async function mountAtBrowser(
|
|||||||
handle: string,
|
handle: string,
|
||||||
collection: string | null,
|
collection: string | null,
|
||||||
rkey: string | null,
|
rkey: string | null,
|
||||||
|
service: string | null = null,
|
||||||
loginDid: string | null = null
|
loginDid: string | null = null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
container.innerHTML = '<p class="loading">Loading...</p>'
|
container.innerHTML = '<p class="loading">Loading...</p>'
|
||||||
@@ -135,10 +181,16 @@ export async function mountAtBrowser(
|
|||||||
nav = `<a href="?mode=browser&handle=${handle}&collection=${encodeURIComponent(collection)}" class="back-link">← Back</a>`
|
nav = `<a href="?mode=browser&handle=${handle}&collection=${encodeURIComponent(collection)}" class="back-link">← Back</a>`
|
||||||
content = await renderRecordDetail(did, handle, collection, rkey, canDelete)
|
content = await renderRecordDetail(did, handle, collection, rkey, canDelete)
|
||||||
} else if (collection) {
|
} else if (collection) {
|
||||||
nav = `<a href="?mode=browser&handle=${handle}" class="back-link">← Collections</a>`
|
// Get service from collection for back link
|
||||||
|
const info = getServiceInfo(collection)
|
||||||
|
const backService = info ? info.domain : ''
|
||||||
|
nav = `<a href="?mode=browser&handle=${handle}&service=${encodeURIComponent(backService)}" class="back-link">← ${info?.name || 'Back'}</a>`
|
||||||
content = await renderRecordList(did, handle, collection)
|
content = await renderRecordList(did, handle, collection)
|
||||||
|
} else if (service) {
|
||||||
|
nav = `<a href="?mode=browser&handle=${handle}" class="back-link">← Services</a>`
|
||||||
|
content = await renderCollections(did, handle, service)
|
||||||
} else {
|
} else {
|
||||||
content = await renderCollections(did, handle)
|
content = await renderServices(did, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
container.innerHTML = nav + content
|
container.innerHTML = nav + content
|
||||||
|
|||||||
42
src/components/services.ts
Normal file
42
src/components/services.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { describeRepo, getServiceInfo, resolveHandle } from '../lib/api.js'
|
||||||
|
|
||||||
|
export async function renderServices(handle: string): Promise<string> {
|
||||||
|
const did = handle.startsWith('did:') ? handle : await resolveHandle(handle)
|
||||||
|
const collections = await describeRepo(did)
|
||||||
|
|
||||||
|
if (collections.length === 0) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group by service
|
||||||
|
const serviceMap = new Map<string, { name: string; favicon: string; collections: string[] }>()
|
||||||
|
|
||||||
|
for (const col of collections) {
|
||||||
|
const info = getServiceInfo(col)
|
||||||
|
if (info) {
|
||||||
|
const key = info.domain
|
||||||
|
if (!serviceMap.has(key)) {
|
||||||
|
serviceMap.set(key, { name: info.name, favicon: info.favicon, collections: [] })
|
||||||
|
}
|
||||||
|
serviceMap.get(key)!.collections.push(col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = Array.from(serviceMap.entries()).map(([domain, info]) => {
|
||||||
|
const url = `?mode=browser&handle=${handle}&service=${encodeURIComponent(domain)}`
|
||||||
|
|
||||||
|
return `
|
||||||
|
<a href="${url}" class="service-item" title="${info.collections.join(', ')}">
|
||||||
|
<img src="${info.favicon}" class="service-favicon" alt="" onerror="this.style.display='none'">
|
||||||
|
<span class="service-name">${info.name}</span>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
return `<div class="services">${items}</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function mountServices(container: HTMLElement, handle: string): Promise<void> {
|
||||||
|
const html = await renderServices(handle)
|
||||||
|
container.innerHTML = html
|
||||||
|
}
|
||||||
@@ -178,16 +178,16 @@ export async function fetchLexicon(nsid: string): Promise<any | null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Known service mappings for collections
|
// Known service mappings for collections
|
||||||
const SERVICE_MAP: Record<string, { name: string; domain: string; icon?: string }> = {
|
const SERVICE_MAP: Record<string, { domain: string; icon?: string }> = {
|
||||||
'app.bsky': { name: 'Bluesky', domain: 'bsky.app', icon: 'https://bsky.app/static/favicon-32x32.png' },
|
'app.bsky': { domain: 'bsky.app', icon: 'https://bsky.app/static/favicon-32x32.png' },
|
||||||
'ai.syui': { name: 'syui.ai', domain: 'syui.ai' },
|
'chat.bsky': { domain: 'bsky.app', icon: 'https://bsky.app/static/favicon-32x32.png' },
|
||||||
'com.whtwnd': { name: 'WhiteWind', domain: 'whtwnd.com' },
|
'ai.syui': { domain: 'syui.ai' },
|
||||||
'fyi.unravel.frontpage': { name: 'Frontpage', domain: 'frontpage.fyi' },
|
'com.whtwnd': { domain: 'whtwnd.com' },
|
||||||
'com.shinolabs.pinksea': { name: 'PinkSea', domain: 'pinksea.art' },
|
'fyi.unravel.frontpage': { domain: 'frontpage.fyi' },
|
||||||
'blue.linkat': { name: 'Linkat', domain: 'linkat.blue' },
|
'com.shinolabs.pinksea': { domain: 'pinksea.art' },
|
||||||
'sh.tangled': { name: 'Tangled', domain: 'tangled.sh' },
|
'blue.linkat': { domain: 'linkat.blue' },
|
||||||
'pub.leaflet': { name: 'Leaflet', domain: 'leaflet.pub' },
|
'sh.tangled': { domain: 'tangled.sh' },
|
||||||
'chat.bsky': { name: 'Bluesky Chat', domain: 'bsky.app' },
|
'pub.leaflet': { domain: 'leaflet.pub' },
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getServiceInfo(collection: string): { name: string; domain: string; favicon: string } | null {
|
export function getServiceInfo(collection: string): { name: string; domain: string; favicon: string } | null {
|
||||||
@@ -195,7 +195,7 @@ export function getServiceInfo(collection: string): { name: string; domain: stri
|
|||||||
for (const [prefix, info] of Object.entries(SERVICE_MAP)) {
|
for (const [prefix, info] of Object.entries(SERVICE_MAP)) {
|
||||||
if (collection.startsWith(prefix)) {
|
if (collection.startsWith(prefix)) {
|
||||||
return {
|
return {
|
||||||
name: info.name,
|
name: info.domain,
|
||||||
domain: info.domain,
|
domain: info.domain,
|
||||||
favicon: info.icon || `https://www.google.com/s2/favicons?domain=${info.domain}&sz=32`
|
favicon: info.icon || `https://www.google.com/s2/favicons?domain=${info.domain}&sz=32`
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main.ts
13
src/main.ts
@@ -1,4 +1,5 @@
|
|||||||
import { getProfile, listRecords, getRecord, setNetworkConfig } from './lib/api.js'
|
import { getProfile, listRecords, getRecord, setNetworkConfig } from './lib/api.js'
|
||||||
|
import { renderServices } from './components/services.js'
|
||||||
import { login, logout, restoreSession, handleOAuthCallback, setAuthNetworkConfig, type AuthSession } from './lib/auth.js'
|
import { login, logout, restoreSession, handleOAuthCallback, setAuthNetworkConfig, type AuthSession } from './lib/auth.js'
|
||||||
import { mountProfile } from './components/profile.js'
|
import { mountProfile } from './components/profile.js'
|
||||||
import { mountPostList, mountPostDetail } from './components/posts.js'
|
import { mountPostList, mountPostDetail } from './components/posts.js'
|
||||||
@@ -52,6 +53,11 @@ async function init(): Promise<void> {
|
|||||||
// Set page title
|
// Set page title
|
||||||
document.title = config.title || 'ailog'
|
document.title = config.title || 'ailog'
|
||||||
|
|
||||||
|
// Set theme color
|
||||||
|
if (config.color) {
|
||||||
|
document.documentElement.style.setProperty('--btn-color', config.color)
|
||||||
|
}
|
||||||
|
|
||||||
// Set network config
|
// Set network config
|
||||||
const networkConfig = networks[config.network]
|
const networkConfig = networks[config.network]
|
||||||
if (networkConfig) {
|
if (networkConfig) {
|
||||||
@@ -72,6 +78,7 @@ async function init(): Promise<void> {
|
|||||||
const mode = params.get('mode')
|
const mode = params.get('mode')
|
||||||
const rkey = params.get('rkey')
|
const rkey = params.get('rkey')
|
||||||
const collection = params.get('collection')
|
const collection = params.get('collection')
|
||||||
|
const service = params.get('service')
|
||||||
const handle = params.get('handle') || config.handle
|
const handle = params.get('handle') || config.handle
|
||||||
|
|
||||||
const profileEl = document.getElementById('profile')
|
const profileEl = document.getElementById('profile')
|
||||||
@@ -126,7 +133,7 @@ async function init(): Promise<void> {
|
|||||||
if (mode === 'browser') {
|
if (mode === 'browser') {
|
||||||
profileEl.innerHTML = renderTabs(handle, mode, isLoggedIn)
|
profileEl.innerHTML = renderTabs(handle, mode, isLoggedIn)
|
||||||
const loginDid = authSession?.did || null
|
const loginDid = authSession?.did || null
|
||||||
await mountAtBrowser(contentEl, handle, collection, rkey, loginDid)
|
await mountAtBrowser(contentEl, handle, collection, rkey, service, loginDid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +146,10 @@ async function init(): Promise<void> {
|
|||||||
profileEl.appendChild(profileContentEl)
|
profileEl.appendChild(profileContentEl)
|
||||||
mountProfile(profileContentEl, profile)
|
mountProfile(profileContentEl, profile)
|
||||||
|
|
||||||
|
// Add services
|
||||||
|
const servicesHtml = await renderServices(handle)
|
||||||
|
profileContentEl.insertAdjacentHTML('beforeend', servicesHtml)
|
||||||
|
|
||||||
if (rkey) {
|
if (rkey) {
|
||||||
const post = await getRecord(profile.did, config.collection, rkey)
|
const post = await getRecord(profile.did, config.collection, rkey)
|
||||||
if (post) {
|
if (post) {
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
:root {
|
||||||
|
--btn-color: #0066cc;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -26,6 +30,16 @@ body {
|
|||||||
.profile {
|
.profile {
|
||||||
background: #1a1a1a;
|
background: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
.services {
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
.service-item {
|
||||||
|
background: #2a2a2a;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.service-item:hover {
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
.post-item {
|
.post-item {
|
||||||
border-color: #333;
|
border-color: #333;
|
||||||
}
|
}
|
||||||
@@ -78,9 +92,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-btn.at-btn {
|
.header-btn.at-btn {
|
||||||
background: #0066cc;
|
background: var(--btn-color);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: #0066cc;
|
border-color: var(--btn-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-btn.at-btn:hover {
|
.header-btn.at-btn:hover {
|
||||||
@@ -92,9 +106,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header-btn.user-btn {
|
.header-btn.user-btn {
|
||||||
background: #0066cc;
|
background: var(--btn-color);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: #0066cc;
|
border-color: var(--btn-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Post Form */
|
/* Post Form */
|
||||||
@@ -144,7 +158,7 @@ body {
|
|||||||
|
|
||||||
.post-form-btn {
|
.post-form-btn {
|
||||||
padding: 10px 24px;
|
padding: 10px 24px;
|
||||||
background: #0066cc;
|
background: var(--btn-color);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -211,6 +225,42 @@ body {
|
|||||||
color: #444;
|
color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Services */
|
||||||
|
.services {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 20px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
font-size: 13px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-item:hover {
|
||||||
|
background: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-favicon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-name {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
/* Post List */
|
/* Post List */
|
||||||
.post-list {
|
.post-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
@@ -392,7 +442,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.back-link {
|
.back-link {
|
||||||
color: #0066cc;
|
color: var(--btn-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,17 +498,19 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab.active {
|
.tab.active {
|
||||||
background: #0066cc;
|
background: var(--btn-color);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AT Browser */
|
/* AT Browser */
|
||||||
|
.services-list,
|
||||||
.collections,
|
.collections,
|
||||||
.records,
|
.records,
|
||||||
.record-detail {
|
.record-detail {
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.services-list h3,
|
||||||
.collections h3,
|
.collections h3,
|
||||||
.records h3,
|
.records h3,
|
||||||
.record-detail h3 {
|
.record-detail h3 {
|
||||||
@@ -466,6 +518,53 @@ body {
|
|||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.service-list {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-list-item {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-list-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-list-link:hover {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-list-favicon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-list-name {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-list-count {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collection-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collection-header-favicon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.collection-list,
|
.collection-list,
|
||||||
.record-list {
|
.record-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
@@ -515,7 +614,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.record-rkey {
|
.record-rkey {
|
||||||
color: #0066cc;
|
color: var(--btn-color);
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,8 +721,8 @@ body {
|
|||||||
}
|
}
|
||||||
.header-btn.at-btn,
|
.header-btn.at-btn,
|
||||||
.header-btn.user-btn {
|
.header-btn.user-btn {
|
||||||
background: #0066cc;
|
background: var(--btn-color);
|
||||||
border-color: #0066cc;
|
border-color: var(--btn-color);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.post-form-title,
|
.post-form-title,
|
||||||
@@ -650,12 +749,14 @@ body {
|
|||||||
background: #333;
|
background: #333;
|
||||||
}
|
}
|
||||||
.tab.active {
|
.tab.active {
|
||||||
background: #0066cc;
|
background: var(--btn-color);
|
||||||
}
|
}
|
||||||
|
.service-list-link:hover,
|
||||||
.collection-link:hover,
|
.collection-link:hover,
|
||||||
.record-link:hover {
|
.record-link:hover {
|
||||||
background: #1a1a1a;
|
background: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
.service-list-item,
|
||||||
.collection-item,
|
.collection-item,
|
||||||
.record-item,
|
.record-item,
|
||||||
.record-header {
|
.record-header {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export interface AppConfig {
|
|||||||
handle: string
|
handle: string
|
||||||
collection: string
|
collection: string
|
||||||
network: string
|
network: string
|
||||||
|
color?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Networks = Record<string, NetworkConfig>
|
export type Networks = Record<string, NetworkConfig>
|
||||||
|
|||||||
Reference in New Issue
Block a user