Add interactive REST API docs with full endpoint tester and complete metadata serialization
This commit is contained in:
parent
d4d1b8c7a0
commit
d4eadef374
5 changed files with 1152 additions and 329 deletions
|
|
@ -260,7 +260,7 @@ def register_routes(bp):
|
|||
|
||||
Query params:
|
||||
type: 'artist', 'album', or 'track' (required)
|
||||
provider: 'spotify', 'musicbrainz', 'itunes', 'deezer', 'audiodb' (required)
|
||||
provider: 'spotify', 'musicbrainz', 'itunes', 'deezer', 'audiodb', 'tidal', 'qobuz', 'genius' (required)
|
||||
id: the external ID value (required)
|
||||
"""
|
||||
entity_type = request.args.get("type")
|
||||
|
|
@ -276,8 +276,12 @@ def register_routes(bp):
|
|||
if not table:
|
||||
return api_error("BAD_REQUEST", "type must be 'artist', 'album', or 'track'.", 400)
|
||||
|
||||
if provider not in ("spotify", "musicbrainz", "itunes", "deezer", "audiodb"):
|
||||
return api_error("BAD_REQUEST", "provider must be spotify, musicbrainz, itunes, deezer, or audiodb.", 400)
|
||||
# genius only exists on artists and tracks, not albums
|
||||
valid_providers = ("spotify", "musicbrainz", "itunes", "deezer", "audiodb", "tidal", "qobuz", "genius")
|
||||
if provider not in valid_providers:
|
||||
return api_error("BAD_REQUEST", f"provider must be one of: {', '.join(valid_providers)}.", 400)
|
||||
if provider == "genius" and entity_type == "album":
|
||||
return api_error("BAD_REQUEST", "Genius IDs are not available for albums. Use artist or track.", 400)
|
||||
|
||||
try:
|
||||
db = get_database()
|
||||
|
|
|
|||
|
|
@ -86,18 +86,40 @@ def serialize_artist(obj, fields: Optional[Set[str]] = None) -> dict:
|
|||
"itunes_artist_id": d.get("itunes_artist_id"),
|
||||
"audiodb_id": d.get("audiodb_id"),
|
||||
"deezer_id": d.get("deezer_id"),
|
||||
"tidal_id": d.get("tidal_id"),
|
||||
"qobuz_id": d.get("qobuz_id"),
|
||||
"genius_id": d.get("genius_id"),
|
||||
# Match statuses
|
||||
"musicbrainz_match_status": d.get("musicbrainz_match_status"),
|
||||
"spotify_match_status": d.get("spotify_match_status"),
|
||||
"itunes_match_status": d.get("itunes_match_status"),
|
||||
"audiodb_match_status": d.get("audiodb_match_status"),
|
||||
"deezer_match_status": d.get("deezer_match_status"),
|
||||
"lastfm_match_status": d.get("lastfm_match_status"),
|
||||
"genius_match_status": d.get("genius_match_status"),
|
||||
"tidal_match_status": d.get("tidal_match_status"),
|
||||
"qobuz_match_status": d.get("qobuz_match_status"),
|
||||
# Last attempted timestamps
|
||||
"musicbrainz_last_attempted": _isoformat(d.get("musicbrainz_last_attempted")),
|
||||
"spotify_last_attempted": _isoformat(d.get("spotify_last_attempted")),
|
||||
"itunes_last_attempted": _isoformat(d.get("itunes_last_attempted")),
|
||||
"audiodb_last_attempted": _isoformat(d.get("audiodb_last_attempted")),
|
||||
"deezer_last_attempted": _isoformat(d.get("deezer_last_attempted")),
|
||||
"lastfm_last_attempted": _isoformat(d.get("lastfm_last_attempted")),
|
||||
"genius_last_attempted": _isoformat(d.get("genius_last_attempted")),
|
||||
"tidal_last_attempted": _isoformat(d.get("tidal_last_attempted")),
|
||||
"qobuz_last_attempted": _isoformat(d.get("qobuz_last_attempted")),
|
||||
# Last.fm metadata
|
||||
"lastfm_listeners": d.get("lastfm_listeners"),
|
||||
"lastfm_playcount": d.get("lastfm_playcount"),
|
||||
"lastfm_tags": d.get("lastfm_tags"),
|
||||
"lastfm_similar": d.get("lastfm_similar"),
|
||||
"lastfm_bio": d.get("lastfm_bio"),
|
||||
"lastfm_url": d.get("lastfm_url"),
|
||||
# Genius metadata
|
||||
"genius_description": d.get("genius_description"),
|
||||
"genius_alt_names": d.get("genius_alt_names"),
|
||||
"genius_url": d.get("genius_url"),
|
||||
}
|
||||
# Preserve extra keys from enriched queries (album_count, track_count, is_watched)
|
||||
for extra_key in ("album_count", "track_count", "is_watched", "image_url"):
|
||||
|
|
@ -126,24 +148,40 @@ def serialize_album(obj, fields: Optional[Set[str]] = None) -> dict:
|
|||
"server_source": d.get("server_source"),
|
||||
"created_at": _isoformat(d.get("created_at")),
|
||||
"updated_at": _isoformat(d.get("updated_at")),
|
||||
"upc": d.get("upc"),
|
||||
"copyright": d.get("copyright"),
|
||||
# External IDs
|
||||
"musicbrainz_release_id": d.get("musicbrainz_release_id"),
|
||||
"spotify_album_id": d.get("spotify_album_id"),
|
||||
"itunes_album_id": d.get("itunes_album_id"),
|
||||
"audiodb_id": d.get("audiodb_id"),
|
||||
"deezer_id": d.get("deezer_id"),
|
||||
"tidal_id": d.get("tidal_id"),
|
||||
"qobuz_id": d.get("qobuz_id"),
|
||||
# Match statuses
|
||||
"musicbrainz_match_status": d.get("musicbrainz_match_status"),
|
||||
"spotify_match_status": d.get("spotify_match_status"),
|
||||
"itunes_match_status": d.get("itunes_match_status"),
|
||||
"audiodb_match_status": d.get("audiodb_match_status"),
|
||||
"deezer_match_status": d.get("deezer_match_status"),
|
||||
"lastfm_match_status": d.get("lastfm_match_status"),
|
||||
"tidal_match_status": d.get("tidal_match_status"),
|
||||
"qobuz_match_status": d.get("qobuz_match_status"),
|
||||
# Last attempted timestamps
|
||||
"musicbrainz_last_attempted": _isoformat(d.get("musicbrainz_last_attempted")),
|
||||
"spotify_last_attempted": _isoformat(d.get("spotify_last_attempted")),
|
||||
"itunes_last_attempted": _isoformat(d.get("itunes_last_attempted")),
|
||||
"audiodb_last_attempted": _isoformat(d.get("audiodb_last_attempted")),
|
||||
"deezer_last_attempted": _isoformat(d.get("deezer_last_attempted")),
|
||||
"lastfm_last_attempted": _isoformat(d.get("lastfm_last_attempted")),
|
||||
"tidal_last_attempted": _isoformat(d.get("tidal_last_attempted")),
|
||||
"qobuz_last_attempted": _isoformat(d.get("qobuz_last_attempted")),
|
||||
# Last.fm metadata
|
||||
"lastfm_listeners": d.get("lastfm_listeners"),
|
||||
"lastfm_playcount": d.get("lastfm_playcount"),
|
||||
"lastfm_tags": d.get("lastfm_tags"),
|
||||
"lastfm_wiki": d.get("lastfm_wiki"),
|
||||
"lastfm_url": d.get("lastfm_url"),
|
||||
}
|
||||
return filter_fields(result, fields)
|
||||
|
||||
|
|
@ -169,24 +207,46 @@ def serialize_track(obj, fields: Optional[Set[str]] = None) -> dict:
|
|||
"server_source": d.get("server_source"),
|
||||
"created_at": _isoformat(d.get("created_at")),
|
||||
"updated_at": _isoformat(d.get("updated_at")),
|
||||
"isrc": d.get("isrc"),
|
||||
"copyright": d.get("copyright"),
|
||||
# External IDs
|
||||
"musicbrainz_recording_id": d.get("musicbrainz_recording_id"),
|
||||
"spotify_track_id": d.get("spotify_track_id"),
|
||||
"itunes_track_id": d.get("itunes_track_id"),
|
||||
"audiodb_id": d.get("audiodb_id"),
|
||||
"deezer_id": d.get("deezer_id"),
|
||||
"tidal_id": d.get("tidal_id"),
|
||||
"qobuz_id": d.get("qobuz_id"),
|
||||
"genius_id": d.get("genius_id"),
|
||||
# Match statuses
|
||||
"musicbrainz_match_status": d.get("musicbrainz_match_status"),
|
||||
"spotify_match_status": d.get("spotify_match_status"),
|
||||
"itunes_match_status": d.get("itunes_match_status"),
|
||||
"audiodb_match_status": d.get("audiodb_match_status"),
|
||||
"deezer_match_status": d.get("deezer_match_status"),
|
||||
"lastfm_match_status": d.get("lastfm_match_status"),
|
||||
"genius_match_status": d.get("genius_match_status"),
|
||||
"tidal_match_status": d.get("tidal_match_status"),
|
||||
"qobuz_match_status": d.get("qobuz_match_status"),
|
||||
# Last attempted timestamps
|
||||
"musicbrainz_last_attempted": _isoformat(d.get("musicbrainz_last_attempted")),
|
||||
"spotify_last_attempted": _isoformat(d.get("spotify_last_attempted")),
|
||||
"itunes_last_attempted": _isoformat(d.get("itunes_last_attempted")),
|
||||
"audiodb_last_attempted": _isoformat(d.get("audiodb_last_attempted")),
|
||||
"deezer_last_attempted": _isoformat(d.get("deezer_last_attempted")),
|
||||
"lastfm_last_attempted": _isoformat(d.get("lastfm_last_attempted")),
|
||||
"genius_last_attempted": _isoformat(d.get("genius_last_attempted")),
|
||||
"tidal_last_attempted": _isoformat(d.get("tidal_last_attempted")),
|
||||
"qobuz_last_attempted": _isoformat(d.get("qobuz_last_attempted")),
|
||||
# Last.fm metadata
|
||||
"lastfm_listeners": d.get("lastfm_listeners"),
|
||||
"lastfm_playcount": d.get("lastfm_playcount"),
|
||||
"lastfm_tags": d.get("lastfm_tags"),
|
||||
"lastfm_url": d.get("lastfm_url"),
|
||||
# Genius metadata
|
||||
"genius_lyrics": d.get("genius_lyrics"),
|
||||
"genius_description": d.get("genius_description"),
|
||||
"genius_url": d.get("genius_url"),
|
||||
}
|
||||
# Preserve extra keys from joined queries (artist_name, album_title)
|
||||
for extra_key in ("artist_name", "album_title"):
|
||||
|
|
|
|||
|
|
@ -7141,7 +7141,8 @@ class MusicDatabase:
|
|||
|
||||
Args:
|
||||
table: 'artists', 'albums', or 'tracks'
|
||||
provider: 'spotify', 'musicbrainz', 'itunes', 'deezer', 'audiodb'
|
||||
provider: 'spotify', 'musicbrainz', 'itunes', 'deezer', 'audiodb',
|
||||
'tidal', 'qobuz', 'genius' (genius: artists/tracks only)
|
||||
"""
|
||||
column_map = {
|
||||
"artists": {
|
||||
|
|
@ -7150,6 +7151,9 @@ class MusicDatabase:
|
|||
"itunes": "itunes_artist_id",
|
||||
"deezer": "deezer_id",
|
||||
"audiodb": "audiodb_id",
|
||||
"tidal": "tidal_id",
|
||||
"qobuz": "qobuz_id",
|
||||
"genius": "genius_id",
|
||||
},
|
||||
"albums": {
|
||||
"spotify": "spotify_album_id",
|
||||
|
|
@ -7157,6 +7161,8 @@ class MusicDatabase:
|
|||
"itunes": "itunes_album_id",
|
||||
"deezer": "deezer_id",
|
||||
"audiodb": "audiodb_id",
|
||||
"tidal": "tidal_id",
|
||||
"qobuz": "qobuz_id",
|
||||
},
|
||||
"tracks": {
|
||||
"spotify": "spotify_track_id",
|
||||
|
|
@ -7164,6 +7170,9 @@ class MusicDatabase:
|
|||
"itunes": "itunes_track_id",
|
||||
"deezer": "deezer_id",
|
||||
"audiodb": "audiodb_id",
|
||||
"tidal": "tidal_id",
|
||||
"qobuz": "qobuz_id",
|
||||
"genius": "genius_id",
|
||||
},
|
||||
}
|
||||
if table not in column_map or provider not in column_map[table]:
|
||||
|
|
|
|||
1023
webui/static/docs.js
1023
webui/static/docs.js
File diff suppressed because it is too large
Load diff
|
|
@ -35612,6 +35612,383 @@ tr.tag-diff-same {
|
|||
margin: 16px 0 4px;
|
||||
}
|
||||
|
||||
/* ── API Tester ── */
|
||||
|
||||
.api-key-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 10px;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.api-key-bar label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.api-key-bar input {
|
||||
flex: 1;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
padding: 7px 12px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.api-key-bar input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.api-key-bar .api-key-status {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.api-key-bar .api-key-status.connected {
|
||||
color: rgba(var(--accent-rgb), 0.8);
|
||||
}
|
||||
|
||||
.api-endpoint-group {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.api-endpoint-group-title {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.api-endpoint {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 10px;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.api-endpoint:hover {
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.api-endpoint.expanded {
|
||||
border-color: rgba(var(--accent-rgb), 0.25);
|
||||
}
|
||||
|
||||
.api-endpoint-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 14px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.api-endpoint-header:hover {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.api-method {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
min-width: 52px;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.api-method.get { background: rgba(72, 199, 142, 0.15); color: #48c78e; }
|
||||
.api-method.post { background: rgba(62, 142, 208, 0.15); color: #3e8ed0; }
|
||||
.api-method.put { background: rgba(255, 183, 77, 0.15); color: #ffb74d; }
|
||||
.api-method.patch { background: rgba(171, 130, 255, 0.15); color: #ab82ff; }
|
||||
.api-method.delete { background: rgba(241, 70, 104, 0.15); color: #f14668; }
|
||||
|
||||
.api-endpoint-path {
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 12.5px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.api-endpoint-desc {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
margin-left: auto;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
.api-endpoint-arrow {
|
||||
color: rgba(255, 255, 255, 0.2);
|
||||
font-size: 10px;
|
||||
transition: transform 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.api-endpoint.expanded .api-endpoint-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.api-endpoint-body {
|
||||
display: none;
|
||||
padding: 0 14px 14px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.api-endpoint.expanded .api-endpoint-body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.api-endpoint-detail {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.api-detail-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin: 14px 0 6px;
|
||||
}
|
||||
|
||||
.api-params-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.api-params-table th {
|
||||
text-align: left;
|
||||
font-size: 10.5px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 6px 10px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.api-params-table td {
|
||||
padding: 6px 10px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.api-params-table td:first-child {
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 11.5px;
|
||||
color: rgb(var(--accent-light-rgb));
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.api-params-table td:nth-child(2) {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.api-param-required {
|
||||
color: #f14668 !important;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.api-param-optional {
|
||||
color: rgba(255, 255, 255, 0.25) !important;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.api-try-bar {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.api-try-params {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.api-try-param {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.api-try-param label {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.api-try-param input,
|
||||
.api-try-param select {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 5px;
|
||||
padding: 5px 8px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 11.5px;
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.api-try-body {
|
||||
flex: 1;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
padding: 8px 10px;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 11.5px;
|
||||
resize: vertical;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.api-try-btn {
|
||||
background: rgba(var(--accent-rgb), 0.15);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.3);
|
||||
border-radius: 6px;
|
||||
color: rgb(var(--accent-light-rgb));
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
padding: 0 16px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.api-try-btn:hover {
|
||||
background: rgba(var(--accent-rgb), 0.25);
|
||||
border-color: rgba(var(--accent-rgb), 0.5);
|
||||
}
|
||||
|
||||
.api-try-btn:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.api-try-btn.loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.api-response-panel {
|
||||
margin-top: 10px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.api-response-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.api-response-status {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.api-response-status.s2xx { background: rgba(72, 199, 142, 0.15); color: #48c78e; }
|
||||
.api-response-status.s4xx { background: rgba(255, 183, 77, 0.15); color: #ffb74d; }
|
||||
.api-response-status.s5xx { background: rgba(241, 70, 104, 0.15); color: #f14668; }
|
||||
|
||||
.api-response-time {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.api-response-body {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 12px;
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 11.5px;
|
||||
line-height: 1.5;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.api-response-body .json-key { color: rgb(var(--accent-light-rgb)); }
|
||||
.api-response-body .json-string { color: rgba(152, 195, 121, 0.9); }
|
||||
.api-response-body .json-number { color: rgba(209, 154, 102, 0.9); }
|
||||
.api-response-body .json-bool { color: rgba(86, 182, 194, 0.9); }
|
||||
.api-response-body .json-null { color: rgba(255, 255, 255, 0.3); }
|
||||
|
||||
.api-example-json {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 11.5px;
|
||||
line-height: 1.5;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
margin: 6px 0 10px;
|
||||
}
|
||||
|
||||
.api-base-url {
|
||||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 4px 10px;
|
||||
border-radius: 5px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.api-note {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
padding: 8px 12px;
|
||||
background: rgba(var(--accent-rgb), 0.05);
|
||||
border-left: 3px solid rgba(var(--accent-rgb), 0.3);
|
||||
border-radius: 0 6px 6px 0;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
/* Screenshot responsive adjustments */
|
||||
@media (max-width: 600px) {
|
||||
.docs-screenshot {
|
||||
|
|
|
|||
Loading…
Reference in a new issue