RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
MarkdownRenderer.h
Go to the documentation of this file.
1#pragma once
2#include "imgui_md.h"
3#include <functional>
4#include <string>
5#include <algorithm>
6#include <cctype>
7#include <cstring> // for std::strlen
8
9// Links-only markdown renderer:
10// - overrides imgui_md::open_url()
11// - supports: http(s), roll:, note:/ and note://open/ refs
13{
14public:
15 enum class Action
16 {
17 OpenExternal, // http/https
18 RollExpr, // roll:1d20+5 or roll://1d20+5
19 NoteOpen, // note://open/<ref>, note:/<ref>, note:<ref>
21 };
22
23 // Optional hooks:
24 std::function<void(const std::string& url)> onOpenExternal; // external URL
25 std::function<void(const std::string& expr)> onRoll; // dice expression
26 std::function<void(const std::string& uuid)> onNoteOpen; // open note by resolved UUID
27
28 // Ref resolver: title / short-id / uuid -> uuid (empty if not found)
29 std::function<std::string(const std::string& ref)> resolveNoteRef;
30
31 MarkdownRenderer() = default;
32
33 // imgui_md hook: called when a markdown link is activated
34 void open_url() const override;
35 void soft_break() override;
36
37 static void openDefaultBrowser_(const std::string& url);
38
39private:
40 struct Parsed
41 {
43 std::string payload; // expr, url, or note ref
44 };
45
46 // Parsing & resolution (helpers)
47 Parsed parseHref_(const std::string& href) const;
48 std::string resolveNote_(const std::string& ref) const;
49
50 // Platform opener (implemented in .cpp, no headers leak here)
51
52 // Small helpers
53 static bool startsWith_(const std::string& s, const char* pref);
54 static bool looksLikeUuid_(const std::string& s);
55 static bool looksLikeShortId_(const std::string& s);
56 static bool isHex_(char c);
57};
58
60//#pragma once
61//#include "imgui_md.h"
62//#include <functional>
63//#include <string>
64//#include <cctype>
65//#include <algorithm>
66//#include <windows.h>
67//#include <shellapi.h>
68//
72//class MarkdownRenderer : public imgui_md
73//{
74//public:
75// enum class Action
76// {
77// OpenExternal, // http/https
78// RollExpr, // roll:1d20+5 or roll://1d20+5
79// NoteOpen, // note://open/<ref> or note:/<ref> (short form)
80// Unknown
81// };
82//
83// // Callbacks you can hook from NoteEditorUI:
84// // - If not set, defaults will be used (open external in browser; others no-op).
85// std::function<void(const std::string& url)> onOpenExternal; // default: opens default browser
86// std::function<void(const std::string& expr)> onRoll; // e.g., send to chat dice roller
87// std::function<void(const std::string& uuid)> onNoteOpen; // open a note tab by final UUID
88//
89// // Resolver to map human-friendly refs -> UUID (title, slug, short id, etc.)
90// // Return empty string if not found. If not set, note links will be ignored gracefully.
91// std::function<std::string(const std::string& ref)> resolveNoteRef;
92//
93// MarkdownRenderer() = default;
94//
95// // imgui_md hook: invoked on link click with m_href filled.
96// void open_url() const override
97// {
98// const auto a = parseHref_(m_href);
99// switch (a.kind)
100// {
101// case Action::OpenExternal:
102// openDefaultBrowser_(a.payload);
103// break;
104//
105// case Action::RollExpr:
106// if (onRoll)
107// onRoll(a.payload);
108// break;
109//
110// case Action::NoteOpen:
111// {
112// // allow full UUID, short UUID prefix, title/slug
113// std::string uuid = resolveNote_(a.payload);
114// if (!uuid.empty() && onNoteOpen)
115// onNoteOpen(uuid);
116// break;
117// }
118//
119// default:
120// // Fallback: try opening externally
121// openDefaultBrowser_(m_href);
122// break;
123// }
124// }
125//
126//private:
127// struct Parsed
128// {
129// Action kind{Action::Unknown};
130// std::string payload; // expr, url, ref, etc.
131// };
132//
133// // -------- HREF parsing (all-in-one) --------
134// Parsed parseHref_(const std::string& href) const
135// {
136// // external?
137// if (startsWith_(href, "http://") || startsWith_(href, "https://"))
138// {
139// return {Action::OpenExternal, href};
140// }
141//
142// // roll://expr or roll:expr
143// if (startsWith_(href, "roll://"))
144// {
145// return {Action::RollExpr, href.substr(7)};
146// }
147// if (startsWith_(href, "roll:"))
148// {
149// return {Action::RollExpr, href.substr(5)};
150// }
151//
152// // note://open/<ref>
153// if (startsWith_(href, "note://open/"))
154// {
155// return {Action::NoteOpen, href.substr(std::string("note://open/").size())};
156// }
157//
158// // Short form: note:/<ref> (less typing)
159// if (startsWith_(href, "note:/"))
160// {
161// return {Action::NoteOpen, href.substr(6)};
162// }
163//
164// // Optional: allow bare "note:<ref>"
165// if (startsWith_(href, "note:"))
166// {
167// return {Action::NoteOpen, href.substr(5)};
168// }
169//
170// return {};
171// }
172//
173// // -------- Note reference resolution --------
174// // Accepts:
175// // - full UUID string
176// // - short uuid prefix (>= 8 hex chars)
177// // - title/slug (delegate to resolveNoteRef)
178// std::string resolveNote_(const std::string& ref) const
179// {
180// // If looks like a full UUID (very loose check), accept as-is.
181// if (looksLikeUuid_(ref))
182// return ref;
183//
184// // If short hex-ish id (>=8), let resolver handle it (could be prefix).
185// if (looksLikeShortId_(ref))
186// {
187// if (resolveNoteRef)
188// return resolveNoteRef(ref);
189// return {};
190// }
191//
192// // Otherwise treat as title/slug/alias
193// if (resolveNoteRef)
194// return resolveNoteRef(ref);
195// return {};
196// }
197//
198// // -------- Platform opener (default browser) --------
199//private:
200// static void openDefaultBrowser_(const std::string& url)
201// {
202// if (url.empty())
203// return;
204// // prefer ShellExecuteA for default handler
205// HINSTANCE r = ShellExecuteA(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
206// if ((INT_PTR)r <= 32)
207// {
208// // fallback
209// std::string cmd = "start \"\" \"" + url + "\"";
210// std::system(cmd.c_str());
211// }
212// }
213//
214// // -------- small helpers --------
215// static bool startsWith_(const std::string& s, const char* pref)
216// {
217// const size_t n = std::strlen(pref);
218// return s.size() >= n && std::equal(pref, pref + n, s.begin());
219// }
220//
221// static bool looksLikeUuid_(const std::string& s)
222// {
223// // Very loose check: 36 chars 8-4-4-4-12 with hex + dashes
224// if (s.size() != 36)
225// return false;
226// const int dashPos[4] = {8, 13, 18, 23};
227// for (int i = 0; i < 36; ++i)
228// {
229// if (i == dashPos[0] || i == dashPos[1] || i == dashPos[2] || i == dashPos[3])
230// {
231// if (s[i] != '-')
232// return false;
233// }
234// else if (!isHex_(s[i]))
235// {
236// return false;
237// }
238// }
239// return true;
240// }
241//
242// static bool looksLikeShortId_(const std::string& s)
243// {
244// // Accept short hex prefixes >= 8 chars (no dashes)
245// if (s.size() < 8)
246// return false;
247// for (char c : s)
248// {
249// if (c == '-')
250// return false;
251// if (!isHex_(c))
252// return false;
253// }
254// return true;
255// }
256//
257// static bool isHex_(char c)
258// {
259// return std::isxdigit(static_cast<unsigned char>(c)) != 0;
260// }
261//};
static bool startsWith_(const std::string &s, const char *pref)
MarkdownRenderer()=default
static void openDefaultBrowser_(const std::string &url)
std::function< std::string(const std::string &ref) resolveNoteRef)
void open_url() const override
std::string resolveNote_(const std::string &ref) const
std::function< void(const std::string &expr) onRoll)
static bool isHex_(char c)
std::function< void(const std::string &url) onOpenExternal)
std::function< void(const std::string &uuid) onNoteOpen)
static bool looksLikeShortId_(const std::string &s)
void soft_break() override
Parsed parseHref_(const std::string &href) const
static bool looksLikeUuid_(const std::string &s)