From 0a4f921fd9db69ee81d487f953c12612579b3c09 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 4 Jun 2026 16:53:22 -0500 Subject: [PATCH] Format JSON examples with proper indentation and syntax highlighting --- assets/css/app.css | 51 ++++++ lib/aprsme_web/live/api_docs_live.ex | 264 +++++++++++++++------------ 2 files changed, 203 insertions(+), 112 deletions(-) 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

Success Response (200 OK)

-
<%= 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()) %>

Not Found Response (404)

-
<%= raw ~s|{
-    "data": null,
-    "message": "No packets found for callsign N0CALL-9"
-    }| %>
+
<%= raw highlight_json(callsign_not_found_json()) %>

Error Response (400)

-
<%= raw ~s|{
-    "error": {
-    "message": "Invalid callsign format",
-    "code": "bad_request"
-    }
-    }| %>
+
<%= raw highlight_json(callsign_error_json()) %>
@@ -512,77 +607,22 @@ defmodule AprsmeWeb.ApiDocsLive do

Success Response (200 OK)

-
<%= 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()) %>

Empty Response (200 OK)

-
<%= 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()) %>

Error Response (400 Bad Request)

-
<%= raw ~s|{
-    "error": "Missing required parameter: lat"
-    }| %>
+
<%= raw highlight_json(weather_error_param_json()) %>

Error Response (422 Unprocessable Entity)

-
<%= raw ~s|{
-    "error": "Invalid latitude: must be a number between -90 and 90"
-    }| %>
+
<%= raw highlight_json(weather_error_invalid_json()) %>