diff --git a/src/TeamsISO.App/Services/ControlPanelHtml.cs b/src/TeamsISO.App/Services/ControlPanelHtml.cs new file mode 100644 index 0000000..242ed1c --- /dev/null +++ b/src/TeamsISO.App/Services/ControlPanelHtml.cs @@ -0,0 +1,196 @@ +namespace TeamsISO.App.Services; + +/// +/// The HTML / CSS / JS for the embedded control panel served at +/// GET /ui. Single self-contained string — no external CDN deps, no +/// build step, no React. Just enough to give operators a phone-friendly +/// remote that connects via WebSocket to /ws and posts to the +/// existing REST endpoints. +/// +/// Visual language matches the WPF host: dark canvas, cyan accent, mono +/// font for codey labels. Keeping the styling minimal so a future iteration +/// can swap in a fancier UI without breaking operator workflows that already +/// bookmark the URL. +/// +internal static class ControlPanelHtml +{ + private const string Html = @" + + + + +TeamsISO Control + + + +

TeamsISO control surface

+ +
+
+ connecting… + +
+
+ +
+
+ + + + + + + + +
+
+ +
+ + + + +"; + + public static string Get() => Html; +} diff --git a/src/TeamsISO.App/Services/NotesService.cs b/src/TeamsISO.App/Services/NotesService.cs new file mode 100644 index 0000000..7bbd280 --- /dev/null +++ b/src/TeamsISO.App/Services/NotesService.cs @@ -0,0 +1,60 @@ +using System.IO; +using System.Text; + +namespace TeamsISO.App.Services; + +/// +/// Append-only show-notes log. Each call writes a timestamped line to a daily +/// markdown file at %LOCALAPPDATA%\TeamsISO\Notes\<YYYY-MM-DD>.md. +/// Operators stamp notes via the REST POST /notes endpoint or the OSC +/// /teamsiso/notes "..." address — typically wired to a Stream Deck +/// button so a note can be left without leaving the show. +/// +/// We deliberately don't surface the notes inside the WPF UI: the file is +/// trivial to open in any editor, and inline note-taking would be a much +/// bigger feature (textarea, scrollback, autosave). The endpoint is the +/// minimum-viable affordance for live note capture. +/// +public static class NotesService +{ + private static readonly object _gate = new(); + + private static string NotesDirectory => + Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "TeamsISO", "Notes"); + + /// Today's notes file path (created lazily on first append). + public static string TodayPath => + Path.Combine(NotesDirectory, $"{DateTimeOffset.Now:yyyy-MM-dd}.md"); + + /// + /// Append a single timestamped line. Concurrent callers serialize through + /// the static gate so we don't end up with interleaved writes from the + /// REST handler thread vs. the OSC dispatcher. + /// + public static bool Append(string text) + { + if (string.IsNullOrWhiteSpace(text)) return false; + try + { + lock (_gate) + { + Directory.CreateDirectory(NotesDirectory); + var path = TodayPath; + var line = $"- **{DateTimeOffset.Now:HH:mm:ss}** — {text.Trim()}{Environment.NewLine}"; + if (!File.Exists(path)) + { + var header = $"# TeamsISO show notes — {DateTimeOffset.Now:yyyy-MM-dd}{Environment.NewLine}{Environment.NewLine}"; + File.WriteAllText(path, header, Encoding.UTF8); + } + File.AppendAllText(path, line, Encoding.UTF8); + } + return true; + } + catch + { + return false; + } + } +}