sparx 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +446 -0
- data/bin/sparx +133 -0
- data/editors/sublime-text/README.md +0 -0
- data/editors/sublime-text/package.json +13 -0
- data/editors/textmate/README.md +0 -0
- data/editors/vscode/README.md +0 -0
- data/editors/vscode/language-configuration.json +0 -0
- data/editors/vscode/package.json +33 -0
- data/editors/vscode/snippets.json +0 -0
- data/lib/sparx/constants.rb +71 -0
- data/lib/sparx/container_processor.rb +217 -0
- data/lib/sparx/image_processor.rb +267 -0
- data/lib/sparx/inline_processor.rb +204 -0
- data/lib/sparx/parser.rb +64 -0
- data/lib/sparx/version.rb +5 -0
- data/lib/sparx.rb +5 -0
- data/syntaxes/sparx.sublime-syntax +173 -0
- data/syntaxes/sparx.tmLanguage +431 -0
- data/syntaxes/sparx.tmLanguage.json +135 -0
- metadata +106 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 205c14fff56e350a5da9ded41bf7ed7a6deb90a03cf511f51647e59c1febda56
|
|
4
|
+
data.tar.gz: be9ed8508d53945c5610cd3d8aa4c79bb86305307676eec136284686c8da67ea
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c995d71f8f0c758c53c9eb0309fa3c693858e601a66078f99d9537df50604ca35a445491b3c486434df4dd6ef1c88ff505de85d0291a3bee6a2f8a0a770f68e2
|
|
7
|
+
data.tar.gz: c7188911afe4c0b8cbb1aa50919a885cc1a8382c8a06781c81da6d132a547df3d8fcf0889e4797c30d5a98f5a4f12830d4bdc7c90c5278d52e3e766d5b836f46
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Steven Garcia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# Sparx - Markup That Sparks Joy
|
|
2
|
+
|
|
3
|
+
**Markdown was revolutionary in 2004. It's 2025. Time for the next evolution.**
|
|
4
|
+
|
|
5
|
+
Born from the same "spark joy" philosophy as the **[Joys](https://github.com/activestylus/joys)** view engine, Sparx eliminates the daily frustrations that developers face, thanks to its clean syntax, semantic HTML and zero compromise on ergonomics and features.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The Problem With Markdown
|
|
10
|
+
|
|
11
|
+
After 20 years, we're still fighting the same battles:
|
|
12
|
+
- `**bold**` vs `__bold__` vs `*italic*` confusion
|
|
13
|
+
- Broken nesting in complex formatting
|
|
14
|
+
- HTML soup whenever you need anything semantic
|
|
15
|
+
- Copying URLs everywhere like it's 1999
|
|
16
|
+
- Zero support for modern responsive images
|
|
17
|
+
- Academic citations? Good luck.
|
|
18
|
+
|
|
19
|
+
**Sparx** solves every single one of these problems.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick Taste: Before & After
|
|
24
|
+
|
|
25
|
+
### The Old Way (Markdown)
|
|
26
|
+
```markdown
|
|
27
|
+
**Bold** and *italic* with [broken **nesting**](https://example.com).
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
<details>
|
|
32
|
+
<summary>Advanced Content</summary>
|
|
33
|
+
More HTML soup when you need structure.
|
|
34
|
+
</details>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### The Sparx Way
|
|
38
|
+
```sparx
|
|
39
|
+
*[Bold] and /[italic] with perfect */[nesting]https://example.com.
|
|
40
|
+
|
|
41
|
+
i[Image]@cdn/image.jpg
|
|
42
|
+
|
|
43
|
+
+[Advanced Content]{
|
|
44
|
+
Clean structure without HTML soup.
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@cdn: https://cdn.example.com/very/long/path/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Same result. Half the characters. Semantic HTML throughout. URLs defined once.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Installation & Usage
|
|
55
|
+
|
|
56
|
+
### With Joys Framework
|
|
57
|
+
```ruby
|
|
58
|
+
gem install sparx
|
|
59
|
+
|
|
60
|
+
class ArticlePage < Joys::Component
|
|
61
|
+
def initialize(content)
|
|
62
|
+
@html = Sparx.parse(content)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def template
|
|
66
|
+
article do
|
|
67
|
+
unsafe_raw @html # Already sanitized and semantic
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Standalone
|
|
74
|
+
```ruby
|
|
75
|
+
gem install sparx
|
|
76
|
+
require 'sparx'
|
|
77
|
+
|
|
78
|
+
html = Sparx.parse(your_content)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Core Philosophy: Consistency Over Convention
|
|
84
|
+
|
|
85
|
+
### Logical, Predictable Formatting
|
|
86
|
+
|
|
87
|
+
**Input:**
|
|
88
|
+
```
|
|
89
|
+
*[bold] /[italic] -[deleted] `code`
|
|
90
|
+
*/[bold italic] -*[bold deleted]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Output:**
|
|
94
|
+
```html
|
|
95
|
+
<strong>bold</strong> <em>italic</em> <del>deleted</del> <code>code</code>
|
|
96
|
+
<strong><em>bold italic</em></strong> <del><strong>bold deleted</strong></del>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Every formatting element follows the same pattern: `symbol[content]`. No exceptions. No edge cases. Perfect nesting every time.
|
|
100
|
+
|
|
101
|
+
### Links That Don't Break Your Flow
|
|
102
|
+
|
|
103
|
+
**Input:**
|
|
104
|
+
```
|
|
105
|
+
[GitHub]https://github.com
|
|
106
|
+
[Docs|Complete documentation]@docs^
|
|
107
|
+
[Call us]tel:+1-555-0199
|
|
108
|
+
|
|
109
|
+
@docs: https://sparx.dev/docs
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Output:**
|
|
113
|
+
```html
|
|
114
|
+
<a href="https://github.com">GitHub</a>
|
|
115
|
+
<a href="https://sparx.dev/docs" title="Complete documentation" target="_blank">Docs</a>
|
|
116
|
+
<a href="tel:+1-555-0199">Call us</a>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Links, tooltips, targets, and protocols all work the same way. Define URLs once with `@references`, use them everywhere.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Where Sparx Shines: Complex Content
|
|
124
|
+
|
|
125
|
+
### Responsive Images (Finally!)
|
|
126
|
+
|
|
127
|
+
**Basic responsive images:**
|
|
128
|
+
```
|
|
129
|
+
i[Hero]hero.jpg 400w|hero@2x.jpg 800w|hero@3x.jpg 1200w
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Art direction with format fallbacks:**
|
|
133
|
+
```
|
|
134
|
+
src[>800px]desktop.{webp,jpg}
|
|
135
|
+
src[>400px]tablet.{webp,jpg}
|
|
136
|
+
i[Hero]mobile.jpg
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Output:**
|
|
140
|
+
```html
|
|
141
|
+
<picture>
|
|
142
|
+
<source srcset="desktop.webp" type="image/webp" media="(min-width: 800px)">
|
|
143
|
+
<source srcset="desktop.jpg" type="image/jpeg" media="(min-width: 800px)">
|
|
144
|
+
<source srcset="tablet.webp" type="image/webp" media="(min-width: 400px)">
|
|
145
|
+
<source srcset="tablet.jpg" type="image/jpeg" media="(min-width: 400px)">
|
|
146
|
+
<img src="mobile.jpg" alt="Hero">
|
|
147
|
+
</picture>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
One input. Perfect responsive HTML5. Try doing this in Markdown!
|
|
151
|
+
|
|
152
|
+
### Semantic Containers
|
|
153
|
+
|
|
154
|
+
You can create linkable sections with very simple syntax:
|
|
155
|
+
|
|
156
|
+
`$[dom-id] { fully parseable content }`
|
|
157
|
+
|
|
158
|
+
**Input:**
|
|
159
|
+
```
|
|
160
|
+
$[hero]{
|
|
161
|
+
# Welcome to */[the future]@site
|
|
162
|
+
|
|
163
|
+
>{
|
|
164
|
+
Finally, markup that doesn't make me want to scream.
|
|
165
|
+
- Every developer who tries Sparx
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.warning{
|
|
169
|
+
This will change how you think about markup.
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
+[Technical Details]{
|
|
173
|
+
:Performance: 100x faster parsing than traditional regex approaches
|
|
174
|
+
:Output: Clean, semantic HTML5 throughout
|
|
175
|
+
:DRY: URL references eliminate repetition
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@site: https://sparx.dev
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Output:**
|
|
183
|
+
```html
|
|
184
|
+
<section id="hero">
|
|
185
|
+
<h1>Welcome to <strong><em><a href="https://sparx.dev">the future</a></em></strong></h1>
|
|
186
|
+
<blockquote>
|
|
187
|
+
<p>Finally, markup that doesn't make me want to scream.</p>
|
|
188
|
+
<p>- Every developer who tries Sparx</p>
|
|
189
|
+
</blockquote>
|
|
190
|
+
<div class="warning">
|
|
191
|
+
<p>This will change how you think about markup.</p>
|
|
192
|
+
</div>
|
|
193
|
+
<details>
|
|
194
|
+
<summary>Technical Details</summary>
|
|
195
|
+
<dl>
|
|
196
|
+
<dt>Performance</dt><dd>100x faster parsing than traditional regex approaches</dd>
|
|
197
|
+
<dt>Output</dt><dd>Clean, semantic HTML5 throughout</dd>
|
|
198
|
+
<dt>DRY</dt><dd>URL references eliminate repetition</dd>
|
|
199
|
+
</dl>
|
|
200
|
+
</details>
|
|
201
|
+
</section>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Every element generates proper semantic HTML. Your accessibility audits will love you.
|
|
205
|
+
|
|
206
|
+
Links to these sections can then be easily created:
|
|
207
|
+
|
|
208
|
+
`[Go To Hero]#hero`
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Real-World Comparison
|
|
213
|
+
|
|
214
|
+
### Documentation Page: Markdown vs Sparx
|
|
215
|
+
|
|
216
|
+
**Markdown approach:**
|
|
217
|
+
```markdown
|
|
218
|
+
## API Authentication
|
|
219
|
+
|
|
220
|
+
> **Warning:** Never commit tokens to version control.
|
|
221
|
+
|
|
222
|
+
See our [security guide](https://docs.example.com/security) and
|
|
223
|
+
[API docs](https://docs.example.com/api).
|
|
224
|
+
|
|
225
|
+
<details>
|
|
226
|
+
<summary>Troubleshooting</summary>
|
|
227
|
+
|
|
228
|
+
**Token Issues:**
|
|
229
|
+
- Check for whitespace
|
|
230
|
+
- Verify token type
|
|
231
|
+
|
|
232
|
+
**Expired Tokens:**
|
|
233
|
+
- Tokens expire in 30 days
|
|
234
|
+
- Generate new ones [here](https://app.example.com/tokens)
|
|
235
|
+
|
|
236
|
+
</details>
|
|
237
|
+
|
|
238
|
+
References:
|
|
239
|
+
1. [OAuth 2.0](https://tools.ietf.org/html/rfc6749) - Official spec
|
|
240
|
+
2. [JWT Guide](https://jwt.io/introduction/) - Token format
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Sparx approach:**
|
|
244
|
+
```
|
|
245
|
+
## API Authentication
|
|
246
|
+
|
|
247
|
+
.warning{
|
|
248
|
+
**Never commit tokens to version control.**
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
See our [security guide]@docs/security and [API docs]@docs/api.
|
|
252
|
+
|
|
253
|
+
+[Troubleshooting]{
|
|
254
|
+
:Token Issues:{
|
|
255
|
+
- Check for whitespace
|
|
256
|
+
- Verify token type
|
|
257
|
+
}
|
|
258
|
+
:Expired Tokens:{
|
|
259
|
+
- Tokens expire in 30 days
|
|
260
|
+
- Generate new ones [here]@app/tokens
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
See the [OAuth 2.0 spec]:1 and [JWT guide]:2 for details.
|
|
265
|
+
|
|
266
|
+
@docs: https://docs.example.com
|
|
267
|
+
@app: https://app.example.com
|
|
268
|
+
|
|
269
|
+
1[OAuth 2.0]https://tools.ietf.org/html/rfc6749 "Official spec"
|
|
270
|
+
2[JWT Guide]https://jwt.io/introduction/ "Token format"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Result:** 16% shorter, semantic HTML throughout, DRY URLs, automatic citations.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Advanced Features
|
|
278
|
+
|
|
279
|
+
### Academic Citations
|
|
280
|
+
```
|
|
281
|
+
Research shows[significant performance gains]:1 in modern applications.
|
|
282
|
+
|
|
283
|
+
1[Performance Study]https://journal.example.com "Peer-reviewed research"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Automatic numbering, proper linking, and citation sections.
|
|
287
|
+
|
|
288
|
+
### Complex Lists
|
|
289
|
+
```
|
|
290
|
+
- Simple item
|
|
291
|
+
-{
|
|
292
|
+
## Complex item with full formatting
|
|
293
|
+
|
|
294
|
+
Complete *[markup support], [links]@docs, and more.
|
|
295
|
+
|
|
296
|
+
>{
|
|
297
|
+
Even blockquotes work perfectly inside lists.
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
- Back to simple
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Try nesting a blockquote inside a Markdown list item. I'll wait.
|
|
304
|
+
|
|
305
|
+
### Protocol Links
|
|
306
|
+
```
|
|
307
|
+
[Email us]mailto:hello@example.com
|
|
308
|
+
[Call support]tel:+1-555-0199
|
|
309
|
+
[Text us]sms:+1-555-0199
|
|
310
|
+
[Video chat]facetime:user@example.com
|
|
311
|
+
[Open in VS Code]vscode://file/path/to/file
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Built-in support for `tel:`, `mailto:`, `sms:`, `facetime:`, `skype:`, `whatsapp:`, `zoom:`, `spotify:`, `vscode:`, and `geo:` protocols.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Security: Safe Mode
|
|
319
|
+
|
|
320
|
+
Processing user-generated content? Sparx's safe mode protects against XSS while preserving all formatting functionality.
|
|
321
|
+
|
|
322
|
+
```ruby
|
|
323
|
+
# Safe mode ON - for user content
|
|
324
|
+
html = Sparx.parse(user_content, safe: true)
|
|
325
|
+
|
|
326
|
+
# Safe mode OFF - for trusted content
|
|
327
|
+
html = Sparx.parse(trusted_content)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Automatically escapes HTML tags, blocks dangerous protocols (`javascript:`, `data:`), and sanitizes attributes while preserving code blocks exactly as written.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
## Performance
|
|
334
|
+
|
|
335
|
+
Sparx prioritizes developer experience and semantic output over raw speed. While pure Markdown parsers like Redcarpet are faster for basic formatting, Sparx delivers significantly more functionality:
|
|
336
|
+
|
|
337
|
+
### Performance Benchmarks
|
|
338
|
+
|
|
339
|
+
Sparx balances speed with advanced functionality. While pure Markdown parsers prioritize raw throughput, Sparx generates semantic HTML5 with features impossible in traditional markup languages.
|
|
340
|
+
|
|
341
|
+
### Benchmark Results
|
|
342
|
+
|
|
343
|
+
*Tested on Ruby 3.3.5 (arm64-darwin23) across three document types*
|
|
344
|
+
|
|
345
|
+
#### Blog Post (746 characters)
|
|
346
|
+
|
|
347
|
+
| Parser | Speed (ops/sec) | Relative | Memory |
|
|
348
|
+
|--------|-----------------|----------|--------|
|
|
349
|
+
| Redcarpet | 257,851 | **1.0x** | <1 KB |
|
|
350
|
+
| **Sparx** | **4,576** | **56x slower** | **64 KB** |
|
|
351
|
+
| Kramdown | 2,207 | 117x slower | 3,520 KB |
|
|
352
|
+
| CommonMarker | 478 | 539x slower | 2,608 KB |
|
|
353
|
+
|
|
354
|
+
#### Technical Documentation (2,367 characters)
|
|
355
|
+
|
|
356
|
+
| Parser | Speed (ops/sec) | Relative | Memory |
|
|
357
|
+
|--------|-----------------|----------|--------|
|
|
358
|
+
| Redcarpet | 79,432 | **1.0x** | <1 KB|
|
|
359
|
+
| **Sparx** | **1,450** | **55x slower** | **<1 KB** |
|
|
360
|
+
| Kramdown | 840 | 95x slower | <1 KB |
|
|
361
|
+
| CommonMarker | 147 | 541x slower | 384 KB |
|
|
362
|
+
|
|
363
|
+
#### Novel Chapter (27,465 characters)
|
|
364
|
+
|
|
365
|
+
| Parser | Speed (ops/sec) | Relative | Memory |
|
|
366
|
+
|--------|-----------------|----------|--------|
|
|
367
|
+
| Redcarpet | 12,738 | **1.0x** | 48 KB |
|
|
368
|
+
| CommonMarker | 898 | 14x slower | 432 KB |
|
|
369
|
+
| **Sparx** | **457** | **28x slower** | **16 KB** |
|
|
370
|
+
| Kramdown | 290 | 44x slower | 16 KB |
|
|
371
|
+
|
|
372
|
+
### What These Numbers Mean
|
|
373
|
+
|
|
374
|
+
#### The Speed Tradeoff
|
|
375
|
+
- **Redcarpet wins on pure speed** but only handles basic Markdown
|
|
376
|
+
- **Sparx is 2x faster than Kramdown** while delivering dramatically more features
|
|
377
|
+
- **Performance scales well** with document size (28x slower vs 56x slower)
|
|
378
|
+
|
|
379
|
+
### Feature Comparison
|
|
380
|
+
|
|
381
|
+
| Feature | Redcarpet | Kramdown | CommonMarker | **Sparx** |
|
|
382
|
+
|---------|-----------|----------|--------------|---------------|
|
|
383
|
+
| Basic Markdown | ✅ | ✅ | ✅ | ✅ |
|
|
384
|
+
| Semantic HTML5 | ❌ | Limited | ❌ | **✅** |
|
|
385
|
+
| URL References | ❌ | ❌ | ❌ | **✅** |
|
|
386
|
+
| Responsive Images | ❌ | ❌ | ❌ | **✅** |
|
|
387
|
+
| Citations | ❌ | ❌ | ❌ | **✅** |
|
|
388
|
+
| Complex Containers | ❌ | Limited | ❌ | **✅** |
|
|
389
|
+
| Definition Lists | ❌ | ✅ | ❌ | **✅** |
|
|
390
|
+
| Perfect Nesting | ❌ | ❌ | ❌ | **✅** |
|
|
391
|
+
|
|
392
|
+
### Real-World Performance
|
|
393
|
+
|
|
394
|
+
For typical use cases, Sparx's parsing time is negligible:
|
|
395
|
+
|
|
396
|
+
- **Blog post** (746 chars): **0.22ms** to parse
|
|
397
|
+
- **Documentation** (2.3K chars): **0.69ms** to parse
|
|
398
|
+
- **Long content** (27K chars): **2.19ms** to parse
|
|
399
|
+
|
|
400
|
+
The development time saved by consistent syntax and semantic output far outweighs the microseconds spent parsing.
|
|
401
|
+
|
|
402
|
+
## Bottom Line
|
|
403
|
+
|
|
404
|
+
**Choose based on your needs:**
|
|
405
|
+
|
|
406
|
+
- **Need raw speed only?** Redcarpet is unbeatable for basic Markdown
|
|
407
|
+
- **Need features + reasonable speed?** Sparx delivers 2x better performance than Kramdown with 10x more functionality
|
|
408
|
+
- **Building semantic, accessible content?** Sparx is the only option that generates proper HTML5 throughout
|
|
409
|
+
|
|
410
|
+
*Performance measured in iterations per second. Higher is better. Memory usage measured as RSS delta during parsing.*
|
|
411
|
+
|
|
412
|
+
Conclusion: For most real-world use cases, the parsing time is negligible compared to the development time saved by cleaner syntax and semantic output.
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## When to Use Sparx
|
|
417
|
+
|
|
418
|
+
**Perfect for:**
|
|
419
|
+
- Technical documentation requiring semantic structure
|
|
420
|
+
- Content sites where HTML5 semantics matter
|
|
421
|
+
- Academic writing with proper citations
|
|
422
|
+
- Any project where Markdown feels limiting
|
|
423
|
+
- Teams tired of mixed HTML/Markdown soup
|
|
424
|
+
|
|
425
|
+
**Stick with Markdown when:**
|
|
426
|
+
- Writing simple blog posts without complex structure
|
|
427
|
+
- Your toolchain is deeply invested in Markdown
|
|
428
|
+
- Your team values familiarity over power
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## The Bottom Line
|
|
433
|
+
|
|
434
|
+
Markdown taught us that writing shouldn't require thinking about HTML. Sparx teaches us that **developers** shouldn't need to compromise on semantic markup quality.
|
|
435
|
+
|
|
436
|
+
It's 2025. Your markup language should be as sophisticated as your code.
|
|
437
|
+
|
|
438
|
+
**Does your current markup spark joy?**
|
|
439
|
+
|
|
440
|
+
```ruby
|
|
441
|
+
gem install sparx
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
*Sparx is part of the [Joys](https://github.com/activestylus/joys) ecosystem - Ruby tools that spark joy for developers.*
|
data/bin/sparx
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require_relative '../lib/sparx'
|
|
4
|
+
require 'listen'
|
|
5
|
+
|
|
6
|
+
# --- Configuration ---
|
|
7
|
+
WATCH_INTERVAL = 0.5 # Debounce interval for listen
|
|
8
|
+
|
|
9
|
+
# --- Helper Methods ---
|
|
10
|
+
|
|
11
|
+
def display_help
|
|
12
|
+
puts <<~HELP
|
|
13
|
+
Sparx CLI - Markup that sparks joy ✨
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
sparx convert <input.sx> [output.html] [--safe]
|
|
17
|
+
sparx watch <directory>
|
|
18
|
+
sparx init
|
|
19
|
+
sparx version
|
|
20
|
+
sparx help
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--safe, -s Enable safe mode for user-generated content
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
sparx convert blog.sx # Output to stdout
|
|
27
|
+
sparx convert blog.sx blog.html # Output to file
|
|
28
|
+
sparx convert comments.sx --safe # Safe mode for user content
|
|
29
|
+
sparx watch docs/ # Auto-convert files in docs/
|
|
30
|
+
HELP
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def process_file(file, output_file, safe_mode)
|
|
34
|
+
begin
|
|
35
|
+
content = File.read(file)
|
|
36
|
+
html = Sparx.parse(content, safe: safe_mode)
|
|
37
|
+
|
|
38
|
+
if output_file
|
|
39
|
+
File.write(output_file, html)
|
|
40
|
+
puts "✅ Converted #{File.basename(file)} -> #{File.basename(output_file)}"
|
|
41
|
+
else
|
|
42
|
+
# Output to stdout only if a single file is being processed in non-watch mode
|
|
43
|
+
puts html
|
|
44
|
+
end
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
puts "❌ Error processing #{file}: #{e.message}"
|
|
47
|
+
exit 1 unless output_file # Exit if in single-convert mode
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# --- Main Logic ---
|
|
52
|
+
|
|
53
|
+
command = ARGV[0] || 'help'
|
|
54
|
+
options = ARGV[1..-1] || []
|
|
55
|
+
|
|
56
|
+
# Parse common options
|
|
57
|
+
safe_mode = options.include?('--safe') || options.include?('-s')
|
|
58
|
+
options.delete('--safe')
|
|
59
|
+
options.delete('-s')
|
|
60
|
+
|
|
61
|
+
case command
|
|
62
|
+
when 'convert'
|
|
63
|
+
input_file = options[0]
|
|
64
|
+
output_file = options[1]
|
|
65
|
+
|
|
66
|
+
unless input_file && File.exist?(input_file)
|
|
67
|
+
puts "File not found: #{input_file}"
|
|
68
|
+
exit 1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
process_file(input_file, output_file, safe_mode)
|
|
72
|
+
|
|
73
|
+
when 'watch'
|
|
74
|
+
dir = options[0]
|
|
75
|
+
|
|
76
|
+
unless dir && File.directory?(dir)
|
|
77
|
+
puts "Directory not found or specified: #{dir}"
|
|
78
|
+
exit 1
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
puts "✨ Sparx Watch Mode: Watching #{dir} for changes..."
|
|
82
|
+
puts " (Press Ctrl+C to stop)"
|
|
83
|
+
|
|
84
|
+
# Set up the listener
|
|
85
|
+
listener = Listen.to(dir, only: /\.sx\z/i, latency: WATCH_INTERVAL) do |modified, added, removed|
|
|
86
|
+
|
|
87
|
+
# Process modified and added files
|
|
88
|
+
(modified + added).each do |file_path|
|
|
89
|
+
# Generate output path (e.g., input/doc.sx -> output/doc.html)
|
|
90
|
+
output_path = file_path.sub(/\.sx\z/i, '.html')
|
|
91
|
+
process_file(file_path, output_path, safe_mode)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Handle removed files (optional, but good practice)
|
|
95
|
+
removed.each do |file_path|
|
|
96
|
+
puts "🗑️ File removed: #{File.basename(file_path)}"
|
|
97
|
+
# You could add logic here to delete the corresponding HTML file
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
listener.start
|
|
102
|
+
sleep # Keep the main thread alive until Ctrl+C is pressed
|
|
103
|
+
|
|
104
|
+
when 'init'
|
|
105
|
+
sample_content = <<~SPARKDOWN
|
|
106
|
+
# Welcome to Sparx
|
|
107
|
+
|
|
108
|
+
This is a */[sample document] to get you started.
|
|
109
|
+
|
|
110
|
+
i[Sparx Logo]https://placehold.co/400x200/EEE/31343C?text=Sparx
|
|
111
|
+
|
|
112
|
+
+[Quick Start]{
|
|
113
|
+
- Install: \`gem install sparx\`
|
|
114
|
+
- Convert: \`sparx convert sample.sx\`
|
|
115
|
+
- Learn: https://sparx.dev/docs
|
|
116
|
+
}
|
|
117
|
+
SPARKDOWN
|
|
118
|
+
|
|
119
|
+
File.write('sample.sx', sample_content)
|
|
120
|
+
puts "Created sample.sx - try: sparx convert sample.sx"
|
|
121
|
+
|
|
122
|
+
when 'version'
|
|
123
|
+
# Assuming Sparx::VERSION is defined in your main library file
|
|
124
|
+
puts "Sparx #{Sparx::VERSION}"
|
|
125
|
+
|
|
126
|
+
when 'help', '--help', '-h'
|
|
127
|
+
display_help
|
|
128
|
+
|
|
129
|
+
else
|
|
130
|
+
puts "Unknown command: #{command}"
|
|
131
|
+
display_help
|
|
132
|
+
exit 1
|
|
133
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Sparx",
|
|
3
|
+
"details": "https://github.com/activestylus/sparx",
|
|
4
|
+
"description": "Sparx syntax highlighting for Sublime Text",
|
|
5
|
+
"author": "yourname",
|
|
6
|
+
"releases": [
|
|
7
|
+
{
|
|
8
|
+
"sublime_text": ">=3000",
|
|
9
|
+
"tags": true
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"version": "1.0.0"
|
|
13
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sparx",
|
|
3
|
+
"displayName": "Sparx Syntax Highlighting",
|
|
4
|
+
"description": "Syntax highlighting for Sparx markup language",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"publisher": "sparx",
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.60.0"
|
|
9
|
+
},
|
|
10
|
+
"categories": ["Programming Languages"],
|
|
11
|
+
"contributes": {
|
|
12
|
+
"languages": [{
|
|
13
|
+
"id": "sparx",
|
|
14
|
+
"aliases": ["Sparx", "sparx"],
|
|
15
|
+
"extensions": [".hug", ".sparx"],
|
|
16
|
+
"configuration": "./language-configuration.json"
|
|
17
|
+
}],
|
|
18
|
+
"grammars": [{
|
|
19
|
+
"language": "sparx",
|
|
20
|
+
"scopeName": "source.sparx",
|
|
21
|
+
"path": "../../syntaxes/sparx.tmLanguage.json"
|
|
22
|
+
}],
|
|
23
|
+
"themes": [{
|
|
24
|
+
"label": "Sparx Dark",
|
|
25
|
+
"uiTheme": "vs-dark",
|
|
26
|
+
"path": "../../themes/sparx-dark-color-theme.json"
|
|
27
|
+
}]
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/activestylus/sparx"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
File without changes
|