1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
|
/*
* Ricochet - A minimalist CLI web browser for fast, distraction-free navigation.
* Copyright (C) 2026 Richard Qin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "ricochet/tui/position.hh"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>
namespace ricochet::tui {
struct TextStyle
{
bool emphasis { false };
bool strong { false };
bool code { false };
bool subdued { false };
bool warning { false };
};
struct StyledRange
{
ByteRange bytes {};
TextStyle style {};
};
// Leaf IDs and structural block IDs share one document-local namespace.
// All text offsets address a Text leaf, not a concatenation of rendered lines.
struct Text
{
BlockId id {};
// LF is an explicit hard break; soft wrapping never changes this string.
std::string utf8 {};
std::vector<StyledRange> styles {};
};
struct ControlPlaceholder
{ ControlId control {}; };
using Inline = std::variant<Text, ControlPlaceholder>;
struct Paragraph
{ std::vector<Inline> content {}; };
struct Heading
{
unsigned int level { 1 };
std::vector<Inline> content {};
};
struct Preformatted
{
Text content {};
std::optional<std::string> language {};
};
struct Block;
struct ListItem
{
std::optional<std::string> marker {};
std::vector<Block> content {};
};
enum class ListKind : std::uint8_t
{
Unordered,
Ordered
};
struct List
{
ListKind kind { ListKind::Unordered };
std::int64_t first_number { 1 };
std::vector<ListItem> items {};
};
struct TableCell
{
// The producer resolves HTML spans to grid coordinates before delivery.
std::size_t column { 0 };
std::size_t column_span { 1 };
std::size_t row_span { 1 };
bool header { false };
std::vector<Block> content {};
};
struct TableRow
{ std::vector<TableCell> cells {}; };
struct Table
{
std::size_t columns { 0 };
std::vector<TableRow> rows {};
};
struct Alternative
{ Text description {}; };
struct Separator
{};
struct Block
{
// Structural identity supports empty blocks and per-block horizontal scroll.
BlockId id {};
std::variant<Paragraph, Heading, Preformatted, List, Table, ControlPlaceholder, Alternative, Separator>
content {};
};
struct Link
{
LinkId id {};
std::string target {};
std::string description {};
// Multiple ranges can belong to one link, independently of text styling.
std::vector<TextRange> ranges {};
};
enum class TextControlKind : std::uint8_t
{
SingleLine,
MultiLine,
Password
};
struct TextControl
{
TextControlKind kind { TextControlKind::SingleLine };
std::string value {};
std::string default_value {};
};
struct Checkbox
{
bool checked { false };
bool default_checked { false };
};
struct RadioButton
{
RadioGroupId group {};
bool checked { false };
bool default_checked { false };
};
struct SelectOption
{
OptionId id {};
std::string label {};
bool disabled { false };
};
enum class SelectKind : std::uint8_t
{
Single,
Multiple
};
struct SelectControl
{
SelectKind kind { SelectKind::Single };
std::vector<SelectOption> options {};
std::vector<OptionId> selected {};
std::vector<OptionId> default_selected {};
};
enum class ButtonKind : std::uint8_t
{
Submit,
Reset
};
struct Button
{ ButtonKind kind { ButtonKind::Submit }; };
struct UnsupportedControl
{ std::string description {}; };
struct Control
{
ControlId id {};
std::optional<FormId> form {};
std::string label {};
std::string hint {};
std::optional<std::string> error {};
bool disabled { false };
bool read_only { false };
std::variant<TextControl, Checkbox, RadioButton, SelectControl, Button, UnsupportedControl> content {};
};
struct Form
{
FormId id {};
std::string label {};
};
struct Anchor
{
std::string name {};
ContentPoint point {};
};
struct DocumentMetadata
{
std::string title {};
std::string display_url {};
std::optional<std::string> language {};
};
// Producers build owned values. No DOM pointers, terminal codes or screen coordinates.
struct DocumentData
{
DocumentVersion version {};
DocumentMetadata metadata {};
std::vector<Block> blocks {};
std::vector<Link> links {};
std::vector<Control> controls {};
std::vector<Form> forms {};
std::vector<Anchor> anchors {};
};
class Document
{
public:
explicit Document( DocumentData data );
const DocumentData& data() const noexcept;
private:
std::shared_ptr<const DocumentData> data_;
};
} // namespace ricochet::tui
|