diff --git a/examples/cards/README.md b/examples/cards/README.md new file mode 100644 index 0000000..550bacb --- /dev/null +++ b/examples/cards/README.md @@ -0,0 +1,11 @@ +# Example Cards + +This directory contains checked-in snapshots of the card templates and saved card instances +currently used in the local Nanobot web UI setup. + +Structure: +- `templates/`: reusable card templates (`manifest.json` + `template.html`) +- `instances/`: example saved cards (`card.json` + `state.json`) + +These files are examples for reference, iteration, and testing. The running web UI loads cards +from `~/.nanobot/cards`, not from this directory. diff --git a/examples/cards/instances/6633c6dc-4772-4e47-a402-1e103026208a/card.json b/examples/cards/instances/6633c6dc-4772-4e47-a402-1e103026208a/card.json new file mode 100644 index 0000000..5fc2d94 --- /dev/null +++ b/examples/cards/instances/6633c6dc-4772-4e47-a402-1e103026208a/card.json @@ -0,0 +1,18 @@ +{ + "id": "6633c6dc-4772-4e47-a402-1e103026208a", + "kind": "text", + "title": "3-Day Agenda", + "content": "", + "question": "", + "choices": [], + "response_value": "", + "slot": "live-calendar-3day", + "lane": "context", + "priority": 50, + "state": "active", + "template_key": "calendar-agenda-live", + "context_summary": "", + "chat_id": "web", + "created_at": "2026-03-11T18:18:25.621797+00:00", + "updated_at": "2026-03-11T18:18:25.621797+00:00" +} diff --git a/examples/cards/instances/6633c6dc-4772-4e47-a402-1e103026208a/state.json b/examples/cards/instances/6633c6dc-4772-4e47-a402-1e103026208a/state.json new file mode 100644 index 0000000..63f9ec0 --- /dev/null +++ b/examples/cards/instances/6633c6dc-4772-4e47-a402-1e103026208a/state.json @@ -0,0 +1,12 @@ +{ + "title": "3-Day Agenda", + "subtitle": "Family Calendar", + "tool_name": "mcp_home_assistant_calendar_get_events", + "calendar_names": [ + "Family Calendar" + ], + "range_days": 3, + "max_events": 8, + "refresh_ms": 900000, + "empty_text": "No events scheduled." +} diff --git a/examples/cards/instances/live-bedroom-co2/card.json b/examples/cards/instances/live-bedroom-co2/card.json new file mode 100644 index 0000000..58a16a1 --- /dev/null +++ b/examples/cards/instances/live-bedroom-co2/card.json @@ -0,0 +1,17 @@ +{ + "id": "live-bedroom-co2", + "kind": "text", + "title": "Bedroom CO2", + "question": "", + "choices": [], + "response_value": "", + "slot": "live-bedroom-co2", + "lane": "context", + "priority": 50, + "state": "active", + "template_key": "sensor-live", + "context_summary": "", + "chat_id": "web", + "created_at": "2026-03-09T22:39:28.502257+00:00", + "updated_at": "2026-03-11T04:12:48.601255+00:00" +} diff --git a/examples/cards/instances/live-bedroom-co2/state.json b/examples/cards/instances/live-bedroom-co2/state.json new file mode 100644 index 0000000..5b3e468 --- /dev/null +++ b/examples/cards/instances/live-bedroom-co2/state.json @@ -0,0 +1,14 @@ +{ + "title": "Bedroom CO2", + "subtitle": "Home Assistant live context", + "tool_name": "mcp_home_assistant_GetLiveContext", + "match_name": "Bedroom-Esp-Sensor CO2", + "device_class": "carbon_dioxide", + "unit": "ppm", + "refresh_ms": 15000, + "value_decimals": 0, + "thresholds": { + "good_max": 900, + "elevated_max": 1200 + } +} diff --git a/examples/cards/instances/live-calendar-today/card.json b/examples/cards/instances/live-calendar-today/card.json new file mode 100644 index 0000000..33e57d2 --- /dev/null +++ b/examples/cards/instances/live-calendar-today/card.json @@ -0,0 +1,17 @@ +{ + "id": "live-calendar-today", + "kind": "text", + "title": "Today's Agenda", + "question": "", + "choices": [], + "response_value": "", + "slot": "live-calendar-today", + "lane": "context", + "priority": 50, + "state": "active", + "template_key": "calendar-agenda-live", + "context_summary": "", + "chat_id": "web", + "created_at": "2026-03-09T23:37:10.542306+00:00", + "updated_at": "2026-03-11T04:12:48.601255+00:00" +} diff --git a/examples/cards/instances/live-calendar-today/state.json b/examples/cards/instances/live-calendar-today/state.json new file mode 100644 index 0000000..42c6074 --- /dev/null +++ b/examples/cards/instances/live-calendar-today/state.json @@ -0,0 +1,12 @@ +{ + "title": "Today's Agenda", + "subtitle": "Family Calendar", + "tool_name": "mcp_home_assistant_calendar_get_events", + "calendar_names": [ + "Family Calendar" + ], + "range_days": 1, + "max_events": 8, + "refresh_ms": 900000, + "empty_text": "No events for today." +} diff --git a/examples/cards/instances/live-litellm-ups-usage/card.json b/examples/cards/instances/live-litellm-ups-usage/card.json new file mode 100644 index 0000000..f89404f --- /dev/null +++ b/examples/cards/instances/live-litellm-ups-usage/card.json @@ -0,0 +1,18 @@ +{ + "id": "live-litellm-ups-usage", + "kind": "text", + "title": "LiteLLM + UPS Usage", + "content": "", + "question": "", + "choices": [], + "response_value": "", + "slot": "live-litellm-ups-usage", + "lane": "context", + "priority": 48, + "state": "active", + "template_key": "litellm-ups-usage-live", + "context_summary": "LiteLLM token usage and UPS energy usage for the last 24 hours and current month.", + "chat_id": "web", + "created_at": "2026-03-11T23:05:00+00:00", + "updated_at": "2026-03-11T23:05:00+00:00" +} diff --git a/examples/cards/instances/live-litellm-ups-usage/state.json b/examples/cards/instances/live-litellm-ups-usage/state.json new file mode 100644 index 0000000..769e526 --- /dev/null +++ b/examples/cards/instances/live-litellm-ups-usage/state.json @@ -0,0 +1,8 @@ +{ + "subtitle": "GLM local usage", + "tool_name_24h": "exec", + "tool_arguments_24h": { + "command": "python3 $HOME/.nanobot/workspace/litellm_ups_price_per_token.py --24h" + }, + "refresh_ms": 900000 +} diff --git a/examples/cards/instances/live-nanobot-git-diff/card.json b/examples/cards/instances/live-nanobot-git-diff/card.json new file mode 100644 index 0000000..a5c0309 --- /dev/null +++ b/examples/cards/instances/live-nanobot-git-diff/card.json @@ -0,0 +1,14 @@ +{ + "id": "live-nanobot-git-diff", + "kind": "text", + "title": "nanobot git diff", + "slot": "git-diff:/home/kacper/nanobot", + "lane": "work", + "priority": 72, + "state": "active", + "template_key": "git-diff-live", + "context_summary": "Live git working tree summary for /home/kacper/nanobot.", + "chat_id": "web", + "created_at": "2026-03-12T14:00:00+00:00", + "updated_at": "2026-03-12T14:00:00+00:00" +} diff --git a/examples/cards/instances/live-nanobot-git-diff/state.json b/examples/cards/instances/live-nanobot-git-diff/state.json new file mode 100644 index 0000000..61ccbaa --- /dev/null +++ b/examples/cards/instances/live-nanobot-git-diff/state.json @@ -0,0 +1,8 @@ +{ + "subtitle": "nanobot", + "tool_name": "exec", + "tool_arguments": { + "command": "python3 $HOME/.nanobot/workspace/git_repo_diff_summary.py --repo /home/kacper/nanobot --max-files 8", + "max_output_chars": 200000 + } +} diff --git a/examples/cards/instances/live-weather-01545/card.json b/examples/cards/instances/live-weather-01545/card.json new file mode 100644 index 0000000..c949ef3 --- /dev/null +++ b/examples/cards/instances/live-weather-01545/card.json @@ -0,0 +1,17 @@ +{ + "id": "live-weather-01545", + "kind": "text", + "title": "Weather 01545", + "question": "", + "choices": [], + "response_value": "", + "slot": "live-weather-01545", + "lane": "context", + "priority": 50, + "state": "active", + "template_key": "weather-live", + "context_summary": "", + "chat_id": "web", + "created_at": "2026-03-09T23:26:30.195376+00:00", + "updated_at": "2026-03-11T04:12:48.601255+00:00" +} diff --git a/examples/cards/instances/live-weather-01545/state.json b/examples/cards/instances/live-weather-01545/state.json new file mode 100644 index 0000000..df80a06 --- /dev/null +++ b/examples/cards/instances/live-weather-01545/state.json @@ -0,0 +1,10 @@ +{ + "title": "Weather 01545", + "subtitle": "OpenWeatherMap live context", + "tool_name": "mcp_home_assistant_GetLiveContext", + "provider_prefix": "OpenWeatherMap", + "temperature_name": "OpenWeatherMap Temperature", + "humidity_name": "OpenWeatherMap Humidity", + "condition_label": "OpenWeatherMap live context", + "refresh_ms": 86400000 +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-031fac06-1f50-11f1-b5e6-001e06480aef/card.json b/examples/cards/instances/todo-todo-kacpers-to-do-031fac06-1f50-11f1-b5e6-001e06480aef/card.json new file mode 100644 index 0000000..e9ba1c0 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-031fac06-1f50-11f1-b5e6-001e06480aef/card.json @@ -0,0 +1,14 @@ +{ + "id": "todo-todo-kacpers-to-do-031fac06-1f50-11f1-b5e6-001e06480aef", + "kind": "text", + "title": "buy socks", + "slot": "todo:todo.kacpers_to_do:031fac06-1f50-11f1-b5e6-001e06480aef", + "lane": "attention", + "priority": 70, + "state": "active", + "template_key": "todo-item-live", + "context_summary": "Todo item from Kacper’s To-Do: buy socks", + "chat_id": "web", + "created_at": "2026-03-14T02:46:33.662524+00:00", + "updated_at": "2026-03-14T21:56:17.153704+00:00" +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-031fac06-1f50-11f1-b5e6-001e06480aef/state.json b/examples/cards/instances/todo-todo-kacpers-to-do-031fac06-1f50-11f1-b5e6-001e06480aef/state.json new file mode 100644 index 0000000..e020822 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-031fac06-1f50-11f1-b5e6-001e06480aef/state.json @@ -0,0 +1,14 @@ +{ + "source_id": "ha-todo-kacpers-to-do", + "entity_id": "todo.kacpers_to_do", + "list_name": "Kacper’s To-Do", + "uid": "031fac06-1f50-11f1-b5e6-001e06480aef", + "summary": "buy socks", + "status": "needs_action", + "completed": false, + "due": null, + "due_datetime": null, + "description": null, + "generated_at": "2026-03-14T21:56:17.117855+00:00", + "can_complete": true +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-7994f6d2-1f9d-11f1-b5e6-001e06480aef/card.json b/examples/cards/instances/todo-todo-kacpers-to-do-7994f6d2-1f9d-11f1-b5e6-001e06480aef/card.json new file mode 100644 index 0000000..b6b2485 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-7994f6d2-1f9d-11f1-b5e6-001e06480aef/card.json @@ -0,0 +1,14 @@ +{ + "id": "todo-todo-kacpers-to-do-7994f6d2-1f9d-11f1-b5e6-001e06480aef", + "kind": "text", + "title": "Do the laundry", + "slot": "todo:todo.kacpers_to_do:7994f6d2-1f9d-11f1-b5e6-001e06480aef", + "lane": "attention", + "priority": 70, + "state": "active", + "template_key": "todo-item-live", + "context_summary": "Todo item from Kacper’s To-Do: Do the laundry", + "chat_id": "web", + "created_at": "2026-03-14T12:01:03.651640+00:00", + "updated_at": "2026-03-14T21:56:17.153704+00:00" +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-7994f6d2-1f9d-11f1-b5e6-001e06480aef/state.json b/examples/cards/instances/todo-todo-kacpers-to-do-7994f6d2-1f9d-11f1-b5e6-001e06480aef/state.json new file mode 100644 index 0000000..8d49fd0 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-7994f6d2-1f9d-11f1-b5e6-001e06480aef/state.json @@ -0,0 +1,14 @@ +{ + "source_id": "ha-todo-kacpers-to-do", + "entity_id": "todo.kacpers_to_do", + "list_name": "Kacper’s To-Do", + "uid": "7994f6d2-1f9d-11f1-b5e6-001e06480aef", + "summary": "Do the laundry", + "status": "needs_action", + "completed": false, + "due": null, + "due_datetime": null, + "description": "Scheduled laundry reminder", + "generated_at": "2026-03-14T21:56:17.117855+00:00", + "can_complete": true +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-b9b79f12-1ef3-11f1-b5e6-001e06480aef/card.json b/examples/cards/instances/todo-todo-kacpers-to-do-b9b79f12-1ef3-11f1-b5e6-001e06480aef/card.json new file mode 100644 index 0000000..f559670 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-b9b79f12-1ef3-11f1-b5e6-001e06480aef/card.json @@ -0,0 +1,14 @@ +{ + "id": "todo-todo-kacpers-to-do-b9b79f12-1ef3-11f1-b5e6-001e06480aef", + "kind": "text", + "title": "Pack for Japan", + "slot": "todo:todo.kacpers_to_do:b9b79f12-1ef3-11f1-b5e6-001e06480aef", + "lane": "attention", + "priority": 70, + "state": "active", + "template_key": "todo-item-live", + "context_summary": "Todo item from Kacper’s To-Do: Pack for Japan", + "chat_id": "web", + "created_at": "2026-03-13T16:03:26.838387+00:00", + "updated_at": "2026-03-14T21:56:17.153704+00:00" +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-b9b79f12-1ef3-11f1-b5e6-001e06480aef/state.json b/examples/cards/instances/todo-todo-kacpers-to-do-b9b79f12-1ef3-11f1-b5e6-001e06480aef/state.json new file mode 100644 index 0000000..43dd881 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-b9b79f12-1ef3-11f1-b5e6-001e06480aef/state.json @@ -0,0 +1,14 @@ +{ + "source_id": "ha-todo-kacpers-to-do", + "entity_id": "todo.kacpers_to_do", + "list_name": "Kacper’s To-Do", + "uid": "b9b79f12-1ef3-11f1-b5e6-001e06480aef", + "summary": "Pack for Japan", + "status": "needs_action", + "completed": false, + "due": null, + "due_datetime": null, + "description": null, + "generated_at": "2026-03-14T21:56:17.117855+00:00", + "can_complete": true +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-dc554d5e-1fa6-11f1-b5e6-001e06480aef/card.json b/examples/cards/instances/todo-todo-kacpers-to-do-dc554d5e-1fa6-11f1-b5e6-001e06480aef/card.json new file mode 100644 index 0000000..39f0e10 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-dc554d5e-1fa6-11f1-b5e6-001e06480aef/card.json @@ -0,0 +1,14 @@ +{ + "id": "todo-todo-kacpers-to-do-dc554d5e-1fa6-11f1-b5e6-001e06480aef", + "kind": "text", + "title": "return the sneakers", + "slot": "todo:todo.kacpers_to_do:dc554d5e-1fa6-11f1-b5e6-001e06480aef", + "lane": "attention", + "priority": 70, + "state": "active", + "template_key": "todo-item-live", + "context_summary": "Todo item from Kacper’s To-Do: return the sneakers", + "chat_id": "web", + "created_at": "2026-03-14T13:08:14.788903+00:00", + "updated_at": "2026-03-14T21:56:17.153704+00:00" +} diff --git a/examples/cards/instances/todo-todo-kacpers-to-do-dc554d5e-1fa6-11f1-b5e6-001e06480aef/state.json b/examples/cards/instances/todo-todo-kacpers-to-do-dc554d5e-1fa6-11f1-b5e6-001e06480aef/state.json new file mode 100644 index 0000000..1873852 --- /dev/null +++ b/examples/cards/instances/todo-todo-kacpers-to-do-dc554d5e-1fa6-11f1-b5e6-001e06480aef/state.json @@ -0,0 +1,14 @@ +{ + "source_id": "ha-todo-kacpers-to-do", + "entity_id": "todo.kacpers_to_do", + "list_name": "Kacper’s To-Do", + "uid": "dc554d5e-1fa6-11f1-b5e6-001e06480aef", + "summary": "return the sneakers", + "status": "needs_action", + "completed": false, + "due": null, + "due_datetime": null, + "description": null, + "generated_at": "2026-03-14T21:56:17.117855+00:00", + "can_complete": true +} diff --git a/examples/cards/templates/calendar-agenda-live/manifest.json b/examples/cards/templates/calendar-agenda-live/manifest.json new file mode 100644 index 0000000..3ef8c97 --- /dev/null +++ b/examples/cards/templates/calendar-agenda-live/manifest.json @@ -0,0 +1,18 @@ +{ + "key": "calendar-agenda-live", + "title": "Live Calendar Agenda", + "notes": "Agenda card for one or more Home Assistant calendars. Fill template_state with subtitle, tool_name (defaults to calendar_get_events), optional calendar_names, range_days up to 7, max_events, refresh_ms, and empty_text. The card title comes from the feed header, not the template body.", + "example_state": { + "subtitle": "Family Calendar", + "tool_name": "mcp_home_assistant_calendar_get_events", + "calendar_names": [ + "Family Calendar" + ], + "range_days": 3, + "max_events": 8, + "refresh_ms": 900000, + "empty_text": "No events scheduled." + }, + "created_at": "2026-03-11T04:12:48.601255+00:00", + "updated_at": "2026-03-11T19:18:04.632189+00:00" +} diff --git a/examples/cards/templates/calendar-agenda-live/template.html b/examples/cards/templates/calendar-agenda-live/template.html new file mode 100644 index 0000000..2a0a86d --- /dev/null +++ b/examples/cards/templates/calendar-agenda-live/template.html @@ -0,0 +1,257 @@ +