diff --git a/assets/css/app.css b/assets/css/app.css index 589ca63..87f15b8 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -243,3 +243,54 @@ .historical-dot-marker { z-index: 15 !important; } + +/* JSON syntax highlighting for API docs */ +.json-key { + color: #7c3aed; +} + +.json-string { + color: #059669; +} + +.json-number { + color: #d97706; +} + +.json-boolean { + color: #dc2626; +} + +.json-null { + color: #6b7280; + font-style: italic; +} + +.json-punc { + color: #6b7280; +} + +[data-theme="dark"] .json-key { + color: #a78bfa; +} + +[data-theme="dark"] .json-string { + color: #34d399; +} + +[data-theme="dark"] .json-number { + color: #fbbf24; +} + +[data-theme="dark"] .json-boolean { + color: #f87171; +} + +[data-theme="dark"] .json-null { + color: #9ca3af; + font-style: italic; +} + +[data-theme="dark"] .json-punc { + color: #9ca3af; +} diff --git a/lib/aprsme_web/live/api_docs_live.ex b/lib/aprsme_web/live/api_docs_live.ex index 96aa22e..b5c8793 100644 --- a/lib/aprsme_web/live/api_docs_live.ex +++ b/lib/aprsme_web/live/api_docs_live.ex @@ -208,6 +208,151 @@ defmodule AprsmeWeb.ApiDocsLive do defp sanitize_raw_packet(raw_packet), do: raw_packet + defp highlight_json(text) do + highlighted = + Regex.replace( + ~r/(? + ~s(#{key}) + + _, "", str, "", "", "" -> + ~s(#{str}) + + _, "", "", bool, "", "" -> + ~s(#{bool}) + + _, "", "", "", null, "" -> + ~s(#{null}) + + _, "", "", "", "", num -> + ~s(#{num}) + end + ) + + Regex.replace(~r/[{}[\]\:,]/, highlighted, ~s(\\0)) + end + + defp callsign_success_json, do: ~s|{ + "data": { + "id": "d7249877-d4a6-45c2-b314-2a8a355d2566", + "callsign": "N0CALL-9", + "base_callsign": "N0CALL", + "ssid": "9", + "sender": "N0CALL-9", + "destination": "APRS", + "path": "WIDE1-1,WIDE2-1", + "data_type": "position", + "information_field": "!4740.00N/12200.00W>Mobile Station", + "raw_packet": "N0CALL-9>APRS,WIDE1-1,WIDE2-1:!4740.00N/12200.00W>Mobile Station", + "received_at": "2024-01-15T10:30:45Z", + "region": "US-West", + "position": { + "latitude": 47.666667, + "longitude": -122.0, + "course": 90, + "speed": 35.5, + "altitude": 152.4 + }, + "symbol": { + "code": ">", + "table_id": "/" + }, + "comment": "Mobile Station", + "timestamp": null, + "aprs_messaging": false, + "weather": null, + "equipment": { + "device_identifier": "APK004", + "manufacturer": "Kenwood", + "equipment_type": "TM-D710", + "device_model": "TM-D710G", + "device_vendor": "Kenwood", + "device_class": "rig" + }, + "message": null, + "has_position": true, + "inserted_at": "2024-01-15T10:30:45Z", + "updated_at": "2024-01-15T10:30:45Z" + } +}| + + defp callsign_not_found_json, do: ~s|{ + "data": null, + "message": "No packets found for callsign N0CALL-9" +}| + + defp callsign_error_json, do: ~s|{ + "error": { + "message": "Invalid callsign format", + "code": "bad_request" + } +}| + + defp weather_success_json, do: ~s|{ + "data": [ + { + "callsign": "N0CALL-13", + "base_callsign": "N0CALL", + "position": { + "lat": 37.7849, + "lon": -122.4094 + }, + "distance_miles": 0.87, + "weather": { + "temperature": 72.5, + "humidity": 65.0, + "pressure": 1013.2, + "wind_speed": 8.5, + "wind_direction": 270, + "wind_gust": 12.0, + "rain_1h": 0.0, + "rain_24h": 0.5, + "rain_since_midnight": 0.3 + }, + "symbol": { + "table_id": "/", + "code": "_" + }, + "comment": "Davis Vantage Pro2", + "last_report": "2026-03-22T15:30:00Z" + } + ], + "meta": { + "count": 1, + "params": { + "latitude": 37.7749, + "longitude": -122.4194, + "radius_miles": 25.0, + "hours": 6, + "limit": 50 + } + } +}| + + defp weather_empty_json, do: ~s|{ + "data": [], + "meta": { + "count": 0, + "params": { + "latitude": 0.0, + "longitude": -180.0, + "radius_miles": 10.0, + "hours": 6, + "limit": 50 + } + } +}| + + defp weather_error_param_json, do: ~s|{ + "error": "Missing required parameter: lat" +}| + + defp weather_error_invalid_json, do: ~s|{ + "error": "Invalid latitude: must be a number between -90 and 90" +}| + @impl true def render(assigns) do ~H""" @@ -339,67 +484,17 @@ defmodule AprsmeWeb.ApiDocsLive do
<%= raw ~s|{
- "data": {
- "id": "d7249877-d4a6-45c2-b314-2a8a355d2566",
- "callsign": "N0CALL-9",
- "base_callsign": "N0CALL",
- "ssid": "9",
- "sender": "N0CALL-9",
- "destination": "APRS",
- "path": "WIDE1-1,WIDE2-1",
- "data_type": "position",
- "information_field": "!4740.00N/12200.00W>Mobile Station",
- "raw_packet": "N0CALL-9>APRS,WIDE1-1,WIDE2-1:!4740.00N/12200.00W>Mobile Station",
- "received_at": "2024-01-15T10:30:45Z",
- "region": "US-West",
- "position": {
- "latitude": 47.666667,
- "longitude": -122.0,
- "course": 90,
- "speed": 35.5,
- "altitude": 152.4
- },
- "symbol": {
- "code": ">",
- "table_id": "/"
- },
- "comment": "Mobile Station",
- "timestamp": null,
- "aprs_messaging": false,
- "weather": null,
- "equipment": {
- "device_identifier": "APK004",
- "manufacturer": "Kenwood",
- "equipment_type": "TM-D710",
- "device_model": "TM-D710G",
- "device_vendor": "Kenwood",
- "device_class": "rig"
- },
- "message": null,
- "has_position": true,
- "inserted_at": "2024-01-15T10:30:45Z",
- "updated_at": "2024-01-15T10:30:45Z"
- }
- }| %>
+ <%= raw highlight_json(callsign_success_json()) %>
<%= raw ~s|{
- "data": null,
- "message": "No packets found for callsign N0CALL-9"
- }| %>
+ <%= raw highlight_json(callsign_not_found_json()) %>
<%= raw ~s|{
- "error": {
- "message": "Invalid callsign format",
- "code": "bad_request"
- }
- }| %>
+ <%= raw highlight_json(callsign_error_json()) %>
<%= raw ~s|{
- "data": [
- {
- "callsign": "N0CALL-13",
- "base_callsign": "N0CALL",
- "position": {
- "lat": 37.7849,
- "lon": -122.4094
- },
- "distance_miles": 0.87,
- "weather": {
- "temperature": 72.5,
- "humidity": 65.0,
- "pressure": 1013.2,
- "wind_speed": 8.5,
- "wind_direction": 270,
- "wind_gust": 12.0,
- "rain_1h": 0.0,
- "rain_24h": 0.5,
- "rain_since_midnight": 0.3
- },
- "symbol": {
- "table_id": "/",
- "code": "_"
- },
- "comment": "Davis Vantage Pro2",
- "last_report": "2026-03-22T15:30:00Z"
- }
- ],
- "meta": {
- "count": 1,
- "params": {
- "latitude": 37.7749,
- "longitude": -122.4194,
- "radius_miles": 25.0,
- "hours": 6,
- "limit": 50
- }
- }
- }| %>
+ <%= raw highlight_json(weather_success_json()) %>
<%= raw ~s|{
- "data": [],
- "meta": {
- "count": 0,
- "params": {
- "latitude": 0.0,
- "longitude": -180.0,
- "radius_miles": 10.0,
- "hours": 6,
- "limit": 50
- }
- }
- }| %>
+ <%= raw highlight_json(weather_empty_json()) %>
<%= raw ~s|{
- "error": "Missing required parameter: lat"
- }| %>
+ <%= raw highlight_json(weather_error_param_json()) %>
<%= raw ~s|{
- "error": "Invalid latitude: must be a number between -90 and 90"
- }| %>
+ <%= raw highlight_json(weather_error_invalid_json()) %>